From ecf1ea6b6c605990ebd00f45bf47a68873d62de4 Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Fri, 24 Jan 2025 00:34:02 -0500 Subject: [PATCH 01/91] update diamond bard for new talisman --- mod/FusionJokers.lua | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/mod/FusionJokers.lua b/mod/FusionJokers.lua index 4f95aff..dc5680c 100644 --- a/mod/FusionJokers.lua +++ b/mod/FusionJokers.lua @@ -117,6 +117,10 @@ local to_number = to_number or function(num) return num end +local to_big = to_big or function(num) + return num +end + local function has_joker(val, start_pos) if not start_pos then start_pos = 0 @@ -545,11 +549,11 @@ function SMODS.INIT.FusionJokers() function SMODS.Jokers.j_diamond_bard.calculate(card, context) if context.individual and context.cardarea == G.play and context.other_card:is_suit('Diamonds') then - G.GAME.dollar_buffer = (G.GAME.dollar_buffer or 0) + card.ability.extra.money + G.GAME.dollar_buffer = (G.GAME.dollar_buffer or 0) + to_big(card.ability.extra.money) G.E_MANAGER:add_event(Event({func = (function() G.GAME.dollar_buffer = 0; return true end)})) return { dollars = card.ability.extra.money, - mult = card.ability.extra.mult * (1 + math.floor(G.GAME.dollars / card.ability.extra.money_threshold)), + mult = card.ability.extra.mult * (1 + math.floor(to_number(G.GAME.dollars) / card.ability.extra.money_threshold)), card = card } end From 06d5413b7b87e07daabd72eccff47bb50f7e187e Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Fri, 24 Jan 2025 00:57:00 -0500 Subject: [PATCH 02/91] fixed context for several jokers --- mod/FusionJokers.lua | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/mod/FusionJokers.lua b/mod/FusionJokers.lua index dc5680c..0913d97 100644 --- a/mod/FusionJokers.lua +++ b/mod/FusionJokers.lua @@ -714,7 +714,7 @@ function SMODS.INIT.FusionJokers() end function SMODS.Jokers.j_big_bang.calculate(card, context) - if context.cardarea == G.jokers and not context.after and not context.before and context.scoring_name then + if context.cardarea == G.jokers and context.joker_main and context.scoring_name then local mult_val = 1 + card.ability.extra.Xmult * (G.GAME.hands[context.scoring_name].level + G.GAME.hands[context.scoring_name].played) return { message = localize{type='variable',key='a_xmult',vars={mult_val}}, @@ -791,7 +791,7 @@ function SMODS.INIT.FusionJokers() end)})) end - if context.cardarea == G.jokers and card.ability.mult > 0 and not context.after and not context.before then + if context.cardarea == G.jokers and card.ability.mult > 0 and context.joker_main then return { message = localize{type='variable',key='a_mult',vars={card.ability.mult}}, mult_mod = card.ability.mult @@ -888,7 +888,7 @@ function SMODS.INIT.FusionJokers() end - if context.cardarea == G.jokers and not context.after and not context.before then + if context.cardarea == G.jokers and context.joker_main then if card.ability.extra.side == "mult" then return { message = localize{type='variable',key='a_mult',vars={card.ability.extra.mult}}, @@ -994,7 +994,7 @@ function SMODS.INIT.FusionJokers() end end - if context.cardarea == G.jokers and not context.after and not context.before then + if context.cardarea == G.jokers and context.joker_main then local x = 0 for i = 1, #G.jokers.cards do if G.jokers.cards[i].ability.set == 'Joker' then x = x + 1 end @@ -1091,7 +1091,7 @@ function SMODS.INIT.FusionJokers() end end - if context.cardarea == G.jokers and not context.after and not context.before then + if context.cardarea == G.jokers and context.joker_main then local mult = card.ability.mult * G.GAME.current_round.discards_left return { message = localize{type='variable',key='a_mult',vars={mult}}, @@ -1184,7 +1184,7 @@ function SMODS.INIT.FusionJokers() end end - if context.cardarea == G.jokers and not context.before and not context.after then + if context.cardarea == G.jokers and context.joker_main then return { message = localize{type='variable',key='a_xmult',vars={card.ability.extra.total}}, Xmult_mod = card.ability.extra.total From 082014de2841d478464ecec3b2cd55c530eecaf5 Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Fri, 24 Jan 2025 01:06:37 -0500 Subject: [PATCH 03/91] added old talisman as conflict --- mod/FusionJokers.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/mod/FusionJokers.lua b/mod/FusionJokers.lua index 0913d97..27233e4 100644 --- a/mod/FusionJokers.lua +++ b/mod/FusionJokers.lua @@ -6,6 +6,7 @@ --- BADGE_COLOUR: B26CBB --- PRIORITY: -10000 --- PREFIX: fuse +--- CONFLICTS: [Talisman<=2.0] ---------------------------------------------- ------------MOD CODE ------------------------- From 1b6b56cac31a39c1556861c134609d7a9ad01a2c Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Fri, 24 Jan 2025 01:15:26 -0500 Subject: [PATCH 04/91] readme update (i don't think more jokers are coming at this point :v) --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 73e9504..094df83 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ When you have a fusable joker, pressing on it will show a **"fuse" button**. Whe Showcase of jokers tab 5 -The are a total of 15 fusions added in the mod, with more coming in the future! +There are a total of 15 fusions added in the mod! You can find a list of their abilities, as well as the jokers needed to make them, in this link: https://itayfeder.github.io/Fusion-Jokers/ @@ -51,4 +51,5 @@ Developers can call the function `FusionJokers.fusions:add_fusion()` to register ## 🎉 Credits -- The mod was written by [**Itayfeder**](https://github.com/stars/itayfeder/lists/balatro-modding), with art created by [**Lyman**](https://github.com/spikeof2010) +- The original mod was written by [**Itayfeder**](https://github.com/stars/itayfeder/lists/balatro-modding), with art created by [**Lyman**](https://github.com/spikeof2010) +- [**elbe**](https://github.com/lshtech) did several patches to handle breaking changes in Steamodded, as well as various other features From f2d63f46fdccc7e7c71565d71ce3f0966fabf81e Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Fri, 24 Jan 2025 01:32:25 -0500 Subject: [PATCH 05/91] oh yeah fix the stone card thing --- mod/FusionJokers.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mod/FusionJokers.lua b/mod/FusionJokers.lua index 27233e4..9128a2a 100644 --- a/mod/FusionJokers.lua +++ b/mod/FusionJokers.lua @@ -750,7 +750,7 @@ function SMODS.INIT.FusionJokers() function SMODS.Jokers.j_dynamic_duo.calculate(card, context) if context.individual and context.cardarea == G.play and - not context.other_card:is_face() then + not context.other_card:is_face() and not SMODS.has_no_rank(context.other_card) then return { mult = card.ability.extra.mult, chips = card.ability.extra.chips, From ecaf911962db9c55cf471a54c3bf210a75301f85 Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Fri, 24 Jan 2025 01:34:30 -0500 Subject: [PATCH 06/91] that's a bettercalc function, so dependency --- mod/FusionJokers.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/mod/FusionJokers.lua b/mod/FusionJokers.lua index 9128a2a..0286e5d 100644 --- a/mod/FusionJokers.lua +++ b/mod/FusionJokers.lua @@ -6,6 +6,7 @@ --- BADGE_COLOUR: B26CBB --- PRIORITY: -10000 --- PREFIX: fuse +--- DEPENDENCIES: [Steamodded>=1.0.0~ALPHA-1307d] --- CONFLICTS: [Talisman<=2.0] ---------------------------------------------- ------------MOD CODE ------------------------- From 78d95f0c18b132fb69a559713e5bc956601d3ed1 Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Mon, 24 Mar 2025 17:24:25 -0400 Subject: [PATCH 07/91] Block fusion ingredience from appearing if fusion is present --- mod/FusionJokers.lua | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/mod/FusionJokers.lua b/mod/FusionJokers.lua index 0286e5d..644c4dc 100644 --- a/mod/FusionJokers.lua +++ b/mod/FusionJokers.lua @@ -81,6 +81,23 @@ FusionJokers.fusions = { }, result_joker = "j_camping_trip", cost = 10 }, } +for _, fusion in ipairs(FusionJokers.fusions) do + local fused = fusion.result_joker + + for _, component in ipairs(fusion.jokers) do + local component_name = component.name + + SMODS.Joker:take_ownership(component_name, { + in_pool = function(self, args) + if #SMODS.find_card('j_showman') > 0 then return true end -- Allow finding copies if Showman is present + if #SMODS.find_card(fused) > 0 then return false end -- If the fused Joker exists, remove both components + return true + end + }, true) -- silent | suppresses mod badge + end +end + + local rarity = SMODS.Rarity{ key = "fusion", loc_txt = { From b9072b3917d76334f25011778386857d7c60f83a Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Mon, 24 Mar 2025 17:34:12 -0400 Subject: [PATCH 08/91] added a config about it --- README.md | 5 +++++ mod/FusionJokers.lua | 3 +++ mod/config.lua | 3 +++ mod/configui.lua | 11 +++++++++++ 4 files changed, 22 insertions(+) create mode 100644 mod/config.lua create mode 100644 mod/configui.lua diff --git a/README.md b/README.md index 094df83..3fc7ce2 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ - [About](#about) - [How to Download](#how_to_download) +- [Config](#config) - [Fusion API (For Developers)](#fusion_api) - [Credits](#credits) @@ -43,6 +44,10 @@ You can find a list of their abilities, as well as the jokers needed to make the - Download the latest release of Fusion Jokers - Extract the downloaded mod to the Mods folder (at %appdata%/Balatro/Mods) +## ⚙️ Config + +- '''Block used components from reappearing''': Jokers used in a fusion cannot reappear if the fusion is present. Currently only applies to Fusion Jokers fusions; cross-mod functionality is planned. Default true. + ## ➕ Fusion API (For Developers) The mod adds a way for other developers to create their own fusions! diff --git a/mod/FusionJokers.lua b/mod/FusionJokers.lua index 644c4dc..ba310ba 100644 --- a/mod/FusionJokers.lua +++ b/mod/FusionJokers.lua @@ -81,6 +81,9 @@ FusionJokers.fusions = { }, result_joker = "j_camping_trip", cost = 10 }, } +FusionJokers.fusionconfig = SMODS.current_mod.config +SMODS.load_file('configui.lua')() + for _, fusion in ipairs(FusionJokers.fusions) do local fused = fusion.result_joker diff --git a/mod/config.lua b/mod/config.lua new file mode 100644 index 0000000..a2dda85 --- /dev/null +++ b/mod/config.lua @@ -0,0 +1,3 @@ +return { + ["block_components"] = true, +} \ No newline at end of file diff --git a/mod/configui.lua b/mod/configui.lua new file mode 100644 index 0000000..a90fed8 --- /dev/null +++ b/mod/configui.lua @@ -0,0 +1,11 @@ +SMODS.current_mod.config_tab = function() + return {n = G.UIT.ROOT, config = {r = 0.1, minw = 8, minh = 6, align = "tl", padding = 0.2, colour = G.C.BLACK}, nodes = { + {n = G.UIT.C, config = {minw=1, minh=1, align = "tl", colour = G.C.CLEAR, padding = 0.15}, nodes = { + create_toggle({ + label = "Block used components from reappearing", + ref_table = FusionJokers.fusionconfig, + ref_value = 'block_components', + }), + }} + }} +end \ No newline at end of file From 7f902c2cfcdf4b0d6f008e74d08116e29cc4f186 Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Mon, 24 Mar 2025 17:45:05 -0400 Subject: [PATCH 09/91] ... make the config *functional*, minty --- mod/FusionJokers.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/mod/FusionJokers.lua b/mod/FusionJokers.lua index ba310ba..d0bebd9 100644 --- a/mod/FusionJokers.lua +++ b/mod/FusionJokers.lua @@ -93,6 +93,7 @@ for _, fusion in ipairs(FusionJokers.fusions) do SMODS.Joker:take_ownership(component_name, { in_pool = function(self, args) if #SMODS.find_card('j_showman') > 0 then return true end -- Allow finding copies if Showman is present + if not FusionJokers.fusionconfig.block_components then return true end --If the option is disabled, don't do the check if #SMODS.find_card(fused) > 0 then return false end -- If the fused Joker exists, remove both components return true end From 9d0441b2796da1fe39053fd1ff0572e2b5ea5a00 Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Sun, 20 Apr 2025 13:04:14 -0400 Subject: [PATCH 10/91] update so many things to modern standards --- mod/FusionJokers.json | 19 + mod/FusionJokers.lua | 1036 ++----------------------------- mod/assets/1x/j_flip_flop.png | Bin 1335 -> 23470 bytes mod/assets/2x/j_flip_flop.png | Bin 1779 -> 23688 bytes mod/jokers/bigbang.lua | 50 ++ mod/jokers/campingtrip.lua | 66 ++ mod/jokers/clubwizard.lua | 50 ++ mod/jokers/collectiblechaos.lua | 68 ++ mod/jokers/commercialdriver.lua | 70 +++ mod/jokers/dementiajoker.lua | 70 +++ mod/jokers/diamondbard.lua | 57 ++ mod/jokers/dynamicduo.lua | 53 ++ mod/jokers/flagbearer.lua | 82 +++ mod/jokers/flipflop.lua | 135 ++++ mod/jokers/goldenegg.lua | 57 ++ mod/jokers/heartpaladin.lua | 64 ++ mod/jokers/moonmarauder.lua | 117 ++++ mod/jokers/royaldecree.lua | 61 ++ mod/jokers/spadearcher.lua | 69 ++ mod/jokers/staroracle.lua | 68 ++ mod/jokers/uncannyface.lua | 59 ++ mod/localization/en-us.lua | 189 ++++++ 22 files changed, 1465 insertions(+), 975 deletions(-) create mode 100644 mod/FusionJokers.json create mode 100644 mod/jokers/bigbang.lua create mode 100644 mod/jokers/campingtrip.lua create mode 100644 mod/jokers/clubwizard.lua create mode 100644 mod/jokers/collectiblechaos.lua create mode 100644 mod/jokers/commercialdriver.lua create mode 100644 mod/jokers/dementiajoker.lua create mode 100644 mod/jokers/diamondbard.lua create mode 100644 mod/jokers/dynamicduo.lua create mode 100644 mod/jokers/flagbearer.lua create mode 100644 mod/jokers/flipflop.lua create mode 100644 mod/jokers/goldenegg.lua create mode 100644 mod/jokers/heartpaladin.lua create mode 100644 mod/jokers/moonmarauder.lua create mode 100644 mod/jokers/royaldecree.lua create mode 100644 mod/jokers/spadearcher.lua create mode 100644 mod/jokers/staroracle.lua create mode 100644 mod/jokers/uncannyface.lua create mode 100644 mod/localization/en-us.lua diff --git a/mod/FusionJokers.json b/mod/FusionJokers.json new file mode 100644 index 0000000..c248312 --- /dev/null +++ b/mod/FusionJokers.json @@ -0,0 +1,19 @@ +{ + "id": "FusionJokers", + "name": "Fusion Jokers", + "display_name": "Fusion Jokers", + "author": ["itayfeder", "Lyman"], + "description": "Adds the ability to fuse jokers into special new jokers!", + "prefix": "fuse", + "main_file": "FusionJokers.lua", + "priority": -10000, + "badge_colour": "B26CBB", + "badge_text_colour": "FFFFFF", + "version": "1.0.0", + "dependencies": [ + "Steamodded (>=1.0.0~ALPHA-1307d)", + ], + "conflicts": [ + "Talisman (<=2.0)", + ], +} \ No newline at end of file diff --git a/mod/FusionJokers.lua b/mod/FusionJokers.lua index d0bebd9..2156724 100644 --- a/mod/FusionJokers.lua +++ b/mod/FusionJokers.lua @@ -1,17 +1,3 @@ ---- STEAMODDED HEADER ---- MOD_NAME: Fusion Jokers ---- MOD_ID: FusionJokers ---- MOD_AUTHOR: [itayfeder, Lyman] ---- MOD_DESCRIPTION: Adds the ability to fuse jokers into special new jokers! ---- BADGE_COLOUR: B26CBB ---- PRIORITY: -10000 ---- PREFIX: fuse ---- DEPENDENCIES: [Steamodded>=1.0.0~ALPHA-1307d] ---- CONFLICTS: [Talisman<=2.0] ----------------------------------------------- -------------MOD CODE ------------------------- - - G.localization.misc.dictionary["k_fusion"] = "Fusion" G.localization.misc.dictionary["k_flipped_ex"] = "Flipped!" G.localization.misc.dictionary["k_copied_ex"] = "Cloned!" @@ -22,63 +8,63 @@ FusionJokers.fusions = { { jokers = { { name = "j_greedy_joker", carry_stat = nil, extra_stat = false }, { name = "j_rough_gem", carry_stat = nil, extra_stat = false } - }, result_joker = "j_diamond_bard", cost = 12 }, + }, result_joker = "j_fuse_diamond_bard", cost = 12 }, { jokers = { { name = "j_lusty_joker", carry_stat = nil, extra_stat = false }, { name = "j_bloodstone", carry_stat = nil, extra_stat = false } - }, result_joker = "j_heart_paladin", cost = 12 }, + }, result_joker = "j_fuse_heart_paladin", cost = 12 }, { jokers = { { name = "j_wrathful_joker", carry_stat = nil, extra_stat = false }, { name = "j_arrowhead", carry_stat = nil, extra_stat = false } - }, result_joker = "j_spade_archer", cost = 12 }, + }, result_joker = "j_fuse_spade_archer", cost = 12 }, { jokers = { { name = "j_gluttenous_joker", carry_stat = nil, extra_stat = false }, { name = "j_onyx_agate", carry_stat = nil, extra_stat = false } - }, result_joker = "j_club_wizard", cost = 12 }, + }, result_joker = "j_fuse_club_wizard", cost = 12 }, { jokers = { { name = "j_supernova", carry_stat = nil, extra_stat = false }, { name = "j_constellation", carry_stat = nil, extra_stat = false } - }, result_joker = "j_big_bang", cost = 11 }, + }, result_joker = "j_fuse_big_bang", cost = 11 }, { jokers = { { name = "j_even_steven", carry_stat = nil, extra_stat = false }, { name = "j_odd_todd", carry_stat = nil, extra_stat = false } - }, result_joker = "j_dynamic_duo", cost = 8 }, + }, result_joker = "j_fuse_dynamic_duo", cost = 8 }, { jokers = { { name = "j_flash", carry_stat = "mult", extra_stat = false }, { name = "j_chaos", carry_stat = nil, extra_stat = false } - }, result_joker = "j_collectible_chaos_card", cost = 9 }, + }, result_joker = "j_fuse_collectible_chaos_card", cost = 9 }, { jokers = { { name = "j_juggler", carry_stat = nil, extra_stat = false }, { name = "j_drunkard", carry_stat = nil, extra_stat = false } - }, result_joker = "j_flip_flop", cost = 9 }, + }, result_joker = "j_fuse_flip_flop", cost = 9 }, { jokers = { { name = "j_business", carry_stat = nil, extra_stat = false }, { name = "j_reserved_parking", carry_stat = nil, extra_stat = false } - }, result_joker = "j_royal_decree", cost = 10 }, + }, result_joker = "j_fuse_royal_decree", cost = 10 }, { jokers = { { name = "j_abstract", carry_stat = nil, extra_stat = false }, { name = "j_riff_raff", carry_stat = nil, extra_stat = false } - }, result_joker = "j_dementia_joker", cost = 8 }, + }, result_joker = "j_fuse_dementia_joker", cost = 8 }, { jokers = { { name = "j_egg", carry_stat = "extra_value", extra_stat = false }, { name = "j_golden", carry_stat = nil, extra_stat = false } - }, result_joker = "j_golden_egg", cost = 10 }, + }, result_joker = "j_fuse_golden_egg", cost = 10 }, { jokers = { { name = "j_banner", carry_stat = nil, extra_stat = false }, { name = "j_green_joker", carry_stat = "mult", extra_stat = false } - }, result_joker = "j_flag_bearer", cost = 9 }, + }, result_joker = "j_fuse_flag_bearer", cost = 9 }, { jokers = { { name = "j_scary_face", carry_stat = nil, extra_stat = false }, { name = "j_smiley", carry_stat = nil, extra_stat = false } - }, result_joker = "j_uncanny_face", cost = 8 }, + }, result_joker = "j_fuse_uncanny_face", cost = 8 }, { jokers = { { name = "j_ride_the_bus", carry_stat = nil, extra_stat = false }, { name = "j_drivers_license", carry_stat = nil, extra_stat = false } - }, result_joker = "j_commercial_driver", cost = 8 }, + }, result_joker = "j_fuse_commercial_driver", cost = 8 }, { jokers = { { name = "j_hiker", carry_stat = nil, extra_stat = false }, { name = "j_dusk", carry_stat = nil, extra_stat = false } - }, result_joker = "j_camping_trip", cost = 10 }, + }, result_joker = "j_fuse_camping_trip", cost = 10 }, } FusionJokers.fusionconfig = SMODS.current_mod.config @@ -102,16 +88,8 @@ for _, fusion in ipairs(FusionJokers.fusions) do end -local rarity = SMODS.Rarity{ +SMODS.Rarity{ key = "fusion", - loc_txt = { - name = "Fusion", - text = { - "Can only be obtained", - "through {C:attention}Fusing{}", - "two specific Jokers" - } - }, default_weight = 0, badge_colour = HEX("F7D762"), pools = {["Joker"] = false}, @@ -122,20 +100,40 @@ local rarity = SMODS.Rarity{ local SMODS_Joker_inject=SMODS.Joker.inject SMODS.Joker.inject =function(self) - if self.rarity == 5 then - self.rarity = rarity.key + if self.rarity == 5 or self.rarity == "fusion" then + self.rarity = "fuse_fusion" end SMODS_Joker_inject(self) end function FusionJokers.fusions:add_fusion(joker1, carry_stat1, extra1, joker2, carry_stat2, extra2, result_joker, cost, merged_stat, merge_stat1, merge_stat2, merge_extra) - table.insert(self, + table.insert(self, { jokers = { { name = joker1, carry_stat = carry_stat1, extra_stat = extra1, merge_stat = merge_stat1 }, { name = joker2, carry_stat = carry_stat2, extra_stat = extra2, merge_stat = merge_stat2 } }, result_joker = result_joker, cost = cost, merged_stat = merged_stat, merge_extra = merge_extra }) end +SMODS.load_file('jokers/diamondbard.lua')() +SMODS.load_file('jokers/heartpaladin.lua')() +SMODS.load_file('jokers/spadearcher.lua')() +SMODS.load_file('jokers/clubwizard.lua')() +if (SMODS.Mods["SixSuits"] or {}).can_load then + SMODS.load_file('jokers/moonmarauder.lua')() + SMODS.load_file('jokers/staroracle.lua')() +end +SMODS.load_file('jokers/bigbang.lua')() +SMODS.load_file('jokers/dynamicduo.lua')() +SMODS.load_file('jokers/collectiblechaos.lua')() +SMODS.load_file('jokers/flipflop.lua')() +SMODS.load_file('jokers/royaldecree.lua')() +SMODS.load_file('jokers/dementiajoker.lua')() +SMODS.load_file('jokers/goldenegg.lua')() +SMODS.load_file('jokers/flagbearer.lua')() +SMODS.load_file('jokers/uncannyface.lua')() +SMODS.load_file('jokers/commercialdriver.lua')() +SMODS.load_file('jokers/campingtrip.lua')() + local to_number = to_number or function(num) return num end @@ -180,7 +178,7 @@ function Card:can_fuse_card() return true end end - end + end return false end @@ -191,7 +189,7 @@ function Card:get_card_fusion() return fusion end end - end + end return nil end @@ -308,7 +306,7 @@ function Card:fuse_card() --G.jokers.cards[pos]:start_dissolve({G.C.GOLD}) G.jokers.cards[pos.pos]:remove() end - + delay(0.3) j_fusion:add_to_deck() @@ -344,7 +342,7 @@ G.FUNCS.fuse_card = function(e) end G.FUNCS.can_fuse_card = function(e) - if e.config.ref_table:can_fuse_card() then + if e.config.ref_table:can_fuse_card() then e.config.colour = G.C.DARK_EDITION e.config.button = 'fuse_card' else @@ -358,11 +356,11 @@ G.FUNCS.can_fuse_card = function(e) local use_and_sell_buttonsref = G.UIDEF.use_and_sell_buttons function G.UIDEF.use_and_sell_buttons(card) local retval = use_and_sell_buttonsref(card) - + if card.area and card.area.config.type == 'joker' and card.ability.set == 'Joker' and card.ability.fusion then - local fuse = + local fuse = {n=G.UIT.C, config={align = "cr"}, nodes={ - + {n=G.UIT.C, config={ref_table = card, align = "cr",maxw = 1.25, padding = 0.1, r=0.08, minw = 1.25, hover = true, shadow = true, colour = G.C.GOLD, one_press = true, button = 'sell_card', func = 'can_fuse_card'}, nodes={ {n=G.UIT.B, config = {w=0.1,h=0.6}}, {n=G.UIT.C, config={align = "tm"}, nodes={ @@ -402,42 +400,35 @@ function G.UIDEF.card_h_popup(card) return retval end + + +local flip = { + x = 0, + y = 0 +} +local flop = { + x = 0, + y = 1 +} local updateref = Card.update function Card:update(dt) updateref(self, dt) if G.STAGE == G.STAGES.RUN then - if self.ability.name == "Flip-Flop" then - if self.ability.extra.side == "mult" then - if (self.config.center.atlas ~= "j_flip_flop" or G.localization.descriptions["Joker"]["j_flip_flop"] ~= G.localization.descriptions["Joker"]["j_flip_flop_mult"]) then - G.localization.descriptions["Joker"]["j_flip_flop"] = G.localization.descriptions["Joker"]["j_flip_flop_mult"] - self.config.center.atlas = "j_flip_flop" - self:set_sprites(self.config.center) - end - else - if (self.config.center.atlas ~= "j_flop_flip" or G.localization.descriptions["Joker"]["j_flip_flop"] ~= G.localization.descriptions["Joker"]["j_flip_flop_chips"]) then - G.localization.descriptions["Joker"]["j_flip_flop"] = G.localization.descriptions["Joker"]["j_flip_flop_chips"] - self.config.center.atlas = "j_flop_flip" - self:set_sprites(self.config.center) - end - end - end - - if self:get_card_fusion() ~= nil then self.ability.fusion = self.ability.fusion or {} local my_fusion = self:get_card_fusion() self.fusion_cost = my_fusion.cost - if self:can_fuse_card() and not self.ability.fusion.jiggle then + if self:can_fuse_card() and not self.ability.fusion.jiggle then juice_card_until(self, function(card) return (card:can_fuse_card()) end, true) self.ability.fusion.jiggle = true end - - if not self:can_fuse_card() and self.ability.fusion.jiggle then + + if not self:can_fuse_card() and self.ability.fusion.jiggle then self.ability.fusion.jiggle = false end end @@ -476,7 +467,7 @@ function Card:remove_from_deck(from_debuff) G.GAME.current_round.free_rerolls = G.GAME.current_round.free_rerolls - 1 calculate_reroll_cost(true) end - + if self.ability.name == 'Flip-Flop' then if self.ability.extra.side == "mult" then G.hand:change_size(-self.ability.extra.hands) @@ -502,17 +493,6 @@ function new_round() calculate_reroll_cost(true) end - -local calculate_dollar_bonusref = Card.calculate_dollar_bonus -function Card:calculate_dollar_bonus() - if self.ability.set == "Joker" then - if self.ability.name == 'Golden Egg' then - return self.ability.extra.dollars - end - end - return calculate_dollar_bonusref(self) -end - -- local shatterref = Card.shatter -- function Card:shatter() -- G.E_MANAGER:add_event(Event({ @@ -526,7 +506,7 @@ end -- end -- return true end) -- })) - + -- shatterref(self) -- end @@ -534,897 +514,3 @@ end - -function SMODS.INIT.FusionJokers() - local mod_id = "FusionJokers" - local mod_obj = SMODS.findModByID(mod_id) - - table.insert(G.P_JOKER_RARITY_POOLS, {}) - table.insert(G.C.RARITY, HEX("F7D762")) - - loc_colour("mult", nil) - G.ARGS.LOC_COLOURS["fusion"] = G.C.RARITY[5] - - - - local diamond_bard_def = { - name = "Diamond Bard", - text = { - "Played cards with {C:diamonds}Diamond{} suit give", - "{C:money}$#1#{}, as well as {C:mult}+#2#{} Mult for every ", - "{C:money}$#3#{} you have when scored", - "{C:inactive}(#4# + #5#)" - } - } - - local diamond_bard = SMODS.Joker:new("Diamond Bard", "diamond_bard", {extra = { - money_threshold = 20, mult = 4, money = 1, joker1 = "j_greedy_joker", joker2 = "j_rough_gem" - }}, { x = 0, y = 0 }, diamond_bard_def, rarity.key, 12, true, false, true, true) - SMODS.Sprite:new("j_diamond_bard", mod_obj.path, "j_diamond_bard.png", 71, 95, "asset_atli"):register(); - diamond_bard:register() - - function SMODS.Jokers.j_diamond_bard.loc_def(card) - return {card.ability.extra.money, card.ability.extra.mult, card.ability.extra.money_threshold, - localize{type = 'name_text', key = card.ability.extra.joker1, set = 'Joker'}, - localize{type = 'name_text', key = card.ability.extra.joker2, set = 'Joker'}} - end - - function SMODS.Jokers.j_diamond_bard.calculate(card, context) - if context.individual and context.cardarea == G.play and - context.other_card:is_suit('Diamonds') then - G.GAME.dollar_buffer = (G.GAME.dollar_buffer or 0) + to_big(card.ability.extra.money) - G.E_MANAGER:add_event(Event({func = (function() G.GAME.dollar_buffer = 0; return true end)})) - return { - dollars = card.ability.extra.money, - mult = card.ability.extra.mult * (1 + math.floor(to_number(G.GAME.dollars) / card.ability.extra.money_threshold)), - card = card - } - end - end - - - - local heart_paladin_def = { - name = "Heart Paladin", - text = { - "Played cards with {C:hearts}Heart{} suit give", - "{X:mult,C:white}X#1#{} Mult when scored.", - "{C:green}#2# in #3#{} chance to re-trigger", - "{C:inactive}(#4# + #5#)" - } - } - - local heart_paladin = SMODS.Joker:new("Heart Paladin", "heart_paladin", {extra = { - odds = 3, Xmult = 1.5, joker1 = "j_lusty_joker", joker2 = "j_bloodstone" - }}, { x = 0, y = 0 }, heart_paladin_def, rarity.key, 12, true, false, true, true) - SMODS.Sprite:new("j_heart_paladin", mod_obj.path, "j_heart_paladin.png", 71, 95, "asset_atli"):register(); - heart_paladin:register() - - function SMODS.Jokers.j_heart_paladin.loc_def(card) - return {card.ability.extra.Xmult, ''..(G.GAME and G.GAME.probabilities.normal or 1), card.ability.extra.odds, - localize{type = 'name_text', key = card.ability.extra.joker1, set = 'Joker'}, - localize{type = 'name_text', key = card.ability.extra.joker2, set = 'Joker'}} - end - - function SMODS.Jokers.j_heart_paladin.calculate(card, context) - if context.individual and context.cardarea == G.play and - context.other_card:is_suit('Hearts') then - return { - x_mult = card.ability.extra.Xmult, - card = card - } - end - - if context.repetition and context.cardarea == G.play and - context.other_card:is_suit('Hearts') then - if pseudorandom('heart_paladin') < G.GAME.probabilities.normal/card.ability.extra.odds then - return { - message = localize('k_again_ex'), - repetitions = 1, - card = card - } - end - end - end - - - - local spade_archer_def = { - name = "Spade Archer", - text = { - "Played cards with {C:spades}Spade{} suit give", - "{C:chips}+#1#{} Chips when scored. Gains {C:chips}+#2#{} ", - "chips when 5 {C:spades}Spades{} are played", - "{C:inactive}(#3# + #4#)" - } - } - - local spade_archer = SMODS.Joker:new("Spade Archer", "spade_archer", {extra = { - chips = 50, chip_mod = 10, joker1 = "j_wrathful_joker", joker2 = "j_arrowhead" - }}, { x = 0, y = 0 }, spade_archer_def, rarity.key, 12, true, false, true, true) - SMODS.Sprite:new("j_spade_archer", mod_obj.path, "j_spade_archer.png", 71, 95, "asset_atli"):register(); - spade_archer:register() - - function SMODS.Jokers.j_spade_archer.loc_def(card) - return {card.ability.extra.chips, card.ability.extra.chip_mod, - localize{type = 'name_text', key = card.ability.extra.joker1, set = 'Joker'}, - localize{type = 'name_text', key = card.ability.extra.joker2, set = 'Joker'}} - end - - function SMODS.Jokers.j_spade_archer.calculate(card, context) - if context.individual and context.cardarea == G.play and - context.other_card:is_suit('Spades') then - return { - chips = card.ability.extra.chips, - card = card - } - end - - if context.before and context.cardarea == G.jokers then - local spades = 0 - for k, v in ipairs(context.scoring_hand) do - if v:is_suit('Spades') then - spades = spades + 1 - end - end - if spades == 5 then - card.ability.extra.chips = card.ability.extra.chips + card.ability.extra.chip_mod - return { - message = localize('k_upgrade_ex'), - colour = G.C.CHIPS, - card = card - } - end - end - end - - - - local club_wizard_def = { - name = "Club Wizard", - text = { - "Played cards with {C:clubs}Club{} suit", - "give {C:mult}+#1#{} Mult when scored", - "{C:inactive}(#2# + #3#)" - } - } - - local club_wizard = SMODS.Joker:new("Club Wizard", "club_wizard", {extra = { - mult = 24, joker1 = "j_gluttenous_joker", joker2 = "j_onyx_agate" - }}, { x = 0, y = 0 }, club_wizard_def, rarity.key, 12, true, false, true, true) - SMODS.Sprite:new("j_club_wizard", mod_obj.path, "j_club_wizard.png", 71, 95, "asset_atli"):register(); - club_wizard:register() - - function SMODS.Jokers.j_club_wizard.loc_def(card) - return {card.ability.extra.mult, - localize{type = 'name_text', key = card.ability.extra.joker1, set = 'Joker'}, - localize{type = 'name_text', key = card.ability.extra.joker2, set = 'Joker'}} - end - - function SMODS.Jokers.j_club_wizard.calculate(card, context) - if context.individual and context.cardarea == G.play and - context.other_card:is_suit('Clubs') then - return { - mult = card.ability.extra.mult, - card = card - } - end - end - - - - local big_bang_def = { - name = "Big Bang", - text = { - "{X:mult,C:white} X#1# {} Mult per the number", - "of times {C:attention}poker hand{} has been played", - "plus the level of the {C:attention}poker hand{}.", - "{C:inactive}(#2# + #3#)" - - } - } - - local big_bang = SMODS.Joker:new("Big Bang", "big_bang", {extra = { - Xmult = 0.1, joker1 = "j_supernova", joker2 = "j_constellation" - }}, { x = 0, y = 0 }, big_bang_def, rarity.key, 11, true, false, true, true) - SMODS.Sprite:new("j_big_bang", mod_obj.path, "j_big_bang.png", 71, 95, "asset_atli"):register(); - big_bang:register() - - function SMODS.Jokers.j_big_bang.loc_def(card) - return {card.ability.extra.Xmult, - localize{type = 'name_text', key = card.ability.extra.joker1, set = 'Joker'}, - localize{type = 'name_text', key = card.ability.extra.joker2, set = 'Joker'}} - end - - function SMODS.Jokers.j_big_bang.calculate(card, context) - if context.cardarea == G.jokers and context.joker_main and context.scoring_name then - local mult_val = 1 + card.ability.extra.Xmult * (G.GAME.hands[context.scoring_name].level + G.GAME.hands[context.scoring_name].played) - return { - message = localize{type='variable',key='a_xmult',vars={mult_val}}, - Xmult_mod = mult_val - } - end - end - - - - local dynamic_duo_def = { - name = "Dynamic Duo", - text = { - "Played {C:attention}number{} cards give {C:mult}+#1#{} Mult ", - "and {C:chips}+#2#{} Chips when scored.", - "{C:inactive}(#3# + #4#)" - - } - } - - local dynamic_duo = SMODS.Joker:new("Dynamic Duo", "dynamic_duo", {extra = { - mult = 4, chips = 30, joker1 = "j_even_steven", joker2 = "j_odd_todd" - }}, { x = 0, y = 0 }, dynamic_duo_def, rarity.key, 8, true, false, true, true) - SMODS.Sprite:new("j_dynamic_duo", mod_obj.path, "j_dynamic_duo.png", 71, 95, "asset_atli"):register(); - dynamic_duo:register() - - function SMODS.Jokers.j_dynamic_duo.loc_def(card) - return {card.ability.extra.mult, card.ability.extra.chips, - localize{type = 'name_text', key = card.ability.extra.joker1, set = 'Joker'}, - localize{type = 'name_text', key = card.ability.extra.joker2, set = 'Joker'}} - end - - function SMODS.Jokers.j_dynamic_duo.calculate(card, context) - if context.individual and context.cardarea == G.play and - not context.other_card:is_face() and not SMODS.has_no_rank(context.other_card) then - return { - mult = card.ability.extra.mult, - chips = card.ability.extra.chips, - card = card - } - end - end - - local collectible_chaos_card_def = { - name = "Collectible Chaos Card", - text = { - "{C:mult}+#1#{} Mult per {C:attention}reroll{} in the shop.", - "{C:attention}#2#{} free {C:green}Reroll{} per shop", - "{C:inactive}(Currently {C:mult}+#3#{C:inactive} Mult)", - "{C:inactive}(#4# + #5#)" - - } - } - - local collectible_chaos_card = SMODS.Joker:new("Collectible Chaos Card", "collectible_chaos_card", {extra = { - per_reroll = 2, free = 1, joker1 = "j_chaos", joker2 = "j_flash" - }, mult = 0}, { x = 0, y = 0 }, collectible_chaos_card_def, rarity.key, 9, true, false, true, true) - SMODS.Sprite:new("j_collectible_chaos_card", mod_obj.path, "j_collectible_chaos_card.png", 71, 95, "asset_atli"):register(); - collectible_chaos_card:register() - - function SMODS.Jokers.j_collectible_chaos_card.loc_def(card) - return {card.ability.extra.per_reroll, card.ability.extra.free, card.ability.mult, - localize{type = 'name_text', key = card.ability.extra.joker1, set = 'Joker'}, - localize{type = 'name_text', key = card.ability.extra.joker2, set = 'Joker'}} - end - - function SMODS.Jokers.j_collectible_chaos_card.calculate(card, context) - if context.reroll_shop and not context.blueprint then - card.ability.mult = card.ability.mult + card.ability.extra.per_reroll - G.E_MANAGER:add_event(Event({ - func = (function() - card_eval_status_text(card, 'extra', nil, nil, nil, {message = localize{type = 'variable', key = 'a_mult', vars = {card.ability.mult}}, colour = G.C.MULT}) - return true - end)})) - end - - if context.cardarea == G.jokers and card.ability.mult > 0 and context.joker_main then - return { - message = localize{type='variable',key='a_mult',vars={card.ability.mult}}, - mult_mod = card.ability.mult - } - end - end - - - - local flip_flop_hand_def = { - name = "Flip-Flop", - text = { - "{C:attention}+#1#{} hand size. {C:red}+#2#{} Mult", - "{C:attention}Flips{} after each blind", - "{C:inactive}(#3# + #4#)" - - } - } - - local flip_flop_discard_def = { - name = "Flip-Flop", - text = { - "{C:red}+#1#{} discard. {C:chips}+#2#{} Chips", - "{C:attention}Flips{} after each blind", - "{C:inactive}(#3# + #4#)" - - } - } - - local flip_flop = SMODS.Joker:new("Flip-Flop", "flip_flop", {extra = { - hands = 2, discards = 2, mult = 8, chips = 50, side = "mult", joker1 = "j_juggler", joker2 = "j_drunkard" - }}, { x = 0, y = 0 }, flip_flop_hand_def, rarity.key, 9, true, false, false, true) - SMODS.Sprite:new("j_flip_flop", mod_obj.path, "j_flip_flop.png", 71, 95, "asset_atli"):register(); - SMODS.Sprite:new("j_flop_flip", mod_obj.path, "j_flop_flip.png", 71, 95, "asset_atli"):register(); - flip_flop:register() - - G.localization.descriptions["Joker"]["j_flip_flop_chips"] = flip_flop_discard_def - G.localization.descriptions["Joker"]["j_flip_flop_mult"] = flip_flop_hand_def - - - function SMODS.Jokers.j_flip_flop.loc_def(card) - if card.ability.extra.side == "mult" then - return {card.ability.extra.hands, card.ability.extra.mult, - localize{type = 'name_text', key = card.ability.extra.joker1, set = 'Joker'}, - localize{type = 'name_text', key = card.ability.extra.joker2, set = 'Joker'}} - else - return {card.ability.extra.discards, card.ability.extra.chips, - localize{type = 'name_text', key = card.ability.extra.joker1, set = 'Joker'}, - localize{type = 'name_text', key = card.ability.extra.joker2, set = 'Joker'}} - end - end - - function SMODS.Jokers.j_flip_flop.calculate(card, context) - if context.end_of_round and not context.blueprint and not context.repetition and not context.individual then - if card.ability.extra.side == "mult" then - G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.3, blockable = false, - func = function() - card.ability.extra.side = "chips" - G.localization.descriptions["Joker"]["j_flip_flop"] = G.localization.descriptions["Joker"]["j_flip_flop_chips"] - card:juice_up(1, 1) - card.config.center.atlas = "j_flop_flip" - card:set_sprites(card.config.center) - - return true; end})) - - G.hand:change_size(-card.ability.extra.hands) - G.GAME.round_resets.discards = G.GAME.round_resets.discards + card.ability.extra.discards - ease_discard(card.ability.extra.discards) - - return { - message = localize('k_flipped_ex'), - colour = G.C.CHIPS - } - else - G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.3, blockable = false, - func = function() - card.ability.extra.side = "mult" - G.localization.descriptions["Joker"]["j_flip_flop"] = G.localization.descriptions["Joker"]["j_flip_flop_mult"] - card:juice_up(1, 1) - card.config.center.atlas = "j_flip_flop" - card:set_sprites(card.config.center) - - return true; end})) - - G.hand:change_size(card.ability.extra.hands) - G.GAME.round_resets.discards = G.GAME.round_resets.discards - card.ability.extra.discards - ease_discard(-card.ability.extra.discards) - - return { - message = localize('k_flipped_ex'), - colour = G.C.MULT - } - end - - end - - if context.cardarea == G.jokers and context.joker_main then - if card.ability.extra.side == "mult" then - return { - message = localize{type='variable',key='a_mult',vars={card.ability.extra.mult}}, - mult_mod = card.ability.extra.mult, - colour = G.C.MULT - } - else - return { - message = localize{type='variable',key='a_chips',vars={card.ability.extra.chips}}, - chip_mod = card.ability.extra.chips, - colour = G.C.CHIPS - } - end - - end - end - - - - local royal_decree_def = { - name = "Royal Decree", - text = { - "Played {C:attention}face{} cards give {C:money}$#1#{} when scored.", - "Each {C:attention}face{} card held in hand", - "at end of round gives {C:money}$#1#{}", - "{C:inactive}(#2# + #3#)" - - } - } - - local royal_decree = SMODS.Joker:new("Royal Decree", "royal_decree", {extra = { - dollars = 2, joker1 = "j_business", joker2 = "j_reserved_parking" - }, mult = 0}, { x = 0, y = 0 }, royal_decree_def, rarity.key, 10, true, false, true, true) - SMODS.Sprite:new("j_royal_decree", mod_obj.path, "j_royal_decree.png", 71, 95, "asset_atli"):register(); - royal_decree:register() - - function SMODS.Jokers.j_royal_decree.loc_def(card) - return {card.ability.extra.dollars, - localize{type = 'name_text', key = card.ability.extra.joker1, set = 'Joker'}, - localize{type = 'name_text', key = card.ability.extra.joker2, set = 'Joker'}} - end - - function SMODS.Jokers.j_royal_decree.calculate(card, context) - if context.end_of_round and context.individual then - if context.cardarea == G.hand and context.other_card:is_face() then - return { - h_dollars = 2, - card = card - } - end - end - - if context.cardarea == G.play and context.individual and context.other_card:is_face() then - G.GAME.dollar_buffer = (G.GAME.dollar_buffer or 0) + 2 - G.E_MANAGER:add_event(Event({func = (function() G.GAME.dollar_buffer = 0; return true end)})) - return { - dollars = 2, - card = card - } - - end - end - - - - local dementia_joker_def = { - name = "Dementia Joker", - text = { - "{C:mult}+#1#{} Mult for each {C:attention}Joker{} card.", - "{C:green}#2# in #3#{} chance to {C:attention}clone{} if ", - "not {C:dark_edition}Negative{} after you beat a blind", - "{C:inactive}(Currently {C:mult}+#4#{C:inactive} Mult)", - "{C:inactive}(#5# + #6#)" - - } - } - - local dementia_joker = SMODS.Joker:new("Dementia Joker", "dementia_joker", {extra = { - mult = 3, odds = 3, joker1 = "j_abstract", joker2 = "j_riff_raff" - }, mult = 0}, { x = 0, y = 0 }, dementia_joker_def, rarity.key, 8, true, false, true, true) - SMODS.Sprite:new("j_dementia_joker", mod_obj.path, "j_dementia_joker.png", 71, 95, "asset_atli"):register(); - dementia_joker:register() - - function SMODS.Jokers.j_dementia_joker.loc_def(card) - return {card.ability.extra.mult, ''..(G.GAME and G.GAME.probabilities.normal or 1), card.ability.extra.odds, - (G.jokers and G.jokers.cards and #G.jokers.cards or 0)*card.ability.extra.mult, - localize{type = 'name_text', key = card.ability.extra.joker1, set = 'Joker'}, - localize{type = 'name_text', key = card.ability.extra.joker2, set = 'Joker'}} - end - - function SMODS.Jokers.j_dementia_joker.calculate(card, context) - if context.end_of_round and not context.blueprint and not context.repetition and not context.individual then - if pseudorandom('dementia_joker') < G.GAME.probabilities.normal/card.ability.extra.odds - and not (card.edition and card.edition.negative) then - local card = copy_card(card, nil, nil, nil, card.edition and card.edition.negative) - card:set_edition({negative = true}, true) - card:add_to_deck() - G.jokers:emplace(card) - return { - message = localize('k_copied_ex'), - colour = G.C.DARK_EDITION - } - end - end - - if context.cardarea == G.jokers and context.joker_main then - local x = 0 - for i = 1, #G.jokers.cards do - if G.jokers.cards[i].ability.set == 'Joker' then x = x + 1 end - end - return { - message = localize{type='variable',key='a_mult',vars={x*card.ability.extra.mult}}, - mult_mod = x*card.ability.extra.mult - } - end - end - - - - local golden_egg_def = { - name = "Golden Egg", - text = { - "Gains {C:money}$#1#{} of {C:attention}sell value{}", - " at end of round.", - " Earn {C:money}$#1#{} at end of round", - "{C:inactive}(#2# + #3#)" - - } - } - - local golden_egg = SMODS.Joker:new("Golden Egg", "golden_egg", {extra = { - dollars = 4, joker1 = "j_egg", joker2 = "j_golden" - }, mult = 0}, { x = 0, y = 0 }, golden_egg_def, rarity.key, 10, true, false, false, true) - SMODS.Sprite:new("j_golden_egg", mod_obj.path, "j_golden_egg.png", 71, 95, "asset_atli"):register(); - golden_egg:register() - - function SMODS.Jokers.j_golden_egg.loc_def(card) - return {card.ability.extra.dollars, - localize{type = 'name_text', key = card.ability.extra.joker1, set = 'Joker'}, - localize{type = 'name_text', key = card.ability.extra.joker2, set = 'Joker'}} - end - - function SMODS.Jokers.j_golden_egg.calculate(card, context) - if context.end_of_round and not context.blueprint and not context.repetition and not context.individual then - card.ability.extra_value = card.ability.extra_value + card.ability.extra.dollars - card:set_cost() - return { - message = localize('k_val_up'), - colour = G.C.MONEY - } - end - end - - - - local flag_bearer_def = { - name = "Flag Bearer", - text = { - "{C:mult}+#1#{} Mult per hand played, {C:mult}-#2#{} Mult", - "per discard. Mult is multiplied by", - " remaining {C:attention}discards{}", - "{C:inactive}(Currently {C:mult}+#3#{C:inactive} Mult)", - "{C:inactive}(#4# + #5#)" - - } - } - - local flag_bearer = SMODS.Joker:new("Flag Bearer", "flag_bearer", {extra = { - hand_add = 1, discard_sub = 1, joker1 = "j_banner", joker2 = "j_green_joker" - }, mult = 0}, { x = 0, y = 0 }, flag_bearer_def, rarity.key, 9, true, false, false, true) - SMODS.Sprite:new("j_flag_bearer", mod_obj.path, "j_flag_bearer.png", 71, 95, "asset_atli"):register(); - flag_bearer:register() - - function SMODS.Jokers.j_flag_bearer.loc_def(card) - return {card.ability.extra.hand_add, card.ability.extra.discard_sub, card.ability.mult * (G.GAME.current_round.discards_left or 0), - localize{type = 'name_text', key = card.ability.extra.joker1, set = 'Joker'}, - localize{type = 'name_text', key = card.ability.extra.joker2, set = 'Joker'}} - end - - function SMODS.Jokers.j_flag_bearer.calculate(card, context) - if context.discard and not context.blueprint and context.other_card == context.full_hand[#context.full_hand] then - local prev_mult = card.ability.mult - card.ability.mult = math.max(0, card.ability.mult - card.ability.extra.discard_sub) - if card.ability.mult ~= prev_mult then - return { - message = localize{type='variable',key='a_mult_minus',vars={card.ability.extra.discard_sub}}, - colour = G.C.RED, - card = card - } - end - end - - if context.cardarea == G.jokers and context.before then - if not context.blueprint then - card.ability.mult = card.ability.mult + card.ability.extra.hand_add - return { - card = card, - message = localize{type='variable',key='a_mult',vars={card.ability.extra.hand_add}} - } - end - end - - if context.cardarea == G.jokers and context.joker_main then - local mult = card.ability.mult * G.GAME.current_round.discards_left - return { - message = localize{type='variable',key='a_mult',vars={mult}}, - mult_mod = mult - } - end - end - - - local uncanny_face_def = { - name = "Uncanny Face", - text = { - "Played {C:attention}face{} cards give {C:chips}+#1#{} Chips and", - "{C:mult}+#2#{} Mult for every {C:attention}face{} card", - " in the scoring hand", - "{C:inactive}(#3# + #4#)" - - } - } - - local uncanny_face = SMODS.Joker:new("Uncanny Face", "uncanny_face", {extra = { - chips = 15, mult = 2, joker1 = "j_scary_face", joker2 = "j_smiley" - }}, { x = 0, y = 0 }, uncanny_face_def, rarity.key, 8, true, false, true, true) - SMODS.Sprite:new("j_uncanny_face", mod_obj.path, "j_uncanny_face.png", 71, 95, "asset_atli"):register(); - uncanny_face:register() - - function SMODS.Jokers.j_uncanny_face.loc_def(card) - return {card.ability.extra.chips, card.ability.extra.mult, - localize{type = 'name_text', key = card.ability.extra.joker1, set = 'Joker'}, - localize{type = 'name_text', key = card.ability.extra.joker2, set = 'Joker'}} - end - - function SMODS.Jokers.j_uncanny_face.calculate(card, context) - if context.individual and context.cardarea == G.play and - context.other_card:is_face() then - local faces = 0 - for k, v in ipairs(context.scoring_hand) do - if v:is_face() then - faces = faces + 1 - end - end - return { - mult = card.ability.extra.mult * faces, - chips = card.ability.extra.chips * faces, - card = card - } - end - end - - - local commercial_driver_def = { - name = "Commercial Driver", - text = { - "{X:mult,C:white} X#1# {} Mult per consecutive hand", - "played with a scoring {C:attention}enhanced{} card", - "{C:inactive}(Currently {X:mult,C:white}X#2#{C:inactive} Mult)", - "{C:inactive}(#3# + #4#)" - } - } - - local commercial_driver = SMODS.Joker:new("Commercial Driver", "commercial_driver", {extra = { - bonus = 0.25, total = 1, joker1 = "j_ride_the_bus", joker2 = "j_drivers_license" - }}, { x = 0, y = 0 }, commercial_driver_def, rarity.key, 8, true, false, true, true) - SMODS.Sprite:new("j_commercial_driver", mod_obj.path, "j_commercial_driver.png", 71, 95, "asset_atli"):register(); - commercial_driver:register() - - function SMODS.Jokers.j_commercial_driver.loc_def(card) - return {card.ability.extra.bonus, card.ability.extra.total, - localize{type = 'name_text', key = card.ability.extra.joker1, set = 'Joker'}, - localize{type = 'name_text', key = card.ability.extra.joker2, set = 'Joker'}} - end - - function SMODS.Jokers.j_commercial_driver.calculate(card, context) - if context.before and context.cardarea == G.jokers then - local enhanced = false - for i = 1, #context.scoring_hand do - if context.scoring_hand[i].config.center ~= G.P_CENTERS.c_base then enhanced = true end - end - if not enhanced then - local last_mult = card.ability.extra.total - card.ability.extra.total = 1 - if last_mult > 0 then - return { - card = card, - message = localize('k_reset') - } - end - else - card.ability.extra.total = card.ability.extra.total + card.ability.extra.bonus - end - end - - if context.cardarea == G.jokers and context.joker_main then - return { - message = localize{type='variable',key='a_xmult',vars={card.ability.extra.total}}, - Xmult_mod = card.ability.extra.total - } - end - end - - - local camping_trip_def = { - name = "Camping Trip", - text = { - "Played {C:attention}cards{} permanently gains", - "{C:chips}+#1#{} Chips when scored", - "({C:chips}+#2#{} on the final hand)", - "Your final hand triggers twice", - "{C:inactive}(#3# + #4#)" - } - } - - local camping_trip = SMODS.Joker:new("Camping Trip", "camping_trip", {extra = { - bonus_base = 5, bonus_final = 10, joker1 = "j_hiker", joker2 = "j_dusk" - }}, { x = 0, y = 0 }, camping_trip_def, rarity.key, 10, true, false, true, true) - SMODS.Sprite:new("j_camping_trip", mod_obj.path, "j_camping_trip.png", 71, 95, "asset_atli"):register(); - camping_trip:register() - - function SMODS.Jokers.j_camping_trip.loc_def(card) - return {card.ability.extra.bonus_base, card.ability.extra.bonus_final, - localize{type = 'name_text', key = card.ability.extra.joker1, set = 'Joker'}, - localize{type = 'name_text', key = card.ability.extra.joker2, set = 'Joker'}} - end - - function SMODS.Jokers.j_camping_trip.calculate(card, context) - if context.individual and context.cardarea == G.play then - context.other_card.ability.perma_bonus = context.other_card.ability.perma_bonus or 0 - if G.GAME.current_round.hands_left == 0 then - context.other_card.ability.perma_bonus = context.other_card.ability.perma_bonus + card.ability.extra.bonus_final - else - context.other_card.ability.perma_bonus = context.other_card.ability.perma_bonus + card.ability.extra.bonus_base - end - - return { - extra = {message = localize('k_upgrade_ex'), colour = G.C.CHIPS}, - colour = G.C.CHIPS, - card = card - } - end - - if context.repetition and context.cardarea == G.play and G.GAME.current_round.hands_left == 0 then - return { - message = localize('k_again_ex'), - repetitions = 1, - card = card - } - end - end - - - ----- SixSuits collab !!!!! ------------------ - ---------------------------------------------- - - if SMODS.INIT.SixSuit then - - local star_oracle_def = { - name = "Star Oracle", - text = { - "{C:attention}+2{} consumable slots.", - "{C:green}#1# in #2#{} chance for played cards", - " with {C:stars}Star{} suit to create a", - "random {C:spectral}Spectral{} card when scored", - "{C:inactive}(#3# + #4#)" - - } - } - - local star_oracle = SMODS.Joker:new("Star Oracle", "star_oracle", {extra = { - odds = 10, slots = 2, joker1 = "j_envious_joker", joker2 = "j_star_ruby" - }}, { x = 0, y = 0 }, star_oracle_def, rarity.key, 12, true, false, true, true) - SMODS.Sprite:new("j_star_oracle", mod_obj.path, "j_star_oracle.png", 71, 95, "asset_atli"):register(); - star_oracle:register() - - function SMODS.Jokers.j_star_oracle.loc_def(card) - return { '' .. (G.GAME and G.GAME.probabilities.normal or 1), card.ability.extra.odds, - localize{type = 'name_text', key = card.ability.extra.joker1, set = 'Joker'}, - localize{type = 'name_text', key = card.ability.extra.joker2, set = 'Joker'}} - end - - function SMODS.Jokers.j_star_oracle.calculate(card, context) - if context.individual and - context.cardarea == G.play and - context.other_card:is_suit('Stars') and - #G.consumeables.cards + G.GAME.consumeable_buffer < G.consumeables.config.card_limit and - pseudorandom('starruby') < G.GAME.probabilities.normal / card.ability.extra.odds then - G.GAME.consumeable_buffer = G.GAME.consumeable_buffer + 1 - G.E_MANAGER:add_event(Event({ - trigger = 'before', - delay = 0.0, - func = (function() - local _card = create_card('Spectral', G.consumeables, nil, nil, nil, nil, nil, 'ruby') - _card:add_to_deck() - G.consumeables:emplace(_card) - G.GAME.consumeable_buffer = 0 - return true - end) - })) - card_eval_status_text(context.blueprint_card or card, 'extra', nil, nil, nil, - { message = localize('k_plus_spectral'), colour = G.C.SECONDARY_SET.Spectral }) - return {} - end - end - - - - local moon_marauder_def = { - name = "Moon Marauder", - text = { - "{C:green}#1# in #2#{} chance for played cards with", - "{C:moons}Moon{} suit to become {C:attention}Glass{} Cards.", - "played {C:moons}Moon{} {C:attention}Glass{} cards never shatter", - "{C:inactive}(#3# + #4#)" - - } - } - - local moon_marauder = SMODS.Joker:new("Moon Marauder", "moon_marauder", {extra = { - odds = 3, joker1 = "j_slothful_joker", joker2 = "j_rainbow_moonstone" - }}, { x = 0, y = 0 }, moon_marauder_def, rarity.key, 12, true, false, true, true) - SMODS.Sprite:new("j_moon_marauder", mod_obj.path, "j_moon_marauder.png", 71, 95, "asset_atli"):register(); - moon_marauder:register() - - function SMODS.Jokers.j_moon_marauder.loc_def(card) - return { '' .. (G.GAME and G.GAME.probabilities.normal or 1), card.ability.extra.odds, - localize{type = 'name_text', key = card.ability.extra.joker1, set = 'Joker'}, - localize{type = 'name_text', key = card.ability.extra.joker2, set = 'Joker'}} - end - - function SMODS.Jokers.j_moon_marauder.calculate(card, context) - if context.before and (context.cardarea == G.jokers) then - local moons = {} - for _, v in ipairs(context.full_hand) do - if v:is_suit('Moons') and not (v.ability.name == 'Glass Card') and pseudorandom('moon_marauder') < G.GAME.probabilities.normal / card.ability.extra.odds then - moons[#moons + 1] = v - v:set_ability(G.P_CENTERS.m_glass, nil, true) - G.E_MANAGER:add_event(Event({ - func = function() - v:juice_up() - return true - end - })) - end - end - if #moons > 0 then - return { - message = localize('k_glass'), - colour = G.C.SECONDARY_SET.Enhanced, - card = context.blueprint_card or card - } - end - end - - if context.remove_playing_cards and not context.blueprint then - -- G.E_MANAGER:add_event(Event({ - -- trigger = 'after', - -- delay = 1, - -- func = function() - -- sendDebugMessage(tostring(#context.removed)) - -- for k, v in ipairs(context.removed) do - -- sendDebugMessage(tostring(v.shattered)) - -- if v.shattered then - -- local _card = Card(G.play.T.x, G.play.T.y, G.CARD_W, G.CARD_H, G.P_CARDS.empty, G.P_CENTERS.c_base, {playing_card = G.playing_card}) - - -- _card = copy_card(v, _card, nil, G.playing_card) - -- _card:start_materialize({G.C.SECONDARY_SET.Enhanced}) - -- G.deck:emplace(_card) - - -- G.E_MANAGER:add_event(Event({ - -- func = function() - -- card_eval_status_text(context.blueprint_card or card, 'extra', nil, nil, nil, - -- { message = localize('k_in_tact_ex'), colour = G.C.SECONDARY_SET.edition }) - -- return true - -- end - -- })) - -- end - -- end - -- end - -- })) - sendDebugMessage(tostring(#context.removed)) - for k, v in ipairs(context.removed) do - sendDebugMessage(tostring(v.shattered)) - if v.shattered then - local _card = Card(G.play.T.x, G.play.T.y, G.CARD_W, G.CARD_H, G.P_CARDS.empty, G.P_CENTERS.c_base, {playing_card = G.playing_card}) - - G.playing_card = (G.playing_card and G.playing_card + 1) or 1 - _card = copy_card(v, _card, nil, G.playing_card) - _card:start_materialize({G.C.SECONDARY_SET.Enhanced}) - G.deck:emplace(_card) - table.insert(G.playing_cards, _card) - - G.E_MANAGER:add_event(Event({ - func = function() - card_eval_status_text(context.blueprint_card or card, 'extra', nil, nil, nil, - { message = localize('k_in_tact_ex'), colour = G.C.SECONDARY_SET.edition }) - return true - end - })) - end - end - end - end - - FusionJokers.fusions:add_fusion("j_envious_joker", nil, false, "j_star_ruby", nil, false, "j_star_oracle", 12) - FusionJokers.fusions:add_fusion("j_slothful_joker", nil, false, "j_rainbow_moonstone", nil, false, "j_moon_marauder", 12) - end - - ---------------------------------------------- - ---------------------------------------------- - -end - ----------------------------------------------- -------------MOD CODE END---------------------- diff --git a/mod/assets/1x/j_flip_flop.png b/mod/assets/1x/j_flip_flop.png index 2919b84ced4aeaa5b27bf0b2becaef28dc031301..65156730f41d4cbf94ca8f2530e605bda806df79 100644 GIT binary patch literal 23470 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 R7WT_5xnLTiaDd-@>t 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
V61y8p90=!(cE_ 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 literal 1335 zcmV-71<3k|P)Px#1ZP1_K>z@;j|==^1poj58&FJCMZd?@|Ns9_V`qWR{Q#W(+}zy#q5%D0RR910 zGx`cN00009bW%=J|NsC0|NsC008>wR5&!@I32;bRa{vGi!vFvd!vV){sAK>D1dvHY zK~z{rt(l8<+aM4`Q#G&u|KiSTVUZ>5HXX-if!)i1WTicM`~E)?=j~?-=k4v+&qNY` z(*1Xd^AhHO5?%?EmpI37Xajn}DHp`xQ)3=IF*~QvD)l(tAvEF0P(}yTY8(|hWD=80 zhqlFy8S;+#TMkJHn3Q#`;$(=tWJWg}g`H&1Z+A`%>W&jl(jGH8HlQ&}#|62zahUY5 zV&klvuA_|yfz&GJ_&9+EN9!w)iUO&6aGY~|?5@{T!>pWCgR_Dfr}-g<{=^ALL1k<> z{d^8k)S4s?xijIGqm<;~lRq92s0jW<`#J{~Ts?r={g~p{IA|w_!PHEsTr&+*AUVfcgkRd*kT`Eo~^9C~#CAu@-r0|cs_$-xB@OO4ZXe4#vk z(4!TopY7>*`uS*Mp1tO9BJG2wA0lcI0K%~fgitMXA}*YfReo>`zA`#x%p*JjqRwfH zg~Ka7K_C5cZ~RPNEXA7fZT(r~0fW0>UJ=m`fU1!{hrxqw!VSsl|rb9BeS za+JbZ0lgP!yvCQdz# zQlgrblO@DqKoU81*fL2FbTf>+LmeZlb? zdd9(W3+Q26cN||#mS|s}DxO<=ox@!VGyDt?PkX_jf6sxM9~l}TkT^E&?ELV}8dca_ zT{VR%HDkxYixrI%P@$AZhQ#nGhffTa=F8?|Qr@B+2Oyq?Vy*b42)65|VwNQ2W1 z#Zf914d|sGeuTq+g$<4@(S!1yb2uGNJwyrwO%5XVsXIT}*?bxW#M(-Mb{vRv+z*o` z2z_B>SYl_((PXVf(2yyf;QXFbiTV^sgJEP^RDM9098IeQf&+tN245#VaI`?u$Wg$J z36#;yE8D@DQ<)Ol{L4ayAfT#};*#z@a_~#4aY}>JN5wGc%pqvQk)2^_$6;~OCI^*O z3*pP_&X$u2s}pz!#LqaYrUX*VCJD@Nu;WybC5|;PQyWfI ztL>bI;e>*Fj*1&+fEEnX7~OJOt;2y#0>#drql%R?{XBw28XimaMPG5ykaTw0g3te!!w6RV3&=bxR7gOYH tL9Z!)2l*)|e@`LlN6O!K{)4{1{{R^$J?gOfX5#<=002ovPDHLkV1iL$WfuSd diff --git a/mod/assets/2x/j_flip_flop.png b/mod/assets/2x/j_flip_flop.png index 3933296a23a9984dfaa0a314d0e816314cea7227..3d609a6c58447154244cd59db3b19fdb0cbba520 100644 GIT binary patch literal 23688 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 0 and context.joker_main then + return { + message = localize{type='variable',key='a_mult',vars={card.ability.extra.mult}}, + mult_mod = card.ability.extra.mult + } + end + end +} + +-- See localization/en-us.lua to create joker text \ No newline at end of file diff --git a/mod/jokers/commercialdriver.lua b/mod/jokers/commercialdriver.lua new file mode 100644 index 0000000..45ed394 --- /dev/null +++ b/mod/jokers/commercialdriver.lua @@ -0,0 +1,70 @@ +SMODS.Atlas { + key = 'commercial_driver', + path = "j_commercial_driver.png", + px = 71, + py = 95 +} + +SMODS.Joker { + key = "commercial_driver", + name = "Commercial Driver", + atlas = 'commercial_driver', + pos = { + x = 0, + y = 0 + }, + rarity = "fuse_fusion", + cost = 12, + unlocked = true, + discovered = false, + eternal_compat = true, + perishable_compat = true, + blueprint_compat = true, + config = { + extra = { + bonus = 0.25, + total = 1, + joker1 = "j_ride_the_bus", + joker2 = "j_drivers_license" + } + }, + loc_vars = function(self, info_queue, card) + return { + vars = { + card.ability.extra.bonus, + card.ability.extra.total, + localize{type = 'name_text', key = card.ability.extra.joker1, set = 'Joker'}, + localize{type = 'name_text', key = card.ability.extra.joker2, set = 'Joker'} + } + } + end, + calculate = function(self, card, context) + if context.before and context.cardarea == G.jokers then + local enhanced = false + for i = 1, #context.scoring_hand do + if context.scoring_hand[i].config.center ~= G.P_CENTERS.c_base then enhanced = true end + end + if not enhanced then + local last_mult = card.ability.extra.total + card.ability.extra.total = 1 + if last_mult > 0 then + return { + card = card, + message = localize('k_reset') + } + end + else + card.ability.extra.total = card.ability.extra.total + card.ability.extra.bonus + end + end + + if context.cardarea == G.jokers and context.joker_main then + return { + message = localize{type='variable',key='a_xmult',vars={card.ability.extra.total}}, + Xmult_mod = card.ability.extra.total + } + end + end +} + +-- See localization/en-us.lua to create joker text \ No newline at end of file diff --git a/mod/jokers/dementiajoker.lua b/mod/jokers/dementiajoker.lua new file mode 100644 index 0000000..f046768 --- /dev/null +++ b/mod/jokers/dementiajoker.lua @@ -0,0 +1,70 @@ +SMODS.Atlas { + key = 'dementia_joker', + path = "j_dementia_joker.png", + px = 71, + py = 95 +} + +SMODS.Joker { + key = "dementia_joker", + name = "Dementia Joker", + atlas = 'dementia_joker', + pos = { + x = 0, + y = 0 + }, + rarity = "fuse_fusion", + cost = 12, + unlocked = true, + discovered = false, + eternal_compat = true, + perishable_compat = true, + blueprint_compat = true, + config = { + extra = { + mult = 3, + odds = 3, + joker1 = "j_abstract", + joker2 = "j_riff_raff", + } + }, + loc_vars = function(self, info_queue, card) + return { + vars = { + card.ability.extra.mult, + ''..(G.GAME and G.GAME.probabilities.normal or 1), card.ability.extra.odds, + (G.jokers and G.jokers.cards and #G.jokers.cards or 0)*card.ability.extra.mult, + localize{type = 'name_text', key = card.ability.extra.joker1, set = 'Joker'}, + localize{type = 'name_text', key = card.ability.extra.joker2, set = 'Joker'} + } + } + end, + calculate = function(self, card, context) + if context.end_of_round and not context.blueprint and not context.repetition and not context.individual then + if pseudorandom('dementia_joker') < G.GAME.probabilities.normal/card.ability.extra.odds + and not (card.edition and card.edition.negative) then + local card = copy_card(card, nil, nil, nil, card.edition and card.edition.negative) + card:set_edition({negative = true}, true) + card:add_to_deck() + G.jokers:emplace(card) + return { + message = localize('k_copied_ex'), + colour = G.C.DARK_EDITION + } + end + end + + if context.cardarea == G.jokers and context.joker_main then + local x = 0 + for i = 1, #G.jokers.cards do + if G.jokers.cards[i].ability.set == 'Joker' then x = x + 1 end + end + return { + message = localize{type='variable',key='a_mult',vars={x*card.ability.extra.mult}}, + mult_mod = x*card.ability.extra.mult + } + end + end +} + +-- See localization/en-us.lua to create joker text \ No newline at end of file diff --git a/mod/jokers/diamondbard.lua b/mod/jokers/diamondbard.lua new file mode 100644 index 0000000..e23b0e3 --- /dev/null +++ b/mod/jokers/diamondbard.lua @@ -0,0 +1,57 @@ +SMODS.Atlas { + key = 'diamond_bard', + path = "j_diamond_bard.png", + px = 71, + py = 95 +} + +SMODS.Joker { + key = "diamond_bard", + name = "Diamond Bard", + atlas = 'diamond_bard', + pos = { + x = 0, + y = 0 + }, + rarity = "fuse_fusion", + cost = 12, + unlocked = true, + discovered = false, + eternal_compat = true, + perishable_compat = true, + blueprint_compat = true, + config = { + extra = { + money_threshold = 20, + mult = 4, + money = 1, + joker1 = "j_greedy_joker", + joker2 = "j_rough_gem", + } + }, + loc_vars = function(self, info_queue, card) + return { + vars = { + card.ability.extra.money, + card.ability.extra.mult, + card.ability.extra.money_threshold, + localize{type = 'name_text', key = card.ability.extra.joker1, set = 'Joker'}, + localize{type = 'name_text', key = card.ability.extra.joker2, set = 'Joker'} + } + } + end, + calculate = function(self, card, context) + if context.individual and context.cardarea == G.play and + context.other_card:is_suit('Diamonds') then + G.GAME.dollar_buffer = (G.GAME.dollar_buffer or 0) + to_big(card.ability.extra.money) + G.E_MANAGER:add_event(Event({func = (function() G.GAME.dollar_buffer = 0; return true end)})) + return { + dollars = card.ability.extra.money, + mult = card.ability.extra.mult * (1 + math.floor(to_number(G.GAME.dollars) / card.ability.extra.money_threshold)), + card = card + } + end + end +} + +-- See localization/en-us.lua to create joker text \ No newline at end of file diff --git a/mod/jokers/dynamicduo.lua b/mod/jokers/dynamicduo.lua new file mode 100644 index 0000000..3a64e4b --- /dev/null +++ b/mod/jokers/dynamicduo.lua @@ -0,0 +1,53 @@ +SMODS.Atlas { + key = 'dynamic_duo', + path = "j_dynamic_duo.png", + px = 71, + py = 95 +} + +SMODS.Joker { + key = "dynamic_duo", + name = "Dynamic Duo", + atlas = 'dynamic_duo', + pos = { + x = 0, + y = 0 + }, + rarity = "fuse_fusion", + cost = 12, + unlocked = true, + discovered = false, + eternal_compat = true, + perishable_compat = true, + blueprint_compat = true, + config = { + extra = { + mult = 4, + chips = 30, + joker1 = "j_even_steven", + joker2 = "j_odd_todd" + } + }, + loc_vars = function(self, info_queue, card) + return { + vars = { + card.ability.extra.mult, + card.ability.extra.chips, + localize{type = 'name_text', key = card.ability.extra.joker1, set = 'Joker'}, + localize{type = 'name_text', key = card.ability.extra.joker2, set = 'Joker'} + } + } + end, + calculate = function(self, card, context) + if context.individual and context.cardarea == G.play and + not context.other_card:is_face() and not SMODS.has_no_rank(context.other_card) then + return { + mult = card.ability.extra.mult, + chips = card.ability.extra.chips, + card = card + } + end + end +} + +-- See localization/en-us.lua to create joker text \ No newline at end of file diff --git a/mod/jokers/flagbearer.lua b/mod/jokers/flagbearer.lua new file mode 100644 index 0000000..51819e8 --- /dev/null +++ b/mod/jokers/flagbearer.lua @@ -0,0 +1,82 @@ +SMODS.Atlas { + key = 'flag_bearer', + path = "j_flag_bearer.png", + px = 71, + py = 95 +} + +SMODS.Joker { + key = "flag_bearer", + name = "Flag Bearer", + atlas = 'flag_bearer', + pos = { + x = 0, + y = 0 + }, + rarity = "fuse_fusion", + cost = 12, + unlocked = true, + discovered = false, + eternal_compat = true, + perishable_compat = true, + blueprint_compat = true, + config = { + extra = { + hand_add = 1, + discard_sub = 1, + mult = 0, + joker1 = "j_banner", + joker2 = "j_green_joker" + } + }, + loc_vars = function(self, info_queue, card) + return { + vars = { + card.ability.extra.hand_add, + card.ability.extra.discard_sub, + card.ability.extra.mult * (G.GAME.current_round.discards_left or 0), + localize{type = 'name_text', key = card.ability.extra.joker1, set = 'Joker'}, + localize{type = 'name_text', key = card.ability.extra.joker2, set = 'Joker'} + } + } + end, + set_ability = function(self, card, initial, delay_sprites) + if card.ability.mult and (card.ability.extra.mult == 0) then + card.ability.extra.mult = card.ability.mult + card.ability.mult = nil + end + end, + calculate = function(self, card, context) + if context.discard and not context.blueprint and context.other_card == context.full_hand[#context.full_hand] then + local prev_mult = card.ability.extra.mult + card.ability.extra.mult = math.max(0, card.ability.extra.mult - card.ability.extra.discard_sub) + if card.ability.extra.mult ~= prev_mult then + return { + message = localize{type='variable',key='a_mult_minus',vars={card.ability.extra.discard_sub}}, + colour = G.C.RED, + card = card + } + end + end + + if context.cardarea == G.jokers and context.before then + if not context.blueprint then + card.ability.extra.mult = card.ability.extra.mult + card.ability.extra.hand_add + return { + card = card, + message = localize{type='variable',key='a_mult',vars={card.ability.extra.hand_add}} + } + end + end + + if context.cardarea == G.jokers and context.joker_main then + local mult = card.ability.extra.mult * G.GAME.current_round.discards_left + return { + message = localize{type='variable',key='a_mult',vars={mult}}, + mult_mod = mult + } + end + end +} + +-- See localization/en-us.lua to create joker text \ No newline at end of file diff --git a/mod/jokers/flipflop.lua b/mod/jokers/flipflop.lua new file mode 100644 index 0000000..a3e42aa --- /dev/null +++ b/mod/jokers/flipflop.lua @@ -0,0 +1,135 @@ +SMODS.Atlas { + key = 'flip_flop', + path = "j_flip_flop.png", + px = 71, + py = 95 +} + +local flip = { + x = 0, + y = 0 +} + +local flop = { + x = 1, + y = 0 +} + +SMODS.Joker { + key = "flip_flop", + name = "Flip-Flop", + atlas = "flip_flop", + pos = flip, + rarity = "fuse_fusion", + cost = 12, + unlocked = true, + discovered = false, + eternal_compat = true, + perishable_compat = true, + blueprint_compat = true, + config = { + extra = { + hands = 2, + discards = 2, + mult = 8, + chips = 50, + side = "mult", + joker1 = "j_juggler", + joker2 = "j_drunkard" + } + }, + loc_vars = function(self, info_queue, card) + local result = {} + if card.ability.extra.side == "mult" then + result = { + card.ability.extra.hands, + card.ability.extra.mult, + localize{type = 'name_text', key = card.ability.extra.joker1, set = 'Joker'}, + localize{type = 'name_text', key = card.ability.extra.joker2, set = 'Joker'} + } + else + result = { + card.ability.extra.discards, + card.ability.extra.chips, + localize{type = 'name_text', key = card.ability.extra.joker1, set = 'Joker'}, + localize{type = 'name_text', key = card.ability.extra.joker2, set = 'Joker'} + } + end + + return { + key = self.key.."_"..card.ability.extra.side, + vars = result + } + end, + set_ability = function(self, card, initial, delay_sprites) + local flipref = card.config.center + if card.ability.extra.side == "mult" then + flipref.pos = flip + card:set_sprites(flipref) + else + flipref.pos = flop + card:set_sprites(flipref) + end + end, + calculate = function(self, card, context) + local flipref = card.config.center + if context.end_of_round and not context.blueprint and not context.repetition and not context.individual then + if card.ability.extra.side == "mult" then + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.3, blockable = false, + func = function() + card.ability.extra.side = "chips" + card:juice_up(1, 1) + flipref.pos = flop + card:set_sprites(flipref) + + return true; end})) + + G.hand:change_size(-card.ability.extra.hands) + G.GAME.round_resets.discards = G.GAME.round_resets.discards + card.ability.extra.discards + ease_discard(card.ability.extra.discards) + + return { + message = localize('k_flipped_ex'), + colour = G.C.CHIPS + } + else + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.3, blockable = false, + func = function() + card.ability.extra.side = "mult" + card:juice_up(1, 1) + flipref.pos = flip + card:set_sprites(flipref) + + return true; end})) + + G.hand:change_size(card.ability.extra.hands) + G.GAME.round_resets.discards = G.GAME.round_resets.discards - card.ability.extra.discards + ease_discard(-card.ability.extra.discards) + + return { + message = localize('k_flipped_ex'), + colour = G.C.MULT + } + end + + end + + if context.cardarea == G.jokers and context.joker_main then + if card.ability.extra.side == "mult" then + return { + message = localize{type='variable',key='a_mult',vars={card.ability.extra.mult}}, + mult_mod = card.ability.extra.mult, + colour = G.C.MULT + } + else + return { + message = localize{type='variable',key='a_chips',vars={card.ability.extra.chips}}, + chip_mod = card.ability.extra.chips, + colour = G.C.CHIPS + } + end + end + end +} + +-- See localization/en-us.lua to create joker text \ No newline at end of file diff --git a/mod/jokers/goldenegg.lua b/mod/jokers/goldenegg.lua new file mode 100644 index 0000000..fddaa59 --- /dev/null +++ b/mod/jokers/goldenegg.lua @@ -0,0 +1,57 @@ +SMODS.Atlas { + key = 'golden_egg', + path = "j_golden_egg.png", + px = 71, + py = 95 +} + +SMODS.Joker { + key = "golden_egg", + name = "Golden Egg", + atlas = 'golden_egg', + pos = { + x = 0, + y = 0 + }, + rarity = "fuse_fusion", + cost = 12, + unlocked = true, + discovered = false, + eternal_compat = true, + perishable_compat = true, + blueprint_compat = true, + config = { + extra = { + dollars = 4, + joker1 = "j_egg", + joker2 = "j_golden" + } + }, + loc_vars = function(self, info_queue, card) + return { + vars = { + card.ability.extra.dollars, + localize{type = 'name_text', key = card.ability.extra.joker1, set = 'Joker'}, + localize{type = 'name_text', key = card.ability.extra.joker2, set = 'Joker'} + } + } + end, + set_ability = function(self, card, initial, delay_sprites) + card:set_cost() + end, + calc_dollar_bonus = function(self, card) + return card.ability.extra.dollars + end, + calculate = function(self, card, context) + if context.end_of_round and not context.blueprint and not context.repetition and not context.individual then + card.ability.extra_value = card.ability.extra_value + card.ability.extra.dollars + card:set_cost() + return { + message = localize('k_val_up'), + colour = G.C.MONEY + } + end + end +} + +-- See localization/en-us.lua to create joker text \ No newline at end of file diff --git a/mod/jokers/heartpaladin.lua b/mod/jokers/heartpaladin.lua new file mode 100644 index 0000000..6dc7bd6 --- /dev/null +++ b/mod/jokers/heartpaladin.lua @@ -0,0 +1,64 @@ +SMODS.Atlas { + key = 'heart_paladin', + path = "j_heart_paladin.png", + px = 71, + py = 95 +} + +SMODS.Joker { + key = "heart_paladin", + name = "Heart Paladin", + atlas = 'heart_paladin', + pos = { + x = 0, + y = 0 + }, + rarity = "fuse_fusion", + cost = 12, + unlocked = true, + discovered = false, + eternal_compat = true, + perishable_compat = true, + blueprint_compat = true, + config = { + extra = { + odds = 3, + Xmult = 1.5, + joker1 = "j_lusty_joker", + joker2 = "j_bloodstone" + } + }, + loc_vars = function(self, info_queue, card) + return { + vars = { + card.ability.extra.Xmult, + ''..(G.GAME and G.GAME.probabilities.normal or 1), + card.ability.extra.odds, + localize{type = 'name_text', key = card.ability.extra.joker1, set = 'Joker'}, + localize{type = 'name_text', key = card.ability.extra.joker2, set = 'Joker'} + } + } + end, + calculate = function(self, card, context) + if context.individual and context.cardarea == G.play and + context.other_card:is_suit('Hearts') then + return { + x_mult = card.ability.extra.Xmult, + card = card + } + end + + if context.repetition and context.cardarea == G.play and + context.other_card:is_suit('Hearts') then + if pseudorandom('heart_paladin') < G.GAME.probabilities.normal/card.ability.extra.odds then + return { + message = localize('k_again_ex'), + repetitions = 1, + card = card + } + end + end + end +} + +-- See localization/en-us.lua to create joker text \ No newline at end of file diff --git a/mod/jokers/moonmarauder.lua b/mod/jokers/moonmarauder.lua new file mode 100644 index 0000000..fba3a36 --- /dev/null +++ b/mod/jokers/moonmarauder.lua @@ -0,0 +1,117 @@ +FusionJokers.fusions:add_fusion("j_slothful_joker", nil, false, "j_rainbow_moonstone", nil, false, "j_fuse_moon_marauder", 12) + +SMODS.Atlas { + key = 'moon_marauder', + path = "j_moon_marauder.png", + px = 71, + py = 95 +} + +SMODS.Joker { + key = "moon_marauder", + name = "Moon Marauder", + atlas = 'moon_marauder', + pos = { + x = 0, + y = 0 + }, + rarity = "fuse_fusion", + cost = 12, + unlocked = true, + discovered = false, + eternal_compat = true, + perishable_compat = true, + blueprint_compat = true, + config = { + extra = { + odds = 3, + joker1 = "j_slothful_joker", + joker2 = "j_rainbow_moonstone" + } + }, + loc_vars = function(self, info_queue, card) + return { + vars = { + '' .. (G.GAME and G.GAME.probabilities.normal or 1), + card.ability.extra.odds, + localize{type = 'name_text', key = card.ability.extra.joker1, set = 'Joker'}, + localize{type = 'name_text', key = card.ability.extra.joker2, set = 'Joker'} + } + } + end, + calculate = function(self, card, context) + if context.before and (context.cardarea == G.jokers) then + local moons = {} + for _, v in ipairs(context.full_hand) do + if v:is_suit('Moons') and not (v.ability.name == 'Glass Card') and pseudorandom('moon_marauder') < G.GAME.probabilities.normal / card.ability.extra.odds then + moons[#moons + 1] = v + v:set_ability(G.P_CENTERS.m_glass, nil, true) + G.E_MANAGER:add_event(Event({ + func = function() + v:juice_up() + return true + end + })) + end + end + if #moons > 0 then + return { + message = localize('k_glass'), + colour = G.C.SECONDARY_SET.Enhanced, + card = context.blueprint_card or card + } + end + end + + if context.remove_playing_cards and not context.blueprint then + -- G.E_MANAGER:add_event(Event({ + -- trigger = 'after', + -- delay = 1, + -- func = function() + -- sendDebugMessage(tostring(#context.removed)) + -- for k, v in ipairs(context.removed) do + -- sendDebugMessage(tostring(v.shattered)) + -- if v.shattered then + -- local _card = Card(G.play.T.x, G.play.T.y, G.CARD_W, G.CARD_H, G.P_CARDS.empty, G.P_CENTERS.c_base, {playing_card = G.playing_card}) + + -- _card = copy_card(v, _card, nil, G.playing_card) + -- _card:start_materialize({G.C.SECONDARY_SET.Enhanced}) + -- G.deck:emplace(_card) + + -- G.E_MANAGER:add_event(Event({ + -- func = function() + -- card_eval_status_text(context.blueprint_card or card, 'extra', nil, nil, nil, + -- { message = localize('k_in_tact_ex'), colour = G.C.SECONDARY_SET.edition }) + -- return true + -- end + -- })) + -- end + -- end + -- end + -- })) + sendDebugMessage(tostring(#context.removed)) + for k, v in ipairs(context.removed) do + sendDebugMessage(tostring(v.shattered)) + if v.shattered then + local _card = Card(G.play.T.x, G.play.T.y, G.CARD_W, G.CARD_H, G.P_CARDS.empty, G.P_CENTERS.c_base, {playing_card = G.playing_card}) + + G.playing_card = (G.playing_card and G.playing_card + 1) or 1 + _card = copy_card(v, _card, nil, G.playing_card) + _card:start_materialize({G.C.SECONDARY_SET.Enhanced}) + G.deck:emplace(_card) + table.insert(G.playing_cards, _card) + + G.E_MANAGER:add_event(Event({ + func = function() + card_eval_status_text(context.blueprint_card or card, 'extra', nil, nil, nil, + { message = localize('k_in_tact_ex'), colour = G.C.SECONDARY_SET.edition }) + return true + end + })) + end + end + end + end +} + +-- See localization/en-us.lua to create joker text \ No newline at end of file diff --git a/mod/jokers/royaldecree.lua b/mod/jokers/royaldecree.lua new file mode 100644 index 0000000..11188a3 --- /dev/null +++ b/mod/jokers/royaldecree.lua @@ -0,0 +1,61 @@ +SMODS.Atlas { + key = 'royal_decree', + path = "j_royal_decree.png", + px = 71, + py = 95 +} + +SMODS.Joker { + key = "royal_decree", + name = "Royal Decree", + atlas = 'royal_decree', + pos = { + x = 0, + y = 0 + }, + rarity = "fuse_fusion", + cost = 12, + unlocked = true, + discovered = false, + eternal_compat = true, + perishable_compat = true, + blueprint_compat = true, + config = { + extra = { + dollars = 2, + joker1 = "j_business", + joker2 = "j_reserved_parking", + } + }, + loc_vars = function(self, info_queue, card) + return { + vars = { + card.ability.extra.dollars, + localize{type = 'name_text', key = card.ability.extra.joker1, set = 'Joker'}, + localize{type = 'name_text', key = card.ability.extra.joker2, set = 'Joker'} + } + } + end, + calculate = function(self, card, context) + if context.end_of_round and context.individual then + if context.cardarea == G.hand and context.other_card:is_face() then + return { + h_dollars = 2, + card = card + } + end + end + + if context.cardarea == G.play and context.individual and context.other_card:is_face() then + G.GAME.dollar_buffer = (G.GAME.dollar_buffer or 0) + 2 + G.E_MANAGER:add_event(Event({func = (function() G.GAME.dollar_buffer = 0; return true end)})) + return { + dollars = 2, + card = card + } + + end + end +} + +-- See localization/en-us.lua to create joker text \ No newline at end of file diff --git a/mod/jokers/spadearcher.lua b/mod/jokers/spadearcher.lua new file mode 100644 index 0000000..698df0c --- /dev/null +++ b/mod/jokers/spadearcher.lua @@ -0,0 +1,69 @@ +SMODS.Atlas { + key = 'spade_archer', + path = "j_spade_archer.png", + px = 71, + py = 95 +} + +SMODS.Joker { + key = "spade_archer", + name = "Spade Archer", + atlas = 'spade_archer', + pos = { + x = 0, + y = 0 + }, + rarity = "fuse_fusion", + cost = 12, + unlocked = true, + discovered = false, + eternal_compat = true, + perishable_compat = true, + blueprint_compat = true, + config = { + extra = { + chips = 50, + chip_mod = 10, + joker1 = "j_wrathful_joker", + joker2 = "j_arrowhead" + } + }, + loc_vars = function(self, info_queue, card) + return { + vars = { + card.ability.extra.chips, + card.ability.extra.chip_mod, + localize{type = 'name_text', key = card.ability.extra.joker1, set = 'Joker'}, + localize{type = 'name_text', key = card.ability.extra.joker2, set = 'Joker'} + } + } + end, + calculate = function(self, card, context) + if context.individual and context.cardarea == G.play and + context.other_card:is_suit('Spades') then + return { + chips = card.ability.extra.chips, + card = card + } + end + + if context.before and context.cardarea == G.jokers then + local spades = 0 + for k, v in ipairs(context.scoring_hand) do + if v:is_suit('Spades') then + spades = spades + 1 + end + end + if spades == 5 then + card.ability.extra.chips = card.ability.extra.chips + card.ability.extra.chip_mod + return { + message = localize('k_upgrade_ex'), + colour = G.C.CHIPS, + card = card + } + end + end + end +} + +-- See localization/en-us.lua to create joker text \ No newline at end of file diff --git a/mod/jokers/staroracle.lua b/mod/jokers/staroracle.lua new file mode 100644 index 0000000..ad00cce --- /dev/null +++ b/mod/jokers/staroracle.lua @@ -0,0 +1,68 @@ +FusionJokers.fusions:add_fusion("j_envious_joker", nil, false, "j_star_ruby", nil, false, "j_fuse_star_oracle", 12) + +SMODS.Atlas { + key = 'star_oracle', + path = "j_star_oracle.png", + px = 71, + py = 95 +} + +SMODS.Joker { + key = "star_oracle", + name = "Star Oracle", + atlas = 'star_oracle', + pos = { + x = 0, + y = 0 + }, + rarity = "fuse_fusion", + cost = 12, + unlocked = true, + discovered = false, + eternal_compat = true, + perishable_compat = true, + blueprint_compat = true, + config = { + extra = { + odds = 10, + slots = 2, + joker1 = "j_envious_joker", + joker2 = "j_star_ruby", + } + }, + loc_vars = function(self, info_queue, card) + return { + vars = { + '' .. (G.GAME and G.GAME.probabilities.normal or 1), + card.ability.extra.odds, + localize{type = 'name_text', key = card.ability.extra.joker1, set = 'Joker'}, + localize{type = 'name_text', key = card.ability.extra.joker2, set = 'Joker'} + } + } + end, + calculate = function(self, card, context) + if context.individual and + context.cardarea == G.play and + context.other_card:is_suit('Stars') and + #G.consumeables.cards + G.GAME.consumeable_buffer < G.consumeables.config.card_limit and + pseudorandom('starruby') < G.GAME.probabilities.normal / card.ability.extra.odds then + G.GAME.consumeable_buffer = G.GAME.consumeable_buffer + 1 + G.E_MANAGER:add_event(Event({ + trigger = 'before', + delay = 0.0, + func = (function() + local _card = create_card('Spectral', G.consumeables, nil, nil, nil, nil, nil, 'ruby') + _card:add_to_deck() + G.consumeables:emplace(_card) + G.GAME.consumeable_buffer = 0 + return true + end) + })) + card_eval_status_text(context.blueprint_card or card, 'extra', nil, nil, nil, + { message = localize('k_plus_spectral'), colour = G.C.SECONDARY_SET.Spectral }) + return {} + end + end +} + +-- See localization/en-us.lua to create joker text \ No newline at end of file diff --git a/mod/jokers/uncannyface.lua b/mod/jokers/uncannyface.lua new file mode 100644 index 0000000..d8a301e --- /dev/null +++ b/mod/jokers/uncannyface.lua @@ -0,0 +1,59 @@ +SMODS.Atlas { + key = 'uncanny_face', + path = "j_uncanny_face.png", + px = 71, + py = 95 +} + +SMODS.Joker { + key = "uncanny_face", + name = "Uncanny Face", + atlas = 'uncanny_face', + pos = { + x = 0, + y = 0 + }, + rarity = "fuse_fusion", + cost = 12, + unlocked = true, + discovered = false, + eternal_compat = true, + perishable_compat = true, + blueprint_compat = true, + config = { + extra = { + chips = 15, + mult = 2, + joker1 = "j_scary_face", + joker2 = "j_smiley" + } + }, + loc_vars = function(self, info_queue, card) + return { + vars = { + card.ability.extra.chips, + card.ability.extra.mult, + localize{type = 'name_text', key = card.ability.extra.joker1, set = 'Joker'}, + localize{type = 'name_text', key = card.ability.extra.joker2, set = 'Joker'} + } + } + end, + calculate = function(self, card, context) + if context.individual and context.cardarea == G.play and + context.other_card:is_face() then + local faces = 0 + for k, v in ipairs(context.scoring_hand) do + if v:is_face() then + faces = faces + 1 + end + end + return { + mult = card.ability.extra.mult * faces, + chips = card.ability.extra.chips * faces, + card = card + } + end + end +} + +-- See localization/en-us.lua to create joker text \ No newline at end of file diff --git a/mod/localization/en-us.lua b/mod/localization/en-us.lua new file mode 100644 index 0000000..e062610 --- /dev/null +++ b/mod/localization/en-us.lua @@ -0,0 +1,189 @@ +return { + ["misc"] = { + ["dictionary"] = { + ["k_fusion"] = "Fusion", + ["k_fuse_fusion"] = "Fusion", + ["k_flipped_ex"] = "Flipped!", + ["k_copied_ex"] = "Cloned!", + ["k_in_tact_ex"] = "In-Tact!", + ["b_fuse"] = "FUSE", + }, + ["labels"] = { + ["k_fuse_fusion"] = "Fusion", + }, + }, + ["descriptions"] = { + ["Joker"] = { + ["j_fuse_diamond_bard"] = { + ["name"] = "Diamond Bard", + ["text"] = { + "Played cards with {C:diamonds}Diamond{} suit give", + "{C:money}$#1#{}, as well as {C:mult}+#2#{} Mult for every ", + "{C:money}$#3#{} you have when scored", + "{C:inactive}(#4# + #5#)", + }, + }, + ["j_fuse_heart_paladin"] = { + ["name"] = "Heart Paladin", + ["text"] = { + "Played cards with {C:hearts}Heart{} suit give", + "{X:mult,C:white}X#1#{} Mult when scored.", + "{C:green}#2# in #3#{} chance to re-trigger", + "{C:inactive}(#4# + #5#)", + }, + }, + ["j_fuse_spade_archer"] = { + ["name"] = "Spade Archer", + ["text"] = { + "Played cards with {C:spades}Spade{} suit give", + "{C:chips}+#1#{} Chips when scored. Gains {C:chips}+#2#{} ", + "chips when 5 {C:spades}Spades{} are played", + "{C:inactive}(#3# + #4#)", + }, + }, + ["j_fuse_club_wizard"] = { + ["name"] = "Club Wizard", + ["text"] = { + "Played cards with {C:clubs}Club{} suit", + "give {C:mult}+#1#{} Mult when scored", + "{C:inactive}(#2# + #3#)", + }, + }, + ["j_fuse_big_bang"] = { + ["name"] = "Big Bang", + ["text"] = { + "{X:mult,C:white} X#1# {} Mult per the number", + "of times {C:attention}poker hand{} has been played", + "plus the level of the {C:attention}poker hand{}.", + "{C:inactive}(#2# + #3#)", + }, + }, + ["j_fuse_dynamic_duo"] = { + ["name"] = "Dynamic Duo", + ["text"] = { + "Played {C:attention}number{} cards give {C:mult}+#1#{} Mult ", + "and {C:chips}+#2#{} Chips when scored.", + "{C:inactive}(#3# + #4#)", + }, + }, + ["j_fuse_collectible_chaos_card"] = { + ["name"] = "Collectible Chaos Card", + ["text"] = { + "{C:mult}+#1#{} Mult per {C:attention}reroll{} in the shop.", + "{C:attention}#2#{} free {C:green}Reroll{} per shop", + "{C:inactive}(Currently {C:mult}+#3#{C:inactive} Mult)", + "{C:inactive}(#4# + #5#)", + }, + }, + ["j_fuse_flip_flop"] = { + ["name"] = "Flip-Flop", + ["text"] = { + "{C:attention}+#1#{} hand size. {C:red}+#2#{} Mult", + "{C:attention}Flips{} after each blind", + "{C:inactive}(#3# + #4#)", + }, + }, + ["j_fuse_flip_flop_mult"] = { + ["name"] = "Flip-Flop", + ["text"] = { + "{C:attention}+#1#{} hand size. {C:red}+#2#{} Mult", + "{C:attention}Flips{} after each blind", + "{C:inactive}(#3# + #4#)", + }, + }, + ["j_fuse_flip_flop_chips"] = { + ["name"] = "Flip-Flop", + ["text"] = { + "{C:red}+#1#{} discard. {C:chips}+#2#{} Chips", + "{C:attention}Flips{} after each blind", + "{C:inactive}(#3# + #4#)", + }, + }, + ["j_fuse_royal_decree"] = { + ["name"] = "Royal Decree", + ["text"] = { + "Played {C:attention}face{} cards give {C:money}$#1#{} when scored.", + "Each {C:attention}face{} card held in hand", + "at end of round gives {C:money}$#1#{}", + "{C:inactive}(#2# + #3#)", + }, + }, + ["j_fuse_dementia_joker"] = { + ["name"] = "Dementia Joker", + ["text"] = { + "{C:mult}+#1#{} Mult for each {C:attention}Joker{} card.", + "{C:green}#2# in #3#{} chance to {C:attention}clone{} if ", + "not {C:dark_edition}Negative{} after you beat a blind", + "{C:inactive}(Currently {C:mult}+#4#{C:inactive} Mult)", + "{C:inactive}(#5# + #6#)", + }, + }, + ["j_fuse_golden_egg"] = { + ["name"] = "Golden Egg", + ["text"] = { + "Gains {C:money}$#1#{} of {C:attention}sell value{}", + " at end of round.", + " Earn {C:money}$#1#{} at end of round", + "{C:inactive}(#2# + #3#)", + }, + }, + ["j_fuse_flag_bearer"] = { + ["name"] = "Flag Bearer", + ["text"] = { + "{C:mult}+#1#{} Mult per hand played, {C:mult}-#2#{} Mult", + "per discard. Mult is multiplied by", + " remaining {C:attention}discards{}", + "{C:inactive}(Currently {C:mult}+#3#{C:inactive} Mult)", + "{C:inactive}(#4# + #5#)", + }, + }, + ["j_fuse_uncanny_face"] = { + ["name"] = "Uncanny Face", + ["text"] = { + "Played {C:attention}face{} cards give {C:chips}+#1#{} Chips and", + "{C:mult}+#2#{} Mult for every {C:attention}face{} card", + " in the scoring hand", + "{C:inactive}(#3# + #4#)", + }, + }, + ["j_fuse_commercial_driver"] = { + ["name"] = "Commercial Driver", + ["text"] = { + "{X:mult,C:white} X#1# {} Mult per consecutive hand", + "played with a scoring {C:attention}enhanced{} card", + "{C:inactive}(Currently {X:mult,C:white}X#2#{C:inactive} Mult)", + "{C:inactive}(#3# + #4#)", + }, + }, + ["j_fuse_camping_trip"] = { + ["name"] = "Camping Trip", + ["text"] = { + "Played {C:attention}cards{} permanently gains", + "{C:chips}+#1#{} Chips when scored", + "({C:chips}+#2#{} on the final hand)", + "Your final hand triggers twice", + "{C:inactive}(#3# + #4#)", + }, + }, + ["j_fuse_star_oracle"] = { + ["name"] = "Star Oracle", + ["text"] = { + "{C:attention}+2{} consumable slots.", + "{C:green}#1# in #2#{} chance for played cards", + " with {C:stars}Star{} suit to create a", + "random {C:spectral}Spectral{} card when scored", + "{C:inactive}(#3# + #4#)", + }, + }, + ["j_fuse_moon_marauder"] = { + ["name"] = "Moon Marauder", + ["text"] = { + "{C:green}#1# in #2#{} chance for played cards with", + "{C:moons}Moon{} suit to become {C:attention}Glass{} Cards.", + "played {C:moons}Moon{} {C:attention}Glass{} cards never shatter", + "{C:inactive}(#3# + #4#)", + }, + }, + }, + }, +} \ No newline at end of file From 8e2b58941aeffbba82d8ae5d814b5b0824fb85f0 Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Sun, 20 Apr 2025 13:06:07 -0400 Subject: [PATCH 11/91] right, we ended up not needing that --- mod/FusionJokers.lua | 8 -------- 1 file changed, 8 deletions(-) diff --git a/mod/FusionJokers.lua b/mod/FusionJokers.lua index 2156724..7f52789 100644 --- a/mod/FusionJokers.lua +++ b/mod/FusionJokers.lua @@ -402,14 +402,6 @@ end -local flip = { - x = 0, - y = 0 -} -local flop = { - x = 0, - y = 1 -} local updateref = Card.update function Card:update(dt) updateref(self, dt) From c668b22a580877e021a4589d793052ea6f7bccec Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Sun, 20 Apr 2025 13:21:06 -0400 Subject: [PATCH 12/91] update more things to modern standards --- mod/FusionJokers.lua | 58 --------------------------------- mod/jokers/collectiblechaos.lua | 20 ++++++++++-- mod/jokers/flipflop.lua | 16 +++++++++ mod/jokers/staroracle.lua | 6 ++++ 4 files changed, 40 insertions(+), 60 deletions(-) diff --git a/mod/FusionJokers.lua b/mod/FusionJokers.lua index 7f52789..a38e63b 100644 --- a/mod/FusionJokers.lua +++ b/mod/FusionJokers.lua @@ -427,64 +427,6 @@ function Card:update(dt) end end - -local add_to_deckref = Card.add_to_deck -function Card:add_to_deck(from_debuff) - if not self.added_to_deck then - if self.ability.name == 'Collectible Chaos Card' then - G.GAME.current_round.free_rerolls = G.GAME.current_round.free_rerolls + 1 - calculate_reroll_cost(true) - end - - if self.ability.name == 'Flip-Flop' then - if self.ability.extra.side == "mult" then - G.hand:change_size(self.ability.extra.hands) - else - G.GAME.round_resets.discards = G.GAME.round_resets.discards + self.ability.extra.discards - ease_discard(self.ability.extra.discards) - end - end - - if self.ability.name == 'Star Oracle' then - G.consumeables:change_size(self.ability.extra.slots) - end - end - add_to_deckref(self, from_debuff) -end - -local remove_from_deckref = Card.remove_from_deck -function Card:remove_from_deck(from_debuff) - if self.added_to_deck then - if self.ability.name == 'Collectible Chaos Card' then - G.GAME.current_round.free_rerolls = G.GAME.current_round.free_rerolls - 1 - calculate_reroll_cost(true) - end - - if self.ability.name == 'Flip-Flop' then - if self.ability.extra.side == "mult" then - G.hand:change_size(-self.ability.extra.hands) - else - G.GAME.round_resets.discards = G.GAME.round_resets.discards - self.ability.extra.discards - ease_discard(-self.ability.extra.discards) - end - end - - if self.ability.name == 'Star Oracle' then - G.consumeables:change_size(-self.ability.extra.slots) - end - end - remove_from_deckref(self, from_debuff) -end - -local new_roundref = new_round -function new_round() - new_roundref() - local chaos = find_joker('Collectible Chaos Card') - G.GAME.current_round.free_rerolls = G.GAME.current_round.free_rerolls or 0 - G.GAME.current_round.free_rerolls = G.GAME.current_round.free_rerolls + #chaos - calculate_reroll_cost(true) -end - -- local shatterref = Card.shatter -- function Card:shatter() -- G.E_MANAGER:add_event(Event({ diff --git a/mod/jokers/collectiblechaos.lua b/mod/jokers/collectiblechaos.lua index f02021e..4fc3b5b 100644 --- a/mod/jokers/collectiblechaos.lua +++ b/mod/jokers/collectiblechaos.lua @@ -23,9 +23,9 @@ SMODS.Joker { config = { extra = { per_reroll = 2, - free = 1, + free = 1, mult = 0, - joker1 = "j_chaos", + joker1 = "j_chaos", joker2 = "j_flash" } }, @@ -40,6 +40,16 @@ SMODS.Joker { } } end, + add_to_deck = function(self, from_debuff) + if not from_debuff then -- no gameplan shenanigans for bunco enjoyers :p + G.GAME.current_round.free_rerolls = G.GAME.current_round.free_rerolls + self.ability.extra.free + calculate_reroll_cost(true) + end + end, + remove_from_deck = function(self, from_debuff) + G.GAME.current_round.free_rerolls = math.max(G.GAME.current_round.free_rerolls - self.ability.extra.free, 0) + calculate_reroll_cost(true) + end, set_ability = function(self, card, initial, delay_sprites) if card.ability.mult and (card.ability.extra.mult == 0) then card.ability.extra.mult = card.ability.mult @@ -47,6 +57,12 @@ SMODS.Joker { end end, calculate = function(self, card, context) + if context.starting_shop then + G.GAME.current_round.free_rerolls = G.GAME.current_round.free_rerolls or 0 + G.GAME.current_round.free_rerolls = G.GAME.current_round.free_rerolls + card.ability.extra.free + calculate_reroll_cost(true) + end + if context.reroll_shop and not context.blueprint then card.ability.extra.mult = card.ability.extra.mult + card.ability.extra.per_reroll G.E_MANAGER:add_event(Event({ diff --git a/mod/jokers/flipflop.lua b/mod/jokers/flipflop.lua index a3e42aa..c09d10f 100644 --- a/mod/jokers/flipflop.lua +++ b/mod/jokers/flipflop.lua @@ -71,6 +71,22 @@ SMODS.Joker { card:set_sprites(flipref) end end, + add_to_deck = function(self, from_debuff) + if self.ability.extra.side == "mult" then + G.hand:change_size(self.ability.extra.hands) + else + G.GAME.round_resets.discards = G.GAME.round_resets.discards + self.ability.extra.discards + ease_discard(self.ability.extra.discards) + end + end, + remove_from_deck = function(self, from_debuff) + if self.ability.extra.side == "mult" then + G.hand:change_size(-self.ability.extra.hands) + else + G.GAME.round_resets.discards = G.GAME.round_resets.discards - self.ability.extra.discards + ease_discard(-self.ability.extra.discards) + end + end, calculate = function(self, card, context) local flipref = card.config.center if context.end_of_round and not context.blueprint and not context.repetition and not context.individual then diff --git a/mod/jokers/staroracle.lua b/mod/jokers/staroracle.lua index ad00cce..03409b2 100644 --- a/mod/jokers/staroracle.lua +++ b/mod/jokers/staroracle.lua @@ -40,6 +40,12 @@ SMODS.Joker { } } end, + add_to_deck = function(self, from_debuff) + G.consumeables:change_size(self.ability.extra.slots) + end, + remove_from_deck = function(self, from_debuff) + G.consumeables:change_size(-self.ability.extra.slots) + end, calculate = function(self, card, context) if context.individual and context.cardarea == G.play and From 8d7437d12bb4cef54d41fffa1368b3c097f1474b Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Sun, 20 Apr 2025 13:29:00 -0400 Subject: [PATCH 13/91] you can't copy that :p --- mod/jokers/collectiblechaos.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mod/jokers/collectiblechaos.lua b/mod/jokers/collectiblechaos.lua index 4fc3b5b..cbe4c37 100644 --- a/mod/jokers/collectiblechaos.lua +++ b/mod/jokers/collectiblechaos.lua @@ -57,7 +57,7 @@ SMODS.Joker { end end, calculate = function(self, card, context) - if context.starting_shop then + if context.starting_shop and not context.blueprint then G.GAME.current_round.free_rerolls = G.GAME.current_round.free_rerolls or 0 G.GAME.current_round.free_rerolls = G.GAME.current_round.free_rerolls + card.ability.extra.free calculate_reroll_cost(true) From 8aa3bd5fff24ed9c73007119de63a67e2246fbb7 Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Sun, 20 Apr 2025 13:31:15 -0400 Subject: [PATCH 14/91] what's a 'release', never heard of it --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3fc7ce2..8548a7e 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ You can find a list of their abilities, as well as the jokers needed to make the ## ⬇ How to Download - The mod requires Steamodded. You can see info about how to use it [here](https://github.com/Steamopollys/Steamodded) -- Download the latest release of Fusion Jokers +- Download the latest commit of Fusion Jokers from the green "Code" button up there - Extract the downloaded mod to the Mods folder (at %appdata%/Balatro/Mods) ## ⚙️ Config From 222286db29b203facdde90aab174e35afeb83ced Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Sun, 20 Apr 2025 17:26:03 -0400 Subject: [PATCH 15/91] me: 'i wonder how to fix the issue where it takes another round before it inherits sell value'. the noble add_to_deck(): --- mod/jokers/goldenegg.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mod/jokers/goldenegg.lua b/mod/jokers/goldenegg.lua index fddaa59..93521e8 100644 --- a/mod/jokers/goldenegg.lua +++ b/mod/jokers/goldenegg.lua @@ -36,7 +36,7 @@ SMODS.Joker { } } end, - set_ability = function(self, card, initial, delay_sprites) + add_to_deck = function (self, card, from_debuff) card:set_cost() end, calc_dollar_bonus = function(self, card) From 045371ac43abb1956c419853b4f779bc7e74da9f Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Sun, 20 Apr 2025 18:32:21 -0400 Subject: [PATCH 16/91] why was this local --- mod/FusionJokers.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mod/FusionJokers.lua b/mod/FusionJokers.lua index a38e63b..ea1ae4d 100644 --- a/mod/FusionJokers.lua +++ b/mod/FusionJokers.lua @@ -134,11 +134,11 @@ SMODS.load_file('jokers/uncannyface.lua')() SMODS.load_file('jokers/commercialdriver.lua')() SMODS.load_file('jokers/campingtrip.lua')() -local to_number = to_number or function(num) +to_number = to_number or function(num) return num end -local to_big = to_big or function(num) +to_big = to_big or function(num) return num end From 11eba38283409aa5aefdab62bf924fe9e8f9e35f Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Sun, 27 Apr 2025 12:48:21 -0400 Subject: [PATCH 17/91] update dd to todd's new value --- mod/jokers/dynamicduo.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mod/jokers/dynamicduo.lua b/mod/jokers/dynamicduo.lua index 3a64e4b..047646c 100644 --- a/mod/jokers/dynamicduo.lua +++ b/mod/jokers/dynamicduo.lua @@ -22,16 +22,16 @@ SMODS.Joker { blueprint_compat = true, config = { extra = { - mult = 4, - chips = 30, - joker1 = "j_even_steven", + mult = 4, + chips = 31, + joker1 = "j_even_steven", joker2 = "j_odd_todd" } }, loc_vars = function(self, info_queue, card) return { vars = { - card.ability.extra.mult, + card.ability.extra.mult, card.ability.extra.chips, localize{type = 'name_text', key = card.ability.extra.joker1, set = 'Joker'}, localize{type = 'name_text', key = card.ability.extra.joker2, set = 'Joker'} From 45cac7e5376e1d3b7ccb753435612f945cef9fd4 Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Sun, 27 Apr 2025 12:50:41 -0400 Subject: [PATCH 18/91] ... use markdown syntax, not mediawiki --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8548a7e..686baea 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ You can find a list of their abilities, as well as the jokers needed to make the ## ⚙️ Config -- '''Block used components from reappearing''': Jokers used in a fusion cannot reappear if the fusion is present. Currently only applies to Fusion Jokers fusions; cross-mod functionality is planned. Default true. +- **Block used components from reappearing**: Jokers used in a fusion cannot reappear if the fusion is present. Currently only applies to Fusion Jokers fusions; cross-mod functionality is planned. Default true. ## ➕ Fusion API (For Developers) From 61ab9a02180d6b86676c2163da216a091808f867 Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Sun, 27 Apr 2025 12:51:00 -0400 Subject: [PATCH 19/91] clarify that --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 686baea..9be4e77 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ You can find a list of their abilities, as well as the jokers needed to make the ## ⚙️ Config -- **Block used components from reappearing**: Jokers used in a fusion cannot reappear if the fusion is present. Currently only applies to Fusion Jokers fusions; cross-mod functionality is planned. Default true. +- **Block used components from reappearing**: Jokers used in a fusion cannot reappear if the fusion is present (unless Showman is also present). Currently only applies to Fusion Jokers fusions; cross-mod functionality is planned. Default true. ## ➕ Fusion API (For Developers) From 0498f439fe3252755c1f7cb26111f4fd90e72c89 Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Wed, 30 Apr 2025 13:57:53 -0400 Subject: [PATCH 20/91] document the api --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index 9be4e77..0f781d0 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,20 @@ The mod adds a way for other developers to create their own fusions! Developers can call the function `FusionJokers.fusions:add_fusion()` to register their own fusion to the game. The function also allows them to determine carried stats between the component jokers and the fusion joker. +```lua +FusionJokers.fusions:add_fusion( + joker1, --string, component joker's key + carry_stat1, --string, name of stat to carry into fusion + extra1, --boolean, is the carry stat in an "extra" table or not + joker2, -- + carry_stat2, -- same as 1, but for the other component joker + extra2, -- + result_joker, --result of fusion + cost, --cost to fuse + merged_stat, merge_stat1, merge_stat2, merge_extra --TODO explain these +) +``` + ## 🎉 Credits - The original mod was written by [**Itayfeder**](https://github.com/stars/itayfeder/lists/balatro-modding), with art created by [**Lyman**](https://github.com/spikeof2010) From a9130bab1696ac6a3a92bbe779b7f4fbc2643615 Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Wed, 30 Apr 2025 14:42:03 -0400 Subject: [PATCH 21/91] more smods 1.0 updates --- mod/jokers/collectiblechaos.lua | 8 ++++---- mod/jokers/flipflop.lua | 20 ++++++++++---------- mod/jokers/moonmarauder.lua | 6 +++--- mod/jokers/staroracle.lua | 14 +++++++------- 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/mod/jokers/collectiblechaos.lua b/mod/jokers/collectiblechaos.lua index cbe4c37..112b328 100644 --- a/mod/jokers/collectiblechaos.lua +++ b/mod/jokers/collectiblechaos.lua @@ -40,14 +40,14 @@ SMODS.Joker { } } end, - add_to_deck = function(self, from_debuff) + add_to_deck = function(self, card, from_debuff) if not from_debuff then -- no gameplan shenanigans for bunco enjoyers :p - G.GAME.current_round.free_rerolls = G.GAME.current_round.free_rerolls + self.ability.extra.free + G.GAME.current_round.free_rerolls = G.GAME.current_round.free_rerolls + card.ability.extra.free calculate_reroll_cost(true) end end, - remove_from_deck = function(self, from_debuff) - G.GAME.current_round.free_rerolls = math.max(G.GAME.current_round.free_rerolls - self.ability.extra.free, 0) + remove_from_deck = function(self, card, from_debuff) + G.GAME.current_round.free_rerolls = math.max(G.GAME.current_round.free_rerolls - card.ability.extra.free, 0) calculate_reroll_cost(true) end, set_ability = function(self, card, initial, delay_sprites) diff --git a/mod/jokers/flipflop.lua b/mod/jokers/flipflop.lua index c09d10f..f2b59d6 100644 --- a/mod/jokers/flipflop.lua +++ b/mod/jokers/flipflop.lua @@ -71,20 +71,20 @@ SMODS.Joker { card:set_sprites(flipref) end end, - add_to_deck = function(self, from_debuff) - if self.ability.extra.side == "mult" then - G.hand:change_size(self.ability.extra.hands) + add_to_deck = function(self, card, from_debuff) + if card.ability.extra.side == "mult" then + G.hand:change_size(card.ability.extra.hands) else - G.GAME.round_resets.discards = G.GAME.round_resets.discards + self.ability.extra.discards - ease_discard(self.ability.extra.discards) + G.GAME.round_resets.discards = G.GAME.round_resets.discards + card.ability.extra.discards + ease_discard(card.ability.extra.discards) end end, - remove_from_deck = function(self, from_debuff) - if self.ability.extra.side == "mult" then - G.hand:change_size(-self.ability.extra.hands) + remove_from_deck = function(self, card, from_debuff) + if card.ability.extra.side == "mult" then + G.hand:change_size(-card.ability.extra.hands) else - G.GAME.round_resets.discards = G.GAME.round_resets.discards - self.ability.extra.discards - ease_discard(-self.ability.extra.discards) + G.GAME.round_resets.discards = G.GAME.round_resets.discards - card.ability.extra.discards + ease_discard(-eval_card.ability.extra.discards) end end, calculate = function(self, card, context) diff --git a/mod/jokers/moonmarauder.lua b/mod/jokers/moonmarauder.lua index fba3a36..4ce3784 100644 --- a/mod/jokers/moonmarauder.lua +++ b/mod/jokers/moonmarauder.lua @@ -1,4 +1,4 @@ -FusionJokers.fusions:add_fusion("j_slothful_joker", nil, false, "j_rainbow_moonstone", nil, false, "j_fuse_moon_marauder", 12) +FusionJokers.fusions:add_fusion("j_six_slothful_joker", nil, false, "j_six_moonstone", nil, false, "j_fuse_moon_marauder", 12) SMODS.Atlas { key = 'moon_marauder', @@ -25,8 +25,8 @@ SMODS.Joker { config = { extra = { odds = 3, - joker1 = "j_slothful_joker", - joker2 = "j_rainbow_moonstone" + joker1 = "j_six_slothful_joker", + joker2 = "j_six_moonstone" } }, loc_vars = function(self, info_queue, card) diff --git a/mod/jokers/staroracle.lua b/mod/jokers/staroracle.lua index 03409b2..84447c4 100644 --- a/mod/jokers/staroracle.lua +++ b/mod/jokers/staroracle.lua @@ -1,4 +1,4 @@ -FusionJokers.fusions:add_fusion("j_envious_joker", nil, false, "j_star_ruby", nil, false, "j_fuse_star_oracle", 12) +FusionJokers.fusions:add_fusion("j_six_envious_joker", nil, false, "j_six_star_ruby", nil, false, "j_fuse_star_oracle", 12) SMODS.Atlas { key = 'star_oracle', @@ -26,8 +26,8 @@ SMODS.Joker { extra = { odds = 10, slots = 2, - joker1 = "j_envious_joker", - joker2 = "j_star_ruby", + joker1 = "j_six_envious_joker", + joker2 = "j_six_star_ruby", } }, loc_vars = function(self, info_queue, card) @@ -40,11 +40,11 @@ SMODS.Joker { } } end, - add_to_deck = function(self, from_debuff) - G.consumeables:change_size(self.ability.extra.slots) + add_to_deck = function(self, card, from_debuff) + G.consumeables:change_size(card.ability.extra.slots) end, - remove_from_deck = function(self, from_debuff) - G.consumeables:change_size(-self.ability.extra.slots) + remove_from_deck = function(self, card, from_debuff) + G.consumeables:change_size(-card.ability.extra.slots) end, calculate = function(self, card, context) if context.individual and From 6fc1382830ca21b064ab7f3173f1ebf090e3440b Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Fri, 2 May 2025 11:59:12 -0400 Subject: [PATCH 22/91] don't double upgrade spade archer just because blueprint is there :p --- mod/jokers/spadearcher.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mod/jokers/spadearcher.lua b/mod/jokers/spadearcher.lua index 698df0c..5e923c9 100644 --- a/mod/jokers/spadearcher.lua +++ b/mod/jokers/spadearcher.lua @@ -47,7 +47,7 @@ SMODS.Joker { } end - if context.before and context.cardarea == G.jokers then + if context.before and context.cardarea == G.jokers and not context.blueprint then local spades = 0 for k, v in ipairs(context.scoring_hand) do if v:is_suit('Spades') then From 0ff9279b3ddd6a7293a7e3e576304fa1bfb3ed2d Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Fri, 2 May 2025 12:03:41 -0400 Subject: [PATCH 23/91] ?? where did that come from --- mod/jokers/flipflop.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mod/jokers/flipflop.lua b/mod/jokers/flipflop.lua index f2b59d6..675b24e 100644 --- a/mod/jokers/flipflop.lua +++ b/mod/jokers/flipflop.lua @@ -84,7 +84,7 @@ SMODS.Joker { G.hand:change_size(-card.ability.extra.hands) else G.GAME.round_resets.discards = G.GAME.round_resets.discards - card.ability.extra.discards - ease_discard(-eval_card.ability.extra.discards) + ease_discard(-card.ability.extra.discards) end end, calculate = function(self, card, context) From 3c5f530f4ed351355112bee1c98c7ffdc4d08105 Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Fri, 2 May 2025 13:17:40 -0400 Subject: [PATCH 24/91] give this a beta version number for reference --- mod/FusionJokers.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mod/FusionJokers.json b/mod/FusionJokers.json index c248312..d0718b6 100644 --- a/mod/FusionJokers.json +++ b/mod/FusionJokers.json @@ -9,7 +9,7 @@ "priority": -10000, "badge_colour": "B26CBB", "badge_text_colour": "FFFFFF", - "version": "1.0.0", + "version": "1.0.0~BETA-00502a", "dependencies": [ "Steamodded (>=1.0.0~ALPHA-1307d)", ], From 376a7f959d40dd91a93abd402d7a8bac9cdaf8cc Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Thu, 15 May 2025 22:42:31 -0400 Subject: [PATCH 25/91] spade archer now scales with 5+ spades --- mod/jokers/spadearcher.lua | 2 +- mod/localization/en-us.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mod/jokers/spadearcher.lua b/mod/jokers/spadearcher.lua index 5e923c9..15b96a2 100644 --- a/mod/jokers/spadearcher.lua +++ b/mod/jokers/spadearcher.lua @@ -54,7 +54,7 @@ SMODS.Joker { spades = spades + 1 end end - if spades == 5 then + if spades >= 5 then card.ability.extra.chips = card.ability.extra.chips + card.ability.extra.chip_mod return { message = localize('k_upgrade_ex'), diff --git a/mod/localization/en-us.lua b/mod/localization/en-us.lua index e062610..99ae859 100644 --- a/mod/localization/en-us.lua +++ b/mod/localization/en-us.lua @@ -37,7 +37,7 @@ return { ["text"] = { "Played cards with {C:spades}Spade{} suit give", "{C:chips}+#1#{} Chips when scored. Gains {C:chips}+#2#{} ", - "chips when 5 {C:spades}Spades{} are played", + "chips when 5 or more {C:spades}Spades{} are played", "{C:inactive}(#3# + #4#)", }, }, From f0ded6ece3483e6840ad914d3bd07b39899dfa35 Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Thu, 15 May 2025 22:43:00 -0400 Subject: [PATCH 26/91] version --- mod/FusionJokers.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mod/FusionJokers.json b/mod/FusionJokers.json index d0718b6..27c0944 100644 --- a/mod/FusionJokers.json +++ b/mod/FusionJokers.json @@ -9,7 +9,7 @@ "priority": -10000, "badge_colour": "B26CBB", "badge_text_colour": "FFFFFF", - "version": "1.0.0~BETA-00502a", + "version": "1.0.0~BETA-00515a", "dependencies": [ "Steamodded (>=1.0.0~ALPHA-1307d)", ], From 7004a3428d206d589b4a2a92fa014a39a08cd1dc Mon Sep 17 00:00:00 2001 From: Matthew Yarnot Date: Mon, 19 May 2025 19:10:53 -0500 Subject: [PATCH 27/91] Add files via upload --- mod/jokers/bigbang.lua | 17 +++++++++++++++ mod/jokers/campingtrip.lua | 18 ++++++++++++++++ mod/jokers/clubwizard.lua | 28 ++++++++++++++++++++++++ mod/jokers/collectiblechaos.lua | 9 ++++++++ mod/jokers/commercialdriver.lua | 12 +++++++++++ mod/jokers/dementiajoker.lua | 23 ++++++++++++++++++++ mod/jokers/diamondbard.lua | 34 +++++++++++++++++++++++++++++ mod/jokers/dynamicduo.lua | 29 +++++++++++++++++++++++++ mod/jokers/flagbearer.lua | 12 +++++++++++ mod/jokers/flipflop.lua | 21 ++++++++++++++++++ mod/jokers/goldenegg.lua | 24 +++++++++++++++++++++ mod/jokers/heartpaladin.lua | 36 +++++++++++++++++++++++++++++++ mod/jokers/royaldecree.lua | 38 +++++++++++++++++++++++++++++++++ mod/jokers/spadearcher.lua | 30 +++++++++++++++++++++++++- mod/jokers/uncannyface.lua | 31 +++++++++++++++++++++++++++ 15 files changed, 361 insertions(+), 1 deletion(-) diff --git a/mod/jokers/bigbang.lua b/mod/jokers/bigbang.lua index 1e45860..3e2ff6d 100644 --- a/mod/jokers/bigbang.lua +++ b/mod/jokers/bigbang.lua @@ -44,6 +44,23 @@ SMODS.Joker { Xmult_mod = mult_val } end + end, + joker_display_def = function(JokerDisplay) + return { + text = { + { + border_nodes = { + { text = "X" }, + { ref_table = "card.joker_display_values", ref_value = "Xmult", retrigger_type = "exp" } + } + } + }, + calc_function = function(card) + local text, _, _ = JokerDisplay.evaluate_hand() + card.joker_display_values.Xmult = 1 + card.ability.extra.Xmult * ((text ~= 'Unknown' and G.GAME and G.GAME.hands[text] and G.GAME.hands[text].level + G.GAME.hands[text].played + (next(G.play.cards) and 0 or 1)) or + 0) + end + } end } diff --git a/mod/jokers/campingtrip.lua b/mod/jokers/campingtrip.lua index bbfad48..7f39524 100644 --- a/mod/jokers/campingtrip.lua +++ b/mod/jokers/campingtrip.lua @@ -60,6 +60,24 @@ SMODS.Joker { card = card } end + end, + joker_display_def = function(JokerDisplay) + return { + reminder_text = { + { text = "(" }, + { ref_table = "card.joker_display_values", ref_value = "active" }, + { text = ")" }, + }, + calc_function = function(card) + card.joker_display_values.active = G.GAME and G.GAME.current_round.hands_left <= 1 and + localize("jdis_active") or localize("jdis_inactive") + end, + retrigger_function = function(playing_card, scoring_hand, held_in_hand, joker_card) + if held_in_hand then return 0 end + return G.GAME and G.GAME.current_round.hands_left <= 1 and + joker_card.ability.extra * JokerDisplay.calculate_joker_triggers(joker_card) or 0 + end + } end } diff --git a/mod/jokers/clubwizard.lua b/mod/jokers/clubwizard.lua index 174bc13..6716cb8 100644 --- a/mod/jokers/clubwizard.lua +++ b/mod/jokers/clubwizard.lua @@ -44,6 +44,34 @@ SMODS.Joker { card = card } end + end, + joker_display_def = function(JokerDisplay) + return { + text = { + { text = "+" }, + { ref_table = "card.joker_display_values", ref_value = "mult", retrigger_type = "mult" }, + }, + text_config = { colour = G.C.MULT }, + reminder_text = { + { text = "(" }, + { ref_table = "card.joker_display_values", ref_value = "localized_text", colour = lighten(G.C.SUITS["Clubs"], 0.35) }, + { text = ")" } + }, + calc_function = function(card) + local mult = 0 + local text, _, scoring_hand = JokerDisplay.evaluate_hand() + if text ~= 'Unknown' then + for _, scoring_card in pairs(scoring_hand) do + if scoring_card:is_suit("Clubs") then + mult = mult + + card.ability.extra.mult * JokerDisplay.calculate_card_triggers(scoring_card, scoring_hand) + end + end + end + card.joker_display_values.mult = mult + card.joker_display_values.localized_text = localize("Clubs", 'suits_plural') + end + } end } diff --git a/mod/jokers/collectiblechaos.lua b/mod/jokers/collectiblechaos.lua index 112b328..143c28c 100644 --- a/mod/jokers/collectiblechaos.lua +++ b/mod/jokers/collectiblechaos.lua @@ -78,6 +78,15 @@ SMODS.Joker { mult_mod = card.ability.extra.mult } end + end, + joker_display_def = function(JokerDisplay) + return { + text = { + { text = "+" }, + { ref_table = "card.ability", ref_value = "mult", retrigger_type = "mult" } + }, + text_config = { colour = G.C.MULT }, + } end } diff --git a/mod/jokers/commercialdriver.lua b/mod/jokers/commercialdriver.lua index 45ed394..20753f8 100644 --- a/mod/jokers/commercialdriver.lua +++ b/mod/jokers/commercialdriver.lua @@ -64,6 +64,18 @@ SMODS.Joker { Xmult_mod = card.ability.extra.total } end + end, + joker_display_def = function(JokerDisplay) + return { + text = { + { + border_nodes = { + { text = "X" }, + { ref_table = "card.ability.extra", ref_value = "total" } + } + } + } + } end } diff --git a/mod/jokers/dementiajoker.lua b/mod/jokers/dementiajoker.lua index f046768..12c2a38 100644 --- a/mod/jokers/dementiajoker.lua +++ b/mod/jokers/dementiajoker.lua @@ -64,6 +64,29 @@ SMODS.Joker { mult_mod = x*card.ability.extra.mult } end + end, + joker_display_def = function(JokerDisplay) + return { + text = { + { text = "+" }, + { ref_table = "card.joker_display_values", ref_value = "mult", retrigger_type = "mult" } + }, + extra = { + { + { ref_table = "card.joker_display_values", ref_value = "odds" }, + } + }, + extra_config = { colour = G.C.GREEN, scale = 0.3 }, + text_config = { colour = G.C.MULT }, + calc_function = function(card) + card.joker_display_values.mult = (G.jokers and G.jokers.cards and #G.jokers.cards or 0) * card.ability.extra.mult + if card.edition and card.edition.negative then + card.joker_display_values.odds = "" + else + card.joker_display_values.odds = localize { type = 'variable', key = "jdis_odds", vars = { (G.GAME and G.GAME.probabilities.normal or 1), card.ability.extra.odds } } + end + end + } end } diff --git a/mod/jokers/diamondbard.lua b/mod/jokers/diamondbard.lua index e23b0e3..10ab58c 100644 --- a/mod/jokers/diamondbard.lua +++ b/mod/jokers/diamondbard.lua @@ -51,6 +51,40 @@ SMODS.Joker { card = card } end + end, + joker_display_def = function(JokerDisplay) + return { + text = { + { text = "+", colour = G.C.MULT }, + { ref_table = "card.joker_display_values", ref_value = "mult", retrigger_type = "mult", colour = G.C.MULT }, + { text = ", " }, + { text = "+$", colour = G.C.GOLD }, + { ref_table = "card.joker_display_values", ref_value = "dollars", retrigger_type = "mult", colour = G.C.GOLD }, + }, + reminder_text = { + { text = "(" }, + { ref_table = "card.joker_display_values", ref_value = "localized_text", colour = lighten(G.C.SUITS["Diamonds"], 0.35) }, + { text = ")" } + }, + calc_function = function(card) + local mult = 0 + local dollars = 0 + local text, _, scoring_hand = JokerDisplay.evaluate_hand() + if text ~= 'Unknown' then + for _, scoring_card in pairs(scoring_hand) do + if scoring_card:is_suit("Diamonds") then + mult = mult + + card.ability.extra.mult * (1 + math.floor(to_number(G.GAME.dollars) / card.ability.extra.money_threshold)) * JokerDisplay.calculate_card_triggers(scoring_card, scoring_hand) + dollars = dollars + + card.ability.extra.money * JokerDisplay.calculate_card_triggers(scoring_card, scoring_hand) + end + end + end + card.joker_display_values.mult = mult + card.joker_display_values.dollars = dollars + card.joker_display_values.localized_text = localize("Diamonds", 'suits_plural') + end + } end } diff --git a/mod/jokers/dynamicduo.lua b/mod/jokers/dynamicduo.lua index 047646c..d397ce3 100644 --- a/mod/jokers/dynamicduo.lua +++ b/mod/jokers/dynamicduo.lua @@ -47,6 +47,35 @@ SMODS.Joker { card = card } end + end, + joker_display_def = function(JokerDisplay) + return { + text = { + { text = "+", colour = G.C.CHIPS }, + { ref_table = "card.joker_display_values", ref_value = "chips", colour = G.C.CHIPS, retrigger_type = "mult" }, + { text = " +", colour = G.C.MULT }, + { ref_table = "card.joker_display_values", ref_value = "mult", colour = G.C.MULT, retrigger_type = "mult" } + }, + reminder_text = { + { ref_table = "card.joker_display_values", ref_value = "localized_text" } + }, + calc_function = function(card) + local chips, mult = 0, 0 + local text, _, scoring_hand = JokerDisplay.evaluate_hand() + if text ~= 'Unknown' then + for _, scoring_card in pairs(scoring_hand) do + if not scoring_card:is_face() then + local retriggers = JokerDisplay.calculate_card_triggers(scoring_card, scoring_hand) + chips = chips + card.ability.extra.chips * retriggers + mult = mult + card.ability.extra.mult * retriggers + end + end + end + card.joker_display_values.mult = mult + card.joker_display_values.chips = chips + card.joker_display_values.localized_text = localize("k_numbered_cards") + end + } end } diff --git a/mod/jokers/flagbearer.lua b/mod/jokers/flagbearer.lua index 51819e8..b626b3b 100644 --- a/mod/jokers/flagbearer.lua +++ b/mod/jokers/flagbearer.lua @@ -76,6 +76,18 @@ SMODS.Joker { mult_mod = mult } end + end, + joker_display_def = function(JokerDisplay) + return { + text = { + { text = "+" }, + { ref_table = "card.joker_display_values", ref_value = "mult", retrigger_type = "mult" }, + }, + text_config = { colour = G.C.MULT }, + calc_function = function(card) + card.joker_display_values.mult = card.ability.extra.mult * G.GAME.current_round.discards_left + end + } end } diff --git a/mod/jokers/flipflop.lua b/mod/jokers/flipflop.lua index 675b24e..cd7e027 100644 --- a/mod/jokers/flipflop.lua +++ b/mod/jokers/flipflop.lua @@ -44,6 +44,8 @@ SMODS.Joker { result = { card.ability.extra.hands, card.ability.extra.mult, + card.ability.extra.discards, + card.ability.extra.chips, localize{type = 'name_text', key = card.ability.extra.joker1, set = 'Joker'}, localize{type = 'name_text', key = card.ability.extra.joker2, set = 'Joker'} } @@ -51,6 +53,8 @@ SMODS.Joker { result = { card.ability.extra.discards, card.ability.extra.chips, + card.ability.extra.hands, + card.ability.extra.mult, localize{type = 'name_text', key = card.ability.extra.joker1, set = 'Joker'}, localize{type = 'name_text', key = card.ability.extra.joker2, set = 'Joker'} } @@ -145,6 +149,23 @@ SMODS.Joker { } end end + end, + joker_display_def = function(JokerDisplay) + return { + text = { + { ref_table = "card.joker_display_values", ref_value = "chips", retrigger_type = "mult", colour = G.C.CHIPS }, + { ref_table = "card.joker_display_values", ref_value = "mult", retrigger_type = "mult", colour = G.C.MULT }, + }, + calc_function = function(card) + if card.ability.extra.side == "mult" then + card.joker_display_values.chips = "" + card.joker_display_values.mult = "+" .. card.ability.extra.mult + else + card.joker_display_values.chips = "+" .. card.ability.extra.chips + card.joker_display_values.mult = "" + end + end + } end } diff --git a/mod/jokers/goldenegg.lua b/mod/jokers/goldenegg.lua index 93521e8..79508ae 100644 --- a/mod/jokers/goldenegg.lua +++ b/mod/jokers/goldenegg.lua @@ -51,6 +51,30 @@ SMODS.Joker { colour = G.C.MONEY } end + end, + joker_display_def = function(JokerDisplay) + return { + text = { + { text = "+$" }, + { ref_table = "card.ability.extra", ref_value = "dollars" }, + { text = " /round" }, + }, + text_config = { colour = G.C.GOLD }, + reminder_text = { + { ref_table = "card.joker_display_values", ref_value = "localized_text" }, + }, + calc_function = function(card) + card.joker_display_values.localized_text = "(" .. localize("k_round") .. ")" + end, + reminder_text = { + { text = "(" }, + { text = "$", colour = G.C.GOLD }, + { ref_table = "card", ref_value = "sell_cost", colour = G.C.GOLD }, + { text = " Sell Value", colour = G.C.GOLD }, + { text = ")" }, + }, + reminder_text_config = { scale = 0.35 } + } end } diff --git a/mod/jokers/heartpaladin.lua b/mod/jokers/heartpaladin.lua index 6dc7bd6..c7c551b 100644 --- a/mod/jokers/heartpaladin.lua +++ b/mod/jokers/heartpaladin.lua @@ -58,6 +58,42 @@ SMODS.Joker { } end end + end, + joker_display_def = function(JokerDisplay) + return { + text = { + { + border_nodes = { + { ref_table = "card.joker_display_values", ref_value = "prefix" }, + { ref_table = "card.joker_display_values", ref_value = "Xmult", retrigger_type = "mult" } + } + } + }, + reminder_text = { + { text = "(" }, + { ref_table = "card.joker_display_values", ref_value = "localized_text", colour = lighten(G.C.SUITS["Hearts"], 0.35) }, + { text = ")" } + }, + calc_function = function(card) + local x_mult = 1 + local text, _, scoring_hand = JokerDisplay.evaluate_hand() + local m = 1.5 + card.joker_display_values.prefix = "X" + if G.GAME.probabilities.normal == 1 then + card.joker_display_values.prefix = "X ~" + m = 1.25 + end + if text ~= 'Unknown' then + for _, scoring_card in pairs(scoring_hand) do + if scoring_card:is_suit("Hearts") then + x_mult = x_mult * m ^ JokerDisplay.calculate_card_triggers(scoring_card, scoring_hand) + end + end + end + card.joker_display_values.Xmult = x_mult + card.joker_display_values.localized_text = localize("Hearts", 'suits_plural') + end + } end } diff --git a/mod/jokers/royaldecree.lua b/mod/jokers/royaldecree.lua index 11188a3..b77c369 100644 --- a/mod/jokers/royaldecree.lua +++ b/mod/jokers/royaldecree.lua @@ -55,6 +55,44 @@ SMODS.Joker { } end + end, + joker_display_def = function(JokerDisplay) + return { + text = { + { text = "$", colour = G.C.GOLD }, + { ref_table = "card.joker_display_values", ref_value = "count", retrigger_type = "mult", colour = G.C.GOLD }, + { text = " (played)", colour = G.C.GOLD }, + }, + reminder_text = { + { text = "$", colour = G.C.GOLD }, + { ref_table = "card.joker_display_values", ref_value = "count2", retrigger_type = "mult", colour = G.C.GOLD }, + { text = " (held)", colour = G.C.GOLD }, + }, + reminder_text_config = { scale = 0.35 }, + calc_function = function(card) + local count = 0 + local text, _, scoring_hand = JokerDisplay.evaluate_hand() + if text ~= 'Unknown' then + for _, scoring_card in pairs(scoring_hand) do + if scoring_card:is_face() then + count = count + card.ability.extra.dollars * + JokerDisplay.calculate_card_triggers(scoring_card, scoring_hand) + end + end + end + card.joker_display_values.count = count + local playing_hand = next(G.play.cards) + local count = 0 + for _, playing_card in ipairs(G.hand.cards) do + if playing_hand or not playing_card.highlighted then + if playing_card.facing and not (playing_card.facing == 'back') and playing_card:is_face() then + count = count + 2 * JokerDisplay.calculate_card_triggers(playing_card, nil, true) + end + end + end + card.joker_display_values.count2 = count + end + } end } diff --git a/mod/jokers/spadearcher.lua b/mod/jokers/spadearcher.lua index 15b96a2..0639f49 100644 --- a/mod/jokers/spadearcher.lua +++ b/mod/jokers/spadearcher.lua @@ -54,7 +54,7 @@ SMODS.Joker { spades = spades + 1 end end - if spades >= 5 then + if spades == 5 then card.ability.extra.chips = card.ability.extra.chips + card.ability.extra.chip_mod return { message = localize('k_upgrade_ex'), @@ -63,6 +63,34 @@ SMODS.Joker { } end end + end, + joker_display_def = function(JokerDisplay) + return { + text = { + { text = "+" }, + { ref_table = "card.joker_display_values", ref_value = "chips", retrigger_type = "mult" }, + }, + text_config = { colour = G.C.CHIPS }, + reminder_text = { + { text = "(" }, + { ref_table = "card.joker_display_values", ref_value = "localized_text", colour = lighten(G.C.SUITS["Spades"], 0.35) }, + { text = ")" } + }, + calc_function = function(card) + local chips = 0 + local text, _, scoring_hand = JokerDisplay.evaluate_hand() + if text ~= 'Unknown' then + for _, scoring_card in pairs(scoring_hand) do + if scoring_card:is_suit("Spades") then + chips = chips + + card.ability.extra.chips * JokerDisplay.calculate_card_triggers(scoring_card, scoring_hand) + end + end + end + card.joker_display_values.chips = chips + card.joker_display_values.localized_text = localize("Spades", 'suits_plural') + end + } end } diff --git a/mod/jokers/uncannyface.lua b/mod/jokers/uncannyface.lua index d8a301e..96913b5 100644 --- a/mod/jokers/uncannyface.lua +++ b/mod/jokers/uncannyface.lua @@ -53,6 +53,37 @@ SMODS.Joker { card = card } end + end, + joker_display_def = function(JokerDisplay) + return { + text = { + { text = "+", colour = G.C.CHIPS }, + { ref_table = "card.joker_display_values", ref_value = "chips", colour = G.C.CHIPS, retrigger_type = "mult" }, + { text = " +", colour = G.C.MULT }, + { ref_table = "card.joker_display_values", ref_value = "mult", colour = G.C.MULT, retrigger_type = "mult" } + }, + reminder_text = { + { ref_table = "card.joker_display_values", ref_value = "localized_text" } + }, + calc_function = function(card) + local chips, mult = 0, 0 + local face = 0 + local text, _, scoring_hand = JokerDisplay.evaluate_hand() + if text ~= 'Unknown' then + for _, scoring_card in pairs(scoring_hand) do + if scoring_card:is_face() then + local retriggers = JokerDisplay.calculate_card_triggers(scoring_card, scoring_hand) + face = face + 1 + chips = chips + card.ability.extra.chips * retriggers + mult = mult + card.ability.extra.mult * retriggers + end + end + end + card.joker_display_values.mult = mult * face + card.joker_display_values.chips = chips * face + card.joker_display_values.localized_text = localize("k_face_cards") + end + } end } From 2fc2e44a6167e76745fcabbea51552bfc93649b9 Mon Sep 17 00:00:00 2001 From: Matthew Yarnot Date: Mon, 19 May 2025 19:19:15 -0500 Subject: [PATCH 28/91] Club Wizard/Spade Archer sprites for CB setting --- mod/jokers/clubwizard.lua | 7 +++++++ mod/jokers/spadearcher.lua | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/mod/jokers/clubwizard.lua b/mod/jokers/clubwizard.lua index 6716cb8..9444faa 100644 --- a/mod/jokers/clubwizard.lua +++ b/mod/jokers/clubwizard.lua @@ -72,6 +72,13 @@ SMODS.Joker { card.joker_display_values.localized_text = localize("Clubs", 'suits_plural') end } + end, + update = function(self, card, dt) + if G.SETTINGS.colourblind_option then + card.children.center:set_sprite_pos({ x = 1, y = 0}) + else + card.children.center:set_sprite_pos({ x = 0, y = 0}) + end end } diff --git a/mod/jokers/spadearcher.lua b/mod/jokers/spadearcher.lua index 0639f49..4c63b80 100644 --- a/mod/jokers/spadearcher.lua +++ b/mod/jokers/spadearcher.lua @@ -91,6 +91,13 @@ SMODS.Joker { card.joker_display_values.localized_text = localize("Spades", 'suits_plural') end } + end, + update = function(self, card, dt) + if G.SETTINGS.colourblind_option then + card.children.center:set_sprite_pos({ x = 1, y = 0}) + else + card.children.center:set_sprite_pos({ x = 0, y = 0}) + end end } From 65f5d155420d40f5869f8f4922b9f7276bbca9a4 Mon Sep 17 00:00:00 2001 From: Matthew Yarnot Date: Mon, 19 May 2025 19:22:16 -0500 Subject: [PATCH 29/91] CW/SA sprites for colorblind setting --- mod/assets/2x/j_club_wizard.png | Bin 2040 -> 4705 bytes mod/assets/2x/j_spade_archer.png | Bin 2007 -> 4740 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/mod/assets/2x/j_club_wizard.png b/mod/assets/2x/j_club_wizard.png index 78ca4ea5d7c2615a07af61e89133d099b7799c6c..90eb3f8880a230ba2d0b048a95219e96bd7db201 100644 GIT binary patch literal 4705 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 delta 2032 zcmV@iBL{Q4GJ0x0000DNk~Le0001v0002K2m=5B0MuE>mysbLe;80q zR7JnX)&Kwh0B2qye04;fmFWNfWTl`0008lJrj!5x00?waPE-H?|NsC0{{Y^Dy;%SN z010qNS#tmY4#NNd4#NS*Z>VGd00&h`L_t(|UhSRjb{n@2Mbp}K-v5ougN-AAqmisM zZbT~CYt0M^;$zoi#n;J?`|bNuf2^wW?Wv^1k~#|f^;E31rcOqm$f`P)`TJ=wUqY+u zf{FCH1naknxG4Ofb>N-wOmqE3lkjpJN z<@))&^>tkG_GTGD$&B+lJx}#|ewJr`CmlWn5_%;aHKBoYSP`RyOLxI3e^<||KOk7$ z%A5PGuA?9tJY8y3XceOYbDm|0*6x^h?6-@KM7Z|`6pIWDVn&dGX*`*VL`o2cK{gw( z-$BPhZu_H9r?EBsv0i)VnEr(|A0N4BD6bE+P_I|)tY44qqQlFY8;Xx0H;J+|%tBoZ z=yKLik&5l1BNgtC=~%`We^K8x&>#4{b##Qryb_3TS;=|&BaQruqo8xL$n)B(e!jj= z{cz|j6lQ26N+;$_BLpk=5>;EgrjD}saoB}Q2D;3?Ix3{JuLKHQx(mr5fsU|JM}3st zMOpRO>N=4Z8kQkqBw`g|nXcoAwdlssW4}4JhYl@xQM~~&A_JF^f07aGs4gAd=_Mi< zWMII47oErOz-yhl_pHy@K_|b`qlurL`iAX;(x&O~M}T+GUzF+ncF;kChSds=LxCs| z(Z}GZ)4yp=uh~mS_j~2PpAP8~5!zJ?3b}Ou5goeR@3(`FlGA3Oy_iQr31uK4GV5fM z#(BSebhIj=dph+Of7)P$SJa__T9j6FjRv}VrBlWA6pHd!xVpN|PkbvYtmazryj8AK z#q|`5ezA^%*?oxcRLrQ5(u$K+RK({zC1Mp*{lpGBK(WZcfMTH$KF!$|GCAU2eb$ft z4m#2ELgXD#^!+`%=PNBT9HN!~2s`K?)Sn$bz?R{c9pxGAe|4gJmRxlDFN~(Yyq$Dn zUP~5=po5Txi!d4FcA!&NqeDCb7kbuCI&e!ze-SAd;b-C8D0&$kjx56EbXMpHgam^O zr^YDbV|$RB_s5sPcJK{USOd5 z#P)@BkSRzMe_Am>E74^#l(|CHxeOs`mMRsxd$qn!jms zsPhZUa8eaw2OSzfPp&V0^cduJl5v!0wi%oWWx;^`4mvdHPf;sTDk6IZ85(G(4sh%+ zPhqu}jz8Gq*lZ?D2DuHik4b?#%;!YOA+nQBPdwA z)|p?HA);7Ek$rUTL9ssWk|~`o6p2^QjwSruP3QPan$Q^1 z{U1n7|Hd#DDXT|Vp*LvSW4&n_kRK7%`q%FOl)lc O0000%9){{Qb>=XR2a?Qrc;P{@Tlqg_Ck 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 From c836111925da4618a460fef6309b5a29ec259527 Mon Sep 17 00:00:00 2001 From: Matthew Yarnot Date: Mon, 19 May 2025 19:25:22 -0500 Subject: [PATCH 30/91] CW/SA sprites for colorblind setting --- mod/assets/1x/j_club_wizard.png | Bin 1505 -> 3199 bytes mod/assets/1x/j_spade_archer.png | Bin 1496 -> 3195 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/mod/assets/1x/j_club_wizard.png b/mod/assets/1x/j_club_wizard.png index 83b6e40cd9e3f9853df640eb2bab2b7ef6466e75..58b26e0296b2f80e444388ad85a04fa487b67578 100644 GIT binary patch literal 3199 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 RsLelKqz%ft-15xT{{pNW0?hyb delta 1484 zcmV;-1vC2l7}yITiBL{Q4GJ0x0000DNk~Le0000-0001A2m=5B0FCP^+mRt4e;ZIt zR7JnX)&KwhKsl9qbGwa*y7~A2siw(ZSDXL<0LQfZ3IG5A33O6UQ~&?}|NsC0{{T}@ zcoF~r010qNS#tmY4#NNd4#NS*Z>VGd00l8gL_t(oN3EHGLLxg1MSY;+|9|n`b8j+} z7G!<0K_+dIo=MU&tn1s?zY%lZe_oT~yuJPRnn2>GroR_+z+5iZ>-B={`u+Xg`ib8l zb8NO_*9tSJ=exH5T`HcW`L&QrG8ae?ZUbL6|R` z4|8O8pfCpYeAjF@AkR+@$%quQ0*w)<=essWpiC!6bviCu2)*E}_SQm-bG7t2r$?Fk z`rAI!=zr5^*(#&Q^2E7tbQ_0`e5aNxr1p=xS56M%%y()t6wv-z_r{???B+X107;nj zM?jYk{L(oVLAs}2xVA^2xLLZ=#-6TZL{^@Busw7iXd^I_q`@$Zq7MhpFgK? zvli#(<8%n)IhWk!gON{|4~s+)#GtN2!-|VYul?~y$$1tie-H^AsVplFW`?XA9PfL= z8{}a`AXH)C*lLajCkJER7K3Vx6o-9gEgDh$0}lNm)E9vg8v#`I;t=OG(rbU*9%amh zgbHCX$L72xX(ya-`;0sS`9=W}=b7r-_MMAEEccvvXGkxZ`BXjU^Yac=){c{Qf8>}P zh1%`sr-X$ee?-jDA`Rmo@q^>oma+NA5%VG_NPB$Y4oJeXM<{RfXe^FJ`c`1 zCp&~Vs$#z6v&pfp^u0j>M{418zWwk0ia+YuIkLOa(v#Em9RJ=wII*9dkIsIA&|` zm$pkEATG>*(C4gKK9>tebNX!1lFug1S!9Ah)a7Aa(X~Qq+RIw@hn$)JxnQkpg1)Is zW(P(um|;IT4dVDbI6tj9U*6Q3AG#b~?tMp1e~#*PeDwYE=$U@f=ey_8L+!+#_4)pJ z^hmoue>p!t5jKqX!s*XLZg=V@^}#?-PJbTu{GZha2R%4>T-fZu)_fZQ>iMF@u|7G* z_<3PDv@zcQ%oojWZRbab@&7sb;UdKM=g1i5ulBk1t9@>LaGcPurT$)E&}&kE2YD4# mB7aYT^pe!ycm9EVef Date: Mon, 19 May 2025 20:39:16 -0400 Subject: [PATCH 31/91] restore scaling update that got accidentally removed --- mod/jokers/spadearcher.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mod/jokers/spadearcher.lua b/mod/jokers/spadearcher.lua index 4c63b80..32312a2 100644 --- a/mod/jokers/spadearcher.lua +++ b/mod/jokers/spadearcher.lua @@ -54,7 +54,7 @@ SMODS.Joker { spades = spades + 1 end end - if spades == 5 then + if spades >= 5 then card.ability.extra.chips = card.ability.extra.chips + card.ability.extra.chip_mod return { message = localize('k_upgrade_ex'), @@ -101,4 +101,4 @@ SMODS.Joker { end } --- See localization/en-us.lua to create joker text \ No newline at end of file +-- See localization/en-us.lua to create joker text From 3eaeae0e80fdbc5caaa9fed9a664b78d2b256072 Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Mon, 19 May 2025 21:17:17 -0400 Subject: [PATCH 32/91] version --- mod/FusionJokers.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mod/FusionJokers.json b/mod/FusionJokers.json index 27c0944..95859b7 100644 --- a/mod/FusionJokers.json +++ b/mod/FusionJokers.json @@ -9,7 +9,7 @@ "priority": -10000, "badge_colour": "B26CBB", "badge_text_colour": "FFFFFF", - "version": "1.0.0~BETA-00515a", + "version": "1.0.0~BETA-00519a", "dependencies": [ "Steamodded (>=1.0.0~ALPHA-1307d)", ], From 00674b4fb2738fc3a7bd95f6b085b249d4b1bbd8 Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Wed, 21 May 2025 21:26:56 -0400 Subject: [PATCH 33/91] fix flipflop loc_vars error --- mod/FusionJokers.json | 2 +- mod/jokers/flipflop.lua | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/mod/FusionJokers.json b/mod/FusionJokers.json index 95859b7..35cf447 100644 --- a/mod/FusionJokers.json +++ b/mod/FusionJokers.json @@ -9,7 +9,7 @@ "priority": -10000, "badge_colour": "B26CBB", "badge_text_colour": "FFFFFF", - "version": "1.0.0~BETA-00519a", + "version": "1.0.0~BETA-00521a", "dependencies": [ "Steamodded (>=1.0.0~ALPHA-1307d)", ], diff --git a/mod/jokers/flipflop.lua b/mod/jokers/flipflop.lua index cd7e027..ccaafa5 100644 --- a/mod/jokers/flipflop.lua +++ b/mod/jokers/flipflop.lua @@ -44,8 +44,6 @@ SMODS.Joker { result = { card.ability.extra.hands, card.ability.extra.mult, - card.ability.extra.discards, - card.ability.extra.chips, localize{type = 'name_text', key = card.ability.extra.joker1, set = 'Joker'}, localize{type = 'name_text', key = card.ability.extra.joker2, set = 'Joker'} } @@ -53,8 +51,6 @@ SMODS.Joker { result = { card.ability.extra.discards, card.ability.extra.chips, - card.ability.extra.hands, - card.ability.extra.mult, localize{type = 'name_text', key = card.ability.extra.joker1, set = 'Joker'}, localize{type = 'name_text', key = card.ability.extra.joker2, set = 'Joker'} } From bd2f69fd9dcfd3c1217aa1ddfb1e1810b401e6ac Mon Sep 17 00:00:00 2001 From: Matthew Yarnot Date: Sun, 8 Jun 2025 01:32:58 -0500 Subject: [PATCH 34/91] Update campingtrip.lua --- mod/jokers/campingtrip.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mod/jokers/campingtrip.lua b/mod/jokers/campingtrip.lua index 7f39524..22c28df 100644 --- a/mod/jokers/campingtrip.lua +++ b/mod/jokers/campingtrip.lua @@ -75,10 +75,10 @@ SMODS.Joker { retrigger_function = function(playing_card, scoring_hand, held_in_hand, joker_card) if held_in_hand then return 0 end return G.GAME and G.GAME.current_round.hands_left <= 1 and - joker_card.ability.extra * JokerDisplay.calculate_joker_triggers(joker_card) or 0 + 1 * JokerDisplay.calculate_joker_triggers(joker_card) or 0 end } end } --- See localization/en-us.lua to create joker text \ No newline at end of file +-- See localization/en-us.lua to create joker text From 929170b257df87ce393be99a995600f95bc1578f Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Mon, 14 Jul 2025 20:13:35 -0400 Subject: [PATCH 35/91] preliminary commit for smods probability update (not tested) --- mod/jokers/dementiajoker.lua | 8 +++++--- mod/jokers/heartpaladin.lua | 10 ++++++---- mod/jokers/moonmarauder.lua | 7 ++++--- mod/jokers/staroracle.lua | 7 ++++--- 4 files changed, 19 insertions(+), 13 deletions(-) diff --git a/mod/jokers/dementiajoker.lua b/mod/jokers/dementiajoker.lua index 12c2a38..26b6ee0 100644 --- a/mod/jokers/dementiajoker.lua +++ b/mod/jokers/dementiajoker.lua @@ -29,10 +29,11 @@ SMODS.Joker { } }, loc_vars = function(self, info_queue, card) + local luck, odds = SMODS.get_probability_vars(card, 1, card.ability.extra.odds, "fusion_dementia_desc", false) return { vars = { card.ability.extra.mult, - ''..(G.GAME and G.GAME.probabilities.normal or 1), card.ability.extra.odds, + luck, odds, (G.jokers and G.jokers.cards and #G.jokers.cards or 0)*card.ability.extra.mult, localize{type = 'name_text', key = card.ability.extra.joker1, set = 'Joker'}, localize{type = 'name_text', key = card.ability.extra.joker2, set = 'Joker'} @@ -41,7 +42,7 @@ SMODS.Joker { end, calculate = function(self, card, context) if context.end_of_round and not context.blueprint and not context.repetition and not context.individual then - if pseudorandom('dementia_joker') < G.GAME.probabilities.normal/card.ability.extra.odds + if SMODS.pseudorandom_probability(card, 'dementia_joker', 1, card.ability.extra.odds, 'dementia_joker') and not (card.edition and card.edition.negative) then local card = copy_card(card, nil, nil, nil, card.edition and card.edition.negative) card:set_edition({negative = true}, true) @@ -79,11 +80,12 @@ SMODS.Joker { extra_config = { colour = G.C.GREEN, scale = 0.3 }, text_config = { colour = G.C.MULT }, calc_function = function(card) + local luck, odds = SMODS.get_probability_vars(card, 1, card.ability.extra.odds, "fusion_dementia_desc", false) card.joker_display_values.mult = (G.jokers and G.jokers.cards and #G.jokers.cards or 0) * card.ability.extra.mult if card.edition and card.edition.negative then card.joker_display_values.odds = "" else - card.joker_display_values.odds = localize { type = 'variable', key = "jdis_odds", vars = { (G.GAME and G.GAME.probabilities.normal or 1), card.ability.extra.odds } } + card.joker_display_values.odds = localize { type = 'variable', key = "jdis_odds", vars = { luck, odds } } end end } diff --git a/mod/jokers/heartpaladin.lua b/mod/jokers/heartpaladin.lua index c7c551b..d30aace 100644 --- a/mod/jokers/heartpaladin.lua +++ b/mod/jokers/heartpaladin.lua @@ -29,11 +29,12 @@ SMODS.Joker { } }, loc_vars = function(self, info_queue, card) + local luck, odds = SMODS.get_probability_vars(card, 1, card.ability.extra.odds, "fusion_heartpaladin_desc", false) return { vars = { card.ability.extra.Xmult, - ''..(G.GAME and G.GAME.probabilities.normal or 1), - card.ability.extra.odds, + luck, + odds, localize{type = 'name_text', key = card.ability.extra.joker1, set = 'Joker'}, localize{type = 'name_text', key = card.ability.extra.joker2, set = 'Joker'} } @@ -50,7 +51,7 @@ SMODS.Joker { if context.repetition and context.cardarea == G.play and context.other_card:is_suit('Hearts') then - if pseudorandom('heart_paladin') < G.GAME.probabilities.normal/card.ability.extra.odds then + if SMODS.pseudorandom_probability(card, 'heart_paladin', 1, card.ability.extra.odds, 'heart_paladin') then return { message = localize('k_again_ex'), repetitions = 1, @@ -75,11 +76,12 @@ SMODS.Joker { { text = ")" } }, calc_function = function(card) + local luck, odds = SMODS.get_probability_vars(card, 1, card.ability.extra.odds, "fusion_heartpaladin_desc", false) local x_mult = 1 local text, _, scoring_hand = JokerDisplay.evaluate_hand() local m = 1.5 card.joker_display_values.prefix = "X" - if G.GAME.probabilities.normal == 1 then + if luck < odds then card.joker_display_values.prefix = "X ~" m = 1.25 end diff --git a/mod/jokers/moonmarauder.lua b/mod/jokers/moonmarauder.lua index 4ce3784..60cf0f6 100644 --- a/mod/jokers/moonmarauder.lua +++ b/mod/jokers/moonmarauder.lua @@ -30,10 +30,11 @@ SMODS.Joker { } }, loc_vars = function(self, info_queue, card) + local luck, odds = SMODS.get_probability_vars(card, 1, card.ability.extra.odds, "fusion_moonmarauder_desc", false) return { vars = { - '' .. (G.GAME and G.GAME.probabilities.normal or 1), - card.ability.extra.odds, + luck, + odds, localize{type = 'name_text', key = card.ability.extra.joker1, set = 'Joker'}, localize{type = 'name_text', key = card.ability.extra.joker2, set = 'Joker'} } @@ -43,7 +44,7 @@ SMODS.Joker { if context.before and (context.cardarea == G.jokers) then local moons = {} for _, v in ipairs(context.full_hand) do - if v:is_suit('Moons') and not (v.ability.name == 'Glass Card') and pseudorandom('moon_marauder') < G.GAME.probabilities.normal / card.ability.extra.odds then + if v:is_suit('Moons') and not (v.ability.name == 'Glass Card') and SMODS.pseudorandom_probability(card, 'moon_marauder', 1, card.ability.extra.odds, 'moon_marauder') then moons[#moons + 1] = v v:set_ability(G.P_CENTERS.m_glass, nil, true) G.E_MANAGER:add_event(Event({ diff --git a/mod/jokers/staroracle.lua b/mod/jokers/staroracle.lua index 84447c4..3e62319 100644 --- a/mod/jokers/staroracle.lua +++ b/mod/jokers/staroracle.lua @@ -31,10 +31,11 @@ SMODS.Joker { } }, loc_vars = function(self, info_queue, card) + local luck, odds = SMODS.get_probability_vars(card, 1, card.ability.extra.odds, "fusion_staroracle_desc", false) return { vars = { - '' .. (G.GAME and G.GAME.probabilities.normal or 1), - card.ability.extra.odds, + luck, + odds, localize{type = 'name_text', key = card.ability.extra.joker1, set = 'Joker'}, localize{type = 'name_text', key = card.ability.extra.joker2, set = 'Joker'} } @@ -51,7 +52,7 @@ SMODS.Joker { context.cardarea == G.play and context.other_card:is_suit('Stars') and #G.consumeables.cards + G.GAME.consumeable_buffer < G.consumeables.config.card_limit and - pseudorandom('starruby') < G.GAME.probabilities.normal / card.ability.extra.odds then + SMODS.pseudorandom_probability(card, 'starruby', 1, card.ability.extra.odds, 'starruby') then G.GAME.consumeable_buffer = G.GAME.consumeable_buffer + 1 G.E_MANAGER:add_event(Event({ trigger = 'before', From 8f7c01046ae1bbb3de7279a59186f23b830185b1 Mon Sep 17 00:00:00 2001 From: Sylvi <69098783+wingedcatgirl@users.noreply.github.com> Date: Tue, 15 Jul 2025 04:50:46 -0400 Subject: [PATCH 36/91] Update smods requirement --- mod/FusionJokers.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mod/FusionJokers.json b/mod/FusionJokers.json index 35cf447..c3eaba2 100644 --- a/mod/FusionJokers.json +++ b/mod/FusionJokers.json @@ -11,9 +11,9 @@ "badge_text_colour": "FFFFFF", "version": "1.0.0~BETA-00521a", "dependencies": [ - "Steamodded (>=1.0.0~ALPHA-1307d)", + "Steamodded (>=1.0.0~BETA-0711a)", ], "conflicts": [ "Talisman (<=2.0)", ], -} \ No newline at end of file +} From 9534dfab7232b01852aa1004c8828b507c10ffa4 Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Sun, 10 Aug 2025 15:19:05 -0400 Subject: [PATCH 37/91] fixed this old-ass bug --- mod/FusionJokers.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/mod/FusionJokers.lua b/mod/FusionJokers.lua index ea1ae4d..d686c45 100644 --- a/mod/FusionJokers.lua +++ b/mod/FusionJokers.lua @@ -312,6 +312,7 @@ function Card:fuse_card() j_fusion:add_to_deck() G.jokers:emplace(j_fusion) play_sound('explosion_release1') + G.jokers:unhighlight_all() delay(0.1) G.E_MANAGER:add_event(Event({trigger = 'after',delay = 0.3, blocking = false, From 00b5f7387e420ef67e5f67eff8f7a35e3d491492 Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Sun, 10 Aug 2025 15:19:05 -0400 Subject: [PATCH 38/91] fixed this old-ass bug --- mod/FusionJokers.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/mod/FusionJokers.lua b/mod/FusionJokers.lua index ea1ae4d..d686c45 100644 --- a/mod/FusionJokers.lua +++ b/mod/FusionJokers.lua @@ -312,6 +312,7 @@ function Card:fuse_card() j_fusion:add_to_deck() G.jokers:emplace(j_fusion) play_sound('explosion_release1') + G.jokers:unhighlight_all() delay(0.1) G.E_MANAGER:add_event(Event({trigger = 'after',delay = 0.3, blocking = false, From 5677f7b24011d697bbcd26a80510bab60c66db6c Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Sun, 10 Aug 2025 15:28:00 -0400 Subject: [PATCH 39/91] version update --- mod/FusionJokers.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mod/FusionJokers.json b/mod/FusionJokers.json index c3eaba2..2dcb497 100644 --- a/mod/FusionJokers.json +++ b/mod/FusionJokers.json @@ -9,7 +9,7 @@ "priority": -10000, "badge_colour": "B26CBB", "badge_text_colour": "FFFFFF", - "version": "1.0.0~BETA-00521a", + "version": "1.1.0", "dependencies": [ "Steamodded (>=1.0.0~BETA-0711a)", ], From be32d4f1cf88941a25ac56f708a2be6f9419348c Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Sun, 10 Aug 2025 23:35:37 -0400 Subject: [PATCH 40/91] was it this simple all along?? --- mod/FusionJokers.lua | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/mod/FusionJokers.lua b/mod/FusionJokers.lua index d686c45..23f69e8 100644 --- a/mod/FusionJokers.lua +++ b/mod/FusionJokers.lua @@ -146,6 +146,18 @@ local function has_joker(val, start_pos) if not start_pos then start_pos = 0 end + for i,v in ipairs(G.jokers.highlighted) do + if v.ability.set == 'Joker' and v.config.center_key == val and i > start_pos then + + for ii, vv in ipairs(G.jokers.cards) do + if vv == v and ii > start_pos then + return ii + end + end + + end + end + for i, v in ipairs(G.jokers.cards) do if v.ability.set == 'Joker' and v.config.center_key == val and i > start_pos then return i From 2430e82f67a6de26a92ff0e5927b58e73902dc24 Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Mon, 11 Aug 2025 02:17:54 -0400 Subject: [PATCH 41/91] remove (ish) joker selection limit --- mod/FusionJokers.lua | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/mod/FusionJokers.lua b/mod/FusionJokers.lua index 23f69e8..67e80d6 100644 --- a/mod/FusionJokers.lua +++ b/mod/FusionJokers.lua @@ -458,6 +458,8 @@ end -- end - - - +SMODS.current_mod.reset_game_globals = function (init) + if init then + G.jokers.config.highlighted_limit = math.max(G.jokers.config.highlighted_limit, 1e300) + end +end \ No newline at end of file From 7323febb8b313a6fb5041bcceeae7af1cf829c8b Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Mon, 11 Aug 2025 04:28:22 -0400 Subject: [PATCH 42/91] fix for the issue with multiple fusions of the same joker (please file an issue if anything is fucky) --- mod/FusionJokers.lua | 156 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 131 insertions(+), 25 deletions(-) diff --git a/mod/FusionJokers.lua b/mod/FusionJokers.lua index 67e80d6..f5ec5f0 100644 --- a/mod/FusionJokers.lua +++ b/mod/FusionJokers.lua @@ -142,15 +142,15 @@ to_big = to_big or function(num) return num end -local function has_joker(val, start_pos) +local function has_joker(val, start_pos, highlight_only) if not start_pos then - start_pos = 0 + start_pos = 1 end for i,v in ipairs(G.jokers.highlighted) do - if v.ability.set == 'Joker' and v.config.center_key == val and i > start_pos then - + if v.ability.set == 'Joker' and v.config.center_key == val and i >= start_pos then + for ii, vv in ipairs(G.jokers.cards) do - if vv == v and ii > start_pos then + if vv == v and ii >= start_pos then return ii end end @@ -158,15 +158,18 @@ local function has_joker(val, start_pos) end end - for i, v in ipairs(G.jokers.cards) do - if v.ability.set == 'Joker' and v.config.center_key == val and i > start_pos then - return i + if not highlight_only then + for i, v in ipairs(G.jokers.cards) do + if v.ability.set == 'Joker' and v.config.center_key == val and i >= start_pos then + return i + end end end return -1 end -function Card:can_fuse_card() +function Card:can_fuse_card(juicing) + --[[ for _, fusion in ipairs(FusionJokers.fusions) do if to_number(G.GAME.dollars) >= fusion.cost then local found_me = false @@ -191,22 +194,121 @@ function Card:can_fuse_card() end end end - return false + return false + --]] + local fusion = self:get_card_fusion() + if fusion.cost == "??" then return false end + if fusion.blocked and not juicing then return false end + return to_big(fusion.cost) <= to_big(G.GAME.dollars) end -function Card:get_card_fusion() +function Card:get_card_fusion(debug) + local dprint = function(msg) + if debug then print(msg) end + end + local function deep_copy(tbl) + if type(tbl) ~= "table" then return tbl end + local copy = {} + for k, v in pairs(tbl) do + copy[k] = deep_copy(v) + end + return copy + end + local results = {} + local held = {} + local affordable = {} + local result = { + result_joker = "Cannot fuse", + jokers = { + {name = self.config.center_key, extra_stat = false} + }, + cost = "??" + } + local jokerspos = {} for _, fusion in ipairs(FusionJokers.fusions) do + local valid = false for _, joker in ipairs(fusion.jokers) do if joker.name == self.config.center_key then - return fusion + for i,component in ipairs(fusion.jokers) do + local recipe = {} + recipe[component.name] = (recipe[component.name] or 0) + 1 + --dprint(component.name.."s needed: "..tostring(recipe[component.name])) + --dprint(component.name.."s found: "..tostring(#SMODS.find_card(component.name))) + if #SMODS.find_card(component.name) >= recipe[component.name] then + valid = true + else + valid = false + end + end + if valid then + results[#results+1] = deep_copy(fusion) + end + break end end end - return nil + if #results > 1 then + for i,recipe in ipairs(results) do + dprint("Checking if components for "..recipe.result_joker.." are owned") + jokerspos = {} + local valid = true + local startpos = {} + for ii,component in ipairs(recipe.jokers) do + startpos[component.name] = (startpos[component.name] or 1) + if has_joker(component.name, startpos[component.name]) ~= -1 then + startpos[component.name] = has_joker(component.name, startpos[component.name]) + dprint(component.name.." is owned") + jokerspos[#jokerspos+1] = startpos[component.name] + else + valid = false + dprint(component.name.." is not owned") + break + end + end + if valid then held[#held+1] = deep_copy(results[i]) end + end + if #held == 1 then return held[1] end + if #held == 0 then return result end + for i,recipe in ipairs(held) do + dprint("Checking if components for "..recipe.result_joker.." are highlighted") + local valid = true + local startpos = {} + for ii,component in ipairs(recipe.jokers) do + startpos[component.name] = (startpos[component.name] or 1) + if has_joker(component.name, startpos[component.name], true) ~= -1 then + startpos[component.name] = has_joker(component.name, startpos[component.name], true) + dprint(component.name.." is highlighted") + else + valid = false + dprint(component.name.." is not highlighted") + break + end + end + if to_big(recipe.cost) < to_big(G.GAME.dollars) then + affordable[#affordable+1] = deep_copy(held[i]) + end + if valid then result = held[i] break end --don't overhighlight :v + end + elseif #results == 1 then + result = results[1] + end + if #held > 1 and result.cost == "??" then + dprint("Picking a random possible fusion...") + local possible = #affordable > 0 and affordable or held + local pick = pseudorandom("fusetext", 1, #possible) + result = possible[pick] + result.blocked = true + else + dprint(result.blocked and "Result is blocked when it shouldn't be??" or "Result is not blocked (this is correct)") + end + return result end -function Card:fuse_card() +function Card:fuse_card(debug) + local dprint = function(msg) + if debug then print(msg) end + end G.CONTROLLER.locks.selling_card = true stop_use() local area = self.area @@ -222,12 +324,12 @@ function Card:fuse_card() edition = self.edition end - local chosen_fusion = nil + + local chosen_fusion = self:get_card_fusion() local joker_pos = {} - local found_me = false - for _, fusion in ipairs(FusionJokers.fusions) do - joker_pos = {} - found_me = false + do + local fusion = chosen_fusion + local found_me = false for _, joker in ipairs(fusion.jokers) do if fusion.jokers[1].name == fusion.jokers[2].name then if #SMODS.find_card(joker.name) > 1 and #joker_pos == 0 then @@ -243,9 +345,9 @@ function Card:fuse_card() end end - if #joker_pos == #fusion.jokers and found_me then - chosen_fusion = fusion - break + if not (#joker_pos == #fusion.jokers and found_me) then + dprint("Failed to find component Jokers when fusing?") + chosen_fusion = nil end end @@ -427,13 +529,17 @@ function Card:update(dt) local my_fusion = self:get_card_fusion() self.fusion_cost = my_fusion.cost - if self:can_fuse_card() and not self.ability.fusion.jiggle then - juice_card_until(self, function(card) return (card:can_fuse_card()) end, true) + if self:can_fuse_card(true) and not self.ability.fusion.jiggle then + juice_card_until(self, + function(card) + return (card:can_fuse_card(true)) + end, + true) self.ability.fusion.jiggle = true end - if not self:can_fuse_card() and self.ability.fusion.jiggle then + if not self:can_fuse_card(true) and self.ability.fusion.jiggle then self.ability.fusion.jiggle = false end end From 78a65442cc8f5f1dc7c56fd714dbab07c4a0487e Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Tue, 12 Aug 2025 02:56:16 -0400 Subject: [PATCH 43/91] add flag to components when card is fused --- mod/FusionJokers.json | 2 +- mod/FusionJokers.lua | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/mod/FusionJokers.json b/mod/FusionJokers.json index 2dcb497..069a9db 100644 --- a/mod/FusionJokers.json +++ b/mod/FusionJokers.json @@ -9,7 +9,7 @@ "priority": -10000, "badge_colour": "B26CBB", "badge_text_colour": "FFFFFF", - "version": "1.1.0", + "version": "1.1.0~BETA-20250812", "dependencies": [ "Steamodded (>=1.0.0~BETA-0711a)", ], diff --git a/mod/FusionJokers.lua b/mod/FusionJokers.lua index f5ec5f0..4ab628f 100644 --- a/mod/FusionJokers.lua +++ b/mod/FusionJokers.lua @@ -418,6 +418,7 @@ function Card:fuse_card(debug) end end --G.jokers.cards[pos]:start_dissolve({G.C.GOLD}) + G.jokers.cards[pos.pos].fused = true --Check for this if your on-card-removal function has an opinion on whether being fused counts as removal. G.jokers.cards[pos.pos]:remove() end From f528ac1afba29d650309140519a938e34c27ab62 Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Sat, 16 Aug 2025 05:13:00 -0400 Subject: [PATCH 44/91] fix some logic errors --- mod/FusionJokers.lua | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/mod/FusionJokers.lua b/mod/FusionJokers.lua index 4ab628f..3a74a26 100644 --- a/mod/FusionJokers.lua +++ b/mod/FusionJokers.lua @@ -199,7 +199,7 @@ function Card:can_fuse_card(juicing) local fusion = self:get_card_fusion() if fusion.cost == "??" then return false end if fusion.blocked and not juicing then return false end - return to_big(fusion.cost) <= to_big(G.GAME.dollars) + return (to_big(fusion.cost) + to_big(G.GAME.bankrupt_at or 0)) <= to_big(G.GAME.dollars) end function Card:get_card_fusion(debug) @@ -218,7 +218,7 @@ function Card:get_card_fusion(debug) local held = {} local affordable = {} local result = { - result_joker = "Cannot fuse", + result_joker = "No fusions", jokers = { {name = self.config.center_key, extra_stat = false} }, @@ -226,16 +226,17 @@ function Card:get_card_fusion(debug) } local jokerspos = {} for _, fusion in ipairs(FusionJokers.fusions) do - local valid = false + local valid = true for _, joker in ipairs(fusion.jokers) do if joker.name == self.config.center_key then + result.result_joker = "Cannot fuse" for i,component in ipairs(fusion.jokers) do local recipe = {} recipe[component.name] = (recipe[component.name] or 0) + 1 - --dprint(component.name.."s needed: "..tostring(recipe[component.name])) - --dprint(component.name.."s found: "..tostring(#SMODS.find_card(component.name))) + dprint(component.name.."s needed: "..tostring(recipe[component.name])) + dprint(component.name.."s found: "..tostring(#SMODS.find_card(component.name))) if #SMODS.find_card(component.name) >= recipe[component.name] then - valid = true + valid = valid and true else valid = false end @@ -524,7 +525,7 @@ function Card:update(dt) if G.STAGE == G.STAGES.RUN then - if self:get_card_fusion() ~= nil then + if self.get_card_fusion and self:get_card_fusion().result_joker ~= "No fusions" then self.ability.fusion = self.ability.fusion or {} local my_fusion = self:get_card_fusion() From e3aa084460451edbe629cb37d0eed7b2d6432fe5 Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Thu, 21 Aug 2025 10:17:43 -0400 Subject: [PATCH 45/91] version bump --- mod/FusionJokers.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mod/FusionJokers.json b/mod/FusionJokers.json index 069a9db..6e9ddef 100644 --- a/mod/FusionJokers.json +++ b/mod/FusionJokers.json @@ -9,7 +9,7 @@ "priority": -10000, "badge_colour": "B26CBB", "badge_text_colour": "FFFFFF", - "version": "1.1.0~BETA-20250812", + "version": "1.1.1", "dependencies": [ "Steamodded (>=1.0.0~BETA-0711a)", ], From 38ee2433a94fffca2b3bbb88be06d65d56bcb425 Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Tue, 26 Aug 2025 07:07:48 -0400 Subject: [PATCH 46/91] add a function that does it as a table because lmao this is unwieldy --- mod/FusionJokers.json | 2 +- mod/FusionJokers.lua | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/mod/FusionJokers.json b/mod/FusionJokers.json index 6e9ddef..35f84dc 100644 --- a/mod/FusionJokers.json +++ b/mod/FusionJokers.json @@ -9,7 +9,7 @@ "priority": -10000, "badge_colour": "B26CBB", "badge_text_colour": "FFFFFF", - "version": "1.1.1", + "version": "1.1.2~BETA-20250826", "dependencies": [ "Steamodded (>=1.0.0~BETA-0711a)", ], diff --git a/mod/FusionJokers.lua b/mod/FusionJokers.lua index 3a74a26..110499e 100644 --- a/mod/FusionJokers.lua +++ b/mod/FusionJokers.lua @@ -107,6 +107,23 @@ SMODS.Joker.inject =function(self) end function FusionJokers.fusions:add_fusion(joker1, carry_stat1, extra1, joker2, carry_stat2, extra2, result_joker, cost, merged_stat, merge_stat1, merge_stat2, merge_extra) + if type(joker1) == "table" then + sendWarnMessage("add_fusion expects a list of parameters, not a table; passing table to register_fusion", "Fusion Jokers") + FusionJokers.fusions:register_fusion(joker1) + else table.insert(self, + { jokers = { + { name = joker1, carry_stat = carry_stat1, extra_stat = extra1, merge_stat = merge_stat1 }, + { name = joker2, carry_stat = carry_stat2, extra_stat = extra2, merge_stat = merge_stat2 } + }, result_joker = result_joker, cost = cost, merged_stat = merged_stat, merge_extra = merge_extra }) + end +end + +function FusionJokers.fusions:register_fusion(t) + if type(t) ~= "table" then + sendErrorMessage("Use add_fusion if you're passing a list of parameters; register_fusion needs a table", "Fusion Jokers") + return + end + local joker1, carry_stat1, extra1, joker2, carry_stat2, extra2, result_joker, cost, merged_stat, merge_stat1, merge_stat2, merge_extra = t.joker1, t.carry_stat1, t.extra1, t.joker2, t.carry_stat2, t.extra2, t.result_joker, t.cost, t.merged_stat, t.merge_stat1, t.merge_stat2, t.merge_extra table.insert(self, { jokers = { { name = joker1, carry_stat = carry_stat1, extra_stat = extra1, merge_stat = merge_stat1 }, @@ -397,11 +414,16 @@ function Card:fuse_card(debug) end local check_joker = pos.joker if check_joker.carry_stat then + dprint("There is a carry stat") if check_joker.extra_stat then j_fusion.ability.extra[check_joker.carry_stat] = G.jokers.cards[pos.pos].ability.extra[check_joker.carry_stat] + dprint("It is extra; its value is "..tostring(G.jokers.cards[pos.pos].ability.extra[check_joker.carry_stat])) else j_fusion.ability[check_joker.carry_stat] = G.jokers.cards[pos.pos].ability[check_joker.carry_stat] + dprint("It is not extra; its value is "..tostring(G.jokers.cards[pos.pos].ability[check_joker.carry_stat])) end + else + dprint("There is not a carry stat") end if check_joker.merge_stat then if chosen_fusion.merge_extra then From cce8b31774660d0b305ed8df3fa29dfc7e18f10b Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Wed, 27 Aug 2025 15:05:25 -0400 Subject: [PATCH 47/91] document it --- README.md | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0f781d0..db9a24c 100644 --- a/README.md +++ b/README.md @@ -62,12 +62,35 @@ FusionJokers.fusions:add_fusion( joker2, -- carry_stat2, -- same as 1, but for the other component joker extra2, -- - result_joker, --result of fusion - cost, --cost to fuse + result_joker, --string, key of result joker + cost, --number, cost to fuse merged_stat, merge_stat1, merge_stat2, merge_extra --TODO explain these ) ``` +Alternately, you can call `FusionJokers.fusions:register_fusion()` and pass the desired arguments as a table. + +```lua +FusionJokers.fusions:add_fusion( + "j_egg", + "extra_value", + nil, + "j_golden", + nil, + false, + "j_fuse_golden_egg", + 12, +) +--is equivalent to: +FusionJokers.fusions:register_fusion({ + joker1 = "j_egg", + carry_stat1 = "extra_value", + joker2 = "j_golden", + result_joker = "j_fuse_golden_egg", + cost = 12, +}) +``` + ## 🎉 Credits - The original mod was written by [**Itayfeder**](https://github.com/stars/itayfeder/lists/balatro-modding), with art created by [**Lyman**](https://github.com/spikeof2010) From aba89261de9599e48f2260161011aa396b575b5d Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Wed, 27 Aug 2025 15:06:36 -0400 Subject: [PATCH 48/91] .... be technically accurate (the best kind) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index db9a24c..f191cc8 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ FusionJokers.fusions:add_fusion( nil, "j_golden", nil, - false, + nil, "j_fuse_golden_egg", 12, ) From efd4d70d4baf23067036dc11d976d8d66735cc23 Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Wed, 27 Aug 2025 16:45:54 -0400 Subject: [PATCH 49/91] whoops, that's a syntax error --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f191cc8..61f5fef 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,7 @@ FusionJokers.fusions:add_fusion( nil, nil, "j_fuse_golden_egg", - 12, + 12 ) --is equivalent to: FusionJokers.fusions:register_fusion({ From 86f83a4b4eb83323bc3ce05971374b35a209faf6 Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Tue, 2 Sep 2025 03:38:26 -0400 Subject: [PATCH 50/91] fixed negative fusions being fucky with new smods --- mod/FusionJokers.lua | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/mod/FusionJokers.lua b/mod/FusionJokers.lua index 110499e..5bd5346 100644 --- a/mod/FusionJokers.lua +++ b/mod/FusionJokers.lua @@ -383,10 +383,7 @@ function Card:fuse_card(debug) delay(0.2) G.E_MANAGER:add_event(Event({trigger = 'immediate',func = function() ease_dollars(-chosen_fusion.cost) - local j_fusion = create_card('Joker', G.jokers, nil, nil, nil, nil, chosen_fusion.result_joker, nil) - if edition and not j_fusion.edition then - j_fusion.edition = edition - end + local j_fusion = SMODS.create_card({set = "Joker", area = G.jokers, key = chosen_fusion.result_joker, edition = edition}) table.sort(joker_pos, function (a, b) return a.pos > b.pos end) From 4d1a05e7da345e62f231b13b49ba94e0b8d50eeb Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Wed, 3 Sep 2025 19:29:39 -0400 Subject: [PATCH 51/91] fix timing of set_ability --- mod/jokers/collectiblechaos.lua | 12 ++++++++---- mod/jokers/flagbearer.lua | 12 ++++++++---- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/mod/jokers/collectiblechaos.lua b/mod/jokers/collectiblechaos.lua index 143c28c..d07ff7d 100644 --- a/mod/jokers/collectiblechaos.lua +++ b/mod/jokers/collectiblechaos.lua @@ -51,10 +51,14 @@ SMODS.Joker { calculate_reroll_cost(true) end, set_ability = function(self, card, initial, delay_sprites) - if card.ability.mult and (card.ability.extra.mult == 0) then - card.ability.extra.mult = card.ability.mult - card.ability.mult = nil - end + G.E_MANAGER:add_event(Event({trigger = 'after',delay = 0.3, blocking = false, + func = function() + if card.ability.mult and (card.ability.extra.mult == 0) then + card.ability.extra.mult = card.ability.mult or 0 + card.ability.mult = nil + end + return true + end})) end, calculate = function(self, card, context) if context.starting_shop and not context.blueprint then diff --git a/mod/jokers/flagbearer.lua b/mod/jokers/flagbearer.lua index b626b3b..2e165e0 100644 --- a/mod/jokers/flagbearer.lua +++ b/mod/jokers/flagbearer.lua @@ -41,10 +41,14 @@ SMODS.Joker { } end, set_ability = function(self, card, initial, delay_sprites) - if card.ability.mult and (card.ability.extra.mult == 0) then - card.ability.extra.mult = card.ability.mult - card.ability.mult = nil - end + G.E_MANAGER:add_event(Event({trigger = 'after',delay = 0.3, blocking = false, + func = function() + if card.ability.mult and (card.ability.extra.mult == 0) then + card.ability.extra.mult = card.ability.mult or 0 + card.ability.mult = nil + end + return true + end})) end, calculate = function(self, card, context) if context.discard and not context.blueprint and context.other_card == context.full_hand[#context.full_hand] then From bd75e4c0bc2022af3d35f17149fcf83bddd1b654 Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Wed, 3 Sep 2025 22:22:36 -0400 Subject: [PATCH 52/91] optimization attempt (call `:get_card_fusion()` way less often in update :v) --- mod/FusionJokers.lua | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/mod/FusionJokers.lua b/mod/FusionJokers.lua index 5bd5346..1dc9443 100644 --- a/mod/FusionJokers.lua +++ b/mod/FusionJokers.lua @@ -214,9 +214,9 @@ function Card:can_fuse_card(juicing) return false --]] local fusion = self:get_card_fusion() - if fusion.cost == "??" then return false end - if fusion.blocked and not juicing then return false end - return (to_big(fusion.cost) + to_big(G.GAME.bankrupt_at or 0)) <= to_big(G.GAME.dollars) + if fusion.cost == "??" then return false, fusion end + if fusion.blocked and not juicing then return false, fusion end + return (to_big(fusion.cost) + to_big(G.GAME.bankrupt_at or 0)) <= to_big(G.GAME.dollars), fusion end function Card:get_card_fusion(debug) @@ -302,7 +302,7 @@ function Card:get_card_fusion(debug) break end end - if to_big(recipe.cost) < to_big(G.GAME.dollars) then + if (to_big(recipe.cost) + to_big(G.GAME.bankrupt_at or 0)) < to_big(G.GAME.dollars) then affordable[#affordable+1] = deep_copy(held[i]) end if valid then result = held[i] break end --don't overhighlight :v @@ -543,14 +543,13 @@ function Card:update(dt) updateref(self, dt) if G.STAGE == G.STAGES.RUN then + local fuseable, my_fusion = self:can_fuse_card(true) - if self.get_card_fusion and self:get_card_fusion().result_joker ~= "No fusions" then + if my_fusion and my_fusion.result_joker ~= "No fusions" then self.ability.fusion = self.ability.fusion or {} - - local my_fusion = self:get_card_fusion() self.fusion_cost = my_fusion.cost - if self:can_fuse_card(true) and not self.ability.fusion.jiggle then + if fuseable and not self.ability.fusion.jiggle then juice_card_until(self, function(card) return (card:can_fuse_card(true)) @@ -560,7 +559,7 @@ function Card:update(dt) self.ability.fusion.jiggle = true end - if not self:can_fuse_card(true) and self.ability.fusion.jiggle then + if not fuseable and self.ability.fusion.jiggle then self.ability.fusion.jiggle = false end end From 8d5a2fc54f475fb2bcef6032f52b09dae35ddaf4 Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Fri, 28 Nov 2025 20:45:24 -0500 Subject: [PATCH 53/91] three of them? in this economy? --- README.md | 60 +++++------- mod/FusionJokers.json | 2 +- mod/FusionJokers.lua | 127 ++++++++++++++++---------- mod/assets/1x/j_fuse_three_jimbos.png | Bin 0 -> 2775 bytes mod/assets/2x/j_fuse_three_jimbos.png | Bin 0 -> 3293 bytes mod/jokers/test.lua | 55 +++++++++++ 6 files changed, 159 insertions(+), 85 deletions(-) create mode 100644 mod/assets/1x/j_fuse_three_jimbos.png create mode 100644 mod/assets/2x/j_fuse_three_jimbos.png create mode 100644 mod/jokers/test.lua diff --git a/README.md b/README.md index 61f5fef..30e2114 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ When you have a fusable joker, pressing on it will show a **"fuse" button**. Whe Showcase of jokers tab 5 -There are a total of 15 fusions added in the mod! +There are a total of 17 fusions added in the mod, two of which are exclusive to [Six Suits](https://github.com/Aurelius7309/SixSuits)! You can find a list of their abilities, as well as the jokers needed to make them, in this link: https://itayfeder.github.io/Fusion-Jokers/ @@ -52,46 +52,36 @@ You can find a list of their abilities, as well as the jokers needed to make the The mod adds a way for other developers to create their own fusions! -Developers can call the function `FusionJokers.fusions:add_fusion()` to register their own fusion to the game. The function also allows them to determine carried stats between the component jokers and the fusion joker. +Developers can call the function `FusionJokers.fusions:register_fusion()` to register their own fusion to the game. The function also allows them to determine carried stats between the component jokers and the fusion joker. ```lua -FusionJokers.fusions:add_fusion( - joker1, --string, component joker's key - carry_stat1, --string, name of stat to carry into fusion - extra1, --boolean, is the carry stat in an "extra" table or not - joker2, -- - carry_stat2, -- same as 1, but for the other component joker - extra2, -- - result_joker, --string, key of result joker - cost, --number, cost to fuse - merged_stat, merge_stat1, merge_stat2, merge_extra --TODO explain these -) +FusionJokers.fusions:register_fusion{ + jokers = { + --First component Joker; all values except "name" optional + { name = "j_modprefix_key", carry_stat = "stat_to_carry", merge_stat = "stat_to_merge" }, + -- String, key of component 1 String, name of stat String, name of stat + -- to carry over to merge + { name = "j_modprefix_key", carry_stat = "stat_to_carry", merge_stat = "stat_to_merge" }, + --Second component Joker; same values. + --This works for arbitrary numbers of additional Jokers + --if you want to do the Exodia thing. + } + result_joker = "j_modprefix_key", --String, key of result Joker + cost = number, --Number, cost in $ to fuse this recipe + requirement = func, --Optional function; fusion can only be carried out if this function returns `true`. + merged_stat = "stat", --Optional string, name of stat that contains "merge_stat"s above + aftermath = func, --Optional function; will be run after the fusion is complete. +} ``` -Alternately, you can call `FusionJokers.fusions:register_fusion()` and pass the desired arguments as a table. +> [!NOTE] +> Carried and merged stats _always_ go in the new Joker's `ability.extra` table now! You also no longer need to specify whether the component Jokers' abilities are in an `extra` table or not; this is now detected automatically. -```lua -FusionJokers.fusions:add_fusion( - "j_egg", - "extra_value", - nil, - "j_golden", - nil, - nil, - "j_fuse_golden_egg", - 12 -) ---is equivalent to: -FusionJokers.fusions:register_fusion({ - joker1 = "j_egg", - carry_stat1 = "extra_value", - joker2 = "j_golden", - result_joker = "j_fuse_golden_egg", - cost = 12, -}) -``` +> [!NOTE] +> The old `add_fusion` method is depreciated and kept only for compatibility; please switch to `register_fusion` at your earliest convenience! ## 🎉 Credits - The original mod was written by [**Itayfeder**](https://github.com/stars/itayfeder/lists/balatro-modding), with art created by [**Lyman**](https://github.com/spikeof2010) -- [**elbe**](https://github.com/lshtech) did several patches to handle breaking changes in Steamodded, as well as various other features +- [**elbe**](https://github.com/lshtech) maintained the mod for several months through breaking Steamodded changes, and added some features +- [**wingedcatgirl**](https://github.com/wingedcatgirl) (hi!) is the current mod maintainer diff --git a/mod/FusionJokers.json b/mod/FusionJokers.json index 35f84dc..99ca650 100644 --- a/mod/FusionJokers.json +++ b/mod/FusionJokers.json @@ -9,7 +9,7 @@ "priority": -10000, "badge_colour": "B26CBB", "badge_text_colour": "FFFFFF", - "version": "1.1.2~BETA-20250826", + "version": "1.1.3", "dependencies": [ "Steamodded (>=1.0.0~BETA-0711a)", ], diff --git a/mod/FusionJokers.lua b/mod/FusionJokers.lua index 1dc9443..23d5086 100644 --- a/mod/FusionJokers.lua +++ b/mod/FusionJokers.lua @@ -110,7 +110,9 @@ function FusionJokers.fusions:add_fusion(joker1, carry_stat1, extra1, joker2, ca if type(joker1) == "table" then sendWarnMessage("add_fusion expects a list of parameters, not a table; passing table to register_fusion", "Fusion Jokers") FusionJokers.fusions:register_fusion(joker1) - else table.insert(self, + else + sendWarnMessage("add_fusion is now deprecated, please switch to register_fusion at earliest convenience", "Fusion Jokers") + table.insert(self, { jokers = { { name = joker1, carry_stat = carry_stat1, extra_stat = extra1, merge_stat = merge_stat1 }, { name = joker2, carry_stat = carry_stat2, extra_stat = extra2, merge_stat = merge_stat2 } @@ -120,15 +122,33 @@ end function FusionJokers.fusions:register_fusion(t) if type(t) ~= "table" then - sendErrorMessage("Use add_fusion if you're passing a list of parameters; register_fusion needs a table", "Fusion Jokers") + sendErrorMessage("Invalid format for register_fusion; should be table, got "..type(t), "Fusion Jokers") return end - local joker1, carry_stat1, extra1, joker2, carry_stat2, extra2, result_joker, cost, merged_stat, merge_stat1, merge_stat2, merge_extra = t.joker1, t.carry_stat1, t.extra1, t.joker2, t.carry_stat2, t.extra2, t.result_joker, t.cost, t.merged_stat, t.merge_stat1, t.merge_stat2, t.merge_extra + local jokers + if t.jokers then jokers = t.jokers + else + local i = 1 + jokers = {} + while t["joker"..i] do + jokers[i] = { + name = t["joker"..i], + carry_stat = t["carry_stat"]..i, + merge_stat = t["merge_stat"]..i + } + i = i+1 + end + end + table.insert(self, - { jokers = { - { name = joker1, carry_stat = carry_stat1, extra_stat = extra1, merge_stat = merge_stat1 }, - { name = joker2, carry_stat = carry_stat2, extra_stat = extra2, merge_stat = merge_stat2 } - }, result_joker = result_joker, cost = cost, merged_stat = merged_stat, merge_extra = merge_extra }) + { + jokers = jokers, + result_joker = t.result_joker, + cost = t.cost, + merged_stat = t.merged_stat, + requirement = t.requirement, + aftermath = t.aftermath, + }) end SMODS.load_file('jokers/diamondbard.lua')() @@ -150,6 +170,7 @@ SMODS.load_file('jokers/flagbearer.lua')() SMODS.load_file('jokers/uncannyface.lua')() SMODS.load_file('jokers/commercialdriver.lua')() SMODS.load_file('jokers/campingtrip.lua')() +SMODS.load_file('jokers/test.lua')() to_number = to_number or function(num) return num @@ -216,7 +237,11 @@ function Card:can_fuse_card(juicing) local fusion = self:get_card_fusion() if fusion.cost == "??" then return false, fusion end if fusion.blocked and not juicing then return false, fusion end - return (to_big(fusion.cost) + to_big(G.GAME.bankrupt_at or 0)) <= to_big(G.GAME.dollars), fusion + local reqcheck = true + if type(fusion.requirement) == "function" then + reqcheck = fusion.requirement() + end + return reqcheck and (to_big(fusion.cost) + to_big(G.GAME.bankrupt_at or 0)) <= to_big(G.GAME.dollars), fusion end function Card:get_card_fusion(debug) @@ -237,7 +262,7 @@ function Card:get_card_fusion(debug) local result = { result_joker = "No fusions", jokers = { - {name = self.config.center_key, extra_stat = false} + {name = self.config.center_key } }, cost = "??" } @@ -247,8 +272,8 @@ function Card:get_card_fusion(debug) for _, joker in ipairs(fusion.jokers) do if joker.name == self.config.center_key then result.result_joker = "Cannot fuse" + local recipe = {} for i,component in ipairs(fusion.jokers) do - local recipe = {} recipe[component.name] = (recipe[component.name] or 0) + 1 dprint(component.name.."s needed: "..tostring(recipe[component.name])) dprint(component.name.."s found: "..tostring(#SMODS.find_card(component.name))) @@ -345,22 +370,30 @@ function Card:fuse_card(debug) local chosen_fusion = self:get_card_fusion() local joker_pos = {} + local carried_stats = {} + local merged_stat = 0 do local fusion = chosen_fusion local found_me = false - for _, joker in ipairs(fusion.jokers) do - if fusion.jokers[1].name == fusion.jokers[2].name then - if #SMODS.find_card(joker.name) > 1 and #joker_pos == 0 then - local first_pos = has_joker(joker.name) - table.insert(joker_pos, {pos = first_pos, joker = joker}) - table.insert(joker_pos, {pos = has_joker(joker.name, first_pos), joker = joker}) - end - elseif next(SMODS.find_card(joker.name)) then - table.insert(joker_pos, {pos = has_joker(joker.name), joker = joker}) - end - if joker.name == self.config.center_key then + local list = {} + local recipe = {} + for i,component in ipairs(fusion.jokers) do + list[component.name] = (list[component.name] or 0) + 1 + recipe[#recipe+1] = SMODS.find_card(component.name)[list[component.name]] or "FAILURE!" + if recipe[#recipe] == self then found_me = true end + for ii,vv in ipairs(G.jokers.cards) do + if vv == recipe[#recipe] then + table.insert(joker_pos, {pos = ii, joker = vv}) + if component.carry_stat then + carried_stats[component.carry_stat] = (carried_stats[component.carry_stat] or 0) + (type(vv.ability.extra) == "table" and vv.ability.extra[component.carry_stat] or vv.ability[component.carry_stat] or 0) + end + if component.merge_stat then + merged_stat = merged_stat + (type(vv.ability.extra) == "table" and vv.ability.extra[component.merge_stat] or vv.ability[component.merge_stat]) + end + end + end end if not (#joker_pos == #fusion.jokers and found_me) then @@ -409,39 +442,28 @@ function Card:fuse_card(debug) --end end end - local check_joker = pos.joker - if check_joker.carry_stat then - dprint("There is a carry stat") - if check_joker.extra_stat then - j_fusion.ability.extra[check_joker.carry_stat] = G.jokers.cards[pos.pos].ability.extra[check_joker.carry_stat] - dprint("It is extra; its value is "..tostring(G.jokers.cards[pos.pos].ability.extra[check_joker.carry_stat])) - else - j_fusion.ability[check_joker.carry_stat] = G.jokers.cards[pos.pos].ability[check_joker.carry_stat] - dprint("It is not extra; its value is "..tostring(G.jokers.cards[pos.pos].ability[check_joker.carry_stat])) - end - else - dprint("There is not a carry stat") - end - if check_joker.merge_stat then - if chosen_fusion.merge_extra then - if check_joker.extra_stat then - j_fusion.ability.extra[chosen_fusion.merged_stat] = j_fusion.ability.extra[chosen_fusion.merged_stat] + G.jokers.cards[pos.pos].ability.extra[check_joker.merge_stat] - else - j_fusion.ability.extra[chosen_fusion.merged_stat] = j_fusion.ability.extra[chosen_fusion.merged_stat] + G.jokers.cards[pos.pos].ability[check_joker.merge_stat] - end - else - if check_joker.extra_stat then - j_fusion.ability[chosen_fusion.merged_stat] = j_fusion.ability[chosen_fusion.merged_stat] + G.jokers.cards[pos.pos].ability.extra[check_joker.merge_stat] - else - j_fusion.ability[chosen_fusion.merged_stat] = j_fusion.ability[chosen_fusion.merged_stat] + G.jokers.cards[pos.pos].ability[check_joker.merge_stat] - end - end - end --G.jokers.cards[pos]:start_dissolve({G.C.GOLD}) G.jokers.cards[pos.pos].fused = true --Check for this if your on-card-removal function has an opinion on whether being fused counts as removal. G.jokers.cards[pos.pos]:remove() end + if next(carried_stats) then + if type(j_fusion.ability.extra) == "number" then + local num = j_fusion.ability.extra + j_fusion.ability.extra = { + extra = num + } + elseif type(j_fusion.ability.extra) ~= "table" then + j_fusion.ability.extra = {} + end + for k,v in pairs(carried_stats) do + j_fusion.ability.extra[k] = v + end + end + if chosen_fusion.merged_stat then + j_fusion.ability.extra[chosen_fusion.merged_stat] = merged_stat + end + delay(0.3) j_fusion:add_to_deck() @@ -466,6 +488,13 @@ function Card:fuse_card(debug) end})) return true end})) + + if type(chosen_fusion.aftermath) == "function" then + G.E_MANAGER:add_event(Event({func = function () + chosen_fusion.aftermath() + return true + end})) + end end G.CONTROLLER.locks.selling_card = nil diff --git a/mod/assets/1x/j_fuse_three_jimbos.png b/mod/assets/1x/j_fuse_three_jimbos.png new file mode 100644 index 0000000000000000000000000000000000000000..bcfa723474c4b46c92aff96e23de47133dc15f1e GIT binary patch literal 2775 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 ScZa22E&%0-c6erYG5NoR(?8V! literal 0 HcmV?d00001 diff --git a/mod/jokers/test.lua b/mod/jokers/test.lua new file mode 100644 index 0000000..d831704 --- /dev/null +++ b/mod/jokers/test.lua @@ -0,0 +1,55 @@ +FusionJokers.fusions:register_fusion{ + jokers = { + { name = "j_joker", merge_stat = "mult" }, + { name = "j_joker", merge_stat = "mult" }, + { name = "j_joker", merge_stat = "mult" }, + }, + result_joker = "j_fuse_three_jimbos", + merged_stat = "mult", + cost = 2, + requirement = function () + local dp = not not next(SMODS.find_mod("DebugPlus")) + return dp + end +} + +SMODS.Atlas{ + key = "three", + path = "j_fuse_three_jimbos.png", + px = 71, + py = 95 +} + +SMODS.Joker{ + key = "three_jimbos", + loc_txt = { + name = "Three Fucking Jimbos", + text = { + "{C:mult}+#1#{} Mult", + "{C:inactive,s:0.8}hoLY shit" + } + }, + loc_vars = function (self, info_queue, card) + return { + vars = { + card.ability.extra.mult + } + } + end, + no_collection = true, + config = { + extra = { + mult = 69 + } + }, + rarity = "fuse_fusion", + atlas = "three", + pos = {x=0,y=0}, + calculate = function (self, card, context) + if context.joker_main then + return { + mult = card.ability.extra.mult + } + end + end +} \ No newline at end of file From e10b715aa55eddca5b95aaaacb811eec922f3b7c Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Fri, 28 Nov 2025 20:53:38 -0500 Subject: [PATCH 54/91] ah, but the button would still be there --- mod/jokers/test.lua | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/mod/jokers/test.lua b/mod/jokers/test.lua index d831704..6ea5a38 100644 --- a/mod/jokers/test.lua +++ b/mod/jokers/test.lua @@ -1,3 +1,6 @@ +local dp = not not next(SMODS.find_mod("DebugPlus")) +if not dp then return nil end + FusionJokers.fusions:register_fusion{ jokers = { { name = "j_joker", merge_stat = "mult" }, @@ -7,10 +10,6 @@ FusionJokers.fusions:register_fusion{ result_joker = "j_fuse_three_jimbos", merged_stat = "mult", cost = 2, - requirement = function () - local dp = not not next(SMODS.find_mod("DebugPlus")) - return dp - end } SMODS.Atlas{ From adda13272306aae5cb7cd031239704d0801b7e3e Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Fri, 28 Nov 2025 22:02:15 -0500 Subject: [PATCH 55/91] whoops, syntax error --- README.md | 2 +- mod/jokers/test.lua | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 30e2114..34ba9a7 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ FusionJokers.fusions:register_fusion{ --Second component Joker; same values. --This works for arbitrary numbers of additional Jokers --if you want to do the Exodia thing. - } + }, result_joker = "j_modprefix_key", --String, key of result Joker cost = number, --Number, cost in $ to fuse this recipe requirement = func, --Optional function; fusion can only be carried out if this function returns `true`. diff --git a/mod/jokers/test.lua b/mod/jokers/test.lua index 6ea5a38..e914009 100644 --- a/mod/jokers/test.lua +++ b/mod/jokers/test.lua @@ -35,7 +35,6 @@ SMODS.Joker{ } } end, - no_collection = true, config = { extra = { mult = 69 From f4941f6a74f3551473c2e89f0eed85607632c6b2 Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Thu, 11 Dec 2025 08:04:14 -0500 Subject: [PATCH 56/91] fusion context update --- README.md | 13 +++++++++++++ mod/FusionJokers.lua | 14 ++++++++++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 34ba9a7..a18e42f 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,19 @@ FusionJokers.fusions:register_fusion{ > [!NOTE] > The old `add_fusion` method is depreciated and kept only for compatibility; please switch to `register_fusion` at your earliest convenience! +When fusing, calculation is called with the following context: +```lua +context = { + fusing_jokers = true, --Boolean to flag this context + fusion_components = { + [1] = Card, --first Joker to be fused away + [2] = Card, --next Joker to be fused away + --and so forth + }, + fusion_result = Card --resulting Joker +} +``` + ## 🎉 Credits - The original mod was written by [**Itayfeder**](https://github.com/stars/itayfeder/lists/balatro-modding), with art created by [**Lyman**](https://github.com/spikeof2010) diff --git a/mod/FusionJokers.lua b/mod/FusionJokers.lua index 23d5086..479676e 100644 --- a/mod/FusionJokers.lua +++ b/mod/FusionJokers.lua @@ -420,6 +420,12 @@ function Card:fuse_card(debug) table.sort(joker_pos, function (a, b) return a.pos > b.pos end) + local ingredience = {} + for index, pos in ipairs(joker_pos) do + ingredience[index] = G.jokers.cards[pos.pos] + G.jokers.cards[pos.pos].fused = true --Check for this if your on-card-removal function has an opinion on whether being fused counts as removal. + end + SMODS.calculate_context{fusing_jokers = true, fusion_components = ingredience, fusion_result = j_fusion} for index, pos in ipairs(joker_pos) do local isPrimary = false if G.jokers.cards[pos.pos] == self then @@ -443,8 +449,12 @@ function Card:fuse_card(debug) end end --G.jokers.cards[pos]:start_dissolve({G.C.GOLD}) - G.jokers.cards[pos.pos].fused = true --Check for this if your on-card-removal function has an opinion on whether being fused counts as removal. - G.jokers.cards[pos.pos]:remove() + local flags = SMODS.calculate_context({joker_type_destroyed = true, card = G.jokers.cards[pos.pos]}) + if not flags.no_destroy then + G.jokers.cards[pos.pos]:remove() + else + G.jokers.cards[pos.pos].fused = nil + end end if next(carried_stats) then From faa855a56526fd8de9c078b67462a45c24e3a6f5 Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Sat, 13 Dec 2025 06:12:25 -0500 Subject: [PATCH 57/91] don't let fusion data linger improperly --- mod/FusionJokers.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mod/FusionJokers.lua b/mod/FusionJokers.lua index 479676e..35c1810 100644 --- a/mod/FusionJokers.lua +++ b/mod/FusionJokers.lua @@ -601,6 +601,8 @@ function Card:update(dt) if not fuseable and self.ability.fusion.jiggle then self.ability.fusion.jiggle = false end + else + self.ability.fusion, self.fusion_cost = nil, nil end end end From b46b199f7c60ea9f54ddc4467899d72ba6e715eb Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Sat, 13 Dec 2025 06:18:40 -0500 Subject: [PATCH 58/91] ver --- mod/FusionJokers.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mod/FusionJokers.json b/mod/FusionJokers.json index 99ca650..63264e2 100644 --- a/mod/FusionJokers.json +++ b/mod/FusionJokers.json @@ -9,7 +9,7 @@ "priority": -10000, "badge_colour": "B26CBB", "badge_text_colour": "FFFFFF", - "version": "1.1.3", + "version": "1.1.4~BETA20251213", "dependencies": [ "Steamodded (>=1.0.0~BETA-0711a)", ], From b4bce394613d894f500290248da7e985b4c640eb Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Tue, 16 Dec 2025 16:58:28 -0500 Subject: [PATCH 59/91] OOPS, TYPO --- mod/FusionJokers.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mod/FusionJokers.lua b/mod/FusionJokers.lua index 35c1810..02ab7a3 100644 --- a/mod/FusionJokers.lua +++ b/mod/FusionJokers.lua @@ -133,8 +133,8 @@ function FusionJokers.fusions:register_fusion(t) while t["joker"..i] do jokers[i] = { name = t["joker"..i], - carry_stat = t["carry_stat"]..i, - merge_stat = t["merge_stat"]..i + carry_stat = t["carry_stat"..i], + merge_stat = t["merge_stat"..i] } i = i+1 end From c89c4a9f4d4d9bb658385675702d2ef12b079ea2 Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Sun, 21 Dec 2025 16:11:59 -0500 Subject: [PATCH 60/91] remove obsolete badge function --- mod/FusionJokers.lua | 53 ++++++++++++++++---------------------------- 1 file changed, 19 insertions(+), 34 deletions(-) diff --git a/mod/FusionJokers.lua b/mod/FusionJokers.lua index 02ab7a3..24b30e7 100644 --- a/mod/FusionJokers.lua +++ b/mod/FusionJokers.lua @@ -106,20 +106,6 @@ SMODS.Joker.inject =function(self) SMODS_Joker_inject(self) end -function FusionJokers.fusions:add_fusion(joker1, carry_stat1, extra1, joker2, carry_stat2, extra2, result_joker, cost, merged_stat, merge_stat1, merge_stat2, merge_extra) - if type(joker1) == "table" then - sendWarnMessage("add_fusion expects a list of parameters, not a table; passing table to register_fusion", "Fusion Jokers") - FusionJokers.fusions:register_fusion(joker1) - else - sendWarnMessage("add_fusion is now deprecated, please switch to register_fusion at earliest convenience", "Fusion Jokers") - table.insert(self, - { jokers = { - { name = joker1, carry_stat = carry_stat1, extra_stat = extra1, merge_stat = merge_stat1 }, - { name = joker2, carry_stat = carry_stat2, extra_stat = extra2, merge_stat = merge_stat2 } - }, result_joker = result_joker, cost = cost, merged_stat = merged_stat, merge_extra = merge_extra }) - end -end - function FusionJokers.fusions:register_fusion(t) if type(t) ~= "table" then sendErrorMessage("Invalid format for register_fusion; should be table, got "..type(t), "Fusion Jokers") @@ -151,6 +137,25 @@ function FusionJokers.fusions:register_fusion(t) }) end +function FusionJokers.fusions:add_fusion(joker1, carry_stat1, extra1, joker2, carry_stat2, extra2, result_joker, cost, merged_stat, merge_stat1, merge_stat2) + if type(joker1) == "table" then + sendWarnMessage("add_fusion expects a list of parameters, not a table; passing table to register_fusion", "Fusion Jokers") + FusionJokers.fusions:register_fusion(joker1) + else + sendWarnMessage("add_fusion is now deprecated, please switch to register_fusion at earliest convenience", "Fusion Jokers") + + self:register_fusion{ + jokers = { + {name = joker1, carry_stat = carry_stat1, merge_stat = merge_stat1 }, + {name = joker2, carry_stat = carry_stat2, merge_stat = merge_stat2 }, + }, + merged_stat = merged_stat, + result_joker = result_joker, + cost = cost, + } + end +end + SMODS.load_file('jokers/diamondbard.lua')() SMODS.load_file('jokers/heartpaladin.lua')() SMODS.load_file('jokers/spadearcher.lua')() @@ -557,26 +562,6 @@ function G.UIDEF.use_and_sell_buttons(card) return retval end - -local card_h_popupref = G.UIDEF.card_h_popup -function G.UIDEF.card_h_popup(card) - local retval = card_h_popupref(card) - if not card.config.center or -- no center - (card.config.center.unlocked == false and not card.bypass_lock) or -- locked card - card.debuff or -- debuffed card - (not card.config.center.discovered and ((card.area ~= G.jokers and card.area ~= G.consumeables and card.area) or not card.area)) -- undiscovered card - then return retval end - - if card.config.center.rarity == 5 then - retval.nodes[1].nodes[1].nodes[1].nodes[3].nodes[1].nodes[1].nodes[2].config.object:remove() - retval.nodes[1].nodes[1].nodes[1].nodes[3].nodes[1] = create_badge(localize('k_fusion'), loc_colour("fusion", nil), nil, 1.2) - end - - return retval -end - - - local updateref = Card.update function Card:update(dt) updateref(self, dt) From ee51972b255c4f0d6d7d84b510018c8f499eea14 Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Sun, 21 Dec 2025 16:12:07 -0500 Subject: [PATCH 61/91] add definitions file --- mod/lsp_defs.lua | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 mod/lsp_defs.lua diff --git a/mod/lsp_defs.lua b/mod/lsp_defs.lua new file mode 100644 index 0000000..97ec7ec --- /dev/null +++ b/mod/lsp_defs.lua @@ -0,0 +1,34 @@ +---@meta + +---@class FusionIngredient +---@field name string Key of the ingredient joker +---@field carry_stat? string Name of the stat to be carried over (whether it is in an `extra` table will be detected automatically) +---@field merge_stat? string Name of the stat to be merged (as `carry_stat`); all `merge_stat` are added together. + +---@class FusionRecipe +---@field jokers FusionIngredient[] +---@field result_joker string Key of the Joker to be produced +---@field cost number Cost in dollars to perform this fusion +---@field merged_stat? string Key of the stat in which to place any `merge_stat` values, if present +---@field requirement? fun():boolean Must return `true` to perform this fusion (in addition to having the ingredients and money) +---@field aftermath? fun():nil Will be run after the Jokers are fused3 + +---Register a fusion recipe +---@param t FusionRecipe +function FusionJokers.fusions:register_fusion(t) +end + +---Deprecated, please use `register_fusion` instead. +---@param joker1 string Key of the first Joker to fuse +---@param carry_stat1 string|nil Name of the stat to carry, if any +---@param extra1 nil No longer used +---@param joker2 string Key of the second Joker to fuse +---@param carry_stat2 string|nil Name of the stat to carry, if any +---@param extra2 nil No longer used +---@param result_joker string Key of the output Joker +---@param cost string Cost in dollars to perform this fusion +---@param merged_stat string|nil Name of the stat in which to place `merge_stat`s +---@param merge_stat1 string|nil Name of the stat to merge from Joker 1 +---@param merge_stat2 string|nil ... and from Joker 2 +function FusionJokers.fusions:add_fusion(joker1, carry_stat1, extra1, joker2, carry_stat2, extra2, result_joker, cost, merged_stat, merge_stat1, merge_stat2) +end \ No newline at end of file From 43cc86e738f220e41e4d5c5bc5c98dfbd826c929 Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Sun, 21 Dec 2025 16:13:15 -0500 Subject: [PATCH 62/91] disable poorly-received alt artwork for sparcher; fix bug in update function for clubzard --- mod/jokers/clubwizard.lua | 1 + mod/jokers/spadearcher.lua | 7 ------- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/mod/jokers/clubwizard.lua b/mod/jokers/clubwizard.lua index 9444faa..e29e7ca 100644 --- a/mod/jokers/clubwizard.lua +++ b/mod/jokers/clubwizard.lua @@ -74,6 +74,7 @@ SMODS.Joker { } end, update = function(self, card, dt) + if not self.discovered and not card.bypass_discovery_center then return end if G.SETTINGS.colourblind_option then card.children.center:set_sprite_pos({ x = 1, y = 0}) else diff --git a/mod/jokers/spadearcher.lua b/mod/jokers/spadearcher.lua index 32312a2..c425897 100644 --- a/mod/jokers/spadearcher.lua +++ b/mod/jokers/spadearcher.lua @@ -92,13 +92,6 @@ SMODS.Joker { end } end, - update = function(self, card, dt) - if G.SETTINGS.colourblind_option then - card.children.center:set_sprite_pos({ x = 1, y = 0}) - else - card.children.center:set_sprite_pos({ x = 0, y = 0}) - end - end } -- See localization/en-us.lua to create joker text From 6890ec306d7fc2cdef56f3f24e380288d1e79873 Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Sun, 21 Dec 2025 16:18:35 -0500 Subject: [PATCH 63/91] make a dedicated option for this --- mod/config.lua | 3 +++ mod/configui.lua | 5 +++++ mod/jokers/clubwizard.lua | 9 ++++++--- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/mod/config.lua b/mod/config.lua index a2dda85..d92b514 100644 --- a/mod/config.lua +++ b/mod/config.lua @@ -1,3 +1,6 @@ +local colorblind = G.SETTINGS.colourblind_option + return { ["block_components"] = true, + ["cw_alt_art"] = colorblind } \ No newline at end of file diff --git a/mod/configui.lua b/mod/configui.lua index a90fed8..5a1cb3e 100644 --- a/mod/configui.lua +++ b/mod/configui.lua @@ -6,6 +6,11 @@ SMODS.current_mod.config_tab = function() ref_table = FusionJokers.fusionconfig, ref_value = 'block_components', }), + create_toggle({ + label = "Alt art for Club Wizard", + ref_table = FusionJokers.fusionconfig, + ref_value = 'cw_alt_art', + }), }} }} end \ No newline at end of file diff --git a/mod/jokers/clubwizard.lua b/mod/jokers/clubwizard.lua index e29e7ca..c81eff1 100644 --- a/mod/jokers/clubwizard.lua +++ b/mod/jokers/clubwizard.lua @@ -24,7 +24,8 @@ SMODS.Joker { extra = { mult = 24, joker1 = "j_gluttenous_joker", - joker2 = "j_onyx_agate" + joker2 = "j_onyx_agate", + art = "standard" } }, loc_vars = function(self, info_queue, card) @@ -75,10 +76,12 @@ SMODS.Joker { end, update = function(self, card, dt) if not self.discovered and not card.bypass_discovery_center then return end - if G.SETTINGS.colourblind_option then + if FusionJokers.fusionconfig.cw_alt_art and card.ability.extra.art == "standard" then card.children.center:set_sprite_pos({ x = 1, y = 0}) - else + card.ability.extra.art = "alt" + elseif not FusionJokers.fusionconfig.cw_alt_art and card.ability.extra.art == "alt" then card.children.center:set_sprite_pos({ x = 0, y = 0}) + card.ability.extra.art = "standard" end end } From a2cdf13e220b6323417e8c531e8262190d3bb3dc Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Sun, 21 Dec 2025 16:19:56 -0500 Subject: [PATCH 64/91] better logic, probably --- mod/jokers/clubwizard.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mod/jokers/clubwizard.lua b/mod/jokers/clubwizard.lua index c81eff1..ede034c 100644 --- a/mod/jokers/clubwizard.lua +++ b/mod/jokers/clubwizard.lua @@ -76,10 +76,10 @@ SMODS.Joker { end, update = function(self, card, dt) if not self.discovered and not card.bypass_discovery_center then return end - if FusionJokers.fusionconfig.cw_alt_art and card.ability.extra.art == "standard" then + if FusionJokers.fusionconfig.cw_alt_art and card.ability.extra.art ~= "alt" then card.children.center:set_sprite_pos({ x = 1, y = 0}) card.ability.extra.art = "alt" - elseif not FusionJokers.fusionconfig.cw_alt_art and card.ability.extra.art == "alt" then + elseif not FusionJokers.fusionconfig.cw_alt_art and card.ability.extra.art ~= "standard" then card.children.center:set_sprite_pos({ x = 0, y = 0}) card.ability.extra.art = "standard" end From 0bbe318417c76d2100d821ca38eb7509bb020555 Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Sun, 21 Dec 2025 16:38:02 -0500 Subject: [PATCH 65/91] version --- mod/FusionJokers.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mod/FusionJokers.json b/mod/FusionJokers.json index 63264e2..b41d8bd 100644 --- a/mod/FusionJokers.json +++ b/mod/FusionJokers.json @@ -9,7 +9,7 @@ "priority": -10000, "badge_colour": "B26CBB", "badge_text_colour": "FFFFFF", - "version": "1.1.4~BETA20251213", + "version": "1.1.4", "dependencies": [ "Steamodded (>=1.0.0~BETA-0711a)", ], From e2668e87715f9044c0fa1a9dcf12c84119aebf24 Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Mon, 29 Dec 2025 00:32:58 -0500 Subject: [PATCH 66/91] make component-blocking work on all fusions by hooking smods.add_to_pool. also readme update --- README.md | 4 +++- mod/FusionJokers.lua | 37 +++++++++++++++++++++++++++++-------- mod/jokers/test.lua | 8 +++++++- mod/lsp_defs.lua | 2 +- 4 files changed, 40 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index a18e42f..9c32138 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,9 @@ You can find a list of their abilities, as well as the jokers needed to make the ## ⚙️ Config -- **Block used components from reappearing**: Jokers used in a fusion cannot reappear if the fusion is present (unless Showman is also present). Currently only applies to Fusion Jokers fusions; cross-mod functionality is planned. Default true. +- **Block used components from reappearing**: Jokers used in a fusion cannot reappear if the fusion is present. Default true. + - These Jokers can reappear regardless if Showman or a Showman-like effect is active, if the fused Joker's `in_pool()` has `{allow_returning_components = true}` in its second return value, or if the component joker has `{allow_duplicates = true}` in its second return value. +- **Alt art for Club Wizard**: A lighter color to better match high-contrast Clubs. ## ➕ Fusion API (For Developers) diff --git a/mod/FusionJokers.lua b/mod/FusionJokers.lua index 24b30e7..7711907 100644 --- a/mod/FusionJokers.lua +++ b/mod/FusionJokers.lua @@ -70,20 +70,16 @@ FusionJokers.fusions = { FusionJokers.fusionconfig = SMODS.current_mod.config SMODS.load_file('configui.lua')() +FusionJokers.fusions.ingredience = {} + for _, fusion in ipairs(FusionJokers.fusions) do local fused = fusion.result_joker for _, component in ipairs(fusion.jokers) do local component_name = component.name - SMODS.Joker:take_ownership(component_name, { - in_pool = function(self, args) - if #SMODS.find_card('j_showman') > 0 then return true end -- Allow finding copies if Showman is present - if not FusionJokers.fusionconfig.block_components then return true end --If the option is disabled, don't do the check - if #SMODS.find_card(fused) > 0 then return false end -- If the fused Joker exists, remove both components - return true - end - }, true) -- silent | suppresses mod badge + FusionJokers.fusions.ingredience[component_name] = FusionJokers.fusions.ingredience[component_name] or {} + FusionJokers.fusions.ingredience[component_name][fused] = true end end @@ -126,6 +122,11 @@ function FusionJokers.fusions:register_fusion(t) end end + for i,v in ipairs(jokers) do + FusionJokers.fusions.ingredience[v.name] = FusionJokers.fusions.ingredience[v.name] or {} + FusionJokers.fusions.ingredience[v.name][t.result_joker] = true + end + table.insert(self, { jokers = jokers, @@ -156,6 +157,26 @@ function FusionJokers.fusions:add_fusion(joker1, carry_stat1, extra1, joker2, ca end end +local atpref = SMODS.add_to_pool +SMODS.add_to_pool = function (prototype_obj, args) + if SMODS.showman(prototype_obj.key) then return true end + args = args or {} + if FusionJokers.fusionconfig.block_components and FusionJokers.fusions.ingredience[prototype_obj.key] then + local flags1,flags2 + if type(prototype_obj.in_pool) == "function" then + _,flags1 = prototype_obj:in_pool(args) + end + for k,v in pairs(FusionJokers.fusions.ingredience[prototype_obj.key]) do + if type(G.P_CENTERS[k].in_pool) == "function" then + _,flags2 = G.P_CENTERS[k]:in_pool(args) + end + if not ((flags1 or {}).allow_duplicates or (flags2 or {}).allow_returning_components) and next(SMODS.find_card(k)) then return false end + end + end + + return atpref(prototype_obj, args) +end + SMODS.load_file('jokers/diamondbard.lua')() SMODS.load_file('jokers/heartpaladin.lua')() SMODS.load_file('jokers/spadearcher.lua')() diff --git a/mod/jokers/test.lua b/mod/jokers/test.lua index e914009..7f3e219 100644 --- a/mod/jokers/test.lua +++ b/mod/jokers/test.lua @@ -50,4 +50,10 @@ SMODS.Joker{ } end end -} \ No newline at end of file +} + +SMODS.Joker:take_ownership("joker", { + in_pool = function (self, args) + return true, {allow_duplicates = true} + end +}) \ No newline at end of file diff --git a/mod/lsp_defs.lua b/mod/lsp_defs.lua index 97ec7ec..6f82bd3 100644 --- a/mod/lsp_defs.lua +++ b/mod/lsp_defs.lua @@ -26,7 +26,7 @@ end ---@param carry_stat2 string|nil Name of the stat to carry, if any ---@param extra2 nil No longer used ---@param result_joker string Key of the output Joker ----@param cost string Cost in dollars to perform this fusion +---@param cost number Cost in dollars to perform this fusion ---@param merged_stat string|nil Name of the stat in which to place `merge_stat`s ---@param merge_stat1 string|nil Name of the stat to merge from Joker 1 ---@param merge_stat2 string|nil ... and from Joker 2 From 918e66ac61814724b402454bb7473de12b3ecdf0 Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Mon, 29 Dec 2025 00:34:07 -0500 Subject: [PATCH 67/91] me when definitely i typed all the words i'm thinking --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9c32138..720b026 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ You can find a list of their abilities, as well as the jokers needed to make the ## ⚙️ Config - **Block used components from reappearing**: Jokers used in a fusion cannot reappear if the fusion is present. Default true. - - These Jokers can reappear regardless if Showman or a Showman-like effect is active, if the fused Joker's `in_pool()` has `{allow_returning_components = true}` in its second return value, or if the component joker has `{allow_duplicates = true}` in its second return value. + - These Jokers can reappear regardless if Showman or a Showman-like effect is active, if the fused Joker's `in_pool()` has `{allow_returning_components = true}` in its second return value, or if the component Joker's `in_pool()` has `{allow_duplicates = true}` in its second return value. - **Alt art for Club Wizard**: A lighter color to better match high-contrast Clubs. ## ➕ Fusion API (For Developers) From 0d4736cebf26a8c20e05282182b9dae547ae9150 Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Mon, 29 Dec 2025 00:34:43 -0500 Subject: [PATCH 68/91] ver --- mod/FusionJokers.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mod/FusionJokers.json b/mod/FusionJokers.json index b41d8bd..5825ec8 100644 --- a/mod/FusionJokers.json +++ b/mod/FusionJokers.json @@ -9,7 +9,7 @@ "priority": -10000, "badge_colour": "B26CBB", "badge_text_colour": "FFFFFF", - "version": "1.1.4", + "version": "1.1.5~BETA20251229", "dependencies": [ "Steamodded (>=1.0.0~BETA-0711a)", ], From f989094fd56cd9e5a50119638def0afb060eb00b Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Mon, 29 Dec 2025 00:37:53 -0500 Subject: [PATCH 69/91] requirement bump --- mod/FusionJokers.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mod/FusionJokers.json b/mod/FusionJokers.json index 5825ec8..f85c895 100644 --- a/mod/FusionJokers.json +++ b/mod/FusionJokers.json @@ -11,7 +11,7 @@ "badge_text_colour": "FFFFFF", "version": "1.1.5~BETA20251229", "dependencies": [ - "Steamodded (>=1.0.0~BETA-0711a)", + "Steamodded (>=1.0.0~BETA-0827a)", ], "conflicts": [ "Talisman (<=2.0)", From ec641d14484dacbdcd3db01f36d88b31ee64ac07 Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Wed, 14 Jan 2026 17:20:54 -0500 Subject: [PATCH 70/91] clean up spaghetti in `Card:fuse_card`; fixes https://canary.discord.com/channels/1116389027176787968/1227317656131211284/1453400063991873719 --- mod/FusionJokers.lua | 116 ++++++++++++++++++++++++++----------------- 1 file changed, 70 insertions(+), 46 deletions(-) diff --git a/mod/FusionJokers.lua b/mod/FusionJokers.lua index 7711907..f4fd3ed 100644 --- a/mod/FusionJokers.lua +++ b/mod/FusionJokers.lua @@ -395,34 +395,58 @@ function Card:fuse_card(debug) local chosen_fusion = self:get_card_fusion() - local joker_pos = {} local carried_stats = {} local merged_stat = 0 + local recipe = {} do local fusion = chosen_fusion local found_me = false local list = {} - local recipe = {} for i,component in ipairs(fusion.jokers) do - list[component.name] = (list[component.name] or 0) + 1 - recipe[#recipe+1] = SMODS.find_card(component.name)[list[component.name]] or "FAILURE!" - if recipe[#recipe] == self then + if component.name == self.config.center_key and not found_me then + recipe[#recipe+1] = self found_me = true - end - for ii,vv in ipairs(G.jokers.cards) do - if vv == recipe[#recipe] then - table.insert(joker_pos, {pos = ii, joker = vv}) - if component.carry_stat then - carried_stats[component.carry_stat] = (carried_stats[component.carry_stat] or 0) + (type(vv.ability.extra) == "table" and vv.ability.extra[component.carry_stat] or vv.ability[component.carry_stat] or 0) + self.fused = true + else + local found_it = false + for ii,vv in ipairs(G.jokers.highlighted) do + if vv.config.center_key == component.name and not vv.fused then + recipe[#recipe+1] = vv + vv.fused = true + found_it = true + if component.carry_stat then + carried_stats[component.carry_stat] = (carried_stats[component.carry_stat] or 0) + (type(vv.ability.extra) == "table" and vv.ability.extra[component.carry_stat] or vv.ability[component.carry_stat] or 0) + end + if component.merge_stat then + merged_stat = merged_stat + (type(vv.ability.extra) == "table" and vv.ability.extra[component.merge_stat] or vv.ability[component.merge_stat]) + end + break end - if component.merge_stat then - merged_stat = merged_stat + (type(vv.ability.extra) == "table" and vv.ability.extra[component.merge_stat] or vv.ability[component.merge_stat]) + end + if not found_it then + for ii,vv in ipairs(G.jokers.cards) do + if vv.config.center_key == component.name and not vv.fused then + recipe[#recipe+1] = vv + vv.fused = true + found_it = true + if component.carry_stat then + carried_stats[component.carry_stat] = (carried_stats[component.carry_stat] or 0) + (type(vv.ability.extra) == "table" and vv.ability.extra[component.carry_stat] or vv.ability[component.carry_stat] or 0) + end + if component.merge_stat then + merged_stat = merged_stat + (type(vv.ability.extra) == "table" and vv.ability.extra[component.merge_stat] or vv.ability[component.merge_stat]) + end + break + end end end + if not found_it then + dprint("Failed to find component Jokers when fusing?") + chosen_fusion = nil + end end end - if not (#joker_pos == #fusion.jokers and found_me) then + if not (#recipe == #fusion.jokers and found_me) then dprint("Failed to find component Jokers when fusing?") chosen_fusion = nil end @@ -431,34 +455,32 @@ function Card:fuse_card(debug) if chosen_fusion ~= nil then G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.2,func = function() play_sound('whoosh1') - for _, pos in ipairs(joker_pos) do - if not edition and G.jokers.cards[pos.pos].edition then - edition = G.jokers.cards[pos.pos].edition + for i,v in ipairs(recipe) do + if not edition and v.edition then + edition = v.edition end - G.jokers.cards[pos.pos]:juice_up(0.3, 0.4) + v:juice_up(0.3, 0.4) end return true end})) delay(0.2) G.E_MANAGER:add_event(Event({trigger = 'immediate',func = function() ease_dollars(-chosen_fusion.cost) - local j_fusion = SMODS.create_card({set = "Joker", area = G.jokers, key = chosen_fusion.result_joker, edition = edition}) - table.sort(joker_pos, function (a, b) - return a.pos > b.pos - end) - local ingredience = {} - for index, pos in ipairs(joker_pos) do - ingredience[index] = G.jokers.cards[pos.pos] - G.jokers.cards[pos.pos].fused = true --Check for this if your on-card-removal function has an opinion on whether being fused counts as removal. + local j_fusion = self + + if type(G.P_CENTERS[self.config.center_key].remove_from_deck) == "function" then + G.P_CENTERS[self.config.center_key]:remove_from_deck(self) end - SMODS.calculate_context{fusing_jokers = true, fusion_components = ingredience, fusion_result = j_fusion} - for index, pos in ipairs(joker_pos) do - local isPrimary = false - if G.jokers.cards[pos.pos] == self then - isPrimary = true - end + + self:set_ability(chosen_fusion.result_joker) + if edition and not self.edition then + self:set_edition(edition.key) + end + SMODS.calculate_context{fusing_jokers = true, fusion_components = recipe, fusion_result = j_fusion} + for index, ingredient in ipairs(recipe) do + local isPrimary = ingredient == self for k,_ in pairs(SMODS.Stickers) do - if G.jokers.cards[pos.pos].ability[k] then + if ingredient.ability[k] then -- if string.find(k, "gemslot") then -- local gemExists = false -- for k1, _ in pairs(j_fusion.ability) do @@ -475,35 +497,37 @@ function Card:fuse_card(debug) end end --G.jokers.cards[pos]:start_dissolve({G.C.GOLD}) - local flags = SMODS.calculate_context({joker_type_destroyed = true, card = G.jokers.cards[pos.pos]}) - if not flags.no_destroy then - G.jokers.cards[pos.pos]:remove() + + local flags = SMODS.calculate_context({joker_type_destroyed = true, card = ingredient}) + if not (flags.no_destroy or isPrimary) then + ingredient:remove() else - G.jokers.cards[pos.pos].fused = nil + ingredient.fused = nil end end if next(carried_stats) then - if type(j_fusion.ability.extra) == "number" then - local num = j_fusion.ability.extra - j_fusion.ability.extra = { + if type(self.ability.extra) == "number" then + local num = self.ability.extra + self.ability.extra = { extra = num } - elseif type(j_fusion.ability.extra) ~= "table" then - j_fusion.ability.extra = {} + elseif type(self.ability.extra) ~= "table" then + self.ability.extra = {} end for k,v in pairs(carried_stats) do - j_fusion.ability.extra[k] = v + self.ability.extra[k] = v end end if chosen_fusion.merged_stat then - j_fusion.ability.extra[chosen_fusion.merged_stat] = merged_stat + self.ability.extra[chosen_fusion.merged_stat] = merged_stat end delay(0.3) - j_fusion:add_to_deck() - G.jokers:emplace(j_fusion) + if type(G.P_CENTERS[self.config.center_key].add_to_deck) == "function" then + G.P_CENTERS[self.config.center_key]:add_to_deck(self) + end play_sound('explosion_release1') G.jokers:unhighlight_all() From fcdd98c89d6531a0493982868253710517d3850e Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Fri, 16 Jan 2026 02:39:35 -0500 Subject: [PATCH 71/91] oh, set_ability handles this already --- mod/FusionJokers.lua | 8 -------- 1 file changed, 8 deletions(-) diff --git a/mod/FusionJokers.lua b/mod/FusionJokers.lua index f4fd3ed..c623265 100644 --- a/mod/FusionJokers.lua +++ b/mod/FusionJokers.lua @@ -373,7 +373,6 @@ function Card:get_card_fusion(debug) return result end - function Card:fuse_card(debug) local dprint = function(msg) if debug then print(msg) end @@ -468,10 +467,6 @@ function Card:fuse_card(debug) ease_dollars(-chosen_fusion.cost) local j_fusion = self - if type(G.P_CENTERS[self.config.center_key].remove_from_deck) == "function" then - G.P_CENTERS[self.config.center_key]:remove_from_deck(self) - end - self:set_ability(chosen_fusion.result_joker) if edition and not self.edition then self:set_edition(edition.key) @@ -525,9 +520,6 @@ function Card:fuse_card(debug) delay(0.3) - if type(G.P_CENTERS[self.config.center_key].add_to_deck) == "function" then - G.P_CENTERS[self.config.center_key]:add_to_deck(self) - end play_sound('explosion_release1') G.jokers:unhighlight_all() From 260deecd307cf146dd008d8fa045377d2817e38c Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Sat, 31 Jan 2026 12:07:09 -0500 Subject: [PATCH 72/91] disable fusing from areas other than G.jokers for now --- mod/FusionJokers.json | 2 +- mod/FusionJokers.lua | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/mod/FusionJokers.json b/mod/FusionJokers.json index f85c895..798a4bf 100644 --- a/mod/FusionJokers.json +++ b/mod/FusionJokers.json @@ -9,7 +9,7 @@ "priority": -10000, "badge_colour": "B26CBB", "badge_text_colour": "FFFFFF", - "version": "1.1.5~BETA20251229", + "version": "1.1.5~BETA20260131", "dependencies": [ "Steamodded (>=1.0.0~BETA-0827a)", ], diff --git a/mod/FusionJokers.lua b/mod/FusionJokers.lua index c623265..d597865 100644 --- a/mod/FusionJokers.lua +++ b/mod/FusionJokers.lua @@ -260,6 +260,9 @@ function Card:can_fuse_card(juicing) end return false --]] + + if self.area ~= G.jokers then return false end --Figure out fusing from other areas later; for now just disable it + local fusion = self:get_card_fusion() if fusion.cost == "??" then return false, fusion end if fusion.blocked and not juicing then return false, fusion end @@ -271,6 +274,7 @@ function Card:can_fuse_card(juicing) end function Card:get_card_fusion(debug) + debug = debug or G.fusion_debug_flag --Set this with DebugPlus if you want to local dprint = function(msg) if debug then print(msg) end end @@ -374,6 +378,7 @@ function Card:get_card_fusion(debug) end function Card:fuse_card(debug) + debug = debug or G.fusion_debug_flag --Set this with DebugPlus if you want to local dprint = function(msg) if debug then print(msg) end end From fbe315dac184cdbbba97749aa4fe7409204fb171 Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Tue, 10 Feb 2026 07:36:16 -0500 Subject: [PATCH 73/91] actually get the carry stats from the joker you click the fuse button on --- mod/FusionJokers.lua | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/mod/FusionJokers.lua b/mod/FusionJokers.lua index d597865..d4f8145 100644 --- a/mod/FusionJokers.lua +++ b/mod/FusionJokers.lua @@ -402,6 +402,7 @@ function Card:fuse_card(debug) local carried_stats = {} local merged_stat = 0 local recipe = {} + local me do local fusion = chosen_fusion local found_me = false @@ -411,6 +412,7 @@ function Card:fuse_card(debug) recipe[#recipe+1] = self found_me = true self.fused = true + me = component else local found_it = false for ii,vv in ipairs(G.jokers.highlighted) do @@ -472,6 +474,15 @@ function Card:fuse_card(debug) ease_dollars(-chosen_fusion.cost) local j_fusion = self + if me.carry_stat then + carried_stats[me.carry_stat] = (carried_stats[me.carry_stat] or 0) + (type(self.ability.extra) == "table" and self.ability.extra[me.carry_stat] or self.ability[me.carry_stat] or 0) + if type(self.ability.extra) == "table" then + self.ability.extra[me.carry_stat] = 0 + else + self.ability[me.carry_stat] = 0 + end + end + self:set_ability(chosen_fusion.result_joker) if edition and not self.edition then self:set_edition(edition.key) @@ -552,6 +563,10 @@ function Card:fuse_card(debug) return true end})) end + else + for i,v in ipairs(recipe) do --Shouldn't happen, but don't leave the fused flag lying around if it does + v.fused = nil + end end G.CONTROLLER.locks.selling_card = nil From 3563f46963e42be7716cbe6214326ce140a03a21 Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Tue, 10 Feb 2026 07:45:57 -0500 Subject: [PATCH 74/91] apparently we don't actually need all that roundabout nonsense --- mod/FusionJokers.lua | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/mod/FusionJokers.lua b/mod/FusionJokers.lua index d4f8145..588d277 100644 --- a/mod/FusionJokers.lua +++ b/mod/FusionJokers.lua @@ -412,7 +412,9 @@ function Card:fuse_card(debug) recipe[#recipe+1] = self found_me = true self.fused = true - me = component + if component.carry_stat then + carried_stats[component.carry_stat] = (carried_stats[component.carry_stat] or 0) + (type(self.ability.extra) == "table" and self.ability.extra[component.carry_stat] or self.ability[component.carry_stat] or 0) + end else local found_it = false for ii,vv in ipairs(G.jokers.highlighted) do @@ -474,15 +476,6 @@ function Card:fuse_card(debug) ease_dollars(-chosen_fusion.cost) local j_fusion = self - if me.carry_stat then - carried_stats[me.carry_stat] = (carried_stats[me.carry_stat] or 0) + (type(self.ability.extra) == "table" and self.ability.extra[me.carry_stat] or self.ability[me.carry_stat] or 0) - if type(self.ability.extra) == "table" then - self.ability.extra[me.carry_stat] = 0 - else - self.ability[me.carry_stat] = 0 - end - end - self:set_ability(chosen_fusion.result_joker) if edition and not self.edition then self:set_edition(edition.key) From ec1b30d2d4d77b2ff51ec17d6f470de04dd67cea Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Mon, 23 Feb 2026 11:24:55 -0500 Subject: [PATCH 75/91] delete spaces --- mod/FusionJokers.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mod/FusionJokers.lua b/mod/FusionJokers.lua index 588d277..2826cc8 100644 --- a/mod/FusionJokers.lua +++ b/mod/FusionJokers.lua @@ -144,7 +144,7 @@ function FusionJokers.fusions:add_fusion(joker1, carry_stat1, extra1, joker2, ca FusionJokers.fusions:register_fusion(joker1) else sendWarnMessage("add_fusion is now deprecated, please switch to register_fusion at earliest convenience", "Fusion Jokers") - + self:register_fusion{ jokers = { {name = joker1, carry_stat = carry_stat1, merge_stat = merge_stat1 }, @@ -167,7 +167,7 @@ SMODS.add_to_pool = function (prototype_obj, args) _,flags1 = prototype_obj:in_pool(args) end for k,v in pairs(FusionJokers.fusions.ingredience[prototype_obj.key]) do - if type(G.P_CENTERS[k].in_pool) == "function" then + if type(G.P_CENTERS[k].in_pool) == "function" then _,flags2 = G.P_CENTERS[k]:in_pool(args) end if not ((flags1 or {}).allow_duplicates or (flags2 or {}).allow_returning_components) and next(SMODS.find_card(k)) then return false end @@ -550,7 +550,7 @@ function Card:fuse_card(debug) return true end})) - if type(chosen_fusion.aftermath) == "function" then + if type(chosen_fusion.aftermath) == "function" then G.E_MANAGER:add_event(Event({func = function () chosen_fusion.aftermath() return true From 2775f54c1f17cc6bbd5298379b76ea7998b8d7f9 Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Mon, 23 Feb 2026 11:41:53 -0500 Subject: [PATCH 76/91] putting an assert in here (if the joker doesn't exist it'd just crash later when you tried to do the fusion) --- mod/FusionJokers.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/mod/FusionJokers.lua b/mod/FusionJokers.lua index 2826cc8..6fba595 100644 --- a/mod/FusionJokers.lua +++ b/mod/FusionJokers.lua @@ -167,6 +167,7 @@ SMODS.add_to_pool = function (prototype_obj, args) _,flags1 = prototype_obj:in_pool(args) end for k,v in pairs(FusionJokers.fusions.ingredience[prototype_obj.key]) do + assert(G.P_CENTERS[k], "Registered fusion "..k.." wants to create an unregistered Joker!\nEnsure "..k.." is a registered Joker, or else don't register this fusion.") if type(G.P_CENTERS[k].in_pool) == "function" then _,flags2 = G.P_CENTERS[k]:in_pool(args) end From 96431084fc2ffe4cc5b4fc16d98ea57fec9480ec Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Tue, 31 Mar 2026 16:55:07 -0400 Subject: [PATCH 77/91] slow down price fighting; allow fusion discounts --- README.md | 2 ++ mod/FusionJokers.lua | 23 ++++++++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 720b026..f5d15aa 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,8 @@ context = { } ``` +Fusions can be discounted by indexing a number to the final joker's key in `G.GAME.fujo_fusion_discount` for a flat discount in dollars, or `G.GAME.fujo_fusion_discountpercent` for a percentage discount. Alternately, index at the `universal` key for a discount that applies to all fusions. (Decimals always round down; minimum $1 after all discounts.) + ## 🎉 Credits - The original mod was written by [**Itayfeder**](https://github.com/stars/itayfeder/lists/balatro-modding), with art created by [**Lyman**](https://github.com/spikeof2010) diff --git a/mod/FusionJokers.lua b/mod/FusionJokers.lua index 6fba595..7991bf1 100644 --- a/mod/FusionJokers.lua +++ b/mod/FusionJokers.lua @@ -369,12 +369,31 @@ function Card:get_card_fusion(debug) if #held > 1 and result.cost == "??" then dprint("Picking a random possible fusion...") local possible = #affordable > 0 and affordable or held - local pick = pseudorandom("fusetext", 1, #possible) + local speedfac = 8 + local time = math.floor(love.timer.getTime() * speedfac) + if G.fusion_debug_flag then print(tostring(time)) end + math.randomseed(time) + local pick = math.random(1, #possible) result = possible[pick] result.blocked = true else dprint(result.blocked and "Result is blocked when it shouldn't be??" or "Result is not blocked (this is correct)") end + if type(result.cost) == type(0) then + if G.GAME.fujo_fusion_discount then + if G.GAME.fujo_fusion_discount[result.result_joker] then + result.cost = result.cost - G.GAME.fujo_fusion_discount[result.result_joker] + end + result.cost = result.cost - (G.GAME.fujo_fusion_discount.universal or 0) + end + if G.GAME.fujo_fusion_discountpercent then + if G.GAME.fujo_fusion_discountpercent[result.result_joker] then + result.cost = result.cost * (1 - G.GAME.fujo_fusion_discountpercent[result.result_joker]) + end + result.cost = result.cost * (1 - (G.GAME.fujo_fusion_discountpercent.universal or 0)) + end + result.cost = math.max(math.floor(result.cost), 1) + end return result end @@ -664,5 +683,7 @@ end SMODS.current_mod.reset_game_globals = function (init) if init then G.jokers.config.highlighted_limit = math.max(G.jokers.config.highlighted_limit, 1e300) + G.GAME.fujo_fusion_discount = {} + G.GAME.fujo_fusion_discountpercent = {} end end \ No newline at end of file From 839f0d1fe910be8f694ce16a239c5c21572791bc Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Tue, 31 Mar 2026 16:56:39 -0400 Subject: [PATCH 78/91] wait. why did i put that *there*? i can't tell whether this makes sense --- mod/FusionJokers.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mod/FusionJokers.lua b/mod/FusionJokers.lua index 7991bf1..2908c92 100644 --- a/mod/FusionJokers.lua +++ b/mod/FusionJokers.lua @@ -167,7 +167,7 @@ SMODS.add_to_pool = function (prototype_obj, args) _,flags1 = prototype_obj:in_pool(args) end for k,v in pairs(FusionJokers.fusions.ingredience[prototype_obj.key]) do - assert(G.P_CENTERS[k], "Registered fusion "..k.." wants to create an unregistered Joker!\nEnsure "..k.." is a registered Joker, or else don't register this fusion.") + --assert(G.P_CENTERS[k], "Registered fusion "..k.." wants to create an unregistered Joker!\nEnsure "..k.." is a registered Joker, or else don't register this fusion.") if type(G.P_CENTERS[k].in_pool) == "function" then _,flags2 = G.P_CENTERS[k]:in_pool(args) end From e464dcbe2dd8e662e62b6be36f13702dafeb0d6c Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Tue, 31 Mar 2026 17:11:35 -0400 Subject: [PATCH 79/91] *universally* carry extra value, instead of doing a special-case patch for golden egg --- mod/FusionJokers.lua | 9 +++++++++ mod/jokers/goldenegg.lua | 3 --- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/mod/FusionJokers.lua b/mod/FusionJokers.lua index 2908c92..457ab6a 100644 --- a/mod/FusionJokers.lua +++ b/mod/FusionJokers.lua @@ -421,6 +421,7 @@ function Card:fuse_card(debug) local chosen_fusion = self:get_card_fusion() local carried_stats = {} local merged_stat = 0 + local total_extra_cost = 0 local recipe = {} local me do @@ -432,6 +433,9 @@ function Card:fuse_card(debug) recipe[#recipe+1] = self found_me = true self.fused = true + total_extra_cost = total_extra_cost + (self.ability.extra_value or 0) + print("+"..tostring(self.ability.extra_value)) + print(tostring(total_extra_cost)) if component.carry_stat then carried_stats[component.carry_stat] = (carried_stats[component.carry_stat] or 0) + (type(self.ability.extra) == "table" and self.ability.extra[component.carry_stat] or self.ability[component.carry_stat] or 0) end @@ -442,6 +446,9 @@ function Card:fuse_card(debug) recipe[#recipe+1] = vv vv.fused = true found_it = true + total_extra_cost = total_extra_cost + (vv.ability.extra_value or 0) + print("+"..tostring(vv.ability.extra_value)) + print(tostring(total_extra_cost)) if component.carry_stat then carried_stats[component.carry_stat] = (carried_stats[component.carry_stat] or 0) + (type(vv.ability.extra) == "table" and vv.ability.extra[component.carry_stat] or vv.ability[component.carry_stat] or 0) end @@ -550,6 +557,8 @@ function Card:fuse_card(debug) delay(0.3) play_sound('explosion_release1') + self.ability.extra_value = total_extra_cost + self:set_cost() G.jokers:unhighlight_all() delay(0.1) diff --git a/mod/jokers/goldenegg.lua b/mod/jokers/goldenegg.lua index 79508ae..f85b6c8 100644 --- a/mod/jokers/goldenegg.lua +++ b/mod/jokers/goldenegg.lua @@ -35,9 +35,6 @@ SMODS.Joker { localize{type = 'name_text', key = card.ability.extra.joker2, set = 'Joker'} } } - end, - add_to_deck = function (self, card, from_debuff) - card:set_cost() end, calc_dollar_bonus = function(self, card) return card.ability.extra.dollars From 21ebaefaa79fdf016499d48b89b6425077eb930d Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Tue, 31 Mar 2026 17:18:37 -0400 Subject: [PATCH 80/91] remove debug prints and fix the merge thing (how did i miss _that_ ??) --- mod/FusionJokers.lua | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/mod/FusionJokers.lua b/mod/FusionJokers.lua index 457ab6a..ea3ed93 100644 --- a/mod/FusionJokers.lua +++ b/mod/FusionJokers.lua @@ -434,11 +434,12 @@ function Card:fuse_card(debug) found_me = true self.fused = true total_extra_cost = total_extra_cost + (self.ability.extra_value or 0) - print("+"..tostring(self.ability.extra_value)) - print(tostring(total_extra_cost)) if component.carry_stat then carried_stats[component.carry_stat] = (carried_stats[component.carry_stat] or 0) + (type(self.ability.extra) == "table" and self.ability.extra[component.carry_stat] or self.ability[component.carry_stat] or 0) end + if component.merge_stat then + merged_stat = merged_stat + (type(self.ability.extra) == "table" and self.ability.extra[component.merge_stat] or self.ability[component.merge_stat]) + end else local found_it = false for ii,vv in ipairs(G.jokers.highlighted) do @@ -447,8 +448,6 @@ function Card:fuse_card(debug) vv.fused = true found_it = true total_extra_cost = total_extra_cost + (vv.ability.extra_value or 0) - print("+"..tostring(vv.ability.extra_value)) - print(tostring(total_extra_cost)) if component.carry_stat then carried_stats[component.carry_stat] = (carried_stats[component.carry_stat] or 0) + (type(vv.ability.extra) == "table" and vv.ability.extra[component.carry_stat] or vv.ability[component.carry_stat] or 0) end From e23d521e62a175f574f7ac0b709cefeff21eacd9 Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Tue, 31 Mar 2026 17:41:08 -0400 Subject: [PATCH 81/91] readme update --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f5d15aa..8c7e20c 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ You can find a list of their abilities, as well as the jokers needed to make the ## ⬇ How to Download - The mod requires Steamodded. You can see info about how to use it [here](https://github.com/Steamopollys/Steamodded) -- Download the latest commit of Fusion Jokers from the green "Code" button up there +- Download the latest release of Fusion Jokers from the [releases tab](https://github.com/wingedcatgirl/Fusion-Jokers/releases) (should be stable), or the latest commit from the green "Code" button up there (might include bugfixes for some dumb thing I overlooked earlier) - Extract the downloaded mod to the Mods folder (at %appdata%/Balatro/Mods) ## ⚙️ Config From 091d6a3d509881b4cc523b497e7766322e9b3866 Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Tue, 31 Mar 2026 17:52:24 -0400 Subject: [PATCH 82/91] config to disable price flickering --- README.md | 1 + mod/FusionJokers.lua | 1 + mod/config.lua | 3 ++- mod/configui.lua | 5 +++++ 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8c7e20c..00325fe 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,7 @@ You can find a list of their abilities, as well as the jokers needed to make the - **Block used components from reappearing**: Jokers used in a fusion cannot reappear if the fusion is present. Default true. - These Jokers can reappear regardless if Showman or a Showman-like effect is active, if the fused Joker's `in_pool()` has `{allow_returning_components = true}` in its second return value, or if the component Joker's `in_pool()` has `{allow_duplicates = true}` in its second return value. - **Alt art for Club Wizard**: A lighter color to better match high-contrast Clubs. +- **No price flickering**: Stop the fusion cost from flickering when multiple fusions are possible. ## ➕ Fusion API (For Developers) diff --git a/mod/FusionJokers.lua b/mod/FusionJokers.lua index ea3ed93..5f6a7fa 100644 --- a/mod/FusionJokers.lua +++ b/mod/FusionJokers.lua @@ -371,6 +371,7 @@ function Card:get_card_fusion(debug) local possible = #affordable > 0 and affordable or held local speedfac = 8 local time = math.floor(love.timer.getTime() * speedfac) + if FusionJokers.fusionconfig.no_price_flicker then time = 1 end if G.fusion_debug_flag then print(tostring(time)) end math.randomseed(time) local pick = math.random(1, #possible) diff --git a/mod/config.lua b/mod/config.lua index d92b514..0096f09 100644 --- a/mod/config.lua +++ b/mod/config.lua @@ -2,5 +2,6 @@ local colorblind = G.SETTINGS.colourblind_option return { ["block_components"] = true, - ["cw_alt_art"] = colorblind + ["cw_alt_art"] = colorblind, + ["no_price_flicker"] = false } \ No newline at end of file diff --git a/mod/configui.lua b/mod/configui.lua index 5a1cb3e..d5ed0b8 100644 --- a/mod/configui.lua +++ b/mod/configui.lua @@ -11,6 +11,11 @@ SMODS.current_mod.config_tab = function() ref_table = FusionJokers.fusionconfig, ref_value = 'cw_alt_art', }), + create_toggle({ + label = "No price flickering", + ref_table = FusionJokers.fusionconfig, + ref_value = 'no_price_flicker', + }), }} }} end \ No newline at end of file From fe8749eabb0a73300b5911b1bc48365257a02a27 Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Tue, 31 Mar 2026 17:54:58 -0400 Subject: [PATCH 83/91] ver --- mod/FusionJokers.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mod/FusionJokers.json b/mod/FusionJokers.json index 798a4bf..f9ed70c 100644 --- a/mod/FusionJokers.json +++ b/mod/FusionJokers.json @@ -9,7 +9,7 @@ "priority": -10000, "badge_colour": "B26CBB", "badge_text_colour": "FFFFFF", - "version": "1.1.5~BETA20260131", + "version": "1.2.0", "dependencies": [ "Steamodded (>=1.0.0~BETA-0827a)", ], From f568c4c83e4dbf53c44f62f82480567389fb06db Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Sat, 4 Apr 2026 21:09:25 -0400 Subject: [PATCH 84/91] perishable sticker!!!!!!! --- mod/FusionJokers.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mod/FusionJokers.lua b/mod/FusionJokers.lua index 5f6a7fa..ced9b52 100644 --- a/mod/FusionJokers.lua +++ b/mod/FusionJokers.lua @@ -523,7 +523,11 @@ function Card:fuse_card(debug) -- j_fusion.ability[k] = true -- end -- else + local perish_count = j_fusion.ability.perish_tally or 0 j_fusion.ability[k] = true + if k == "perishable" then + j_fusion.ability.perish_tally = math.max(perish_count, ingredient.ability.perish_tally) + end --end end end From faf8df07696f47bfb2c9c408dc2bfd0500883fa2 Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Sun, 26 Apr 2026 11:44:06 -0400 Subject: [PATCH 85/91] use add_sticker --- mod/FusionJokers.lua | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/mod/FusionJokers.lua b/mod/FusionJokers.lua index ced9b52..ea18ac8 100644 --- a/mod/FusionJokers.lua +++ b/mod/FusionJokers.lua @@ -512,21 +512,23 @@ function Card:fuse_card(debug) local isPrimary = ingredient == self for k,_ in pairs(SMODS.Stickers) do if ingredient.ability[k] then - -- if string.find(k, "gemslot") then - -- local gemExists = false - -- for k1, _ in pairs(j_fusion.ability) do - -- if string.find(k1, "gemslot") then - -- gemExists = true - -- end - -- end - -- if isPrimary then - -- j_fusion.ability[k] = true - -- end - -- else + --[[ + if string.find(k, "gemslot") then + local gemExists = false + for k1, _ in pairs(j_fusion.ability) do + if string.find(k1, "gemslot") then + gemExists = true + end + end + if isPrimary then + j_fusion.ability[k] = true + end + else + --]] local perish_count = j_fusion.ability.perish_tally or 0 - j_fusion.ability[k] = true + j_fusion:add_sticker(k, true) if k == "perishable" then - j_fusion.ability.perish_tally = math.max(perish_count, ingredient.ability.perish_tally) + j_fusion.ability.perish_tally = math.max(perish_count, ingredient.ability.perish_tally or 0) end --end end From aaeba990238e894d162cae0c0d0fcc15bc813de1 Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Sun, 26 Apr 2026 11:44:16 -0400 Subject: [PATCH 86/91] update lsp defs Co-authored-by: Copilot --- mod/lsp_defs.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mod/lsp_defs.lua b/mod/lsp_defs.lua index 6f82bd3..b0c01bb 100644 --- a/mod/lsp_defs.lua +++ b/mod/lsp_defs.lua @@ -11,14 +11,14 @@ ---@field cost number Cost in dollars to perform this fusion ---@field merged_stat? string Key of the stat in which to place any `merge_stat` values, if present ---@field requirement? fun():boolean Must return `true` to perform this fusion (in addition to having the ingredients and money) ----@field aftermath? fun():nil Will be run after the Jokers are fused3 +---@field aftermath? fun():nil Will be run after the Jokers are fused ---Register a fusion recipe ---@param t FusionRecipe function FusionJokers.fusions:register_fusion(t) end ----Deprecated, please use `register_fusion` instead. +---Original fusion registry function, now deprecated. You can use [this regex](https://regex101.com/r/msZHKS/2/substitution) to convert it and avoid warning messages. ---@param joker1 string Key of the first Joker to fuse ---@param carry_stat1 string|nil Name of the stat to carry, if any ---@param extra1 nil No longer used @@ -30,5 +30,6 @@ end ---@param merged_stat string|nil Name of the stat in which to place `merge_stat`s ---@param merge_stat1 string|nil Name of the stat to merge from Joker 1 ---@param merge_stat2 string|nil ... and from Joker 2 +---@deprecated Please use `register_fusion` instead function FusionJokers.fusions:add_fusion(joker1, carry_stat1, extra1, joker2, carry_stat2, extra2, result_joker, cost, merged_stat, merge_stat1, merge_stat2) end \ No newline at end of file From 3577719bf888486a4a2b577f931ee442243a88c1 Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Sun, 26 Apr 2026 11:47:24 -0400 Subject: [PATCH 87/91] readme update --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 00325fe..8ca1435 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,7 @@ FusionJokers.fusions:register_fusion{ > Carried and merged stats _always_ go in the new Joker's `ability.extra` table now! You also no longer need to specify whether the component Jokers' abilities are in an `extra` table or not; this is now detected automatically. > [!NOTE] -> The old `add_fusion` method is depreciated and kept only for compatibility; please switch to `register_fusion` at your earliest convenience! +> The old `add_fusion` method is depreciated and kept only for compatibility; please switch to `register_fusion` at your earliest convenience! You should be able to use [this regex](https://regex101.com/r/msZHKS/2/substitution) to quickly swap everything in your codespace. When fusing, calculation is called with the following context: ```lua From 3037a817f0b79676ebbcc3185dfe1ba50e828f24 Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Sun, 26 Apr 2026 11:50:39 -0400 Subject: [PATCH 88/91] update this link lol --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8ca1435..0b3249f 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@

[![Status](https://img.shields.io/badge/status-active-success.svg)]() -[![GitHub Issues](https://img.shields.io/github/issues/itayfeder/Fusion-Jokers.svg)](https://github.com/itayfeder/Fusion-Jokers/issues) +[![GitHub Issues](https://img.shields.io/github/issues/wingedcatgirl/Fusion-Jokers.svg)](https://github.com/wingedcatgirl/Fusion-Jokers/issues) [![License](https://img.shields.io/badge/license-GNU-blue.svg)](/LICENSE)
From 3314f151f9e12c11860f588629d65a695253c8b3 Mon Sep 17 00:00:00 2001 From: wingedcatgirl <69098783+wingedcatgirl@users.noreply.github.com> Date: Sun, 26 Apr 2026 12:01:46 -0400 Subject: [PATCH 89/91] number! --- mod/FusionJokers.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mod/FusionJokers.json b/mod/FusionJokers.json index f9ed70c..0d6e76a 100644 --- a/mod/FusionJokers.json +++ b/mod/FusionJokers.json @@ -9,7 +9,7 @@ "priority": -10000, "badge_colour": "B26CBB", "badge_text_colour": "FFFFFF", - "version": "1.2.0", + "version": "1.2.1", "dependencies": [ "Steamodded (>=1.0.0~BETA-0827a)", ], From a7d03d62db5bbe194bf7639c57fa88e8f4c74fcc Mon Sep 17 00:00:00 2001 From: Sylvi <69098783+wingedcatgirl@users.noreply.github.com> Date: Wed, 15 Jul 2026 18:40:14 -0400 Subject: [PATCH 90/91] inherit no-flicker from reduced motion --- mod/config.lua | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/mod/config.lua b/mod/config.lua index 0096f09..baaa2df 100644 --- a/mod/config.lua +++ b/mod/config.lua @@ -1,7 +1,8 @@ -local colorblind = G.SETTINGS.colourblind_option +local colorblind = G.SETTINGS.colourblind_option or false +local nomotion = G.SETTINGS.reduced_motion or false return { ["block_components"] = true, ["cw_alt_art"] = colorblind, - ["no_price_flicker"] = false -} \ No newline at end of file + ["no_price_flicker"] = nomotion +} From 3c92fcee5f0314b567a9cd0e2232dd6f826355f8 Mon Sep 17 00:00:00 2001 From: Sylvi <69098783+wingedcatgirl@users.noreply.github.com> Date: Wed, 15 Jul 2026 18:41:48 -0400 Subject: [PATCH 91/91] also, number --- mod/FusionJokers.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mod/FusionJokers.json b/mod/FusionJokers.json index 0d6e76a..71c6d46 100644 --- a/mod/FusionJokers.json +++ b/mod/FusionJokers.json @@ -9,7 +9,7 @@ "priority": -10000, "badge_colour": "B26CBB", "badge_text_colour": "FFFFFF", - "version": "1.2.1", + "version": "1.2.2~BETA-20260715", "dependencies": [ "Steamodded (>=1.0.0~BETA-0827a)", ],