diff --git a/README.md b/README.md index 73e9504..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)
@@ -22,6 +22,7 @@ - [About](#about) - [How to Download](#how_to_download) +- [Config](#config) - [Fusion API (For Developers)](#fusion_api) - [Credits](#credits) @@ -33,22 +34,72 @@ 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 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/ ## ⬇ 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 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 + +- **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) 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: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. +} +``` + +> [!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. + +> [!NOTE] +> 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 +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 +} +``` + +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 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) 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 new file mode 100644 index 0000000..71c6d46 --- /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.2.2~BETA-20260715", + "dependencies": [ + "Steamodded (>=1.0.0~BETA-0827a)", + ], + "conflicts": [ + "Talisman (<=2.0)", + ], +} diff --git a/mod/FusionJokers.lua b/mod/FusionJokers.lua index 4f95aff..ea18ac8 100644 --- a/mod/FusionJokers.lua +++ b/mod/FusionJokers.lua @@ -1,15 +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 ----------------------------------------------- -------------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!" @@ -20,75 +8,84 @@ 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 }, } -local rarity = SMODS.Rarity{ +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 + + FusionJokers.fusions.ingredience[component_name] = FusionJokers.fusions.ingredience[component_name] or {} + FusionJokers.fusions.ingredience[component_name][fused] = true + end +end + + +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}, @@ -99,37 +96,145 @@ 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, - { 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 }) +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") + return + end + 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 + + 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, + result_joker = t.result_joker, + cost = t.cost, + merged_stat = t.merged_stat, + requirement = t.requirement, + aftermath = t.aftermath, + }) +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 + +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 + --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 + 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')() +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')() +SMODS.load_file('jokers/test.lua')() + +to_number = to_number or function(num) + return num end -local to_number = to_number or function(num) +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.cards) do - if v.ability.set == 'Joker' and v.config.center_key == val and i > start_pos then - return i + 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 + + 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 @@ -153,23 +258,151 @@ function Card:can_fuse_card() return true end end - end - return false + 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 + 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() +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 + 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 = "No fusions", + jokers = { + {name = self.config.center_key } + }, + cost = "??" + } + local jokerspos = {} for _, fusion in ipairs(FusionJokers.fusions) do + local valid = true for _, joker in ipairs(fusion.jokers) do if joker.name == self.config.center_key then - return fusion + result.result_joker = "Cannot fuse" + local recipe = {} + for i,component in ipairs(fusion.jokers) do + 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 = valid and true + else + valid = false + end + end + if valid then + results[#results+1] = deep_copy(fusion) + end + break end end - end - return nil + end + 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.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 + 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 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) + 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 - -function Card:fuse_card() +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 G.CONTROLLER.locks.selling_card = true stop_use() local area = self.area @@ -185,108 +418,154 @@ function Card:fuse_card() edition = self.edition end - local chosen_fusion = nil - local joker_pos = {} - local found_me = false - for _, fusion in ipairs(FusionJokers.fusions) do - joker_pos = {} - 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 chosen_fusion = self:get_card_fusion() + local carried_stats = {} + local merged_stat = 0 + local total_extra_cost = 0 + local recipe = {} + local me + do + local fusion = chosen_fusion + local found_me = false + local list = {} + for i,component in ipairs(fusion.jokers) do + if component.name == self.config.center_key and not found_me then + recipe[#recipe+1] = self found_me = true + self.fused = true + total_extra_cost = total_extra_cost + (self.ability.extra_value or 0) + 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 + if vv.config.center_key == component.name and not vv.fused then + recipe[#recipe+1] = vv + vv.fused = true + found_it = true + total_extra_cost = total_extra_cost + (vv.ability.extra_value or 0) + 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 + 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 #joker_pos == #fusion.jokers and found_me then - chosen_fusion = fusion - break + if not (#recipe == #fusion.jokers and found_me) then + dprint("Failed to find component Jokers when fusing?") + chosen_fusion = nil end end 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 = 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 + local j_fusion = self + + self:set_ability(chosen_fusion.result_joker) + if edition and not self.edition then + self:set_edition(edition.key) end - table.sort(joker_pos, function (a, b) - return a.pos > b.pos - end) - for index, pos in ipairs(joker_pos) do - local isPrimary = false - if G.jokers.cards[pos.pos] == self then - isPrimary = true - 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 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 - j_fusion.ability[k] = true + 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 + --]] + local perish_count = j_fusion.ability.perish_tally or 0 + j_fusion:add_sticker(k, true) + if k == "perishable" then + j_fusion.ability.perish_tally = math.max(perish_count, ingredient.ability.perish_tally or 0) + end --end end end - local check_joker = pos.joker - if check_joker.carry_stat then - 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] - else - j_fusion.ability[check_joker.carry_stat] = G.jokers.cards[pos.pos].ability[check_joker.carry_stat] - end + --G.jokers.cards[pos]:start_dissolve({G.C.GOLD}) + + local flags = SMODS.calculate_context({joker_type_destroyed = true, card = ingredient}) + if not (flags.no_destroy or isPrimary) then + ingredient:remove() + else + ingredient.fused = nil 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 + + if next(carried_stats) then + if type(self.ability.extra) == "number" then + local num = self.ability.extra + self.ability.extra = { + extra = num + } + elseif type(self.ability.extra) ~= "table" then + self.ability.extra = {} end - --G.jokers.cards[pos]:start_dissolve({G.C.GOLD}) - G.jokers.cards[pos.pos]:remove() + for k,v in pairs(carried_stats) do + self.ability.extra[k] = v + end + end + if chosen_fusion.merged_stat then + self.ability.extra[chosen_fusion.merged_stat] = merged_stat end - + delay(0.3) - j_fusion:add_to_deck() - G.jokers:emplace(j_fusion) play_sound('explosion_release1') + self.ability.extra_value = total_extra_cost + self:set_cost() + G.jokers:unhighlight_all() delay(0.1) G.E_MANAGER:add_event(Event({trigger = 'after',delay = 0.3, blocking = false, @@ -305,6 +584,17 @@ function Card:fuse_card() 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 + 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 @@ -317,7 +607,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 @@ -331,11 +621,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={ @@ -357,135 +647,36 @@ 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) if G.STAGE == G.STAGES.RUN then + local fuseable, my_fusion = self:can_fuse_card(true) - 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 + 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() and not self.ability.fusion.jiggle then - juice_card_until(self, function(card) return (card:can_fuse_card()) end, true) + if fuseable 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 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 - -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 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({ @@ -499,905 +690,15 @@ end -- end -- return true end) -- })) - + -- shatterref(self) -- 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) + 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)), - 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 not context.after and not context.before 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() 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 not context.after and not context.before 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 +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 - - 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 not context.after and not context.before 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 not context.after and not context.before 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 not context.after and not context.before 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 not context.before and not context.after 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---------------------- +end \ No newline at end of file diff --git a/mod/assets/1x/j_club_wizard.png b/mod/assets/1x/j_club_wizard.png index 83b6e40..58b26e0 100644 Binary files a/mod/assets/1x/j_club_wizard.png and b/mod/assets/1x/j_club_wizard.png differ diff --git a/mod/assets/1x/j_flip_flop.png b/mod/assets/1x/j_flip_flop.png index 2919b84..6515673 100644 Binary files a/mod/assets/1x/j_flip_flop.png and b/mod/assets/1x/j_flip_flop.png differ 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 0000000..bcfa723 Binary files /dev/null and b/mod/assets/1x/j_fuse_three_jimbos.png differ diff --git a/mod/assets/1x/j_spade_archer.png b/mod/assets/1x/j_spade_archer.png index 39727f3..9593965 100644 Binary files a/mod/assets/1x/j_spade_archer.png and b/mod/assets/1x/j_spade_archer.png differ diff --git a/mod/assets/2x/j_club_wizard.png b/mod/assets/2x/j_club_wizard.png index 78ca4ea..90eb3f8 100644 Binary files a/mod/assets/2x/j_club_wizard.png and b/mod/assets/2x/j_club_wizard.png differ diff --git a/mod/assets/2x/j_flip_flop.png b/mod/assets/2x/j_flip_flop.png index 3933296..3d609a6 100644 Binary files a/mod/assets/2x/j_flip_flop.png and b/mod/assets/2x/j_flip_flop.png differ diff --git a/mod/assets/2x/j_fuse_three_jimbos.png b/mod/assets/2x/j_fuse_three_jimbos.png new file mode 100644 index 0000000..d50587a Binary files /dev/null and b/mod/assets/2x/j_fuse_three_jimbos.png differ diff --git a/mod/assets/2x/j_spade_archer.png b/mod/assets/2x/j_spade_archer.png index 91dafd1..0b04a8a 100644 Binary files a/mod/assets/2x/j_spade_archer.png and b/mod/assets/2x/j_spade_archer.png differ diff --git a/mod/config.lua b/mod/config.lua new file mode 100644 index 0000000..baaa2df --- /dev/null +++ b/mod/config.lua @@ -0,0 +1,8 @@ +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"] = nomotion +} diff --git a/mod/configui.lua b/mod/configui.lua new file mode 100644 index 0000000..d5ed0b8 --- /dev/null +++ b/mod/configui.lua @@ -0,0 +1,21 @@ +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', + }), + create_toggle({ + label = "Alt art for Club Wizard", + 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 diff --git a/mod/jokers/bigbang.lua b/mod/jokers/bigbang.lua new file mode 100644 index 0000000..3e2ff6d --- /dev/null +++ b/mod/jokers/bigbang.lua @@ -0,0 +1,67 @@ +SMODS.Atlas { + key = 'big_bang', + path = "j_big_bang.png", + px = 71, + py = 95 +} + +SMODS.Joker { + key = "big_bang", + name = "Big Bang", + atlas = 'big_bang', + 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 = { + Xmult = 0.1, + joker1 = "j_supernova", + joker2 = "j_constellation" + } + }, + loc_vars = function(self, info_queue, card) + return { + vars = { + 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, + calculate = function(self, 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, + 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 +} + +-- See localization/en-us.lua to create joker text \ No newline at end of file diff --git a/mod/jokers/campingtrip.lua b/mod/jokers/campingtrip.lua new file mode 100644 index 0000000..22c28df --- /dev/null +++ b/mod/jokers/campingtrip.lua @@ -0,0 +1,84 @@ +SMODS.Atlas { + key = 'camping_trip', + path = "j_camping_trip.png", + px = 71, + py = 95 +} + +SMODS.Joker { + key = "camping_trip", + name = "Camping Trip", + atlas = 'camping_trip', + 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_base = 5, + bonus_final = 10, + joker1 = "j_hiker", + joker2 = "j_dusk" + } + }, + loc_vars = function(self, info_queue, card) + return { + vars = { + 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, + calculate = function(self, card, context) + if context.individual and context.cardarea == G.play then + local buff = card.ability.extra.bonus_base + if G.GAME.current_round.hands_left == 0 then + buff = card.ability.extra.bonus_final + end + context.other_card.ability.perma_bonus = (context.other_card.ability.perma_bonus or 0) + buff + + 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, + 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 + 1 * JokerDisplay.calculate_joker_triggers(joker_card) or 0 + end + } + end +} + +-- See localization/en-us.lua to create joker text diff --git a/mod/jokers/clubwizard.lua b/mod/jokers/clubwizard.lua new file mode 100644 index 0000000..ede034c --- /dev/null +++ b/mod/jokers/clubwizard.lua @@ -0,0 +1,89 @@ +SMODS.Atlas { + key = 'club_wizard', + path = "j_club_wizard.png", + px = 71, + py = 95 +} + +SMODS.Joker { + key = "club_wizard", + name = "Club Wizard", + atlas = 'club_wizard', + 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 = 24, + joker1 = "j_gluttenous_joker", + joker2 = "j_onyx_agate", + art = "standard" + } + }, + loc_vars = function(self, info_queue, card) + return { + vars = { + 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_suit('Clubs') then + return { + mult = card.ability.extra.mult, + 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, + 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 ~= "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 ~= "standard" then + card.children.center:set_sprite_pos({ x = 0, y = 0}) + card.ability.extra.art = "standard" + end + end +} + +-- See localization/en-us.lua to create joker text \ No newline at end of file diff --git a/mod/jokers/collectiblechaos.lua b/mod/jokers/collectiblechaos.lua new file mode 100644 index 0000000..d07ff7d --- /dev/null +++ b/mod/jokers/collectiblechaos.lua @@ -0,0 +1,97 @@ +SMODS.Atlas { + key = 'collectible_chaos_card', + path = "j_collectible_chaos_card.png", + px = 71, + py = 95 +} + +SMODS.Joker { + key = "collectible_chaos_card", + name = "Collectible Chaos Card", + atlas = 'collectible_chaos_card', + 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 = { + per_reroll = 2, + free = 1, + mult = 0, + joker1 = "j_chaos", + joker2 = "j_flash" + } + }, + loc_vars = function(self, info_queue, card) + return { + vars = { + card.ability.extra.per_reroll, + card.ability.extra.free, + 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, + 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 + card.ability.extra.free + calculate_reroll_cost(true) + end + end, + 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) + 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 + 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({ + func = (function() + card_eval_status_text(card, 'extra', nil, nil, nil, {message = localize{type = 'variable', key = 'a_mult', vars = {card.ability.extra.mult}}, colour = G.C.MULT}) + return true + end)})) + end + + if context.cardarea == G.jokers and card.ability.extra.mult > 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, + 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 +} + +-- 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..20753f8 --- /dev/null +++ b/mod/jokers/commercialdriver.lua @@ -0,0 +1,82 @@ +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, + joker_display_def = function(JokerDisplay) + return { + text = { + { + border_nodes = { + { text = "X" }, + { ref_table = "card.ability.extra", ref_value = "total" } + } + } + } + } + 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..26b6ee0 --- /dev/null +++ b/mod/jokers/dementiajoker.lua @@ -0,0 +1,95 @@ +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) + local luck, odds = SMODS.get_probability_vars(card, 1, card.ability.extra.odds, "fusion_dementia_desc", false) + return { + vars = { + card.ability.extra.mult, + 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'} + } + } + 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 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) + 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, + 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) + 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 = { luck, odds } } + end + 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..10ab58c --- /dev/null +++ b/mod/jokers/diamondbard.lua @@ -0,0 +1,91 @@ +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, + 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 +} + +-- 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..d397ce3 --- /dev/null +++ b/mod/jokers/dynamicduo.lua @@ -0,0 +1,82 @@ +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 = 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.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, + 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 +} + +-- 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..2e165e0 --- /dev/null +++ b/mod/jokers/flagbearer.lua @@ -0,0 +1,98 @@ +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) + 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 + 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, + 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 +} + +-- 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..ccaafa5 --- /dev/null +++ b/mod/jokers/flipflop.lua @@ -0,0 +1,168 @@ +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, + 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 + card.ability.extra.discards + ease_discard(card.ability.extra.discards) + end + end, + 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 - card.ability.extra.discards + ease_discard(-card.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 + 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, + 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 +} + +-- 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..f85b6c8 --- /dev/null +++ b/mod/jokers/goldenegg.lua @@ -0,0 +1,78 @@ +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, + 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, + 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 +} + +-- 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..d30aace --- /dev/null +++ b/mod/jokers/heartpaladin.lua @@ -0,0 +1,102 @@ +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) + local luck, odds = SMODS.get_probability_vars(card, 1, card.ability.extra.odds, "fusion_heartpaladin_desc", false) + return { + vars = { + card.ability.extra.Xmult, + luck, + 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 SMODS.pseudorandom_probability(card, 'heart_paladin', 1, card.ability.extra.odds, 'heart_paladin') then + return { + message = localize('k_again_ex'), + repetitions = 1, + card = card + } + 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 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 luck < odds 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 +} + +-- 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..60cf0f6 --- /dev/null +++ b/mod/jokers/moonmarauder.lua @@ -0,0 +1,118 @@ +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', + 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_six_slothful_joker", + joker2 = "j_six_moonstone" + } + }, + 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 = { + luck, + 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 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({ + 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..b77c369 --- /dev/null +++ b/mod/jokers/royaldecree.lua @@ -0,0 +1,99 @@ +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, + 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 +} + +-- 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..c425897 --- /dev/null +++ b/mod/jokers/spadearcher.lua @@ -0,0 +1,97 @@ +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 and not context.blueprint 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, + 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, +} + +-- See localization/en-us.lua to create joker text diff --git a/mod/jokers/staroracle.lua b/mod/jokers/staroracle.lua new file mode 100644 index 0000000..3e62319 --- /dev/null +++ b/mod/jokers/staroracle.lua @@ -0,0 +1,75 @@ +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', + 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_six_envious_joker", + joker2 = "j_six_star_ruby", + } + }, + 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 = { + luck, + odds, + localize{type = 'name_text', key = card.ability.extra.joker1, set = 'Joker'}, + localize{type = 'name_text', key = card.ability.extra.joker2, set = 'Joker'} + } + } + end, + add_to_deck = function(self, card, from_debuff) + G.consumeables:change_size(card.ability.extra.slots) + end, + 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 + 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 + 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', + 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/test.lua b/mod/jokers/test.lua new file mode 100644 index 0000000..7f3e219 --- /dev/null +++ b/mod/jokers/test.lua @@ -0,0 +1,59 @@ +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" }, + { name = "j_joker", merge_stat = "mult" }, + { name = "j_joker", merge_stat = "mult" }, + }, + result_joker = "j_fuse_three_jimbos", + merged_stat = "mult", + cost = 2, +} + +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, + 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 +} + +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/jokers/uncannyface.lua b/mod/jokers/uncannyface.lua new file mode 100644 index 0000000..96913b5 --- /dev/null +++ b/mod/jokers/uncannyface.lua @@ -0,0 +1,90 @@ +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, + 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 +} + +-- 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..99ae859 --- /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 or more {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 diff --git a/mod/lsp_defs.lua b/mod/lsp_defs.lua new file mode 100644 index 0000000..b0c01bb --- /dev/null +++ b/mod/lsp_defs.lua @@ -0,0 +1,35 @@ +---@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 fused + +---Register a fusion recipe +---@param t FusionRecipe +function FusionJokers.fusions:register_fusion(t) +end + +---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 +---@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 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 +---@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