From 74c41e978a3ccd06fadf80290244bf295e13a3b3 Mon Sep 17 00:00:00 2001 From: Zei33 Date: Tue, 21 May 2024 16:55:56 +1000 Subject: [PATCH 01/15] Added a new command for generating booster packs. --- src/balamod.lua | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/balamod.lua b/src/balamod.lua index 3b7c244..acb1655 100644 --- a/src/balamod.lua +++ b/src/balamod.lua @@ -1026,6 +1026,29 @@ mods["dev_console"] = { "Usage: installmod " ) + console:registerCommand( + "booster", + function (args) + local pack = get_pack("shop_pack", args[1] or nil) + G.FUNCS.use_card({ + config = { + ref_table = Card(0, 0, 0, 0, pack, pack) + } + }) + end, + "Generate a booster pack (random if no argument, otherwise specify type)", + function (current_arg) + local subcommands = { "Standard", "Spectral", "Arcana", "Celestial", "Buffoon" } + for i, v in ipairs(subcommands) do + if v:find(current_arg, 1, true) == 1 then + return {v} + end + end + return nil + end, + "Usage: booster [Standard/Spectral/Arcana/Celestial/Buffoon]" + ) + console.logger:debug("Dev Console on_enable completed") end, on_disable = function() From 86ad9ebc5b10a5bd9c3b153c40557e2d773b2685 Mon Sep 17 00:00:00 2001 From: Zei33 Date: Tue, 21 May 2024 17:13:55 +1000 Subject: [PATCH 02/15] Added deregister function for booster command. --- src/balamod.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/src/balamod.lua b/src/balamod.lua index acb1655..798d82a 100644 --- a/src/balamod.lua +++ b/src/balamod.lua @@ -1062,6 +1062,7 @@ mods["dev_console"] = { console.removeCommand("money") console.removeCommand("discards") console.removeCommand("hands") + console.removeCommand("booster") console.logger:debug("Dev Console disabled") end, on_key_pressed = function (key_name) From 76a89cafcc0e8ba76adf7f5e44e041e1ad0fd2ca Mon Sep 17 00:00:00 2001 From: Zei33 Date: Thu, 30 May 2024 04:13:24 +1000 Subject: [PATCH 03/15] Added the enhance console command. --- src/balamod.lua | 172 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) diff --git a/src/balamod.lua b/src/balamod.lua index 798d82a..fceb00e 100644 --- a/src/balamod.lua +++ b/src/balamod.lua @@ -1049,6 +1049,177 @@ mods["dev_console"] = { "Usage: booster [Standard/Spectral/Arcana/Celestial/Buffoon]" ) + console:registerCommand( + "enhance", + function (args) + local arg = args[1] or nil + if arg == nil then + logger:info("No enhancement specified") + return + end + + local tables = {} + + if G.pack_cards then table.insert(tables, G.pack_cards.highlighted) end + if G.shop_vouchers then table.insert(tables, G.shop_vouchers.highlighted) end + if G.shop_jokers then table.insert(tables, G.shop_jokers.highlighted) end + if G.shop_booster then table.insert(tables, G.shop_booster.highlighted) end + if G.consumeables then table.insert(tables, G.consumeables.highlighted) end + if G.jokers then table.insert(tables, G.jokers.highlighted) end + if G.discard then table.insert(tables, G.discard.highlighted) end + if G.deck then table.insert(tables, G.deck.highlighted) end + if G.hand then table.insert(tables, G.hand.highlighted) end + if G.play then table.insert(tables, G.play.highlighted) end + + for i, t in ipairs(tables) do + for j, card in ipairs(t) do + if card.config then + logger:info("Changing " .. card.config.center.key) + + for k, enhancement in pairs(args) do + + if enhancement == "GoldCard" or enhancement == "m_gold" then + if (card.config.center.set == "Default" or card.config.center.set == "Enhanced") then + card:set_ability(G.P_CENTERS.m_gold, nil, false) + logger:info("Card set to Gold Card (" .. card.config.center.key .. ").") + else + logger:info("Only standard cards should be set to Gold Card (" .. card.config.center.key .. ").") + end + end + + if enhancement == "BonusCard" or enhancement == "m_bonus" then + if (card.config.center.set == "Default" or card.config.center.set == "Enhanced") then + card:set_ability(G.P_CENTERS.m_bonus, nil, false) + logger:info("Card set to Bonus Card (" .. card.config.center.key .. ").") + else + logger:info("Only standard cards should be set to Bonus Card (" .. card.config.center.key .. ").") + end + end + + if enhancement == "MultCard" or enhancement == "m_mult" then + if (card.config.center.set == "Default" or card.config.center.set == "Enhanced") then + card:set_ability(G.P_CENTERS.m_mult, nil, false) + logger:info("Card set to Mult Card (" .. card.config.center.key .. ").") + else + logger:info("Only standard cards should be set to Mult Card (" .. card.config.center.key .. ").") + end + end + + if enhancement == "WildCard" or enhancement == "m_wild" then + if (card.config.center.set == "Default" or card.config.center.set == "Enhanced") then + card:set_ability(G.P_CENTERS.m_wild, nil, false) + logger:info("Card set to Wild Card (" .. card.config.center.key .. ").") + else + logger:info("Only standard cards should be set to Wild Card (" .. card.config.center.key .. ").") + end + end + + if enhancement == "GlassCard" or enhancement == "m_glass" then + if (card.config.center.set == "Default" or card.config.center.set == "Enhanced") then + card:set_ability(G.P_CENTERS.m_glass, nil, false) + logger:info("Card set to Glass Card (" .. card.config.center.key .. ").") + else + logger:info("Only standard cards should be set to Glass Card (" .. card.config.center.key .. ").") + end + end + + if enhancement == "SteelCard" or enhancement == "m_steel" then + if (card.config.center.set == "Default" or card.config.center.set == "Enhanced") then + card:set_ability(G.P_CENTERS.m_steel, nil, false) + logger:info("Card set to Steel Card (" .. card.config.center.key .. ").") + else + logger:info("Only standard cards should be set to Steel Card (" .. card.config.center.key .. ").") + end + end + + if enhancement == "StoneCard" or enhancement == "m_stone" then + if (card.config.center.set == "Default" or card.config.center.set == "Enhanced") then + card:set_ability(G.P_CENTERS.m_stone, nil, false) + logger:info("Card set to Stone Card (" .. card.config.center.key .. ").") + else + logger:info("Only standard cards should be set to Stone Card (" .. card.config.center.key .. ").") + end + end + + if enhancement == "LuckyCard" or enhancement == "m_lucky" then + if (card.config.center.set == "Default" or card.config.center.set == "Enhanced") then + card:set_ability(G.P_CENTERS.m_lucky, nil, false) + logger:info("Card set to Lucky Card (" .. card.config.center.key .. ").") + else + logger:info("Only standard cards should be set to Lucky Card (" .. card.config.center.key .. ").") + end + end + + if enhancement == "RedSeal" then + card:set_seal("Red", true) + logger:info("Red Seal added to card (" .. card.config.center.key .. ").") + end + + if enhancement == "BlueSeal" then + card:set_seal("Blue", true) + logger:info("Blue Seal added to card (" .. card.config.center.key .. ").") + end + + if enhancement == "GoldSeal" then + card:set_seal("Gold", true) + logger:info("Gold Seal added to card (" .. card.config.center.key .. ").") + end + + if enhancement == "PurpleSeal" then + card:set_seal("Purple", true) + logger:info("Purple Seal added to card (" .. card.config.center.key .. ").") + end + + if enhancement == "Base" or enhancement == "e_base" then + card:set_edition({ }, true) + logger:info("Card set to base edition (" .. card.config.center.key .. ").") + end + + if enhancement == "Negative" or enhancement == "e_negative" then + card:set_edition({ negative = true }, true) + logger:info("Card set to negative edition (" .. card.config.center.key .. ").") + end + + if enhancement == "Foil" or enhancement == "e_foil" then + card:set_edition({ foil = true }, true) + logger:info("Card set to foil edition (" .. card.config.center.key .. ").") + end + + if enhancement == "Poly" or enhancement == "Polychrome" or enhancement == "e_polychrome" then + card:set_edition({ polychrome = true }, true) + logger:info("Card set to polychrome edition (" .. card.config.center.key .. ").") + end + + if enhancement == "Holo" or enhancement == "Holographic" or enhancement == "e_holo" then + card:set_edition({ holo = true }, true) + logger:info("Card set to holographic edition (" .. card.config.center.key .. ").") + end + + end + + end + end + end + end, + "Add one or more enhancements to selected cards", + function (current_arg) + local subcommands = { + "GoldCard", "WildCard", "MultCard", "BonusCard", "GlassCard", "SteelCard", "StoneCard", "LuckyCard", + "m_gold", "m_wild", "m_mult", "m_bonus", "m_glass", "m_steel", "m_stone", "m_lucky", + "RedSeal", "BlueSeal", "GoldSeal", "PurpleSeal", + "Base", "Negative", "Foil", "Poly", "Holo", + "e_base", "e_negative", "e_foil", "e_polychrome", "e_holo" + } + for i, v in ipairs(subcommands) do + if v:find(current_arg, 1, true) == 1 then + return {v} + end + end + return nil + end, + "Usage: enhance [arg2] [arg3] ..." + ) + console.logger:debug("Dev Console on_enable completed") end, on_disable = function() @@ -1063,6 +1234,7 @@ mods["dev_console"] = { console.removeCommand("discards") console.removeCommand("hands") console.removeCommand("booster") + console.removeCommand("enhance") console.logger:debug("Dev Console disabled") end, on_key_pressed = function (key_name) From 0c44c3c3d8c24b51cef6dae34792a11aeb3fa3fd Mon Sep 17 00:00:00 2001 From: Zei33 Date: Thu, 30 May 2024 12:34:15 +1000 Subject: [PATCH 04/15] Added support for custom enhancements. --- src/balamod.lua | 299 +++++++++++++++++++++++------------------------- 1 file changed, 142 insertions(+), 157 deletions(-) diff --git a/src/balamod.lua b/src/balamod.lua index fceb00e..9192e01 100644 --- a/src/balamod.lua +++ b/src/balamod.lua @@ -1050,175 +1050,160 @@ mods["dev_console"] = { ) console:registerCommand( - "enhance", - function (args) - local arg = args[1] or nil - if arg == nil then - logger:info("No enhancement specified") - return - end - - local tables = {} - - if G.pack_cards then table.insert(tables, G.pack_cards.highlighted) end - if G.shop_vouchers then table.insert(tables, G.shop_vouchers.highlighted) end - if G.shop_jokers then table.insert(tables, G.shop_jokers.highlighted) end - if G.shop_booster then table.insert(tables, G.shop_booster.highlighted) end - if G.consumeables then table.insert(tables, G.consumeables.highlighted) end - if G.jokers then table.insert(tables, G.jokers.highlighted) end - if G.discard then table.insert(tables, G.discard.highlighted) end - if G.deck then table.insert(tables, G.deck.highlighted) end - if G.hand then table.insert(tables, G.hand.highlighted) end - if G.play then table.insert(tables, G.play.highlighted) end - - for i, t in ipairs(tables) do - for j, card in ipairs(t) do - if card.config then - logger:info("Changing " .. card.config.center.key) - - for k, enhancement in pairs(args) do - - if enhancement == "GoldCard" or enhancement == "m_gold" then - if (card.config.center.set == "Default" or card.config.center.set == "Enhanced") then - card:set_ability(G.P_CENTERS.m_gold, nil, false) - logger:info("Card set to Gold Card (" .. card.config.center.key .. ").") - else - logger:info("Only standard cards should be set to Gold Card (" .. card.config.center.key .. ").") - end - end - - if enhancement == "BonusCard" or enhancement == "m_bonus" then - if (card.config.center.set == "Default" or card.config.center.set == "Enhanced") then - card:set_ability(G.P_CENTERS.m_bonus, nil, false) - logger:info("Card set to Bonus Card (" .. card.config.center.key .. ").") - else - logger:info("Only standard cards should be set to Bonus Card (" .. card.config.center.key .. ").") - end - end - - if enhancement == "MultCard" or enhancement == "m_mult" then - if (card.config.center.set == "Default" or card.config.center.set == "Enhanced") then - card:set_ability(G.P_CENTERS.m_mult, nil, false) - logger:info("Card set to Mult Card (" .. card.config.center.key .. ").") - else - logger:info("Only standard cards should be set to Mult Card (" .. card.config.center.key .. ").") - end - end - - if enhancement == "WildCard" or enhancement == "m_wild" then - if (card.config.center.set == "Default" or card.config.center.set == "Enhanced") then - card:set_ability(G.P_CENTERS.m_wild, nil, false) - logger:info("Card set to Wild Card (" .. card.config.center.key .. ").") - else - logger:info("Only standard cards should be set to Wild Card (" .. card.config.center.key .. ").") - end - end - - if enhancement == "GlassCard" or enhancement == "m_glass" then - if (card.config.center.set == "Default" or card.config.center.set == "Enhanced") then - card:set_ability(G.P_CENTERS.m_glass, nil, false) - logger:info("Card set to Glass Card (" .. card.config.center.key .. ").") - else - logger:info("Only standard cards should be set to Glass Card (" .. card.config.center.key .. ").") - end - end + "enhance", + function (args) + local arg = args[1] or nil + if arg == nil then + logger:info("No enhancement specified") + return + end - if enhancement == "SteelCard" or enhancement == "m_steel" then - if (card.config.center.set == "Default" or card.config.center.set == "Enhanced") then - card:set_ability(G.P_CENTERS.m_steel, nil, false) - logger:info("Card set to Steel Card (" .. card.config.center.key .. ").") - else - logger:info("Only standard cards should be set to Steel Card (" .. card.config.center.key .. ").") + local tables = {} + + if G.pack_cards then table.insert(tables, G.pack_cards.highlighted) end + if G.shop_vouchers then table.insert(tables, G.shop_vouchers.highlighted) end + if G.shop_jokers then table.insert(tables, G.shop_jokers.highlighted) end + if G.shop_booster then table.insert(tables, G.shop_booster.highlighted) end + if G.consumeables then table.insert(tables, G.consumeables.highlighted) end + if G.jokers then table.insert(tables, G.jokers.highlighted) end + if G.discard then table.insert(tables, G.discard.highlighted) end + if G.deck then table.insert(tables, G.deck.highlighted) end + if G.hand then table.insert(tables, G.hand.highlighted) end + if G.play then table.insert(tables, G.play.highlighted) end + + for i, t in ipairs(tables) do + for j, card in ipairs(t) do + if card.config then + logger:info("Changing " .. card.config.center.key) + + for k, enhancement in pairs(args) do + + if enhancement == "GoldCard" or enhancement == "m_gold" then + if (card.config.center.set == "Default" or card.config.center.set == "Enhanced") then + card:set_ability(G.P_CENTERS.m_gold, nil, false) + logger:info("Card set to Gold Card (" .. card.config.center.key .. ").") + else + logger:info("Only standard cards should be set to Gold Card (" .. card.config.center.key .. ").") + end + elseif enhancement == "BonusCard" or enhancement == "m_bonus" then + if (card.config.center.set == "Default" or card.config.center.set == "Enhanced") then + card:set_ability(G.P_CENTERS.m_bonus, nil, false) + logger:info("Card set to Bonus Card (" .. card.config.center.key .. ").") + else + logger:info("Only standard cards should be set to Bonus Card (" .. card.config.center.key .. ").") + end + elseif enhancement == "MultCard" or enhancement == "m_mult" then + if (card.config.center.set == "Default" or card.config.center.set == "Enhanced") then + card:set_ability(G.P_CENTERS.m_mult, nil, false) + logger:info("Card set to Mult Card (" .. card.config.center.key .. ").") + else + logger:info("Only standard cards should be set to Mult Card (" .. card.config.center.key .. ").") + end + elseif enhancement == "WildCard" or enhancement == "m_wild" then + if (card.config.center.set == "Default" or card.config.center.set == "Enhanced") then + card:set_ability(G.P_CENTERS.m_wild, nil, false) + logger:info("Card set to Wild Card (" .. card.config.center.key .. ").") + else + logger:info("Only standard cards should be set to Wild Card (" .. card.config.center.key .. ").") + end + elseif enhancement == "GlassCard" or enhancement == "m_glass" then + if (card.config.center.set == "Default" or card.config.center.set == "Enhanced") then + card:set_ability(G.P_CENTERS.m_glass, nil, false) + logger:info("Card set to Glass Card (" .. card.config.center.key .. ").") + else + logger:info("Only standard cards should be set to Glass Card (" .. card.config.center.key .. ").") + end + elseif enhancement == "SteelCard" or enhancement == "m_steel" then + if (card.config.center.set == "Default" or card.config.center.set == "Enhanced") then + card:set_ability(G.P_CENTERS.m_steel, nil, false) + logger:info("Card set to Steel Card (" .. card.config.center.key .. ").") + else + logger:info("Only standard cards should be set to Steel Card (" .. card.config.center.key .. ").") + end + elseif enhancement == "StoneCard" or enhancement == "m_stone" then + if (card.config.center.set == "Default" or card.config.center.set == "Enhanced") then + card:set_ability(G.P_CENTERS.m_stone, nil, false) + logger:info("Card set to Stone Card (" .. card.config.center.key .. ").") + else + logger:info("Only standard cards should be set to Stone Card (" .. card.config.center.key .. ").") + end + elseif enhancement == "LuckyCard" or enhancement == "m_lucky" then + if (card.config.center.set == "Default" or card.config.center.set == "Enhanced") then + card:set_ability(G.P_CENTERS.m_lucky, nil, false) + logger:info("Card set to Lucky Card (" .. card.config.center.key .. ").") + else + logger:info("Only standard cards should be set to Lucky Card (" .. card.config.center.key .. ").") + end + elseif string.sub(enhancement, 1, 2) == "m_" and G.P_CENTERS[enhancement] ~= nil then + if (card.config.center.set == "Default" or card.config.center.set == "Enhanced") then + card:set_ability(G.P_CENTERS[enhancement], nil, false) + logger:info("Card set to a " .. G.P_CENTERS[enhancement].name .. " (" .. card.config.center.key .. ").") + else + logger:info("Only standard cards should be set to " .. G.P_CENTERS[enhancement].name .. " (" .. card.config.center.key .. ").") + end end - end - - if enhancement == "StoneCard" or enhancement == "m_stone" then - if (card.config.center.set == "Default" or card.config.center.set == "Enhanced") then - card:set_ability(G.P_CENTERS.m_stone, nil, false) - logger:info("Card set to Stone Card (" .. card.config.center.key .. ").") - else - logger:info("Only standard cards should be set to Stone Card (" .. card.config.center.key .. ").") + + if string.sub(enhancement, -4) == "Seal" then + local seal = string.sub(enhancement, 1, -5) + card:set_seal(seal, true) + logger:info(seal .. " Seal added to card (" .. card.config.center.key .. ").") end - end - if enhancement == "LuckyCard" or enhancement == "m_lucky" then - if (card.config.center.set == "Default" or card.config.center.set == "Enhanced") then - card:set_ability(G.P_CENTERS.m_lucky, nil, false) - logger:info("Card set to Lucky Card (" .. card.config.center.key .. ").") - else - logger:info("Only standard cards should be set to Lucky Card (" .. card.config.center.key .. ").") + if enhancement == "Base" or enhancement == "e_base" then + card:set_edition({ }, true) + logger:info("Card set to base edition (" .. card.config.center.key .. ").") + elseif enhancement == "Negative" or enhancement == "e_negative" then + card:set_edition({ negative = true }, true) + logger:info("Card set to negative edition (" .. card.config.center.key .. ").") + elseif enhancement == "Foil" or enhancement == "e_foil" then + card:set_edition({ foil = true }, true) + logger:info("Card set to foil edition (" .. card.config.center.key .. ").") + elseif enhancement == "Poly" or enhancement == "Polychrome" or enhancement == "e_polychrome" then + card:set_edition({ polychrome = true }, true) + logger:info("Card set to polychrome edition (" .. card.config.center.key .. ").") + elseif enhancement == "Holo" or enhancement == "Holographic" or enhancement == "e_holo" then + card:set_edition({ holo = true }, true) + logger:info("Card set to holographic edition (" .. card.config.center.key .. ").") + elseif string.sub(enhancement, 1, 2) == "e_" and G.P_CENTERS[enhancement] ~= nil then + local editionKey = string.sub(enhancement, 3) + card:set_edition({ [editionKey] = true }, true) + logger:info("Card set to " .. G.P_CENTERS[enhancement].name .. " edition (" .. card.config.center.key .. ").") end - end - - if enhancement == "RedSeal" then - card:set_seal("Red", true) - logger:info("Red Seal added to card (" .. card.config.center.key .. ").") - end - - if enhancement == "BlueSeal" then - card:set_seal("Blue", true) - logger:info("Blue Seal added to card (" .. card.config.center.key .. ").") - end - - if enhancement == "GoldSeal" then - card:set_seal("Gold", true) - logger:info("Gold Seal added to card (" .. card.config.center.key .. ").") - end - - if enhancement == "PurpleSeal" then - card:set_seal("Purple", true) - logger:info("Purple Seal added to card (" .. card.config.center.key .. ").") - end - - if enhancement == "Base" or enhancement == "e_base" then - card:set_edition({ }, true) - logger:info("Card set to base edition (" .. card.config.center.key .. ").") + end - if enhancement == "Negative" or enhancement == "e_negative" then - card:set_edition({ negative = true }, true) - logger:info("Card set to negative edition (" .. card.config.center.key .. ").") - end - - if enhancement == "Foil" or enhancement == "e_foil" then - card:set_edition({ foil = true }, true) - logger:info("Card set to foil edition (" .. card.config.center.key .. ").") - end - - if enhancement == "Poly" or enhancement == "Polychrome" or enhancement == "e_polychrome" then - card:set_edition({ polychrome = true }, true) - logger:info("Card set to polychrome edition (" .. card.config.center.key .. ").") - end + end + end + end + end, + "Add one or more enhancements to selected cards. Can use custom values.", + function (current_arg) + local keys = {} + for key, value in pairs(G.P_CENTERS) do + if string.sub(key, 1, 2) == "e_" or string.sub(key, 1, 2) == "m_" then + table.insert(keys, key) + end + end - if enhancement == "Holo" or enhancement == "Holographic" or enhancement == "e_holo" then - card:set_edition({ holo = true }, true) - logger:info("Card set to holographic edition (" .. card.config.center.key .. ").") - end + local subcommands = { + "GoldCard", "WildCard", "MultCard", "BonusCard", "GlassCard", "SteelCard", "StoneCard", "LuckyCard", + "RedSeal", "BlueSeal", "GoldSeal", "PurpleSeal", + "Base", "Negative", "Foil", "Poly", "Holo" + } - end + for _, key in ipairs(keys) do + table.insert(subcommands, key) + end + for i, v in ipairs(subcommands) do + if v:find(current_arg, 1, true) == 1 then + return {v} end end - end - end, - "Add one or more enhancements to selected cards", - function (current_arg) - local subcommands = { - "GoldCard", "WildCard", "MultCard", "BonusCard", "GlassCard", "SteelCard", "StoneCard", "LuckyCard", - "m_gold", "m_wild", "m_mult", "m_bonus", "m_glass", "m_steel", "m_stone", "m_lucky", - "RedSeal", "BlueSeal", "GoldSeal", "PurpleSeal", - "Base", "Negative", "Foil", "Poly", "Holo", - "e_base", "e_negative", "e_foil", "e_polychrome", "e_holo" - } - for i, v in ipairs(subcommands) do - if v:find(current_arg, 1, true) == 1 then - return {v} - end - end - return nil - end, - "Usage: enhance [arg2] [arg3] ..." - ) + return nil + end, + "Usage: enhance [arg2] [arg3] ..." + ) console.logger:debug("Dev Console on_enable completed") end, From e494173a61efc9453dbc7d23c9d79c11a09771f2 Mon Sep 17 00:00:00 2001 From: Zei33 Date: Thu, 30 May 2024 13:35:24 +1000 Subject: [PATCH 05/15] Rewrote enhance command to be more dynamic. --- src/balamod.lua | 159 ++++++++++++++++++------------------------------ 1 file changed, 58 insertions(+), 101 deletions(-) diff --git a/src/balamod.lua b/src/balamod.lua index 9192e01..ca66179 100644 --- a/src/balamod.lua +++ b/src/balamod.lua @@ -1074,100 +1074,48 @@ mods["dev_console"] = { for i, t in ipairs(tables) do for j, card in ipairs(t) do if card.config then - logger:info("Changing " .. card.config.center.key) + logger:info("Enhancing " .. card.config.center.key) for k, enhancement in pairs(args) do - - if enhancement == "GoldCard" or enhancement == "m_gold" then - if (card.config.center.set == "Default" or card.config.center.set == "Enhanced") then - card:set_ability(G.P_CENTERS.m_gold, nil, false) - logger:info("Card set to Gold Card (" .. card.config.center.key .. ").") - else - logger:info("Only standard cards should be set to Gold Card (" .. card.config.center.key .. ").") - end - elseif enhancement == "BonusCard" or enhancement == "m_bonus" then - if (card.config.center.set == "Default" or card.config.center.set == "Enhanced") then - card:set_ability(G.P_CENTERS.m_bonus, nil, false) - logger:info("Card set to Bonus Card (" .. card.config.center.key .. ").") - else - logger:info("Only standard cards should be set to Bonus Card (" .. card.config.center.key .. ").") - end - elseif enhancement == "MultCard" or enhancement == "m_mult" then - if (card.config.center.set == "Default" or card.config.center.set == "Enhanced") then - card:set_ability(G.P_CENTERS.m_mult, nil, false) - logger:info("Card set to Mult Card (" .. card.config.center.key .. ").") - else - logger:info("Only standard cards should be set to Mult Card (" .. card.config.center.key .. ").") - end - elseif enhancement == "WildCard" or enhancement == "m_wild" then - if (card.config.center.set == "Default" or card.config.center.set == "Enhanced") then - card:set_ability(G.P_CENTERS.m_wild, nil, false) - logger:info("Card set to Wild Card (" .. card.config.center.key .. ").") - else - logger:info("Only standard cards should be set to Wild Card (" .. card.config.center.key .. ").") - end - elseif enhancement == "GlassCard" or enhancement == "m_glass" then - if (card.config.center.set == "Default" or card.config.center.set == "Enhanced") then - card:set_ability(G.P_CENTERS.m_glass, nil, false) - logger:info("Card set to Glass Card (" .. card.config.center.key .. ").") - else - logger:info("Only standard cards should be set to Glass Card (" .. card.config.center.key .. ").") - end - elseif enhancement == "SteelCard" or enhancement == "m_steel" then - if (card.config.center.set == "Default" or card.config.center.set == "Enhanced") then - card:set_ability(G.P_CENTERS.m_steel, nil, false) - logger:info("Card set to Steel Card (" .. card.config.center.key .. ").") - else - logger:info("Only standard cards should be set to Steel Card (" .. card.config.center.key .. ").") - end - elseif enhancement == "StoneCard" or enhancement == "m_stone" then - if (card.config.center.set == "Default" or card.config.center.set == "Enhanced") then - card:set_ability(G.P_CENTERS.m_stone, nil, false) - logger:info("Card set to Stone Card (" .. card.config.center.key .. ").") - else - logger:info("Only standard cards should be set to Stone Card (" .. card.config.center.key .. ").") - end - elseif enhancement == "LuckyCard" or enhancement == "m_lucky" then - if (card.config.center.set == "Default" or card.config.center.set == "Enhanced") then - card:set_ability(G.P_CENTERS.m_lucky, nil, false) - logger:info("Card set to Lucky Card (" .. card.config.center.key .. ").") - else - logger:info("Only standard cards should be set to Lucky Card (" .. card.config.center.key .. ").") + + for k, v in pairs(G.P_CENTER_POOLS["Default"]) do + local name = string.gsub(v.label, " ", "") + if enhancement == v.key or enhancement == name then + if (card.config.center.set == "Default" or card.config.center.set == "Enhanced") then + card:set_ability(G.P_CENTERS[v.key], nil, false) + logger:info("Card set to " .. v.label .. " (" .. card.config.center.key .. ").") + else + logger:info("Only standard cards should be set to " .. v.label .. " (" .. card.config.center.key .. ").") + end end - elseif string.sub(enhancement, 1, 2) == "m_" and G.P_CENTERS[enhancement] ~= nil then - if (card.config.center.set == "Default" or card.config.center.set == "Enhanced") then - card:set_ability(G.P_CENTERS[enhancement], nil, false) - logger:info("Card set to a " .. G.P_CENTERS[enhancement].name .. " (" .. card.config.center.key .. ").") - else - logger:info("Only standard cards should be set to " .. G.P_CENTERS[enhancement].name .. " (" .. card.config.center.key .. ").") + end + + for k, v in pairs(G.P_CENTER_POOLS["Enhanced"]) do + local name = string.gsub(v.label, " ", "") + if enhancement == v.key or enhancement == name then + if (card.config.center.set == "Default" or card.config.center.set == "Enhanced") then + card:set_ability(G.P_CENTERS[v.key], nil, false) + logger:info("Card set to " .. v.label .. " (" .. card.config.center.key .. ").") + else + logger:info("Only standard cards should be set to " .. v.label .. " (" .. card.config.center.key .. ").") + end end end - if string.sub(enhancement, -4) == "Seal" then - local seal = string.sub(enhancement, 1, -5) - card:set_seal(seal, true) - logger:info(seal .. " Seal added to card (" .. card.config.center.key .. ").") + for k, v in pairs(G.P_CENTER_POOLS["Edition"]) do + local name = string.gsub(v.name, " ", "") + if enhancement == v.key or enhancement == name then + local editionKey = string.sub(v.key, 3) + card:set_edition({ [editionKey] = true }, true) + logger:info("Card set to " .. v.name .. " edition (" .. card.config.center.key .. ").") + end end - if enhancement == "Base" or enhancement == "e_base" then - card:set_edition({ }, true) - logger:info("Card set to base edition (" .. card.config.center.key .. ").") - elseif enhancement == "Negative" or enhancement == "e_negative" then - card:set_edition({ negative = true }, true) - logger:info("Card set to negative edition (" .. card.config.center.key .. ").") - elseif enhancement == "Foil" or enhancement == "e_foil" then - card:set_edition({ foil = true }, true) - logger:info("Card set to foil edition (" .. card.config.center.key .. ").") - elseif enhancement == "Poly" or enhancement == "Polychrome" or enhancement == "e_polychrome" then - card:set_edition({ polychrome = true }, true) - logger:info("Card set to polychrome edition (" .. card.config.center.key .. ").") - elseif enhancement == "Holo" or enhancement == "Holographic" or enhancement == "e_holo" then - card:set_edition({ holo = true }, true) - logger:info("Card set to holographic edition (" .. card.config.center.key .. ").") - elseif string.sub(enhancement, 1, 2) == "e_" and G.P_CENTERS[enhancement] ~= nil then - local editionKey = string.sub(enhancement, 3) - card:set_edition({ [editionKey] = true }, true) - logger:info("Card set to " .. G.P_CENTERS[enhancement].name .. " edition (" .. card.config.center.key .. ").") + for k, v in pairs(G.P_CENTER_POOLS["Seal"]) do + if enhancement == v.key .. "Seal" then + card:set_seal(v.key, true) + logger:info("Added " .. v.key .. " Seal to card (" .. card.config.center.key .. ").") + end end end @@ -1176,24 +1124,33 @@ mods["dev_console"] = { end end end, - "Add one or more enhancements to selected cards. Can use custom values.", + "Add one or more enhancements to selected cards. Can use key or name (e.g. c_base or BaseCard, e_negative or Negative). Must add Seal to seals (e.g. RedSeal, BlueSeal)", function (current_arg) - local keys = {} - for key, value in pairs(G.P_CENTERS) do - if string.sub(key, 1, 2) == "e_" or string.sub(key, 1, 2) == "m_" then - table.insert(keys, key) - end + local subcommands = { } + + for k, v in pairs(G.P_CENTER_POOLS["Default"]) do + local name = string.gsub(v.label, " ", "") + table.insert(subcommands, v.key) + table.insert(subcommands, name) end - local subcommands = { - "GoldCard", "WildCard", "MultCard", "BonusCard", "GlassCard", "SteelCard", "StoneCard", "LuckyCard", - "RedSeal", "BlueSeal", "GoldSeal", "PurpleSeal", - "Base", "Negative", "Foil", "Poly", "Holo" - } - - for _, key in ipairs(keys) do - table.insert(subcommands, key) + for k, v in pairs(G.P_CENTER_POOLS["Enhanced"]) do + local name = string.gsub(v.label, " ", "") + table.insert(subcommands, v.key) + table.insert(subcommands, name) end + + for k, v in pairs(G.P_CENTER_POOLS["Edition"]) do + local name = string.gsub(v.name, " ", "") + table.insert(subcommands, v.key) + table.insert(subcommands, name) + end + + for k, v in pairs(G.P_CENTER_POOLS["Seal"]) do + table.insert(subcommands, v.key .. "Seal") + end + + logger:info(subcommands) for i, v in ipairs(subcommands) do if v:find(current_arg, 1, true) == 1 then @@ -1202,7 +1159,7 @@ mods["dev_console"] = { end return nil end, - "Usage: enhance [arg2] [arg3] ..." + "Usage: enhance [arg2] [arg3] ..." ) console.logger:debug("Dev Console on_enable completed") From c82650d8e0c132aca32bd8175965a861358e84e8 Mon Sep 17 00:00:00 2001 From: Zei33 Date: Thu, 30 May 2024 14:09:09 +1000 Subject: [PATCH 06/15] Removed case sensitivity. --- src/balamod.lua | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/balamod.lua b/src/balamod.lua index ca66179..fee01da 100644 --- a/src/balamod.lua +++ b/src/balamod.lua @@ -1080,7 +1080,7 @@ mods["dev_console"] = { for k, v in pairs(G.P_CENTER_POOLS["Default"]) do local name = string.gsub(v.label, " ", "") - if enhancement == v.key or enhancement == name then + if enhancement == v.key or string.lower(enhancement) == string.lower(name) then if (card.config.center.set == "Default" or card.config.center.set == "Enhanced") then card:set_ability(G.P_CENTERS[v.key], nil, false) logger:info("Card set to " .. v.label .. " (" .. card.config.center.key .. ").") @@ -1092,7 +1092,7 @@ mods["dev_console"] = { for k, v in pairs(G.P_CENTER_POOLS["Enhanced"]) do local name = string.gsub(v.label, " ", "") - if enhancement == v.key or enhancement == name then + if enhancement == v.key or string.lower(enhancement) == string.lower(name) then if (card.config.center.set == "Default" or card.config.center.set == "Enhanced") then card:set_ability(G.P_CENTERS[v.key], nil, false) logger:info("Card set to " .. v.label .. " (" .. card.config.center.key .. ").") @@ -1104,7 +1104,7 @@ mods["dev_console"] = { for k, v in pairs(G.P_CENTER_POOLS["Edition"]) do local name = string.gsub(v.name, " ", "") - if enhancement == v.key or enhancement == name then + if enhancement == v.key or string.lower(enhancement) == string.lower(name) then local editionKey = string.sub(v.key, 3) card:set_edition({ [editionKey] = true }, true) logger:info("Card set to " .. v.name .. " edition (" .. card.config.center.key .. ").") @@ -1112,7 +1112,7 @@ mods["dev_console"] = { end for k, v in pairs(G.P_CENTER_POOLS["Seal"]) do - if enhancement == v.key .. "Seal" then + if string.lower(enhancement) == string.lower(v.key .. "Seal") then card:set_seal(v.key, true) logger:info("Added " .. v.key .. " Seal to card (" .. card.config.center.key .. ").") end @@ -1125,7 +1125,7 @@ mods["dev_console"] = { end end, "Add one or more enhancements to selected cards. Can use key or name (e.g. c_base or BaseCard, e_negative or Negative). Must add Seal to seals (e.g. RedSeal, BlueSeal)", - function (current_arg) + function (current_arg, previous_arg) local subcommands = { } for k, v in pairs(G.P_CENTER_POOLS["Default"]) do @@ -1150,14 +1150,13 @@ mods["dev_console"] = { table.insert(subcommands, v.key .. "Seal") end - logger:info(subcommands) - - for i, v in ipairs(subcommands) do - if v:find(current_arg, 1, true) == 1 then - return {v} - end - end - return nil + local completions = {} + for k, v in pairs(subcommands) do + if v:find(current_arg, 1, true) == 1 then + table.insert(completions, v) + end + end + return completions end, "Usage: enhance [arg2] [arg3] ..." ) From 47d2d28afb3db13f1e760ab917de1d8e95c6e96a Mon Sep 17 00:00:00 2001 From: Zei33 Date: Thu, 30 May 2024 16:59:46 +1000 Subject: [PATCH 07/15] Ignore files which are normally added after installation. --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 76a786d..ab1be21 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ .DS_Store .idea/ .vscode/ +src/balamod_version.lua +src/https.dll \ No newline at end of file From 79a5861ff083df3efeb0992f9fcd074200bd2bb6 Mon Sep 17 00:00:00 2001 From: Zei33 Date: Thu, 30 May 2024 17:40:09 +1000 Subject: [PATCH 08/15] Fixed console not scrolling correct amount of lines when the output of a line wraps onto multiple lines. --- src/console.lua | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/console.lua b/src/console.lua index f3c12de..10427e5 100644 --- a/src/console.lua +++ b/src/console.lua @@ -130,7 +130,17 @@ return { local text = {} local i = 1 local textLength = 0 - local all_messages = self:getFilteredMessages() + + local base_messages = self:getFilteredMessages() + local all_messages = {} + + for _, message in ipairs(base_messages) do + local wrappedLines = self:wrapText(message.text, love.graphics.getWidth() - 20) + for _, line in ipairs(wrappedLines) do + table.insert(all_messages, {text = line, level = message.level, name = message.name, time = message.time, level_numeric = message.level_numeric, formatted = function() return line end}) + end + end + while textLength < self.max_lines do local index = #all_messages - i + self.start_line_offset if index < 1 then From b05e6b71cdfbd4a96f0643e4a7a78df4d85a7586 Mon Sep 17 00:00:00 2001 From: Zei33 Date: Thu, 30 May 2024 17:50:11 +1000 Subject: [PATCH 09/15] Formatting --- src/balamod.lua | 1192 +++++++++++++++++++++++------------------------ src/console.lua | 189 ++++---- 2 files changed, 701 insertions(+), 680 deletions(-) diff --git a/src/balamod.lua b/src/balamod.lua index 7bbf286..56c66fa 100644 --- a/src/balamod.lua +++ b/src/balamod.lua @@ -13,7 +13,7 @@ local apis = { logging = logging, console = console, math = math, - platform = platform, + platform = platform } is_loaded = false local RESULT = { @@ -25,42 +25,34 @@ local RESULT = { MOD_FS_LOAD_ERROR = 5, MOD_PCALL_ERROR = 6, TAR_DECOMPRESS_ERROR = 7, - MOD_NOT_CONFORM = 8, + MOD_NOT_CONFORM = 8 } local paths = {} -- Paths to the files that will be loaded local _VERSION = require('balamod_version') local function splitstring(inputstr, sep) - if sep == nil then - sep = "%s" - end - local t={} - for str in string.gmatch(inputstr, "([^"..sep.."]+)") do + if sep == nil then sep = "%s" end + local t = {} + for str in string.gmatch(inputstr, "([^" .. sep .. "]+)") do table.insert(t, str) end return t end -function buildPaths(root,ignore) +function buildPaths(root, ignore) local items = love.filesystem.getDirectoryItems(root) for _, file in ipairs(items) do - if root ~= "" then - file = root.."/"..file - end + if root ~= "" then file = root .. "/" .. file end local info = love.filesystem.getInfo(file) if info then if info.type == "file" and file:match("%.lua$") then - table.insert(paths,file) + table.insert(paths, file) elseif info.type == "directory" then local valid = true for _, i in ipairs(ignore) do - if i == file then - valid = false - end - end - if valid then - buildPaths(file,ignore) + if i == file then valid = false end end + if valid then buildPaths(file, ignore) end end end end @@ -70,7 +62,9 @@ local function request(url) logger:debug('Request made with url: ', url) local code local response - code, response, headers = https.request(url, {headers = {['User-Agent'] = 'Balamod-Client'}}) + code, response, headers = https.request(url, { + headers = {['User-Agent'] = 'Balamod-Client'} + }) if (code == 301 or code == 302) and headers.location then -- follow redirects if necessary code, response = request(headers.location) @@ -90,14 +84,13 @@ local function extractFunctionBody(path, function_name) -- This is to catch functions that have incorrect ending indentation by catching the next function in line. -- Can be removed once Card:calculate_joker no longer has this typo. - local typocatch_func_end = current_game_code[path]:find("\n\r?function", fin) + local typocatch_func_end = + current_game_code[path]:find("\n\r?function", fin) if typocatch_func_end and typocatch_func_end < func_end then func_end = typocatch_func_end - 3 end - if not func_end then - return "Can't find function end " .. function_name - end + if not func_end then return "Can't find function end " .. function_name end local func_body = current_game_code[path]:sub(func_begin, func_end + 3) return func_body @@ -108,24 +101,27 @@ local function inject(path, function_name, to_replace, replacement) local function_body = extractFunctionBody(path, function_name) local modified_function_code = function_body:gsub(to_replace, replacement) escaped_function_body = function_body:gsub("([^%w])", "%%%1") -- escape function body for use in gsub - escaped_modified_function_code = modified_function_code:gsub("([^%w])", "%%%1") - current_game_code[path] = current_game_code[path]:gsub(escaped_function_body, escaped_modified_function_code) -- update current game code in memory + escaped_modified_function_code = modified_function_code:gsub("([^%w])", + "%%%1") + current_game_code[path] = current_game_code[path]:gsub( + escaped_function_body, + escaped_modified_function_code) -- update current game code in memory local new_function, load_error = load(modified_function_code) -- load modified function if not new_function then - logger:error("Error loading modified function", function_name, ": ", (load_error or "Unknown error")) + logger:error("Error loading modified function", function_name, ": ", + (load_error or "Unknown error")) logger:error(modified_function_code) end - if setfenv then - setfenv(new_function, getfenv(original_testFunction)) - end -- Set the environment of the new function to the same as the original function + if setfenv then setfenv(new_function, getfenv(original_testFunction)) end -- Set the environment of the new function to the same as the original function local status, result = pcall(new_function) -- Execute the new function if status then testFunction = result -- Overwrite the original function with the result of the new function else - logger:error("Error executing modified function", function_name, ": ", result) -- Safeguard against errors + logger:error("Error executing modified function", function_name, ": ", + result) -- Safeguard against errors logger:error(modified_function_code) end end @@ -134,34 +130,39 @@ local function injectHead(path, function_name, code) local function_body = extractFunctionBody(path, function_name) local pattern = "(function%s+" .. function_name .. ".-)\n" - local modified_function_code, number_of_subs = function_body:gsub(pattern, "%1\n" .. code .. "\n") + local modified_function_code, number_of_subs = + function_body:gsub(pattern, "%1\n" .. code .. "\n") if number_of_subs == 0 then - logger:error("Error: Function start not found in function body or multiple matches encountered.") + logger:error( + "Error: Function start not found in function body or multiple matches encountered.") logger:error(modified_function_code) return end escaped_function_body = function_body:gsub("([^%w])", "%%%1") - escaped_modified_function_code = modified_function_code:gsub("([^%w])", "%%%1") - current_game_code[path] = current_game_code[path]:gsub(escaped_function_body, escaped_modified_function_code) + escaped_modified_function_code = modified_function_code:gsub("([^%w])", + "%%%1") + current_game_code[path] = current_game_code[path]:gsub( + escaped_function_body, + escaped_modified_function_code) local new_function, load_error = load(modified_function_code) if not new_function then - logger:error("Error loading modified function ", function_name, " with head injection: ", (load_error or "Unknown error")) + logger:error("Error loading modified function ", function_name, + " with head injection: ", (load_error or "Unknown error")) logger:error(modified_function_code) return end - if setfenv then - setfenv(new_function, getfenv(original_testFunction)) - end + if setfenv then setfenv(new_function, getfenv(original_testFunction)) end local status, result = pcall(new_function) if status then testFunction = result else - logger:error("Error executing modified function ", function_name, " with head injection: ", result) + logger:error("Error executing modified function ", function_name, + " with head injection: ", result) logger:error(modified_function_code) end end @@ -170,34 +171,40 @@ local function injectTail(path, function_name, code) local function_body = extractFunctionBody(path, function_name) local pattern = "(.-)(end[ \t]*\n?)$" - local modified_function_code, number_of_subs = function_body:gsub(pattern, "%1" .. string.gsub(code, '(.-)%s*$', '%1') .. "\n" .. "%2") + local modified_function_code, number_of_subs = + function_body:gsub(pattern, "%1" .. string.gsub(code, '(.-)%s*$', '%1') .. + "\n" .. "%2") if number_of_subs == 0 then - logger:error("Error: 'end' not found in function '", function_name, "' body or multiple ends encountered.") + logger:error("Error: 'end' not found in function '", function_name, + "' body or multiple ends encountered.") logger:error(modified_function_code) return end escaped_function_body = function_body:gsub("([^%w])", "%%%1") - escaped_modified_function_code = modified_function_code:gsub("([^%w])", "%%%1") - current_game_code[path] = current_game_code[path]:gsub(escaped_function_body, escaped_modified_function_code) + escaped_modified_function_code = modified_function_code:gsub("([^%w])", + "%%%1") + current_game_code[path] = current_game_code[path]:gsub( + escaped_function_body, + escaped_modified_function_code) local new_function, load_error = load(modified_function_code) if not new_function then - logger:error("Error loading modified function ", function_name, " with tail injection: ", (load_error or "Unknown error")) + logger:error("Error loading modified function ", function_name, + " with tail injection: ", (load_error or "Unknown error")) logger:error(modified_function_code) return end - if setfenv then - setfenv(new_function, getfenv(original_testFunction)) - end + if setfenv then setfenv(new_function, getfenv(original_testFunction)) end local status, result = pcall(new_function) if status then testFunction = result else - logger:error("Error executing modified function ", function_name, " with tail injection: ", result) + logger:error("Error executing modified function ", function_name, + " with tail injection: ", result) logger:error(modified_function_code) end end @@ -212,7 +219,8 @@ end local function getRepoMods() local repoMods = {} - local reposIndex = 'https://raw.githubusercontent.com/UwUDev/balamod/master/repos.index' + local reposIndex = + 'https://raw.githubusercontent.com/UwUDev/balamod/master/repos.index' logger:info('Requesting ', reposIndex) local indexCode, indexBody = request(reposIndex) if indexCode ~= 200 then @@ -231,10 +239,9 @@ local function getRepoMods() logger:error('Response: ' .. repoBody) else for modInfo in string.gmatch(repoBody, '([^\n]+)') do - local modId, modVersion, modName, modDesc, modUrl = string.match( - modInfo, - '([^|]+)|([^|]+)|([^|]+)|([^|]+)|([^|]+)' - ) + local modId, modVersion, modName, modDesc, modUrl = + string.match(modInfo, + '([^|]+)|([^|]+)|([^|]+)|([^|]+)|([^|]+)') local modPresent = isModPresent(modId) local needUpdate = true local version = modVersion @@ -244,7 +251,8 @@ local function getRepoMods() if mod.version then version = mod.version local modVersion = utils.parseVersion(mod.version) - needUpdate = utils.v2GreaterThanV1(modVersion, repoVersion) + needUpdate = utils.v2GreaterThanV1(modVersion, + repoVersion) end end table.insert(repoMods, { @@ -255,7 +263,7 @@ local function getRepoMods() version = version, newVersion = modVersion, present = modPresent, - needUpdate = needUpdate, + needUpdate = needUpdate }) end end @@ -274,73 +282,84 @@ local function validateManifest(modFolder, manifest) load_after = true, min_balamod_version = false, max_balamod_version = false, - dependencies = false, + dependencies = false } -- check that all manifest expected fields are present for field, required in pairs(expectedFields) do if manifest[field] == nil and required then - logger:error('Manifest in folder ', modFolder, ' is missing field: ', field) + logger:error('Manifest in folder ', modFolder, + ' is missing field: ', field) return false end end -- check that none of the manifest fields are not in the expected fields for key, _ in pairs(manifest) do if expectedFields[key] == nil then - logger:error('Manifest in folder ', modFolder, ' contains unexpected field: ', key) + logger:error('Manifest in folder ', modFolder, + ' contains unexpected field: ', key) return false end end -- check that the load_before, load_after and description fields are arrays if type(manifest.load_before) ~= 'table' then - logger:error('Manifest in folder ', modFolder, ' has a non-array load_before field') + logger:error('Manifest in folder ', modFolder, + ' has a non-array load_before field') return false end if type(manifest.load_after) ~= 'table' then - logger:error('Manifest in folder ', modFolder, ' has a non-array load_after field') + logger:error('Manifest in folder ', modFolder, + ' has a non-array load_after field') return false end if type(manifest.description) ~= 'table' then - logger:error('Manifest in folder ', modFolder, ' has a non-array description field') + logger:error('Manifest in folder ', modFolder, + ' has a non-array description field') return false end -- check that the load_before and load_after fields are strings for _, modId in ipairs(manifest.load_before) do if type(modId) ~= 'string' then - logger:error('Manifest in folder ', modFolder, ' has a non-string load_before field') + logger:error('Manifest in folder ', modFolder, + ' has a non-string load_before field') return false end end for _, modId in ipairs(manifest.load_after) do if type(modId) ~= 'string' then - logger:error('Manifest in folder ', modFolder, ' has a non-string load_after field') + logger:error('Manifest in folder ', modFolder, + ' has a non-string load_after field') return false end end -- check that the version field is a string, matching semantic versioning if not manifest.version:match('%d+%.%d+%.%d+') then - logger:error('Manifest in folder ', modFolder, ' has a non-semantic versioning version field') + logger:error('Manifest in folder ', modFolder, + ' has a non-semantic versioning version field') return false end -- check that the author field is a string if type(manifest.author) ~= 'string' then - logger:error('Manifest in folder ', modFolder, ' has a non-string author field') + logger:error('Manifest in folder ', modFolder, + ' has a non-string author field') return false end -- check that the id field is a string if type(manifest.id) ~= 'string' then - logger:error('Manifest in folder ', modFolder, ' has a non-string id field') + logger:error('Manifest in folder ', modFolder, + ' has a non-string id field') return false end -- check that the name field is a string if type(manifest.name) ~= 'string' then - logger:error('Manifest in folder ', modFolder, ' has a non-string name field') + logger:error('Manifest in folder ', modFolder, + ' has a non-string name field') return false end @@ -348,16 +367,19 @@ local function validateManifest(modFolder, manifest) if manifest.dependencies then local incorrectDependencies = {} if type(manifest.dependencies) ~= 'table' then - logger:error('Manifest in folder ', modFolder, ' has a non-table dependencies field') + logger:error('Manifest in folder ', modFolder, + ' has a non-table dependencies field') return false end for modId, version in pairs(manifest.dependencies) do if type(modId) ~= 'string' then - logger:error('Manifest in folder ', modFolder, ' has a non-string key in dependencies field') + logger:error('Manifest in folder ', modFolder, + ' has a non-string key in dependencies field') return false end if type(version) ~= 'string' then - logger:error('Manifest in folder ', modFolder, ' has a non-string value in dependencies field') + logger:error('Manifest in folder ', modFolder, + ' has a non-string value in dependencies field') return false end local versionConstraintCorrect = false @@ -373,7 +395,8 @@ local function validateManifest(modFolder, manifest) local versionPatterns = {'%d+', '%d+%.%d+', '%d+%.%d+%.%d+'} for _, versionPattern1 in ipairs(versionPatterns) do for _, versionPattern2 in ipairs(versionPatterns) do - table.insert(patterns, '[<>]=?' .. versionPattern1 .. ', ?[<>]=?' .. versionPattern2) + table.insert(patterns, '[<>]=?' .. versionPattern1 .. + ', ?[<>]=?' .. versionPattern2) end end -- check every generated pattern, one at a time, if any of them matches, then the version constraint is correct @@ -384,12 +407,14 @@ local function validateManifest(modFolder, manifest) end end if not versionConstraintCorrect then - table.insert(incorrectDependencies, modId..':'..version) + table.insert(incorrectDependencies, modId .. ':' .. version) end end if #incorrectDependencies > 0 then -- some of the dependencies are incorrect for the mod, let's log them and return false - logger:error('Manifest in folder ', modFolder, ' has incorrect dependencies field: ', table.concat(incorrectDependencies, ', ')) + logger:error('Manifest in folder ', modFolder, + ' has incorrect dependencies field: ', + table.concat(incorrectDependencies, ', ')) return false end end @@ -405,44 +430,45 @@ local function loadMod(modFolder) return nil end if not love.filesystem.getInfo('mods/' .. modFolder .. '/main.lua', 'file') then - logger:error('Mod folder ', modFolder, ' does not contain a main.lua file') + logger:error('Mod folder ', modFolder, + ' does not contain a main.lua file') return nil end - if not love.filesystem.getInfo('mods/' .. modFolder .. '/manifest.json', 'file') then - logger:error('Mod folder ', modFolder, ' does not contain a manifest.json file') + if not love.filesystem.getInfo('mods/' .. modFolder .. '/manifest.json', + 'file') then + logger:error('Mod folder ', modFolder, + ' does not contain a manifest.json file') return nil end - logger:debug("Loading manifest from: ", 'mods/'..modFolder..'/manifest.json') + logger:debug("Loading manifest from: ", + 'mods/' .. modFolder .. '/manifest.json') -- load the manifest - local manifest = json.decode(love.filesystem.read('mods/'..modFolder..'/manifest.json')) - if not validateManifest(modFolder, manifest) then - return nil - end + local manifest = json.decode(love.filesystem.read( + 'mods/' .. modFolder .. '/manifest.json')) + if not validateManifest(modFolder, manifest) then return nil end logger:debug('Manifest loaded: ', manifest) -- check that the mod is compatible with the current version of balamod if manifest.min_balamod_version then local minVersion = utils.parseVersion(manifest.min_balamod_version) if not utils.v2GreaterThanV1(minVersion, _VERSION) then - logger:error('Mod ', modFolder, ' requires a newer version of balamod') + logger:error('Mod ', modFolder, + ' requires a newer version of balamod') return nil end end if manifest.max_balamod_version then local maxVersion = utils.parseVersion(manifest.max_balamod_version) if utils.v2GreaterThanV1(maxVersion, _VERSION) then - logger:error('Mod ', modFolder, ' requires an older version of balamod') + logger:error('Mod ', modFolder, + ' requires an older version of balamod') return nil end end -- load the hooks (on_enable, on_game_load, etc...) - logger:debug("Loading hooks from: ", 'mods/'..modFolder..'/main.lua') - local modHooks = require('mods/'..modFolder..'/main') - for hookName, hook in pairs(modHooks) do - mod[hookName] = hook - end - for key, value in pairs(manifest) do - mod[key] = value - end + logger:debug("Loading hooks from: ", 'mods/' .. modFolder .. '/main.lua') + local modHooks = require('mods/' .. modFolder .. '/main') + for hookName, hook in pairs(modHooks) do mod[hookName] = hook end + for key, value in pairs(manifest) do mod[key] = value end mod.enabled = true logger:debug('Mod loaded: ', mod.id) logger:debug("Checking if mod is disabled") @@ -461,7 +487,8 @@ local function toggleMod(mod) love.filesystem.remove('mods/' .. mod.id .. '/disable.it') end pcall(mod.on_enable) - elseif not mod.enabled and mod.on_disable and type(mod.on_disable) == 'function' then + elseif not mod.enabled and mod.on_disable and type(mod.on_disable) == + 'function' then love.filesystem.write('mods/' .. mod.id .. '/disable.it', '') pcall(mod.on_disable) end @@ -470,19 +497,19 @@ end local function callModCallbacksIfExists(mods, callback_name, should_log, ...) local sorted = utils.values(mods) - table.sort(sorted, function(a, b) - return a.order < b.order - end) + table.sort(sorted, function(a, b) return a.order < b.order end) local mod_returns = {} -- pre loading all mods for _, mod in ipairs(sorted) do - if mod.enabled and mod[callback_name] and type(mod[callback_name]) == "function" then + if mod.enabled and mod[callback_name] and type(mod[callback_name]) == + "function" then if should_log then logger:info("Calling mod callback", callback_name, "for", mod.id) end local status, message = pcall(mod[callback_name], ...) -- Call the on_pre_load function of the mod if it exists if not status then - logger:warn("Callback", callback_name, "for mod ", mod.id, "failed: ", message) + logger:warn("Callback", callback_name, "for mod ", mod.id, + "failed: ", message) else table.insert(mod_returns, {modId = mod.id, result = message}) end @@ -507,16 +534,12 @@ local function installMod(modInfo) if modInfo.present then logger:debug('Mod ' .. modInfo.id .. ' is already present') local modVersion = modInfo.newVersion - if not modInfo.needUpdate then - return RESULT.SUCCESS - end + if not modInfo.needUpdate then return RESULT.SUCCESS end -- remove old mod for i, mod in ipairs(mods) do if mod.id == modId then - if mod.on_disable then - mod.on_disable() - end + if mod.on_disable then mod.on_disable() end table.remove(mods, i) break @@ -560,12 +583,8 @@ local function installMod(modInfo) if file.type == "file" then -- file.name is a path, we only want the filename local _, _, filename = file.name:find(".+/(.+)") - if filename == "main.lua" then - mainLua = true - end - if filename == "manifest.json" then - manifestJson = true - end + if filename == "main.lua" then mainLua = true end + if filename == "manifest.json" then manifestJson = true end end end if not mainLua then @@ -580,22 +599,20 @@ local function installMod(modInfo) -- replace the first part of file.name with the modId file.name = modId .. file.name:sub(file.name:find("/")) if file.type == "directory" then - love.filesystem.createDirectory("mods/"..file.name) + love.filesystem.createDirectory("mods/" .. file.name) elseif file.type == "file" then - love.filesystem.write("mods/"..file.name, file.data) + love.filesystem.write("mods/" .. file.name, file.data) end end local mod = loadMod(modId) - if mod == nil then - return RESULT.MOD_FS_LOAD_ERROR - end + if mod == nil then return RESULT.MOD_FS_LOAD_ERROR end mods[modId] = mod mods = sortMods(mods) return RESULT.SUCCESS end -buildPaths("",{"mods","apis","resources","localization"}) +buildPaths("", {"mods", "apis", "resources", "localization"}) -- current_game_code = love.filesystem.read(path) buildPaths = nil -- prevent rerunning (i think) @@ -625,8 +642,7 @@ mods["dev_console"] = { author = "sbordeyne & UwUDev", description = { "Press F2 to open/close the console", - "Use command `help` for a list of ", - "available commands and shortcuts", + "Use command `help` for a list of ", "available commands and shortcuts" }, enabled = true, on_game_load = function(args) @@ -662,501 +678,485 @@ mods["dev_console"] = { end console.logger:debug("Registering commands") - console:registerCommand( - "help", - function() - console.logger:print("Available commands:") - for name, cmd in pairs(console.commands) do - if cmd.desc then - console.logger:print(name .. ": " .. cmd.desc) - end - end - return true - end, - "Prints a list of available commands", - function(current_arg) - local completions = {} - for name, _ in pairs(console.commands) do - if name:find(current_arg, 1, true) == 1 then - table.insert(completions, name) - end - end - return completions - end, - "Usage: help " - ) - - console:registerCommand( - "shortcuts", - function() - console.logger:print("Available shortcuts:") - console.logger:print("F2: Open/Close the console") - console.logger:print("F4: Toggle debug mode") - if platform.is_mac then - console.logger:print("Cmd+C: Copy the current command to the clipboard.") - console.logger:print("Cmd+Shift+C: Copies all messages to the clipboard") - console.logger:print("Cmd+V: Paste the clipboard into the current command") - else - console.logger:print("Ctrl+C: Copy the current command to the clipboard.") - console.logger:print("Ctrl+Shift+C: Copies all messages to the clipboard") - console.logger:print("Ctrl+V: Paste the clipboard into the current command") + console:registerCommand("help", function() + console.logger:print("Available commands:") + for name, cmd in pairs(console.commands) do + if cmd.desc then + console.logger:print(name .. ": " .. cmd.desc) end - return true - end, - "Prints a list of available shortcuts", - function(current_arg) - return nil - end, - "Usage: shortcuts" - ) - - console:registerCommand( - "history", - function() - console.logger:print("Command history:") - for i, cmd in ipairs(console.command_history) do - console.logger:print(i .. ": " .. cmd) + end + return true + end, "Prints a list of available commands", function(current_arg) + local completions = {} + for name, _ in pairs(console.commands) do + if name:find(current_arg, 1, true) == 1 then + table.insert(completions, name) end - return true - end, - "Prints the command history" - ) + end + return completions + end, "Usage: help ") + + console:registerCommand("shortcuts", function() + console.logger:print("Available shortcuts:") + console.logger:print("F2: Open/Close the console") + console.logger:print("F4: Toggle debug mode") + if platform.is_mac then + console.logger:print( + "Cmd+C: Copy the current command to the clipboard.") + console.logger:print( + "Cmd+Shift+C: Copies all messages to the clipboard") + console.logger:print( + "Cmd+V: Paste the clipboard into the current command") + else + console.logger:print( + "Ctrl+C: Copy the current command to the clipboard.") + console.logger:print( + "Ctrl+Shift+C: Copies all messages to the clipboard") + console.logger:print( + "Ctrl+V: Paste the clipboard into the current command") + end + return true + end, "Prints a list of available shortcuts", + function(current_arg) return nil end, + "Usage: shortcuts") + + console:registerCommand("history", function() + console.logger:print("Command history:") + for i, cmd in ipairs(console.command_history) do + console.logger:print(i .. ": " .. cmd) + end + return true + end, "Prints the command history") console.logger:debug("Registering command: clear") - console:registerCommand( - "clear", - function() - logging.clearLogs() - return true - end, - "Clear the console" - ) - - console:registerCommand( - "exit", - function() - console:toggle() - return true - end, - "Close the console" - ) - - console:registerCommand( - "give", - function(args) - local id = args[1] - local c1 = nil - if string.sub(id, 1, 2) == "j_" then - c1 = create_card(nil, G.jokers, nil, 1, true, false, id, nil) - else - c1 = create_card(nil, G.consumeables, nil, 1, true, false, id, nil) - end - G.E_MANAGER:add_event(Event({ - trigger = 'after', - delay = 0.1, - func = function() - c1:add_to_deck() - if string.sub(id, 1, 2) == "j_" then - G.jokers:emplace(c1) - else - G.consumeables:emplace(c1) - end - - G.CONTROLLER:save_cardarea_focus('jokers') - G.CONTROLLER:recall_cardarea_focus('jokers') - return true - end - })) - return true - end, - "Give an item to the player", - function(current_arg) - local ret = {} - for k,_ in pairs(G.P_CENTERS) do - if string.find(k, current_arg) == 1 then - table.insert(ret, k) + console:registerCommand("clear", function() + logging.clearLogs() + return true + end, "Clear the console") + + console:registerCommand("exit", function() + console:toggle() + return true + end, "Close the console") + + console:registerCommand("give", function(args) + local id = args[1] + local c1 = nil + if string.sub(id, 1, 2) == "j_" then + c1 = create_card(nil, G.jokers, nil, 1, true, false, id, nil) + else + c1 = create_card(nil, G.consumeables, nil, 1, true, false, id, + nil) + end + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 0.1, + func = function() + c1:add_to_deck() + if string.sub(id, 1, 2) == "j_" then + G.jokers:emplace(c1) + else + G.consumeables:emplace(c1) end + + G.CONTROLLER:save_cardarea_focus('jokers') + G.CONTROLLER:recall_cardarea_focus('jokers') + return true + end + })) + return true + end, "Give an item to the player", function(current_arg) + local ret = {} + for k, _ in pairs(G.P_CENTERS) do + if string.find(k, current_arg) == 1 then + table.insert(ret, k) end - return ret end - ) - - console:registerCommand( - "money", - function(args) - if args[1] and args[2] then - local amount = tonumber(args[2]) - if amount then - if args[1] == "add" then - ease_dollars(amount, true) - console.logger:info("Added " .. amount .. " money to the player") - elseif args[1] == "remove" then - ease_dollars(-amount, true) - console.logger:info("Removed " .. amount .. " money from the player") - elseif args[1] == "set" then - local currentMoney = G.GAME.dollars - local diff = amount - currentMoney - ease_dollars(diff, true) - console.logger:info("Set player money to " .. amount) - else - console.logger:error("Invalid operation, use add, remove or set") - end + return ret + end) + + console:registerCommand("money", function(args) + if args[1] and args[2] then + local amount = tonumber(args[2]) + if amount then + if args[1] == "add" then + ease_dollars(amount, true) + console.logger:info( + "Added " .. amount .. " money to the player") + elseif args[1] == "remove" then + ease_dollars(-amount, true) + console.logger:info( + "Removed " .. amount .. " money from the player") + elseif args[1] == "set" then + local currentMoney = G.GAME.dollars + local diff = amount - currentMoney + ease_dollars(diff, true) + console.logger:info("Set player money to " .. amount) else - console.logger:error("Invalid amount") - return false + console.logger:error( + "Invalid operation, use add, remove or set") end else - console.logger:warn("Usage: money ") + console.logger:error("Invalid amount") return false end - return true - end, - "Change the player's money", - function (current_arg) - local subcommands = {"add", "remove", "set"} - for i, v in ipairs(subcommands) do - if v:find(current_arg, 1, true) == 1 then - return {v} - end + else + console.logger:warn("Usage: money ") + return false + end + return true + end, "Change the player's money", function(current_arg) + local subcommands = {"add", "remove", "set"} + for i, v in ipairs(subcommands) do + if v:find(current_arg, 1, true) == 1 then + return {v} end - return nil end - ) - - console:registerCommand( - "discards", - function(args) - if args[1] and args[2] then - local amount = tonumber(args[2]) - if amount then - if args[1] == "add" then - ease_discard(amount, true) - console.logger:info("Added " .. amount .. " discards to the player") - elseif args[1] == "remove" then - ease_discard(-amount, true) - console.logger:info("Removed " .. amount .. " discards from the player") - elseif args[1] == "set" then - local currentDiscards = G.GAME.current_round.discards_left - local diff = amount - currentDiscards - ease_discard(diff, true) - console.logger:info("Set player discards to " .. amount) - else - console.logger:error("Invalid operation, use add, remove or set") - return false - end + return nil + end) + + console:registerCommand("discards", function(args) + if args[1] and args[2] then + local amount = tonumber(args[2]) + if amount then + if args[1] == "add" then + ease_discard(amount, true) + console.logger:info( + "Added " .. amount .. " discards to the player") + elseif args[1] == "remove" then + ease_discard(-amount, true) + console.logger:info( + "Removed " .. amount .. " discards from the player") + elseif args[1] == "set" then + local currentDiscards = + G.GAME.current_round.discards_left + local diff = amount - currentDiscards + ease_discard(diff, true) + console.logger:info("Set player discards to " .. amount) else - console.logger:error("Invalid amount") + console.logger:error( + "Invalid operation, use add, remove or set") return false end else - console.logger:warn("Usage: discards ") + console.logger:error("Invalid amount") return false end - return true - end, - "Change the player's discards", - function (current_arg) - local subcommands = {"add", "remove", "set"} - for i, v in ipairs(subcommands) do - if v:find(current_arg, 1, true) == 1 then - return {v} - end + else + console.logger:warn("Usage: discards ") + return false + end + return true + end, "Change the player's discards", function(current_arg) + local subcommands = {"add", "remove", "set"} + for i, v in ipairs(subcommands) do + if v:find(current_arg, 1, true) == 1 then + return {v} end - return nil end - ) - - console:registerCommand( - "hands", - function(args) - if args[1] and args[2] then - local amount = tonumber(args[2]) - if amount then - if args[1] == "add" then - ease_hands_played(amount, true) - console.logger:info("Added " .. amount .. " hands to the player") - elseif args[1] == "remove" then - ease_hands_played(-amount, true) - console.logger:info("Removed " .. amount .. " hands from the player") - elseif args[1] == "set" then - local currentHands = G.GAME.current_round.hands_left - local diff = amount - currentHands - ease_hands_played(diff, true) - console.logger:info("Set player hands to " .. amount) - else - console.logger:error("Invalid operation, use add, remove or set") - return false - end + return nil + end) + + console:registerCommand("hands", function(args) + if args[1] and args[2] then + local amount = tonumber(args[2]) + if amount then + if args[1] == "add" then + ease_hands_played(amount, true) + console.logger:info( + "Added " .. amount .. " hands to the player") + elseif args[1] == "remove" then + ease_hands_played(-amount, true) + console.logger:info( + "Removed " .. amount .. " hands from the player") + elseif args[1] == "set" then + local currentHands = G.GAME.current_round.hands_left + local diff = amount - currentHands + ease_hands_played(diff, true) + console.logger:info("Set player hands to " .. amount) else - console.logger:error("Invalid amount") + console.logger:error( + "Invalid operation, use add, remove or set") return false end else - console.logger:warn("Usage: hands ") + console.logger:error("Invalid amount") return false end - return true - end, - "Change the player's remaining hands", - function (current_arg) - local subcommands = {"add", "remove", "set"} - for i, v in ipairs(subcommands) do - if v:find(current_arg, 1, true) == 1 then - return {v} - end + else + console.logger:warn("Usage: hands ") + return false + end + return true + end, "Change the player's remaining hands", function(current_arg) + local subcommands = {"add", "remove", "set"} + for i, v in ipairs(subcommands) do + if v:find(current_arg, 1, true) == 1 then + return {v} end - return nil end - ) - - console:registerCommand( - "luamod", - function(args) - if args[1] then - local modId = args[1] - if isModPresent(modId) then - local mod = mods[modId] - if mod.enabled and mod.on_disable and type(mod.on_disable) == "function" then - local success, result = pcall(mod.on_disable) - if not success then - console.logger:error("Error disabling mod: " .. modId) - console.logger:error(result) - return false - end + return nil + end) + + console:registerCommand("luamod", function(args) + if args[1] then + local modId = args[1] + if isModPresent(modId) then + local mod = mods[modId] + if mod.enabled and mod.on_disable and type(mod.on_disable) == + "function" then + local success, result = pcall(mod.on_disable) + if not success then + console.logger:error( + "Error disabling mod: " .. modId) + console.logger:error(result) + return false end - mod = loadMod(modId) - mods[modId] = mod - mods = sortMods(mods) - -- no need to redo the whole shebang, just call on_enable - -- this is because the dependencies are most likely already loaded - if mod.enabled then - if mod.on_enable and type(mod.on_enable) == 'function' then - local status, message = pcall(mod.on_enable) - if not status then - console.logger:error("Error enabling mod: " .. modId) - console.logger:error(message) - return false - end + end + mod = loadMod(modId) + mods[modId] = mod + mods = sortMods(mods) + -- no need to redo the whole shebang, just call on_enable + -- this is because the dependencies are most likely already loaded + if mod.enabled then + if mod.on_enable and type(mod.on_enable) == 'function' then + local status, message = pcall(mod.on_enable) + if not status then + console.logger:error( + "Error enabling mod: " .. modId) + console.logger:error(message) + return false end end - console.logger:info("Reloaded mod: " .. modId) - else - console.logger:error("Mod not found: " .. modId) - return false end + console.logger:info("Reloaded mod: " .. modId) else - console.logger:error("Usage: luamod ") + console.logger:error("Mod not found: " .. modId) return false end - return true - end, - "Reload a mod using its id", - function (current_arg) - local completions = {} - for modId, _ in pairs(mods) do - if modId:find(current_arg, 1, true) == 1 then - table.insert(completions, modId) - end + else + console.logger:error("Usage: luamod ") + return false + end + return true + end, "Reload a mod using its id", function(current_arg) + local completions = {} + for modId, _ in pairs(mods) do + if modId:find(current_arg, 1, true) == 1 then + table.insert(completions, modId) end - return completions - end, - "Usage: luamod " - ) - - console:registerCommand( - "sandbox", - function (args) - G:sandbox() + end + return completions + end, "Usage: luamod ") + + console:registerCommand("sandbox", function(args) + G:sandbox() + return true + end, "Goes to the sandbox stage", function(current_arg) + return nil + end, "Usage: sandbox") + + console:registerCommand("luarun", function(args) + local code = table.concat(args, " ") + local func, err = load(code) + if func then + console.logger:info("Lua code executed successfully") + console.logger:print(func()) return true - end, - "Goes to the sandbox stage", - function (current_arg) - return nil - end, - "Usage: sandbox" - ) - - console:registerCommand( - "luarun", - function (args) - local code = table.concat(args, " ") - local func, err = load(code) - if func then - console.logger:info("Lua code executed successfully") - console.logger:print(func()) - return true - else - console.logger:error("Error loading lua code: ", err) - return false - end - end, - "Run lua code in the context of the game", - function (current_arg) - return nil - end, - "Usage: luarun " - ) - - console:registerCommand( - "installmod", - function (args) - local url = args[1] - local modInfo = { - id = "testmod", - url = url, - present = false, - needUpdate = true, - } - local result = installModFromTar(modInfo) - if result == RESULT.SUCCESS then - console.logger:info("Mod installed successfully") - return true - else - console.logger:error("Error installing mod: ", result) - return false + else + console.logger:error("Error loading lua code: ", err) + return false + end + end, "Run lua code in the context of the game", + function(current_arg) return nil end, + "Usage: luarun ") + + console:registerCommand("installmod", function(args) + local url = args[1] + local modInfo = { + id = "testmod", + url = url, + present = false, + needUpdate = true + } + local result = installModFromTar(modInfo) + if result == RESULT.SUCCESS then + console.logger:info("Mod installed successfully") + return true + else + console.logger:error("Error installing mod: ", result) + return false + end + end, "Install a mod from a tarball", + function(current_arg) return nil end, + "Usage: installmod ") + + console:registerCommand("booster", function(args) + local pack = get_pack("shop_pack", args[1] or nil) + G.FUNCS.use_card({ + config = {ref_table = Card(0, 0, 0, 0, pack, pack)} + }) + end, + "Generate a booster pack (random if no argument, otherwise specify type)", + function(current_arg) + local subcommands = { + "Standard", "Spectral", "Arcana", "Celestial", "Buffoon" + } + for i, v in ipairs(subcommands) do + if v:find(current_arg, 1, true) == 1 then + return {v} end - end, - "Install a mod from a tarball", - function (current_arg) - return nil - end, - "Usage: installmod " - ) - - console:registerCommand( - "booster", - function (args) - local pack = get_pack("shop_pack", args[1] or nil) - G.FUNCS.use_card({ - config = { - ref_table = Card(0, 0, 0, 0, pack, pack) - } - }) - end, - "Generate a booster pack (random if no argument, otherwise specify type)", - function (current_arg) - local subcommands = { "Standard", "Spectral", "Arcana", "Celestial", "Buffoon" } - for i, v in ipairs(subcommands) do - if v:find(current_arg, 1, true) == 1 then - return {v} + end + return nil + end, "Usage: booster [Standard/Spectral/Arcana/Celestial/Buffoon]") + + console:registerCommand("enhance", function(args) + local arg = args[1] or nil + if arg == nil then + logger:info("No enhancement specified") + return + end + + local tables = {} + + if G.pack_cards then + table.insert(tables, G.pack_cards.highlighted) + end + if G.shop_vouchers then + table.insert(tables, G.shop_vouchers.highlighted) + end + if G.shop_jokers then + table.insert(tables, G.shop_jokers.highlighted) + end + if G.shop_booster then + table.insert(tables, G.shop_booster.highlighted) + end + if G.consumeables then + table.insert(tables, G.consumeables.highlighted) + end + if G.jokers then + table.insert(tables, G.jokers.highlighted) + end + if G.discard then + table.insert(tables, G.discard.highlighted) + end + if G.deck then table.insert(tables, G.deck.highlighted) end + if G.hand then table.insert(tables, G.hand.highlighted) end + if G.play then table.insert(tables, G.play.highlighted) end + + for i, t in ipairs(tables) do + for j, card in ipairs(t) do + if card.config then + logger:info("Enhancing " .. card.config.center.key) + + for k, enhancement in pairs(args) do + + for k, v in pairs(G.P_CENTER_POOLS["Default"]) do + local name = string.gsub(v.label, " ", "") + if enhancement == v.key or + string.lower(enhancement) == + string.lower(name) then + if (card.config.center.set == "Default" or + card.config.center.set == "Enhanced") then + card:set_ability(G.P_CENTERS[v.key], + nil, false) + logger:info( + "Card set to " .. v.label .. " (" .. + card.config.center.key .. ").") + else + logger:info( + "Only standard cards should be set to " .. + v.label .. " (" .. + card.config.center.key .. ").") + end + end + end + + for k, v in pairs(G.P_CENTER_POOLS["Enhanced"]) do + local name = string.gsub(v.label, " ", "") + if enhancement == v.key or + string.lower(enhancement) == + string.lower(name) then + if (card.config.center.set == "Default" or + card.config.center.set == "Enhanced") then + card:set_ability(G.P_CENTERS[v.key], + nil, false) + logger:info( + "Card set to " .. v.label .. " (" .. + card.config.center.key .. ").") + else + logger:info( + "Only standard cards should be set to " .. + v.label .. " (" .. + card.config.center.key .. ").") + end + end + end + + for k, v in pairs(G.P_CENTER_POOLS["Edition"]) do + local name = string.gsub(v.name, " ", "") + if enhancement == v.key or + string.lower(enhancement) == + string.lower(name) then + local editionKey = string.sub(v.key, 3) + card:set_edition({[editionKey] = true}, true) + logger:info( + "Card set to " .. v.name .. " edition (" .. + card.config.center.key .. ").") + end + end + + for k, v in pairs(G.P_CENTER_POOLS["Seal"]) do + if string.lower(enhancement) == + string.lower(v.key .. "Seal") then + card:set_seal(v.key, true) + logger:info( + "Added " .. v.key .. " Seal to card (" .. + card.config.center.key .. ").") + end + end + + end + end end - return nil - end, - "Usage: booster [Standard/Spectral/Arcana/Celestial/Buffoon]" - ) - - console:registerCommand( - "enhance", - function (args) - local arg = args[1] or nil - if arg == nil then - logger:info("No enhancement specified") - return - end - - local tables = {} - - if G.pack_cards then table.insert(tables, G.pack_cards.highlighted) end - if G.shop_vouchers then table.insert(tables, G.shop_vouchers.highlighted) end - if G.shop_jokers then table.insert(tables, G.shop_jokers.highlighted) end - if G.shop_booster then table.insert(tables, G.shop_booster.highlighted) end - if G.consumeables then table.insert(tables, G.consumeables.highlighted) end - if G.jokers then table.insert(tables, G.jokers.highlighted) end - if G.discard then table.insert(tables, G.discard.highlighted) end - if G.deck then table.insert(tables, G.deck.highlighted) end - if G.hand then table.insert(tables, G.hand.highlighted) end - if G.play then table.insert(tables, G.play.highlighted) end - - for i, t in ipairs(tables) do - for j, card in ipairs(t) do - if card.config then - logger:info("Enhancing " .. card.config.center.key) - - for k, enhancement in pairs(args) do - - for k, v in pairs(G.P_CENTER_POOLS["Default"]) do - local name = string.gsub(v.label, " ", "") - if enhancement == v.key or string.lower(enhancement) == string.lower(name) then - if (card.config.center.set == "Default" or card.config.center.set == "Enhanced") then - card:set_ability(G.P_CENTERS[v.key], nil, false) - logger:info("Card set to " .. v.label .. " (" .. card.config.center.key .. ").") - else - logger:info("Only standard cards should be set to " .. v.label .. " (" .. card.config.center.key .. ").") - end - end - end - - for k, v in pairs(G.P_CENTER_POOLS["Enhanced"]) do - local name = string.gsub(v.label, " ", "") - if enhancement == v.key or string.lower(enhancement) == string.lower(name) then - if (card.config.center.set == "Default" or card.config.center.set == "Enhanced") then - card:set_ability(G.P_CENTERS[v.key], nil, false) - logger:info("Card set to " .. v.label .. " (" .. card.config.center.key .. ").") - else - logger:info("Only standard cards should be set to " .. v.label .. " (" .. card.config.center.key .. ").") - end - end - end - - for k, v in pairs(G.P_CENTER_POOLS["Edition"]) do - local name = string.gsub(v.name, " ", "") - if enhancement == v.key or string.lower(enhancement) == string.lower(name) then - local editionKey = string.sub(v.key, 3) - card:set_edition({ [editionKey] = true }, true) - logger:info("Card set to " .. v.name .. " edition (" .. card.config.center.key .. ").") - end - end - - for k, v in pairs(G.P_CENTER_POOLS["Seal"]) do - if string.lower(enhancement) == string.lower(v.key .. "Seal") then - card:set_seal(v.key, true) - logger:info("Added " .. v.key .. " Seal to card (" .. card.config.center.key .. ").") - end - end - - end - - end - end - end - end, - "Add one or more enhancements to selected cards. Can use key or name (e.g. c_base or BaseCard, e_negative or Negative). Must add Seal to seals (e.g. RedSeal, BlueSeal)", - function (current_arg, previous_arg) - local subcommands = { } - - for k, v in pairs(G.P_CENTER_POOLS["Default"]) do - local name = string.gsub(v.label, " ", "") - table.insert(subcommands, v.key) - table.insert(subcommands, name) - end - - for k, v in pairs(G.P_CENTER_POOLS["Enhanced"]) do - local name = string.gsub(v.label, " ", "") - table.insert(subcommands, v.key) - table.insert(subcommands, name) - end - - for k, v in pairs(G.P_CENTER_POOLS["Edition"]) do - local name = string.gsub(v.name, " ", "") - table.insert(subcommands, v.key) - table.insert(subcommands, name) - end - - for k, v in pairs(G.P_CENTER_POOLS["Seal"]) do - table.insert(subcommands, v.key .. "Seal") - end - - local completions = {} - for k, v in pairs(subcommands) do - if v:find(current_arg, 1, true) == 1 then - table.insert(completions, v) - end + end + end, + "Add one or more enhancements to selected cards. Can use key or name (e.g. c_base or BaseCard, e_negative or Negative). Must add Seal to seals (e.g. RedSeal, BlueSeal)", + function(current_arg, previous_arg) + local subcommands = {} + + for k, v in pairs(G.P_CENTER_POOLS["Default"]) do + local name = string.gsub(v.label, " ", "") + table.insert(subcommands, v.key) + table.insert(subcommands, name) + end + + for k, v in pairs(G.P_CENTER_POOLS["Enhanced"]) do + local name = string.gsub(v.label, " ", "") + table.insert(subcommands, v.key) + table.insert(subcommands, name) + end + + for k, v in pairs(G.P_CENTER_POOLS["Edition"]) do + local name = string.gsub(v.name, " ", "") + table.insert(subcommands, v.key) + table.insert(subcommands, name) + end + + for k, v in pairs(G.P_CENTER_POOLS["Seal"]) do + table.insert(subcommands, v.key .. "Seal") + end + + local completions = {} + for k, v in pairs(subcommands) do + if v:find(current_arg, 1, true) == 1 then + table.insert(completions, v) end - return completions - end, - "Usage: enhance [arg2] [arg3] ..." - ) + end + return completions + end, + "Usage: enhance [arg2] [arg3] ...") console.logger:debug("Dev Console on_enable completed") end, @@ -1171,11 +1171,11 @@ mods["dev_console"] = { console.removeCommand("money") console.removeCommand("discards") console.removeCommand("hands") - console.removeCommand("booster") - console.removeCommand("enhance") + console.removeCommand("booster") + console.removeCommand("enhance") console.logger:debug("Dev Console disabled") end, - on_key_pressed = function (key_name) + on_key_pressed = function(key_name) if key_name == "f2" then console:toggle() return true @@ -1195,12 +1195,14 @@ mods["dev_console"] = { end return false end, - on_post_render = function () - console.max_lines = math.floor(love.graphics.getHeight() / console.line_height) - 5 -- 5 lines of bottom padding + on_post_render = function() + console.max_lines = math.floor(love.graphics.getHeight() / + console.line_height) - 5 -- 5 lines of bottom padding local font = love.graphics.getFont() if console.is_open then love.graphics.setColor(0, 0, 0, 0.3) - love.graphics.rectangle("fill", 0, 0, love.graphics.getWidth(), love.graphics.getHeight()) + love.graphics.rectangle("fill", 0, 0, love.graphics.getWidth(), + love.graphics.getHeight()) local messagesToDisplay = console:getMessagesToDisplay() local i = 1 for _, message in ipairs(messagesToDisplay) do @@ -1208,7 +1210,8 @@ mods["dev_console"] = { love.graphics.setColor(r, g, b, 1) local formattedMessage = message:formatted() if font:getWidth(formattedMessage) > love.graphics.getWidth() then - local lines = console:wrapText(formattedMessage, love.graphics.getWidth()) + local lines = console:wrapText(formattedMessage, + love.graphics.getWidth()) for _, line in ipairs(lines) do love.graphics.print(line, 10, 10 + i * 20) i = i + 1 @@ -1222,7 +1225,7 @@ mods["dev_console"] = { love.graphics.print(console.cmd, 10, love.graphics.getHeight() - 30) end end, - on_key_released = function (key_name) + on_key_released = function(key_name) if key_name == "capslock" then console.modifiers.capslock = not console.modifiers.capslock console:modifiersListener() @@ -1262,17 +1265,16 @@ mods["dev_console"] = { end, on_mouse_pressed = function(x, y, button, touches) if console.is_open then - return true -- Do not press buttons through the console, this cancels the event + return true -- Do not press buttons through the console, this cancels the event end end, on_mouse_released = function(x, y, button) if console.is_open then return true -- Do not release buttons through the console, this cancels the event end - end, + end } - -- Topological sort of mods based on load_before and load_after fields -- 1. Create a directed graph with the mods as nodes and the load_before and load_after fields as edges -- 2. Run a topological sort on the graph @@ -1280,29 +1282,29 @@ mods["dev_console"] = { local function sortMods(mods) logger:trace('Sorting mods', utils.keys(mods)) local graph = {} - for modId, mod in pairs(mods) do - graph[modId] = { - before = {}, - } - end + for modId, mod in pairs(mods) do graph[modId] = {before = {}} end logger:trace('Graph generated', graph) for modId, mod in pairs(mods) do for i, before in ipairs(mod.load_before or {}) do -- load_before is a list of mod ids, if its nil, use an empty table to avoid a crash if not graph[before] then - logger:error('Mod ', mod.id, ' has a load_before field that references a non-existent mod: ', before) + logger:error('Mod ', mod.id, + ' has a load_before field that references a non-existent mod: ', + before) return nil end - graph[modId].before[before] = true -- we set to true just because we want a table behaving like a set() instead of an array + graph[modId].before[before] = true -- we set to true just because we want a table behaving like a set() instead of an array end for i, after in ipairs(mod.load_after or {}) do -- load_after is a list of mod ids -- load_after is there to ensure that a mod is loaded after another mod -- this is equivalent to the other mod being loaded before the current mod -- so we add an edge from the other mod to the current mod if not graph[after] then - logger:error('Mod ', mod.id, ' has a load_after field that references a non-existent mod: ', after) + logger:error('Mod ', mod.id, + ' has a load_after field that references a non-existent mod: ', + after) return nil end - graph[after].before[modId] = true -- we set to true just because we want a table behaving like a set() instead of an array + graph[after].before[modId] = true -- we set to true just because we want a table behaving like a set() instead of an array end end logger:trace('Graph nodes and edges', graph) @@ -1320,9 +1322,7 @@ local function sortMods(mods) end visited[node] = "temporary" for other, _ in pairs(graph[node].before) do - if not visit(other) then - return false - end + if not visit(other) then return false end end table.insert(sorted, node) logger:trace("Inserted node ", node, " in sorted list", sorted) @@ -1331,11 +1331,7 @@ local function sortMods(mods) return true end logger:trace("Starting to visit nodes") - for node, _ in pairs(graph) do - if not visited[node] then - visit(node) - end - end + for node, _ in pairs(graph) do if not visited[node] then visit(node) end end local sortedMods = {} local modCount = #sorted -- we need to keep the mapping between the mod id and the mod object @@ -1368,5 +1364,5 @@ return { loadMod = loadMod, toggleMod = toggleMod, sortMods = sortMods, - callModCallbacksIfExists = callModCallbacksIfExists, + callModCallbacksIfExists = callModCallbacksIfExists } diff --git a/src/console.lua b/src/console.lua index 10427e5..146e754 100644 --- a/src/console.lua +++ b/src/console.lua @@ -23,12 +23,12 @@ return { shift = false, ctrl = false, alt = false, - meta = false, + meta = false }, commands = {}, toggle = function(self) self.is_open = not self.is_open - love.keyboard.setKeyRepeat(self.is_open) -- set key repeat to true when console is open + love.keyboard.setKeyRepeat(self.is_open) -- set key repeat to true when console is open if self.is_open then self.start_line_offset = self.max_lines - 1 local oldTextInput = love.textinput @@ -40,16 +40,13 @@ return { end end, longestCommonPrefix = function(self, strings) - if #strings == 0 then - return "" - end + if #strings == 0 then return "" end local prefix = strings[1] for i = 2, #strings do local str = strings[i] local j = 1 - while j <= #prefix and j <= #str and prefix:sub(j, j) == str:sub(j, j) do - j = j + 1 - end + while j <= #prefix and j <= #str and prefix:sub(j, j) == + str:sub(j, j) do j = j + 1 end prefix = prefix:sub(1, j - 1) end return prefix @@ -58,9 +55,7 @@ return { local command = self.cmd:sub(3) -- remove the "> " prefix local cmd = {} -- split command into parts - for part in command:gmatch("%S+") do - table.insert(cmd, part) - end + for part in command:gmatch("%S+") do table.insert(cmd, part) end if #cmd == 0 then -- no command typed, do nothing (no completions possible) logger:trace("No command typed") @@ -82,10 +77,12 @@ return { local previousArgs = cmd local current_arg = table.remove(previousArgs) if command then - completions = command.autocomplete(current_arg, previousArgs) or {} + completions = command.autocomplete(current_arg, previousArgs) or + {} end end - logger:trace("Autocomplete matches: " .. #completions .. " " .. table.concat(completions, ", ")) + logger:trace("Autocomplete matches: " .. #completions .. " " .. + table.concat(completions, ", ")) if #completions == 0 then -- no completions found return nil @@ -96,25 +93,13 @@ return { return self:longestCommonPrefix(completions) end end, - getMessageColor = function (self, message) - if message.level == "PRINT" then - return 1, 1, 1 - end - if message.level == "INFO" then - return 0, 0.9, 1 - end - if message.level == "WARN" then - return 1, 0.5, 0 - end - if message.level == "ERROR" then - return 1, 0, 0 - end - if message.level == "DEBUG" then - return 0.16, 0, 1 - end - if message.level == "TRACE" then - return 1, 1, 1 - end + getMessageColor = function(self, message) + if message.level == "PRINT" then return 1, 1, 1 end + if message.level == "INFO" then return 0, 0.9, 1 end + if message.level == "WARN" then return 1, 0.5, 0 end + if message.level == "ERROR" then return 1, 0, 0 end + if message.level == "DEBUG" then return 0.16, 0, 1 end + if message.level == "TRACE" then return 1, 1, 1 end return 1, 1, 1 end, getFilteredMessages = function(self) @@ -131,21 +116,27 @@ return { local i = 1 local textLength = 0 - local base_messages = self:getFilteredMessages() + local base_messages = self:getFilteredMessages() local all_messages = {} - for _, message in ipairs(base_messages) do - local wrappedLines = self:wrapText(message.text, love.graphics.getWidth() - 20) - for _, line in ipairs(wrappedLines) do - table.insert(all_messages, {text = line, level = message.level, name = message.name, time = message.time, level_numeric = message.level_numeric, formatted = function() return line end}) - end - end + for _, message in ipairs(base_messages) do + local wrappedLines = self:wrapText(message.text, + love.graphics.getWidth() - 20) + for _, line in ipairs(wrappedLines) do + table.insert(all_messages, { + text = line, + level = message.level, + name = message.name, + time = message.time, + level_numeric = message.level_numeric, + formatted = function() return line end + }) + end + end while textLength < self.max_lines do local index = #all_messages - i + self.start_line_offset - if index < 1 then - break - end + if index < 1 then break end local message = all_messages[index] if message then table.insert(text, message) @@ -155,16 +146,23 @@ return { end -- define locally to not pollute the global namespace scope local function reverse(tab) - for i = 1, math.floor(#tab/2), 1 do - tab[i], tab[#tab-i+1] = tab[#tab-i+1], tab[i] + for i = 1, math.floor(#tab / 2), 1 do + tab[i], tab[#tab - i + 1] = tab[#tab - i + 1], tab[i] end return tab end text = reverse(text) -- pad text table so that we always have max_lines lines in there local nLinesToPad = #text - self.max_lines - for i=1,nLinesToPad do - table.insert(text, {text = "", level = "PRINT", name = "", time = 0, level_numeric = 1000, formatted = function() return "" end}) + for i = 1, nLinesToPad do + table.insert(text, { + text = "", + level = "PRINT", + name = "", + time = 0, + level_numeric = 1000, + formatted = function() return "" end + }) end return text end, @@ -181,9 +179,12 @@ return { end end end, - typeKey = function (self, key_name) + typeKey = function(self, key_name) -- cmd+shift+C on mac, ctrl+shift+C on windows/linux - if key_name == "c" and ((platform.is_mac and self.modifiers.meta and self.modifiers.shift) or (not platform.is_mac and self.modifiers.ctrl and self.modifiers.shift)) then + if key_name == "c" and + ((platform.is_mac and self.modifiers.meta and self.modifiers.shift) or + (not platform.is_mac and self.modifiers.ctrl and + self.modifiers.shift)) then local messages = self:getFilteredMessages() local text = "" for _, message in ipairs(messages) do @@ -193,7 +194,8 @@ return { return end -- cmd+C on mac, ctrl+C on windows/linux - if key_name == "c" and ((platform.is_mac and self.modifiers.meta) or (not platform.is_mac and self.modifiers.ctrl)) then + if key_name == "c" and ((platform.is_mac and self.modifiers.meta) or + (not platform.is_mac and self.modifiers.ctrl)) then if self.cmd:sub(3) == "" then -- do nothing if the buffer is empty return @@ -202,7 +204,8 @@ return { return end -- cmd+V on mac, ctrl+V on windows/linux - if key_name == "v" and ((platform.is_mac and self.modifiers.meta) or (not platform.is_mac and self.modifiers.ctrl)) then + if key_name == "v" and ((platform.is_mac and self.modifiers.meta) or + (not platform.is_mac and self.modifiers.ctrl)) then self.cmd = self.cmd .. love.system.getClipboardText() return end @@ -212,40 +215,52 @@ return { return end -- Delete the current command, on mac it's cmd+backspace - if key_name == "delete" or (platform.is_mac and self.modifiers.meta and key_name == "backspace") then + if key_name == "delete" or + (platform.is_mac and self.modifiers.meta and key_name == "backspace") then self.cmd = "> " return end - if key_name == "end" or (platform.is_mac and key_name == "right" and self.modifiers.meta) then + if key_name == "end" or + (platform.is_mac and key_name == "right" and self.modifiers.meta) then -- move text to the most recent (bottom) self.start_line_offset = self.max_lines return end - if key_name == "home" or (platform.is_mac and key_name == "left" and self.modifiers.meta) then + if key_name == "home" or + (platform.is_mac and key_name == "left" and self.modifiers.meta) then -- move text to the oldest (top) local messages = self:getFilteredMessages() self.start_line_offset = self.max_lines - #messages return end - if key_name == "pagedown" or (platform.is_mac and key_name == "down" and self.modifiers.meta) then + if key_name == "pagedown" or + (platform.is_mac and key_name == "down" and self.modifiers.meta) then -- move text down by max_lines - self.start_line_offset = math.min(self.start_line_offset + self.max_lines, self.max_lines) + self.start_line_offset = math.min( + self.start_line_offset + self.max_lines, + self.max_lines) return end - if key_name == "pageup" or (platform.is_mac and key_name == "up" and self.modifiers.meta) then + if key_name == "pageup" or + (platform.is_mac and key_name == "up" and self.modifiers.meta) then -- move text up by max_lines local messages = self:getFilteredMessages() - self.start_line_offset = math.max(self.start_line_offset - self.max_lines, self.max_lines - #messages) + self.start_line_offset = math.max( + self.start_line_offset - self.max_lines, + self.max_lines - #messages) return end if key_name == "up" then -- move to the next command in the history (in reverse order of insertion) - self.history_index = math.min(self.history_index + 1, #self.command_history) + self.history_index = math.min(self.history_index + 1, + #self.command_history) if self.history_index == 0 then self.cmd = "> " return end - self.cmd = "> " .. self.command_history[#self.command_history - self.history_index + 1] + self.cmd = "> " .. + self.command_history[#self.command_history - + self.history_index + 1] return end if key_name == "down" then @@ -255,7 +270,9 @@ return { self.cmd = "> " return end - self.cmd = "> " .. self.command_history[#self.command_history - self.history_index + 1] + self.cmd = "> " .. + self.command_history[#self.command_history - + self.history_index + 1] return end if key_name == "tab" then @@ -307,9 +324,7 @@ return { self.logger:print(self.cmd) local cmdName = self.cmd:sub(3) cmdName = cmdName:match("%S+") - if cmdName == nil then - return - end + if cmdName == nil then return end local args = {} local argString = self.cmd:sub(3 + #cmdName + 1) if argString then @@ -333,15 +348,16 @@ return { end end, addToHistory = function(self, command) - if command == nil or command == "" then - return - end + if command == nil or command == "" then return end table.insert(self.command_history, command) self.history_index = 0 - local success, errormsg = love.filesystem.append(self.history_path, command .. "\n") + local success, errormsg = love.filesystem.append(self.history_path, + command .. "\n") if not success then - self.logger:warn("Error appending ", command, " to history file: ", errormsg) - success, errormsg = love.filesystem.write(self.history_path, command .. "\n") + self.logger:warn("Error appending ", command, " to history file: ", + errormsg) + success, errormsg = love.filesystem.write(self.history_path, + command .. "\n") if not success then self.logger:error("Error writing to history file: ", errormsg) end @@ -353,32 +369,41 @@ return { -- @param short_description: string, a short description of the command -- @param autocomplete: function(current_arg: string), a function that returns a list of possible completions for the current argument -- @param usage: string, a string describing the usage of the command (longer, more detailed description of the command's usage) - registerCommand = function(self, name, callback, short_description, autocomplete, usage) + registerCommand = function(self, name, callback, short_description, + autocomplete, usage) if name == nil then self.logger:error("registerCommand -- name is required") end if callback == nil then - self.logger:error("registerCommand -- callback is required on command", name) + self.logger:error( + "registerCommand -- callback is required on command", name) end if type(callback) ~= "function" then - self.logger:error("registerCommand -- callback must be a function on command", name) + self.logger:error( + "registerCommand -- callback must be a function on command", + name) end if name == nil or callback == nil or type(callback) ~= "function" then return end if short_description == nil then - self.logger:warn("registerCommand -- no description provided, please provide a description for the `help` command") + self.logger:warn( + "registerCommand -- no description provided, please provide a description for the `help` command") short_description = "No help provided" end - if usage == nil then - usage = short_description - end + if usage == nil then usage = short_description end if autocomplete == nil then - autocomplete = function(current_arg, previous_args) return nil end + autocomplete = function(current_arg, previous_args) + return nil + end end if type(autocomplete) ~= "function" then - self.logger:warn("registerCommand -- autocomplete must be a function for command: ", name) - autocomplete = function(current_arg, previous_args) return nil end + self.logger:warn( + "registerCommand -- autocomplete must be a function for command: ", + name) + autocomplete = function(current_arg, previous_args) + return nil + end end if self.commands[name] then self.logger:warn("Command " .. name .. " already exists") @@ -389,7 +414,7 @@ return { call = callback, desc = short_description, autocomplete = autocomplete, - usage = usage, + usage = usage } end, removeCommand = function(self, cmd_name) @@ -416,5 +441,5 @@ return { end table.insert(lines, line) return lines - end, -} \ No newline at end of file + end +} From 20a8c189f8f8f4c226830353efd794dac9a9b2aa Mon Sep 17 00:00:00 2001 From: Zei33 Date: Thu, 30 May 2024 17:57:53 +1000 Subject: [PATCH 10/15] Formatting correction. --- src/balamod.lua | 769 ++++++++++++++++++++++-------------------------- src/console.lua | 244 ++++++++------- 2 files changed, 472 insertions(+), 541 deletions(-) diff --git a/src/balamod.lua b/src/balamod.lua index 56c66fa..0c0fdf8 100644 --- a/src/balamod.lua +++ b/src/balamod.lua @@ -9,12 +9,7 @@ local https = require('https') logger = logging.getLogger('balamod') mods = {} -local apis = { - logging = logging, - console = console, - math = math, - platform = platform -} +local apis = {logging = logging, console = console, math = math, platform = platform} is_loaded = false local RESULT = { SUCCESS = 0, @@ -31,9 +26,11 @@ local paths = {} -- Paths to the files that will be loaded local _VERSION = require('balamod_version') local function splitstring(inputstr, sep) - if sep == nil then sep = "%s" end + if sep == nil then + sep = '%s' + end local t = {} - for str in string.gmatch(inputstr, "([^" .. sep .. "]+)") do + for str in string.gmatch(inputstr, '([^' .. sep .. ']+)') do table.insert(t, str) end return t @@ -42,17 +39,23 @@ end function buildPaths(root, ignore) local items = love.filesystem.getDirectoryItems(root) for _, file in ipairs(items) do - if root ~= "" then file = root .. "/" .. file end + if root ~= '' then + file = root .. '/' .. file + end local info = love.filesystem.getInfo(file) if info then - if info.type == "file" and file:match("%.lua$") then + if info.type == 'file' and file:match('%.lua$') then table.insert(paths, file) - elseif info.type == "directory" then + elseif info.type == 'directory' then local valid = true for _, i in ipairs(ignore) do - if i == file then valid = false end + if i == file then + valid = false + end + end + if valid then + buildPaths(file, ignore) end - if valid then buildPaths(file, ignore) end end end end @@ -62,9 +65,7 @@ local function request(url) logger:debug('Request made with url: ', url) local code local response - code, response, headers = https.request(url, { - headers = {['User-Agent'] = 'Balamod-Client'} - }) + code, response, headers = https.request(url, {headers = {['User-Agent'] = 'Balamod-Client'}}) if (code == 301 or code == 302) and headers.location then -- follow redirects if necessary code, response = request(headers.location) @@ -73,24 +74,25 @@ local function request(url) end local function extractFunctionBody(path, function_name) - local pattern = "\n?%s*function%s+" .. function_name .. "%(" + local pattern = '\n?%s*function%s+' .. function_name .. '%(' local func_begin, fin = current_game_code[path]:find(pattern) if not func_begin then - return "Can't find function begin " .. function_name + return 'Can\'t find function begin ' .. function_name end - local func_end = current_game_code[path]:find("\n\r?end", fin) + local func_end = current_game_code[path]:find('\n\r?end', fin) -- This is to catch functions that have incorrect ending indentation by catching the next function in line. -- Can be removed once Card:calculate_joker no longer has this typo. - local typocatch_func_end = - current_game_code[path]:find("\n\r?function", fin) + local typocatch_func_end = current_game_code[path]:find('\n\r?function', fin) if typocatch_func_end and typocatch_func_end < func_end then func_end = typocatch_func_end - 3 end - if not func_end then return "Can't find function end " .. function_name end + if not func_end then + return 'Can\'t find function end ' .. function_name + end local func_body = current_game_code[path]:sub(func_begin, func_end + 3) return func_body @@ -100,28 +102,25 @@ local function inject(path, function_name, to_replace, replacement) -- Injects code into a function (replaces a string with another string inside a function) local function_body = extractFunctionBody(path, function_name) local modified_function_code = function_body:gsub(to_replace, replacement) - escaped_function_body = function_body:gsub("([^%w])", "%%%1") -- escape function body for use in gsub - escaped_modified_function_code = modified_function_code:gsub("([^%w])", - "%%%1") - current_game_code[path] = current_game_code[path]:gsub( - escaped_function_body, - escaped_modified_function_code) -- update current game code in memory + escaped_function_body = function_body:gsub('([^%w])', '%%%1') -- escape function body for use in gsub + escaped_modified_function_code = modified_function_code:gsub('([^%w])', '%%%1') + current_game_code[path] = current_game_code[path]:gsub(escaped_function_body, escaped_modified_function_code) -- update current game code in memory local new_function, load_error = load(modified_function_code) -- load modified function if not new_function then - logger:error("Error loading modified function", function_name, ": ", - (load_error or "Unknown error")) + logger:error('Error loading modified function', function_name, ': ', (load_error or 'Unknown error')) logger:error(modified_function_code) end - if setfenv then setfenv(new_function, getfenv(original_testFunction)) end -- Set the environment of the new function to the same as the original function + if setfenv then + setfenv(new_function, getfenv(original_testFunction)) + end -- Set the environment of the new function to the same as the original function local status, result = pcall(new_function) -- Execute the new function if status then testFunction = result -- Overwrite the original function with the result of the new function else - logger:error("Error executing modified function", function_name, ": ", - result) -- Safeguard against errors + logger:error('Error executing modified function', function_name, ': ', result) -- Safeguard against errors logger:error(modified_function_code) end end @@ -129,40 +128,36 @@ end local function injectHead(path, function_name, code) local function_body = extractFunctionBody(path, function_name) - local pattern = "(function%s+" .. function_name .. ".-)\n" - local modified_function_code, number_of_subs = - function_body:gsub(pattern, "%1\n" .. code .. "\n") + local pattern = '(function%s+' .. function_name .. '.-)\n' + local modified_function_code, number_of_subs = function_body:gsub(pattern, '%1\n' .. code .. '\n') if number_of_subs == 0 then - logger:error( - "Error: Function start not found in function body or multiple matches encountered.") + logger:error('Error: Function start not found in function body or multiple matches encountered.') logger:error(modified_function_code) return end - escaped_function_body = function_body:gsub("([^%w])", "%%%1") - escaped_modified_function_code = modified_function_code:gsub("([^%w])", - "%%%1") - current_game_code[path] = current_game_code[path]:gsub( - escaped_function_body, - escaped_modified_function_code) + escaped_function_body = function_body:gsub('([^%w])', '%%%1') + escaped_modified_function_code = modified_function_code:gsub('([^%w])', '%%%1') + current_game_code[path] = current_game_code[path]:gsub(escaped_function_body, escaped_modified_function_code) local new_function, load_error = load(modified_function_code) if not new_function then - logger:error("Error loading modified function ", function_name, - " with head injection: ", (load_error or "Unknown error")) + logger:error('Error loading modified function ', function_name, ' with head injection: ', + (load_error or 'Unknown error')) logger:error(modified_function_code) return end - if setfenv then setfenv(new_function, getfenv(original_testFunction)) end + if setfenv then + setfenv(new_function, getfenv(original_testFunction)) + end local status, result = pcall(new_function) if status then testFunction = result else - logger:error("Error executing modified function ", function_name, - " with head injection: ", result) + logger:error('Error executing modified function ', function_name, ' with head injection: ', result) logger:error(modified_function_code) end end @@ -170,41 +165,38 @@ end local function injectTail(path, function_name, code) local function_body = extractFunctionBody(path, function_name) - local pattern = "(.-)(end[ \t]*\n?)$" - local modified_function_code, number_of_subs = - function_body:gsub(pattern, "%1" .. string.gsub(code, '(.-)%s*$', '%1') .. - "\n" .. "%2") + local pattern = '(.-)(end[ \t]*\n?)$' + local modified_function_code, number_of_subs = function_body:gsub(pattern, '%1' .. + string.gsub(code, '(.-)%s*$', '%1') .. '\n' .. + '%2') if number_of_subs == 0 then - logger:error("Error: 'end' not found in function '", function_name, - "' body or multiple ends encountered.") + logger:error('Error: \'end\' not found in function \'', function_name, '\' body or multiple ends encountered.') logger:error(modified_function_code) return end - escaped_function_body = function_body:gsub("([^%w])", "%%%1") - escaped_modified_function_code = modified_function_code:gsub("([^%w])", - "%%%1") - current_game_code[path] = current_game_code[path]:gsub( - escaped_function_body, - escaped_modified_function_code) + escaped_function_body = function_body:gsub('([^%w])', '%%%1') + escaped_modified_function_code = modified_function_code:gsub('([^%w])', '%%%1') + current_game_code[path] = current_game_code[path]:gsub(escaped_function_body, escaped_modified_function_code) local new_function, load_error = load(modified_function_code) if not new_function then - logger:error("Error loading modified function ", function_name, - " with tail injection: ", (load_error or "Unknown error")) + logger:error('Error loading modified function ', function_name, ' with tail injection: ', + (load_error or 'Unknown error')) logger:error(modified_function_code) return end - if setfenv then setfenv(new_function, getfenv(original_testFunction)) end + if setfenv then + setfenv(new_function, getfenv(original_testFunction)) + end local status, result = pcall(new_function) if status then testFunction = result else - logger:error("Error executing modified function ", function_name, - " with tail injection: ", result) + logger:error('Error executing modified function ', function_name, ' with tail injection: ', result) logger:error(modified_function_code) end end @@ -219,8 +211,7 @@ end local function getRepoMods() local repoMods = {} - local reposIndex = - 'https://raw.githubusercontent.com/UwUDev/balamod/master/repos.index' + local reposIndex = 'https://raw.githubusercontent.com/UwUDev/balamod/master/repos.index' logger:info('Requesting ', reposIndex) local indexCode, indexBody = request(reposIndex) if indexCode ~= 200 then @@ -239,9 +230,8 @@ local function getRepoMods() logger:error('Response: ' .. repoBody) else for modInfo in string.gmatch(repoBody, '([^\n]+)') do - local modId, modVersion, modName, modDesc, modUrl = - string.match(modInfo, - '([^|]+)|([^|]+)|([^|]+)|([^|]+)|([^|]+)') + local modId, modVersion, modName, modDesc, modUrl = string.match(modInfo, + '([^|]+)|([^|]+)|([^|]+)|([^|]+)|([^|]+)') local modPresent = isModPresent(modId) local needUpdate = true local version = modVersion @@ -251,8 +241,7 @@ local function getRepoMods() if mod.version then version = mod.version local modVersion = utils.parseVersion(mod.version) - needUpdate = utils.v2GreaterThanV1(modVersion, - repoVersion) + needUpdate = utils.v2GreaterThanV1(modVersion, repoVersion) end end table.insert(repoMods, { @@ -288,78 +277,67 @@ local function validateManifest(modFolder, manifest) -- check that all manifest expected fields are present for field, required in pairs(expectedFields) do if manifest[field] == nil and required then - logger:error('Manifest in folder ', modFolder, - ' is missing field: ', field) + logger:error('Manifest in folder ', modFolder, ' is missing field: ', field) return false end end -- check that none of the manifest fields are not in the expected fields for key, _ in pairs(manifest) do if expectedFields[key] == nil then - logger:error('Manifest in folder ', modFolder, - ' contains unexpected field: ', key) + logger:error('Manifest in folder ', modFolder, ' contains unexpected field: ', key) return false end end -- check that the load_before, load_after and description fields are arrays if type(manifest.load_before) ~= 'table' then - logger:error('Manifest in folder ', modFolder, - ' has a non-array load_before field') + logger:error('Manifest in folder ', modFolder, ' has a non-array load_before field') return false end if type(manifest.load_after) ~= 'table' then - logger:error('Manifest in folder ', modFolder, - ' has a non-array load_after field') + logger:error('Manifest in folder ', modFolder, ' has a non-array load_after field') return false end if type(manifest.description) ~= 'table' then - logger:error('Manifest in folder ', modFolder, - ' has a non-array description field') + logger:error('Manifest in folder ', modFolder, ' has a non-array description field') return false end -- check that the load_before and load_after fields are strings for _, modId in ipairs(manifest.load_before) do if type(modId) ~= 'string' then - logger:error('Manifest in folder ', modFolder, - ' has a non-string load_before field') + logger:error('Manifest in folder ', modFolder, ' has a non-string load_before field') return false end end for _, modId in ipairs(manifest.load_after) do if type(modId) ~= 'string' then - logger:error('Manifest in folder ', modFolder, - ' has a non-string load_after field') + logger:error('Manifest in folder ', modFolder, ' has a non-string load_after field') return false end end -- check that the version field is a string, matching semantic versioning if not manifest.version:match('%d+%.%d+%.%d+') then - logger:error('Manifest in folder ', modFolder, - ' has a non-semantic versioning version field') + logger:error('Manifest in folder ', modFolder, ' has a non-semantic versioning version field') return false end -- check that the author field is a string if type(manifest.author) ~= 'string' then - logger:error('Manifest in folder ', modFolder, - ' has a non-string author field') + logger:error('Manifest in folder ', modFolder, ' has a non-string author field') return false end -- check that the id field is a string if type(manifest.id) ~= 'string' then - logger:error('Manifest in folder ', modFolder, - ' has a non-string id field') + logger:error('Manifest in folder ', modFolder, ' has a non-string id field') return false end -- check that the name field is a string if type(manifest.name) ~= 'string' then - logger:error('Manifest in folder ', modFolder, - ' has a non-string name field') + logger:error('Manifest in folder ', modFolder, ' has a non-string name field') return false end @@ -367,19 +345,16 @@ local function validateManifest(modFolder, manifest) if manifest.dependencies then local incorrectDependencies = {} if type(manifest.dependencies) ~= 'table' then - logger:error('Manifest in folder ', modFolder, - ' has a non-table dependencies field') + logger:error('Manifest in folder ', modFolder, ' has a non-table dependencies field') return false end for modId, version in pairs(manifest.dependencies) do if type(modId) ~= 'string' then - logger:error('Manifest in folder ', modFolder, - ' has a non-string key in dependencies field') + logger:error('Manifest in folder ', modFolder, ' has a non-string key in dependencies field') return false end if type(version) ~= 'string' then - logger:error('Manifest in folder ', modFolder, - ' has a non-string value in dependencies field') + logger:error('Manifest in folder ', modFolder, ' has a non-string value in dependencies field') return false end local versionConstraintCorrect = false @@ -395,8 +370,7 @@ local function validateManifest(modFolder, manifest) local versionPatterns = {'%d+', '%d+%.%d+', '%d+%.%d+%.%d+'} for _, versionPattern1 in ipairs(versionPatterns) do for _, versionPattern2 in ipairs(versionPatterns) do - table.insert(patterns, '[<>]=?' .. versionPattern1 .. - ', ?[<>]=?' .. versionPattern2) + table.insert(patterns, '[<>]=?' .. versionPattern1 .. ', ?[<>]=?' .. versionPattern2) end end -- check every generated pattern, one at a time, if any of them matches, then the version constraint is correct @@ -412,8 +386,7 @@ local function validateManifest(modFolder, manifest) end if #incorrectDependencies > 0 then -- some of the dependencies are incorrect for the mod, let's log them and return false - logger:error('Manifest in folder ', modFolder, - ' has incorrect dependencies field: ', + logger:error('Manifest in folder ', modFolder, ' has incorrect dependencies field: ', table.concat(incorrectDependencies, ', ')) return false end @@ -430,48 +403,47 @@ local function loadMod(modFolder) return nil end if not love.filesystem.getInfo('mods/' .. modFolder .. '/main.lua', 'file') then - logger:error('Mod folder ', modFolder, - ' does not contain a main.lua file') + logger:error('Mod folder ', modFolder, ' does not contain a main.lua file') return nil end - if not love.filesystem.getInfo('mods/' .. modFolder .. '/manifest.json', - 'file') then - logger:error('Mod folder ', modFolder, - ' does not contain a manifest.json file') + if not love.filesystem.getInfo('mods/' .. modFolder .. '/manifest.json', 'file') then + logger:error('Mod folder ', modFolder, ' does not contain a manifest.json file') return nil end - logger:debug("Loading manifest from: ", - 'mods/' .. modFolder .. '/manifest.json') + logger:debug('Loading manifest from: ', 'mods/' .. modFolder .. '/manifest.json') -- load the manifest - local manifest = json.decode(love.filesystem.read( - 'mods/' .. modFolder .. '/manifest.json')) - if not validateManifest(modFolder, manifest) then return nil end + local manifest = json.decode(love.filesystem.read('mods/' .. modFolder .. '/manifest.json')) + if not validateManifest(modFolder, manifest) then + return nil + end logger:debug('Manifest loaded: ', manifest) -- check that the mod is compatible with the current version of balamod if manifest.min_balamod_version then local minVersion = utils.parseVersion(manifest.min_balamod_version) if not utils.v2GreaterThanV1(minVersion, _VERSION) then - logger:error('Mod ', modFolder, - ' requires a newer version of balamod') + logger:error('Mod ', modFolder, ' requires a newer version of balamod') return nil end end if manifest.max_balamod_version then local maxVersion = utils.parseVersion(manifest.max_balamod_version) if utils.v2GreaterThanV1(maxVersion, _VERSION) then - logger:error('Mod ', modFolder, - ' requires an older version of balamod') + logger:error('Mod ', modFolder, ' requires an older version of balamod') return nil end end -- load the hooks (on_enable, on_game_load, etc...) - logger:debug("Loading hooks from: ", 'mods/' .. modFolder .. '/main.lua') + logger:debug('Loading hooks from: ', 'mods/' .. modFolder .. '/main.lua') local modHooks = require('mods/' .. modFolder .. '/main') - for hookName, hook in pairs(modHooks) do mod[hookName] = hook end - for key, value in pairs(manifest) do mod[key] = value end + for hookName, hook in pairs(modHooks) do + mod[hookName] = hook + end + for key, value in pairs(manifest) do + mod[key] = value + end mod.enabled = true logger:debug('Mod loaded: ', mod.id) - logger:debug("Checking if mod is disabled") + logger:debug('Checking if mod is disabled') if love.filesystem.getInfo('mods/' .. modFolder .. '/disable.it', 'file') then mod.enabled = false end @@ -487,8 +459,7 @@ local function toggleMod(mod) love.filesystem.remove('mods/' .. mod.id .. '/disable.it') end pcall(mod.on_enable) - elseif not mod.enabled and mod.on_disable and type(mod.on_disable) == - 'function' then + elseif not mod.enabled and mod.on_disable and type(mod.on_disable) == 'function' then love.filesystem.write('mods/' .. mod.id .. '/disable.it', '') pcall(mod.on_disable) end @@ -497,19 +468,19 @@ end local function callModCallbacksIfExists(mods, callback_name, should_log, ...) local sorted = utils.values(mods) - table.sort(sorted, function(a, b) return a.order < b.order end) + table.sort(sorted, function(a, b) + return a.order < b.order + end) local mod_returns = {} -- pre loading all mods for _, mod in ipairs(sorted) do - if mod.enabled and mod[callback_name] and type(mod[callback_name]) == - "function" then + if mod.enabled and mod[callback_name] and type(mod[callback_name]) == 'function' then if should_log then - logger:info("Calling mod callback", callback_name, "for", mod.id) + logger:info('Calling mod callback', callback_name, 'for', mod.id) end local status, message = pcall(mod[callback_name], ...) -- Call the on_pre_load function of the mod if it exists if not status then - logger:warn("Callback", callback_name, "for mod ", mod.id, - "failed: ", message) + logger:warn('Callback', callback_name, 'for mod ', mod.id, 'failed: ', message) else table.insert(mod_returns, {modId = mod.id, result = message}) end @@ -534,12 +505,16 @@ local function installMod(modInfo) if modInfo.present then logger:debug('Mod ' .. modInfo.id .. ' is already present') local modVersion = modInfo.newVersion - if not modInfo.needUpdate then return RESULT.SUCCESS end + if not modInfo.needUpdate then + return RESULT.SUCCESS + end -- remove old mod for i, mod in ipairs(mods) do if mod.id == modId then - if mod.on_disable then mod.on_disable() end + if mod.on_disable then + mod.on_disable() + end table.remove(mods, i) break @@ -560,7 +535,7 @@ local function installMod(modInfo) logger:debug('Downloaded tarball with body ', body) -- decompress the archive in memory - local decompressedData = love.data.decompress("data", "gzip", body) + local decompressedData = love.data.decompress('data', 'gzip', body) local success, result = pcall(tar.unpack, decompressedData) if not success then logger:error('Error decompressing tarball') @@ -575,16 +550,22 @@ local function installMod(modInfo) -- type = "file" | "directory" -- } -- sort the result table so that directories are processed first - table.sort(result, function(a, b) return a.type < b.type end) + table.sort(result, function(a, b) + return a.type < b.type + end) -- check that the downloaded mod contains a main.lua file as well as a manifest.json file local mainLua = false local manifestJson = false for _, file in ipairs(result) do - if file.type == "file" then + if file.type == 'file' then -- file.name is a path, we only want the filename - local _, _, filename = file.name:find(".+/(.+)") - if filename == "main.lua" then mainLua = true end - if filename == "manifest.json" then manifestJson = true end + local _, _, filename = file.name:find('.+/(.+)') + if filename == 'main.lua' then + mainLua = true + end + if filename == 'manifest.json' then + manifestJson = true + end end end if not mainLua then @@ -597,22 +578,24 @@ local function installMod(modInfo) end for _, file in ipairs(result) do -- replace the first part of file.name with the modId - file.name = modId .. file.name:sub(file.name:find("/")) - if file.type == "directory" then - love.filesystem.createDirectory("mods/" .. file.name) - elseif file.type == "file" then - love.filesystem.write("mods/" .. file.name, file.data) + file.name = modId .. file.name:sub(file.name:find('/')) + if file.type == 'directory' then + love.filesystem.createDirectory('mods/' .. file.name) + elseif file.type == 'file' then + love.filesystem.write('mods/' .. file.name, file.data) end end local mod = loadMod(modId) - if mod == nil then return RESULT.MOD_FS_LOAD_ERROR end + if mod == nil then + return RESULT.MOD_FS_LOAD_ERROR + end mods[modId] = mod mods = sortMods(mods) return RESULT.SUCCESS end -buildPaths("", {"mods", "apis", "resources", "localization"}) +buildPaths('', {'mods', 'apis', 'resources', 'localization'}) -- current_game_code = love.filesystem.read(path) buildPaths = nil -- prevent rerunning (i think) @@ -621,35 +604,33 @@ for _, path in ipairs(paths) do current_game_code[path] = love.filesystem.read(path) end -if not love.filesystem.getInfo("mods", "directory") then -- Create mods folder if it doesn't exist - love.filesystem.createDirectory("mods") +if not love.filesystem.getInfo('mods', 'directory') then -- Create mods folder if it doesn't exist + love.filesystem.createDirectory('mods') end -if not love.filesystem.getInfo("logs", "directory") then -- Create logs folder if it doesn't exist - love.filesystem.createDirectory("logs") +if not love.filesystem.getInfo('logs', 'directory') then -- Create logs folder if it doesn't exist + love.filesystem.createDirectory('logs') end -if not love.filesystem.getInfo("apis", "directory") then -- Create apis folder if it doesn't exist - love.filesystem.createDirectory("apis") +if not love.filesystem.getInfo('apis', 'directory') then -- Create apis folder if it doesn't exist + love.filesystem.createDirectory('apis') end -- apis will be loaded first, then mods -mods["dev_console"] = { - id = "dev_console", - name = "Dev Console", - version = "0.6.0", - author = "sbordeyne & UwUDev", - description = { - "Press F2 to open/close the console", - "Use command `help` for a list of ", "available commands and shortcuts" - }, +mods['dev_console'] = { + id = 'dev_console', + name = 'Dev Console', + version = '0.6.0', + author = 'sbordeyne & UwUDev', + description = {'Press F2 to open/close the console', 'Use command `help` for a list of ', + 'available commands and shortcuts'}, enabled = true, on_game_load = function(args) - console.logger:info("Game loaded", args) + console.logger:info('Game loaded', args) for _, arg in ipairs(args) do - local split = splitstring(arg, "=") - if split[0] == "--log-level" then + local split = splitstring(arg, '=') + if split[0] == '--log-level' then console.logger.level = split[1]:upper() console.log_level = split[1]:upper() end @@ -657,36 +638,36 @@ mods["dev_console"] = { logging.saveLogs() end, on_game_quit = function() - console.logger:info("Quitting Balatro...") + console.logger:info('Quitting Balatro...') logging.saveLogs() end, on_error = function(message) - console.logger:error("Error: ", message) + console.logger:error('Error: ', message) -- on error, write all messages to a file logging.saveLogs() end, on_enable = function() - console.logger:debug("Dev Console enabled") + console.logger:debug('Dev Console enabled') contents, size = love.filesystem.read(console.history_path) if contents then - console.logger:trace("History file size", size) - for line in contents:gmatch("[^\r\n]+") do - if line and line ~= "" then + console.logger:trace('History file size', size) + for line in contents:gmatch('[^\r\n]+') do + if line and line ~= '' then table.insert(console.command_history, line) end end end - console.logger:debug("Registering commands") - console:registerCommand("help", function() - console.logger:print("Available commands:") + console.logger:debug('Registering commands') + console:registerCommand('help', function() + console.logger:print('Available commands:') for name, cmd in pairs(console.commands) do if cmd.desc then - console.logger:print(name .. ": " .. cmd.desc) + console.logger:print(name .. ': ' .. cmd.desc) end end return true - end, "Prints a list of available commands", function(current_arg) + end, 'Prints a list of available commands', function(current_arg) local completions = {} for name, _ in pairs(console.commands) do if name:find(current_arg, 1, true) == 1 then @@ -694,66 +675,59 @@ mods["dev_console"] = { end end return completions - end, "Usage: help ") + end, 'Usage: help ') - console:registerCommand("shortcuts", function() - console.logger:print("Available shortcuts:") - console.logger:print("F2: Open/Close the console") - console.logger:print("F4: Toggle debug mode") + console:registerCommand('shortcuts', function() + console.logger:print('Available shortcuts:') + console.logger:print('F2: Open/Close the console') + console.logger:print('F4: Toggle debug mode') if platform.is_mac then - console.logger:print( - "Cmd+C: Copy the current command to the clipboard.") - console.logger:print( - "Cmd+Shift+C: Copies all messages to the clipboard") - console.logger:print( - "Cmd+V: Paste the clipboard into the current command") + console.logger:print('Cmd+C: Copy the current command to the clipboard.') + console.logger:print('Cmd+Shift+C: Copies all messages to the clipboard') + console.logger:print('Cmd+V: Paste the clipboard into the current command') else - console.logger:print( - "Ctrl+C: Copy the current command to the clipboard.") - console.logger:print( - "Ctrl+Shift+C: Copies all messages to the clipboard") - console.logger:print( - "Ctrl+V: Paste the clipboard into the current command") + console.logger:print('Ctrl+C: Copy the current command to the clipboard.') + console.logger:print('Ctrl+Shift+C: Copies all messages to the clipboard') + console.logger:print('Ctrl+V: Paste the clipboard into the current command') end return true - end, "Prints a list of available shortcuts", - function(current_arg) return nil end, - "Usage: shortcuts") + end, 'Prints a list of available shortcuts', function(current_arg) + return nil + end, 'Usage: shortcuts') - console:registerCommand("history", function() - console.logger:print("Command history:") + console:registerCommand('history', function() + console.logger:print('Command history:') for i, cmd in ipairs(console.command_history) do - console.logger:print(i .. ": " .. cmd) + console.logger:print(i .. ': ' .. cmd) end return true - end, "Prints the command history") + end, 'Prints the command history') - console.logger:debug("Registering command: clear") - console:registerCommand("clear", function() + console.logger:debug('Registering command: clear') + console:registerCommand('clear', function() logging.clearLogs() return true - end, "Clear the console") + end, 'Clear the console') - console:registerCommand("exit", function() + console:registerCommand('exit', function() console:toggle() return true - end, "Close the console") + end, 'Close the console') - console:registerCommand("give", function(args) + console:registerCommand('give', function(args) local id = args[1] local c1 = nil - if string.sub(id, 1, 2) == "j_" then + if string.sub(id, 1, 2) == 'j_' then c1 = create_card(nil, G.jokers, nil, 1, true, false, id, nil) else - c1 = create_card(nil, G.consumeables, nil, 1, true, false, id, - nil) + c1 = create_card(nil, G.consumeables, nil, 1, true, false, id, nil) end G.E_MANAGER:add_event(Event({ trigger = 'after', delay = 0.1, func = function() c1:add_to_deck() - if string.sub(id, 1, 2) == "j_" then + if string.sub(id, 1, 2) == 'j_' then G.jokers:emplace(c1) else G.consumeables:emplace(c1) @@ -765,7 +739,7 @@ mods["dev_console"] = { end })) return true - end, "Give an item to the player", function(current_arg) + end, 'Give an item to the player', function(current_arg) local ret = {} for k, _ in pairs(G.P_CENTERS) do if string.find(k, current_arg) == 1 then @@ -775,38 +749,35 @@ mods["dev_console"] = { return ret end) - console:registerCommand("money", function(args) + console:registerCommand('money', function(args) if args[1] and args[2] then local amount = tonumber(args[2]) if amount then - if args[1] == "add" then + if args[1] == 'add' then ease_dollars(amount, true) - console.logger:info( - "Added " .. amount .. " money to the player") - elseif args[1] == "remove" then + console.logger:info('Added ' .. amount .. ' money to the player') + elseif args[1] == 'remove' then ease_dollars(-amount, true) - console.logger:info( - "Removed " .. amount .. " money from the player") - elseif args[1] == "set" then + console.logger:info('Removed ' .. amount .. ' money from the player') + elseif args[1] == 'set' then local currentMoney = G.GAME.dollars local diff = amount - currentMoney ease_dollars(diff, true) - console.logger:info("Set player money to " .. amount) + console.logger:info('Set player money to ' .. amount) else - console.logger:error( - "Invalid operation, use add, remove or set") + console.logger:error('Invalid operation, use add, remove or set') end else - console.logger:error("Invalid amount") + console.logger:error('Invalid amount') return false end else - console.logger:warn("Usage: money ") + console.logger:warn('Usage: money ') return false end return true - end, "Change the player's money", function(current_arg) - local subcommands = {"add", "remove", "set"} + end, 'Change the player\'s money', function(current_arg) + local subcommands = {'add', 'remove', 'set'} for i, v in ipairs(subcommands) do if v:find(current_arg, 1, true) == 1 then return {v} @@ -815,40 +786,36 @@ mods["dev_console"] = { return nil end) - console:registerCommand("discards", function(args) + console:registerCommand('discards', function(args) if args[1] and args[2] then local amount = tonumber(args[2]) if amount then - if args[1] == "add" then + if args[1] == 'add' then ease_discard(amount, true) - console.logger:info( - "Added " .. amount .. " discards to the player") - elseif args[1] == "remove" then + console.logger:info('Added ' .. amount .. ' discards to the player') + elseif args[1] == 'remove' then ease_discard(-amount, true) - console.logger:info( - "Removed " .. amount .. " discards from the player") - elseif args[1] == "set" then - local currentDiscards = - G.GAME.current_round.discards_left + console.logger:info('Removed ' .. amount .. ' discards from the player') + elseif args[1] == 'set' then + local currentDiscards = G.GAME.current_round.discards_left local diff = amount - currentDiscards ease_discard(diff, true) - console.logger:info("Set player discards to " .. amount) + console.logger:info('Set player discards to ' .. amount) else - console.logger:error( - "Invalid operation, use add, remove or set") + console.logger:error('Invalid operation, use add, remove or set') return false end else - console.logger:error("Invalid amount") + console.logger:error('Invalid amount') return false end else - console.logger:warn("Usage: discards ") + console.logger:warn('Usage: discards ') return false end return true - end, "Change the player's discards", function(current_arg) - local subcommands = {"add", "remove", "set"} + end, 'Change the player\'s discards', function(current_arg) + local subcommands = {'add', 'remove', 'set'} for i, v in ipairs(subcommands) do if v:find(current_arg, 1, true) == 1 then return {v} @@ -857,39 +824,36 @@ mods["dev_console"] = { return nil end) - console:registerCommand("hands", function(args) + console:registerCommand('hands', function(args) if args[1] and args[2] then local amount = tonumber(args[2]) if amount then - if args[1] == "add" then + if args[1] == 'add' then ease_hands_played(amount, true) - console.logger:info( - "Added " .. amount .. " hands to the player") - elseif args[1] == "remove" then + console.logger:info('Added ' .. amount .. ' hands to the player') + elseif args[1] == 'remove' then ease_hands_played(-amount, true) - console.logger:info( - "Removed " .. amount .. " hands from the player") - elseif args[1] == "set" then + console.logger:info('Removed ' .. amount .. ' hands from the player') + elseif args[1] == 'set' then local currentHands = G.GAME.current_round.hands_left local diff = amount - currentHands ease_hands_played(diff, true) - console.logger:info("Set player hands to " .. amount) + console.logger:info('Set player hands to ' .. amount) else - console.logger:error( - "Invalid operation, use add, remove or set") + console.logger:error('Invalid operation, use add, remove or set') return false end else - console.logger:error("Invalid amount") + console.logger:error('Invalid amount') return false end else - console.logger:warn("Usage: hands ") + console.logger:warn('Usage: hands ') return false end return true - end, "Change the player's remaining hands", function(current_arg) - local subcommands = {"add", "remove", "set"} + end, 'Change the player\'s remaining hands', function(current_arg) + local subcommands = {'add', 'remove', 'set'} for i, v in ipairs(subcommands) do if v:find(current_arg, 1, true) == 1 then return {v} @@ -898,17 +862,15 @@ mods["dev_console"] = { return nil end) - console:registerCommand("luamod", function(args) + console:registerCommand('luamod', function(args) if args[1] then local modId = args[1] if isModPresent(modId) then local mod = mods[modId] - if mod.enabled and mod.on_disable and type(mod.on_disable) == - "function" then + if mod.enabled and mod.on_disable and type(mod.on_disable) == 'function' then local success, result = pcall(mod.on_disable) if not success then - console.logger:error( - "Error disabling mod: " .. modId) + console.logger:error('Error disabling mod: ' .. modId) console.logger:error(result) return false end @@ -922,24 +884,23 @@ mods["dev_console"] = { if mod.on_enable and type(mod.on_enable) == 'function' then local status, message = pcall(mod.on_enable) if not status then - console.logger:error( - "Error enabling mod: " .. modId) + console.logger:error('Error enabling mod: ' .. modId) console.logger:error(message) return false end end end - console.logger:info("Reloaded mod: " .. modId) + console.logger:info('Reloaded mod: ' .. modId) else - console.logger:error("Mod not found: " .. modId) + console.logger:error('Mod not found: ' .. modId) return false end else - console.logger:error("Usage: luamod ") + console.logger:error('Usage: luamod ') return false end return true - end, "Reload a mod using its id", function(current_arg) + end, 'Reload a mod using its id', function(current_arg) local completions = {} for modId, _ in pairs(mods) do if modId:find(current_arg, 1, true) == 1 then @@ -947,73 +908,62 @@ mods["dev_console"] = { end end return completions - end, "Usage: luamod ") + end, 'Usage: luamod ') - console:registerCommand("sandbox", function(args) + console:registerCommand('sandbox', function(args) G:sandbox() return true - end, "Goes to the sandbox stage", function(current_arg) + end, 'Goes to the sandbox stage', function(current_arg) return nil - end, "Usage: sandbox") + end, 'Usage: sandbox') - console:registerCommand("luarun", function(args) - local code = table.concat(args, " ") + console:registerCommand('luarun', function(args) + local code = table.concat(args, ' ') local func, err = load(code) if func then - console.logger:info("Lua code executed successfully") + console.logger:info('Lua code executed successfully') console.logger:print(func()) return true else - console.logger:error("Error loading lua code: ", err) + console.logger:error('Error loading lua code: ', err) return false end - end, "Run lua code in the context of the game", - function(current_arg) return nil end, - "Usage: luarun ") + end, 'Run lua code in the context of the game', function(current_arg) + return nil + end, 'Usage: luarun ') - console:registerCommand("installmod", function(args) + console:registerCommand('installmod', function(args) local url = args[1] - local modInfo = { - id = "testmod", - url = url, - present = false, - needUpdate = true - } + local modInfo = {id = 'testmod', url = url, present = false, needUpdate = true} local result = installModFromTar(modInfo) if result == RESULT.SUCCESS then - console.logger:info("Mod installed successfully") + console.logger:info('Mod installed successfully') return true else - console.logger:error("Error installing mod: ", result) + console.logger:error('Error installing mod: ', result) return false end - end, "Install a mod from a tarball", - function(current_arg) return nil end, - "Usage: installmod ") - - console:registerCommand("booster", function(args) - local pack = get_pack("shop_pack", args[1] or nil) - G.FUNCS.use_card({ - config = {ref_table = Card(0, 0, 0, 0, pack, pack)} - }) - end, - "Generate a booster pack (random if no argument, otherwise specify type)", - function(current_arg) - local subcommands = { - "Standard", "Spectral", "Arcana", "Celestial", "Buffoon" - } + end, 'Install a mod from a tarball', function(current_arg) + return nil + end, 'Usage: installmod ') + + console:registerCommand('booster', function(args) + local pack = get_pack('shop_pack', args[1] or nil) + G.FUNCS.use_card({config = {ref_table = Card(0, 0, 0, 0, pack, pack)}}) + end, 'Generate a booster pack (random if no argument, otherwise specify type)', function(current_arg) + local subcommands = {'Standard', 'Spectral', 'Arcana', 'Celestial', 'Buffoon'} for i, v in ipairs(subcommands) do if v:find(current_arg, 1, true) == 1 then return {v} end end return nil - end, "Usage: booster [Standard/Spectral/Arcana/Celestial/Buffoon]") + end, 'Usage: booster [Standard/Spectral/Arcana/Celestial/Buffoon]') - console:registerCommand("enhance", function(args) + console:registerCommand('enhance', function(args) local arg = args[1] or nil if arg == nil then - logger:info("No enhancement specified") + logger:info('No enhancement specified') return end @@ -1040,79 +990,63 @@ mods["dev_console"] = { if G.discard then table.insert(tables, G.discard.highlighted) end - if G.deck then table.insert(tables, G.deck.highlighted) end - if G.hand then table.insert(tables, G.hand.highlighted) end - if G.play then table.insert(tables, G.play.highlighted) end + if G.deck then + table.insert(tables, G.deck.highlighted) + end + if G.hand then + table.insert(tables, G.hand.highlighted) + end + if G.play then + table.insert(tables, G.play.highlighted) + end for i, t in ipairs(tables) do for j, card in ipairs(t) do if card.config then - logger:info("Enhancing " .. card.config.center.key) + logger:info('Enhancing ' .. card.config.center.key) for k, enhancement in pairs(args) do - for k, v in pairs(G.P_CENTER_POOLS["Default"]) do - local name = string.gsub(v.label, " ", "") - if enhancement == v.key or - string.lower(enhancement) == - string.lower(name) then - if (card.config.center.set == "Default" or - card.config.center.set == "Enhanced") then - card:set_ability(G.P_CENTERS[v.key], - nil, false) - logger:info( - "Card set to " .. v.label .. " (" .. - card.config.center.key .. ").") + for k, v in pairs(G.P_CENTER_POOLS['Default']) do + local name = string.gsub(v.label, ' ', '') + if enhancement == v.key or string.lower(enhancement) == string.lower(name) then + if (card.config.center.set == 'Default' or card.config.center.set == 'Enhanced') then + card:set_ability(G.P_CENTERS[v.key], nil, false) + logger:info('Card set to ' .. v.label .. ' (' .. card.config.center.key .. ').') else - logger:info( - "Only standard cards should be set to " .. - v.label .. " (" .. - card.config.center.key .. ").") + logger:info('Only standard cards should be set to ' .. v.label .. ' (' .. + card.config.center.key .. ').') end end end - for k, v in pairs(G.P_CENTER_POOLS["Enhanced"]) do - local name = string.gsub(v.label, " ", "") - if enhancement == v.key or - string.lower(enhancement) == - string.lower(name) then - if (card.config.center.set == "Default" or - card.config.center.set == "Enhanced") then - card:set_ability(G.P_CENTERS[v.key], - nil, false) - logger:info( - "Card set to " .. v.label .. " (" .. - card.config.center.key .. ").") + for k, v in pairs(G.P_CENTER_POOLS['Enhanced']) do + local name = string.gsub(v.label, ' ', '') + if enhancement == v.key or string.lower(enhancement) == string.lower(name) then + if (card.config.center.set == 'Default' or card.config.center.set == 'Enhanced') then + card:set_ability(G.P_CENTERS[v.key], nil, false) + logger:info('Card set to ' .. v.label .. ' (' .. card.config.center.key .. ').') else - logger:info( - "Only standard cards should be set to " .. - v.label .. " (" .. - card.config.center.key .. ").") + logger:info('Only standard cards should be set to ' .. v.label .. ' (' .. + card.config.center.key .. ').') end end end - for k, v in pairs(G.P_CENTER_POOLS["Edition"]) do - local name = string.gsub(v.name, " ", "") - if enhancement == v.key or - string.lower(enhancement) == - string.lower(name) then + for k, v in pairs(G.P_CENTER_POOLS['Edition']) do + local name = string.gsub(v.name, ' ', '') + if enhancement == v.key or string.lower(enhancement) == string.lower(name) then local editionKey = string.sub(v.key, 3) card:set_edition({[editionKey] = true}, true) - logger:info( - "Card set to " .. v.name .. " edition (" .. - card.config.center.key .. ").") + logger:info('Card set to ' .. v.name .. ' edition (' .. card.config.center.key .. + ').') end end - for k, v in pairs(G.P_CENTER_POOLS["Seal"]) do - if string.lower(enhancement) == - string.lower(v.key .. "Seal") then + for k, v in pairs(G.P_CENTER_POOLS['Seal']) do + if string.lower(enhancement) == string.lower(v.key .. 'Seal') then card:set_seal(v.key, true) - logger:info( - "Added " .. v.key .. " Seal to card (" .. - card.config.center.key .. ").") + logger:info('Added ' .. v.key .. ' Seal to card (' .. card.config.center.key .. ').') end end @@ -1122,30 +1056,30 @@ mods["dev_console"] = { end end end, - "Add one or more enhancements to selected cards. Can use key or name (e.g. c_base or BaseCard, e_negative or Negative). Must add Seal to seals (e.g. RedSeal, BlueSeal)", + 'Add one or more enhancements to selected cards. Can use key or name (e.g. c_base or BaseCard, e_negative or Negative). Must add Seal to seals (e.g. RedSeal, BlueSeal)', function(current_arg, previous_arg) local subcommands = {} - for k, v in pairs(G.P_CENTER_POOLS["Default"]) do - local name = string.gsub(v.label, " ", "") + for k, v in pairs(G.P_CENTER_POOLS['Default']) do + local name = string.gsub(v.label, ' ', '') table.insert(subcommands, v.key) table.insert(subcommands, name) end - for k, v in pairs(G.P_CENTER_POOLS["Enhanced"]) do - local name = string.gsub(v.label, " ", "") + for k, v in pairs(G.P_CENTER_POOLS['Enhanced']) do + local name = string.gsub(v.label, ' ', '') table.insert(subcommands, v.key) table.insert(subcommands, name) end - for k, v in pairs(G.P_CENTER_POOLS["Edition"]) do - local name = string.gsub(v.name, " ", "") + for k, v in pairs(G.P_CENTER_POOLS['Edition']) do + local name = string.gsub(v.name, ' ', '') table.insert(subcommands, v.key) table.insert(subcommands, name) end - for k, v in pairs(G.P_CENTER_POOLS["Seal"]) do - table.insert(subcommands, v.key .. "Seal") + for k, v in pairs(G.P_CENTER_POOLS['Seal']) do + table.insert(subcommands, v.key .. 'Seal') end local completions = {} @@ -1156,27 +1090,27 @@ mods["dev_console"] = { end return completions end, - "Usage: enhance [arg2] [arg3] ...") + 'Usage: enhance [arg2] [arg3] ...') - console.logger:debug("Dev Console on_enable completed") + console.logger:debug('Dev Console on_enable completed') end, on_disable = function() - console.removeCommand("help") - console.removeCommand("shortcuts") - console.removeCommand("history") - console.removeCommand("clear") - console.removeCommand("exit") - console.removeCommand("quit") - console.removeCommand("give") - console.removeCommand("money") - console.removeCommand("discards") - console.removeCommand("hands") - console.removeCommand("booster") - console.removeCommand("enhance") - console.logger:debug("Dev Console disabled") + console.removeCommand('help') + console.removeCommand('shortcuts') + console.removeCommand('history') + console.removeCommand('clear') + console.removeCommand('exit') + console.removeCommand('quit') + console.removeCommand('give') + console.removeCommand('money') + console.removeCommand('discards') + console.removeCommand('hands') + console.removeCommand('booster') + console.removeCommand('enhance') + console.logger:debug('Dev Console disabled') end, on_key_pressed = function(key_name) - if key_name == "f2" then + if key_name == 'f2' then console:toggle() return true end @@ -1185,24 +1119,22 @@ mods["dev_console"] = { return true end - if key_name == "f4" then + if key_name == 'f4' then G.DEBUG = not G.DEBUG if G.DEBUG then - console.logger:info("Debug mode enabled") + console.logger:info('Debug mode enabled') else - console.logger:info("Debug mode disabled") + console.logger:info('Debug mode disabled') end end return false end, on_post_render = function() - console.max_lines = math.floor(love.graphics.getHeight() / - console.line_height) - 5 -- 5 lines of bottom padding + console.max_lines = math.floor(love.graphics.getHeight() / console.line_height) - 5 -- 5 lines of bottom padding local font = love.graphics.getFont() if console.is_open then love.graphics.setColor(0, 0, 0, 0.3) - love.graphics.rectangle("fill", 0, 0, love.graphics.getWidth(), - love.graphics.getHeight()) + love.graphics.rectangle('fill', 0, 0, love.graphics.getWidth(), love.graphics.getHeight()) local messagesToDisplay = console:getMessagesToDisplay() local i = 1 for _, message in ipairs(messagesToDisplay) do @@ -1210,8 +1142,7 @@ mods["dev_console"] = { love.graphics.setColor(r, g, b, 1) local formattedMessage = message:formatted() if font:getWidth(formattedMessage) > love.graphics.getWidth() then - local lines = console:wrapText(formattedMessage, - love.graphics.getWidth()) + local lines = console:wrapText(formattedMessage, love.graphics.getWidth()) for _, line in ipairs(lines) do love.graphics.print(line, 10, 10 + i * 20) i = i + 1 @@ -1226,37 +1157,37 @@ mods["dev_console"] = { end end, on_key_released = function(key_name) - if key_name == "capslock" then + if key_name == 'capslock' then console.modifiers.capslock = not console.modifiers.capslock console:modifiersListener() return end - if key_name == "scrolllock" then + if key_name == 'scrolllock' then console.modifiers.scrolllock = not console.modifiers.scrolllock console:modifiersListener() return end - if key_name == "numlock" then + if key_name == 'numlock' then console.modifiers.numlock = not console.modifiers.numlock console:modifiersListener() return end - if key_name == "lalt" or key_name == "ralt" then + if key_name == 'lalt' or key_name == 'ralt' then console.modifiers.alt = false console:modifiersListener() return false end - if key_name == "lctrl" or key_name == "rctrl" then + if key_name == 'lctrl' or key_name == 'rctrl' then console.modifiers.ctrl = false console:modifiersListener() return false end - if key_name == "lshift" or key_name == "rshift" then + if key_name == 'lshift' or key_name == 'rshift' then console.modifiers.shift = false console:modifiersListener() return false end - if key_name == "lgui" or key_name == "rgui" then + if key_name == 'lgui' or key_name == 'rgui' then console.modifiers.meta = false console:modifiersListener() return false @@ -1282,14 +1213,14 @@ mods["dev_console"] = { local function sortMods(mods) logger:trace('Sorting mods', utils.keys(mods)) local graph = {} - for modId, mod in pairs(mods) do graph[modId] = {before = {}} end + for modId, mod in pairs(mods) do + graph[modId] = {before = {}} + end logger:trace('Graph generated', graph) for modId, mod in pairs(mods) do for i, before in ipairs(mod.load_before or {}) do -- load_before is a list of mod ids, if its nil, use an empty table to avoid a crash if not graph[before] then - logger:error('Mod ', mod.id, - ' has a load_before field that references a non-existent mod: ', - before) + logger:error('Mod ', mod.id, ' has a load_before field that references a non-existent mod: ', before) return nil end graph[modId].before[before] = true -- we set to true just because we want a table behaving like a set() instead of an array @@ -1299,9 +1230,7 @@ local function sortMods(mods) -- this is equivalent to the other mod being loaded before the current mod -- so we add an edge from the other mod to the current mod if not graph[after] then - logger:error('Mod ', mod.id, - ' has a load_after field that references a non-existent mod: ', - after) + logger:error('Mod ', mod.id, ' has a load_after field that references a non-existent mod: ', after) return nil end graph[after].before[modId] = true -- we set to true just because we want a table behaving like a set() instead of an array @@ -1311,27 +1240,33 @@ local function sortMods(mods) local sorted = {} local visited = {} local function visit(node) - logger:trace("Visiting node ", node) - if visited[node] == "permanent" then - logger:trace("Node ", node, " already visited") + logger:trace('Visiting node ', node) + if visited[node] == 'permanent' then + logger:trace('Node ', node, ' already visited') return true end - if visited[node] == "temporary" then + if visited[node] == 'temporary' then logger:error('Mod ', node, ' has a circular dependency') return false end - visited[node] = "temporary" + visited[node] = 'temporary' for other, _ in pairs(graph[node].before) do - if not visit(other) then return false end + if not visit(other) then + return false + end end table.insert(sorted, node) - logger:trace("Inserted node ", node, " in sorted list", sorted) - logger:trace("Marking node ", node, " as visited") - visited[node] = "permanent" + logger:trace('Inserted node ', node, ' in sorted list', sorted) + logger:trace('Marking node ', node, ' as visited') + visited[node] = 'permanent' return true end - logger:trace("Starting to visit nodes") - for node, _ in pairs(graph) do if not visited[node] then visit(node) end end + logger:trace('Starting to visit nodes') + for node, _ in pairs(graph) do + if not visited[node] then + visit(node) + end + end local sortedMods = {} local modCount = #sorted -- we need to keep the mapping between the mod id and the mod object @@ -1343,7 +1278,7 @@ local function sortMods(mods) mod.order = modCount - i sortedMods[modId] = mod end - logger:trace("Built sorted mods", utils.keys(sortedMods)) + logger:trace('Built sorted mods', utils.keys(sortedMods)) return sortedMods end diff --git a/src/console.lua b/src/console.lua index 146e754..cc81851 100644 --- a/src/console.lua +++ b/src/console.lua @@ -1,21 +1,21 @@ -local logging = require("logging") -local platform = require("platform") -local utf8 = require("utf8") -local math = require("math") +local logging = require('logging') +local platform = require('platform') +local utf8 = require('utf8') +local math = require('math') -local logger = logging.getLogger("console") +local logger = logging.getLogger('console') return { logger = logger, - log_level = "INFO", + log_level = 'INFO', is_open = false, - cmd = "> ", + cmd = '> ', line_height = 20, max_lines = love.graphics.getHeight() / 20, start_line_offset = 1, history_index = 0, command_history = {}, - history_path = "dev_console.history", + history_path = 'dev_console.history', modifiers = { capslock = false, scrolllock = false, @@ -40,13 +40,16 @@ return { end end, longestCommonPrefix = function(self, strings) - if #strings == 0 then return "" end + if #strings == 0 then + return '' + end local prefix = strings[1] for i = 2, #strings do local str = strings[i] local j = 1 - while j <= #prefix and j <= #str and prefix:sub(j, j) == - str:sub(j, j) do j = j + 1 end + while j <= #prefix and j <= #str and prefix:sub(j, j) == str:sub(j, j) do + j = j + 1 + end prefix = prefix:sub(1, j - 1) end return prefix @@ -55,10 +58,12 @@ return { local command = self.cmd:sub(3) -- remove the "> " prefix local cmd = {} -- split command into parts - for part in command:gmatch("%S+") do table.insert(cmd, part) end + for part in command:gmatch('%S+') do + table.insert(cmd, part) + end if #cmd == 0 then -- no command typed, do nothing (no completions possible) - logger:trace("No command typed") + logger:trace('No command typed') return nil end local completions = {} @@ -77,12 +82,10 @@ return { local previousArgs = cmd local current_arg = table.remove(previousArgs) if command then - completions = command.autocomplete(current_arg, previousArgs) or - {} + completions = command.autocomplete(current_arg, previousArgs) or {} end end - logger:trace("Autocomplete matches: " .. #completions .. " " .. - table.concat(completions, ", ")) + logger:trace('Autocomplete matches: ' .. #completions .. ' ' .. table.concat(completions, ', ')) if #completions == 0 then -- no completions found return nil @@ -94,12 +97,24 @@ return { end end, getMessageColor = function(self, message) - if message.level == "PRINT" then return 1, 1, 1 end - if message.level == "INFO" then return 0, 0.9, 1 end - if message.level == "WARN" then return 1, 0.5, 0 end - if message.level == "ERROR" then return 1, 0, 0 end - if message.level == "DEBUG" then return 0.16, 0, 1 end - if message.level == "TRACE" then return 1, 1, 1 end + if message.level == 'PRINT' then + return 1, 1, 1 + end + if message.level == 'INFO' then + return 0, 0.9, 1 + end + if message.level == 'WARN' then + return 1, 0.5, 0 + end + if message.level == 'ERROR' then + return 1, 0, 0 + end + if message.level == 'DEBUG' then + return 0.16, 0, 1 + end + if message.level == 'TRACE' then + return 1, 1, 1 + end return 1, 1, 1 end, getFilteredMessages = function(self) @@ -120,8 +135,7 @@ return { local all_messages = {} for _, message in ipairs(base_messages) do - local wrappedLines = self:wrapText(message.text, - love.graphics.getWidth() - 20) + local wrappedLines = self:wrapText(message.text, love.graphics.getWidth() - 20) for _, line in ipairs(wrappedLines) do table.insert(all_messages, { text = line, @@ -129,14 +143,18 @@ return { name = message.name, time = message.time, level_numeric = message.level_numeric, - formatted = function() return line end + formatted = function() + return line + end }) end end while textLength < self.max_lines do local index = #all_messages - i + self.start_line_offset - if index < 1 then break end + if index < 1 then + break + end local message = all_messages[index] if message then table.insert(text, message) @@ -156,12 +174,14 @@ return { local nLinesToPad = #text - self.max_lines for i = 1, nLinesToPad do table.insert(text, { - text = "", - level = "PRINT", - name = "", + text = '', + level = 'PRINT', + name = '', time = 0, level_numeric = 1000, - formatted = function() return "" end + formatted = function() + return '' + end }) end return text @@ -170,7 +190,7 @@ return { -- disable text input if ctrl or cmd is pressed -- this is to fallback to love.keypressed when a modifier is pressed that can -- link to a command (like ctrl+c, ctrl+v, etc) - self.logger:trace("modifiers", self.modifiers) + self.logger:trace('modifiers', self.modifiers) if self.modifiers.ctrl or self.modifiers.meta then love.textinput = nil else @@ -181,22 +201,20 @@ return { end, typeKey = function(self, key_name) -- cmd+shift+C on mac, ctrl+shift+C on windows/linux - if key_name == "c" and - ((platform.is_mac and self.modifiers.meta and self.modifiers.shift) or - (not platform.is_mac and self.modifiers.ctrl and - self.modifiers.shift)) then + if key_name == 'c' and ((platform.is_mac and self.modifiers.meta and self.modifiers.shift) or + (not platform.is_mac and self.modifiers.ctrl and self.modifiers.shift)) then local messages = self:getFilteredMessages() - local text = "" + local text = '' for _, message in ipairs(messages) do - text = text .. message:formatted() .. "\n" + text = text .. message:formatted() .. '\n' end love.system.setClipboardText(text) return end -- cmd+C on mac, ctrl+C on windows/linux - if key_name == "c" and ((platform.is_mac and self.modifiers.meta) or - (not platform.is_mac and self.modifiers.ctrl)) then - if self.cmd:sub(3) == "" then + if key_name == 'c' and + ((platform.is_mac and self.modifiers.meta) or (not platform.is_mac and self.modifiers.ctrl)) then + if self.cmd:sub(3) == '' then -- do nothing if the buffer is empty return end @@ -204,82 +222,68 @@ return { return end -- cmd+V on mac, ctrl+V on windows/linux - if key_name == "v" and ((platform.is_mac and self.modifiers.meta) or - (not platform.is_mac and self.modifiers.ctrl)) then + if key_name == 'v' and + ((platform.is_mac and self.modifiers.meta) or (not platform.is_mac and self.modifiers.ctrl)) then self.cmd = self.cmd .. love.system.getClipboardText() return end - if key_name == "escape" then + if key_name == 'escape' then -- close the console self:toggle() return end -- Delete the current command, on mac it's cmd+backspace - if key_name == "delete" or - (platform.is_mac and self.modifiers.meta and key_name == "backspace") then - self.cmd = "> " + if key_name == 'delete' or (platform.is_mac and self.modifiers.meta and key_name == 'backspace') then + self.cmd = '> ' return end - if key_name == "end" or - (platform.is_mac and key_name == "right" and self.modifiers.meta) then + if key_name == 'end' or (platform.is_mac and key_name == 'right' and self.modifiers.meta) then -- move text to the most recent (bottom) self.start_line_offset = self.max_lines return end - if key_name == "home" or - (platform.is_mac and key_name == "left" and self.modifiers.meta) then + if key_name == 'home' or (platform.is_mac and key_name == 'left' and self.modifiers.meta) then -- move text to the oldest (top) local messages = self:getFilteredMessages() self.start_line_offset = self.max_lines - #messages return end - if key_name == "pagedown" or - (platform.is_mac and key_name == "down" and self.modifiers.meta) then + if key_name == 'pagedown' or (platform.is_mac and key_name == 'down' and self.modifiers.meta) then -- move text down by max_lines - self.start_line_offset = math.min( - self.start_line_offset + self.max_lines, - self.max_lines) + self.start_line_offset = math.min(self.start_line_offset + self.max_lines, self.max_lines) return end - if key_name == "pageup" or - (platform.is_mac and key_name == "up" and self.modifiers.meta) then + if key_name == 'pageup' or (platform.is_mac and key_name == 'up' and self.modifiers.meta) then -- move text up by max_lines local messages = self:getFilteredMessages() - self.start_line_offset = math.max( - self.start_line_offset - self.max_lines, - self.max_lines - #messages) + self.start_line_offset = math.max(self.start_line_offset - self.max_lines, self.max_lines - #messages) return end - if key_name == "up" then + if key_name == 'up' then -- move to the next command in the history (in reverse order of insertion) - self.history_index = math.min(self.history_index + 1, - #self.command_history) + self.history_index = math.min(self.history_index + 1, #self.command_history) if self.history_index == 0 then - self.cmd = "> " + self.cmd = '> ' return end - self.cmd = "> " .. - self.command_history[#self.command_history - - self.history_index + 1] + self.cmd = '> ' .. self.command_history[#self.command_history - self.history_index + 1] return end - if key_name == "down" then + if key_name == 'down' then -- move to the previous command in the history (in reverse order of insertion) self.history_index = math.max(self.history_index - 1, 0) if self.history_index == 0 then - self.cmd = "> " + self.cmd = '> ' return end - self.cmd = "> " .. - self.command_history[#self.command_history - - self.history_index + 1] + self.cmd = '> ' .. self.command_history[#self.command_history - self.history_index + 1] return end - if key_name == "tab" then + if key_name == 'tab' then local completion = self:tryAutocomplete() if completion then -- get the last part of the console command - local lastPart = self.cmd:match("%S+$") + local lastPart = self.cmd:match('%S+$') if lastPart == nil then -- cmd ends with a space, so we stop the completion return end @@ -288,28 +292,28 @@ return { end return end - if key_name == "lalt" or key_name == "ralt" then + if key_name == 'lalt' or key_name == 'ralt' then self.modifiers.alt = true self:modifiersListener() return end - if key_name == "lctrl" or key_name == "rctrl" then + if key_name == 'lctrl' or key_name == 'rctrl' then self.modifiers.ctrl = true self:modifiersListener() return end - if key_name == "lshift" or key_name == "rshift" then + if key_name == 'lshift' or key_name == 'rshift' then self.modifiers.shift = true self:modifiersListener() return end - if key_name == "lgui" or key_name == "rgui" then + if key_name == 'lgui' or key_name == 'rgui' then -- windows key / meta / cmd key (on macos) self.modifiers.meta = true self:modifiersListener() return end - if key_name == "backspace" then + if key_name == 'backspace' then if #self.cmd > 2 then local byteoffset = utf8.offset(self.cmd, -1) if byteoffset then @@ -320,15 +324,17 @@ return { end return end - if key_name == "return" or key_name == "kpenter" then + if key_name == 'return' or key_name == 'kpenter' then self.logger:print(self.cmd) local cmdName = self.cmd:sub(3) - cmdName = cmdName:match("%S+") - if cmdName == nil then return end + cmdName = cmdName:match('%S+') + if cmdName == nil then + return + end local args = {} local argString = self.cmd:sub(3 + #cmdName + 1) if argString then - for arg in argString:gmatch("%S+") do + for arg in argString:gmatch('%S+') do table.insert(args, arg) end end @@ -336,30 +342,29 @@ return { if self.commands[cmdName] then success = self.commands[cmdName].call(args) else - self.logger:error("Command not found: " .. cmdName) + self.logger:error('Command not found: ' .. cmdName) end if success then -- only add the command to the history if it was successful self:addToHistory(self.cmd:sub(3)) end - self.cmd = "> " + self.cmd = '> ' return end end, addToHistory = function(self, command) - if command == nil or command == "" then return end + if command == nil or command == '' then + return + end table.insert(self.command_history, command) self.history_index = 0 - local success, errormsg = love.filesystem.append(self.history_path, - command .. "\n") + local success, errormsg = love.filesystem.append(self.history_path, command .. '\n') if not success then - self.logger:warn("Error appending ", command, " to history file: ", - errormsg) - success, errormsg = love.filesystem.write(self.history_path, - command .. "\n") + self.logger:warn('Error appending ', command, ' to history file: ', errormsg) + success, errormsg = love.filesystem.write(self.history_path, command .. '\n') if not success then - self.logger:error("Error writing to history file: ", errormsg) + self.logger:error('Error writing to history file: ', errormsg) end end end, @@ -369,69 +374,60 @@ return { -- @param short_description: string, a short description of the command -- @param autocomplete: function(current_arg: string), a function that returns a list of possible completions for the current argument -- @param usage: string, a string describing the usage of the command (longer, more detailed description of the command's usage) - registerCommand = function(self, name, callback, short_description, - autocomplete, usage) + registerCommand = function(self, name, callback, short_description, autocomplete, usage) if name == nil then - self.logger:error("registerCommand -- name is required") + self.logger:error('registerCommand -- name is required') end if callback == nil then - self.logger:error( - "registerCommand -- callback is required on command", name) + self.logger:error('registerCommand -- callback is required on command', name) end - if type(callback) ~= "function" then - self.logger:error( - "registerCommand -- callback must be a function on command", - name) + if type(callback) ~= 'function' then + self.logger:error('registerCommand -- callback must be a function on command', name) end - if name == nil or callback == nil or type(callback) ~= "function" then + if name == nil or callback == nil or type(callback) ~= 'function' then return end if short_description == nil then self.logger:warn( - "registerCommand -- no description provided, please provide a description for the `help` command") - short_description = "No help provided" + 'registerCommand -- no description provided, please provide a description for the `help` command') + short_description = 'No help provided' + end + if usage == nil then + usage = short_description end - if usage == nil then usage = short_description end if autocomplete == nil then autocomplete = function(current_arg, previous_args) return nil end end - if type(autocomplete) ~= "function" then - self.logger:warn( - "registerCommand -- autocomplete must be a function for command: ", - name) + if type(autocomplete) ~= 'function' then + self.logger:warn('registerCommand -- autocomplete must be a function for command: ', name) autocomplete = function(current_arg, previous_args) return nil end end if self.commands[name] then - self.logger:warn("Command " .. name .. " already exists") + self.logger:warn('Command ' .. name .. ' already exists') return end - self.logger:info("Registering command: ", name) - self.commands[name] = { - call = callback, - desc = short_description, - autocomplete = autocomplete, - usage = usage - } + self.logger:info('Registering command: ', name) + self.commands[name] = {call = callback, desc = short_description, autocomplete = autocomplete, usage = usage} end, removeCommand = function(self, cmd_name) if cmd_name == nil then - self.logger:error("removeCommand -- cmd_name is required") + self.logger:error('removeCommand -- cmd_name is required') end if self.commands[cmd_name] == nil then - self.logger:error("removeCommand -- command not found: ", cmd_name) + self.logger:error('removeCommand -- command not found: ', cmd_name) return end self.commands[cmd_name] = nil end, wrapText = function(self, message, screenWidth) local lines = {} - local line = "" - for word in message:gmatch("%S+") do - local testLine = line .. " " .. word + local line = '' + for word in message:gmatch('%S+') do + local testLine = line .. ' ' .. word if love.graphics.getFont():getWidth(testLine) > screenWidth then table.insert(lines, line) line = word From 6da2f2c68e749393627c0361a42be253ffd42644 Mon Sep 17 00:00:00 2001 From: Zei33 Date: Sat, 1 Jun 2024 00:36:35 +1000 Subject: [PATCH 11/15] Fixed the issue with scrolling when there are long wrapped text strings logged. --- src/logging.lua | 108 ++++++++++++++++++++++-------------------------- 1 file changed, 49 insertions(+), 59 deletions(-) diff --git a/src/logging.lua b/src/logging.lua index 85b3d4e..26a6d2f 100644 --- a/src/logging.lua +++ b/src/logging.lua @@ -5,73 +5,64 @@ local utils = require('utils') LOGGERS = {} START_TIME = socket.gettime() -- in seconds with ms precision -local _MODULE = { - _VERSION = "0.1.0", - LOGGERS = LOGGERS, -} +local _MODULE = {_VERSION = '0.1.0', LOGGERS = LOGGERS} local function createLogger(name, lvl) - local log_levels = { - TRACE = 10, - DEBUG = 20, - INFO = 30, - WARN = 40, - ERROR = 50, - PRINT = 1000, - } + local log_levels = {TRACE = 10, DEBUG = 20, INFO = 30, WARN = 40, ERROR = 50, PRINT = 1000} return { - name=name, - log_levels=log_levels, - level=lvl or "INFO", - numeric_level=log_levels[lvl] or 30, - messages={}, - log=function(self, level, ...) + name = name, + log_levels = log_levels, + level = lvl or 'INFO', + numeric_level = log_levels[lvl] or 30, + messages = {}, + log = function(self, level, ...) local args = {...} - local text = "" - if not love.filesystem.getInfo("logs/" .. generateDateTime(START_TIME) .. ".log") then - love.filesystem.write("logs/" .. generateDateTime(START_TIME) .. ".log", "") + local text = '' + if not love.filesystem.getInfo('logs/' .. generateDateTime(START_TIME) .. '.log') then + love.filesystem.write('logs/' .. generateDateTime(START_TIME) .. '.log', '') end for i, v in ipairs(args) do - text = text .. utils.stringify(v) .. " " + text = text .. utils.stringify(v) .. ' ' end local message = { - level=level, - level_numeric=self.log_levels[level] or 0, - text=text, - time=socket.gettime(), - name=self.name, - formatted=function(self, dump) - if self.level == "PRINT" and not dump then + level = level, + level_numeric = self.log_levels[level] or 0, + text = text, + time = socket.gettime(), + name = self.name, + formatted = function(self, dump) + if self.level == 'PRINT' and not dump then return self.text end if dump then - return string.format("%s [%s] - %s :: %s", generateDateTime(self.time), self.name, self.level, self.text) + return string.format('%s [%s] - %s :: %s', generateDateTime(self.time), self.name, self.level, + self.text) end - return string.format("[%s] - %s :: %s", self.name, self.level, self.text) - end, + return string.format('[%s] - %s :: %s', self.name, self.level, self.text) + end } table.insert(self.messages, message) - love.filesystem.append("logs/" .. generateDateTime(START_TIME) .. ".log", message:formatted(true) .. "\n") - love.filesystem.append("console.txt", message:formatted(true) .. "\n") - end, - info=function(self, ...) - self:log("INFO", ...) + love.filesystem.append('logs/' .. generateDateTime(START_TIME) .. '.log', message:formatted(true) .. '\n') + love.filesystem.append('console.txt', message:formatted(true) .. '\n') end, - warn=function(self, ...) - self:log("WARN", ...) + info = function(self, ...) + self:log('INFO', ...) end, - error=function(self, ...) - self:log("ERROR", ...) + warn = function(self, ...) + self:log('WARN', ...) end, - debug=function(self, ...) - self:log("DEBUG", ...) + error = function(self, ...) + self:log('ERROR', ...) end, - trace=function(self, ...) - self:log("TRACE", ...) + debug = function(self, ...) + self:log('DEBUG', ...) end, - print=function(self, message) - self:log("PRINT", message) + trace = function(self, ...) + self:log('TRACE', ...) end, + print = function(self, message) + self:log('PRINT', message) + end } end @@ -92,7 +83,9 @@ local function getAllMessages() table.insert(messages, message) end end - table.sort(messages, function(a, b) return a.time < b.time end) + table.sort(messages, function(a, b) + return a.time < b.time + end) return messages end @@ -101,17 +94,15 @@ function generateDateTime(start) -- need to round to seconds for an accurate date/time start = math.floor(start or socket.gettime()) local dateTimeTable = os.date('*t', start) - local dateTime = dateTimeTable.year .. "-" - .. addZeroForLessThan10(dateTimeTable.month) .. "-" - .. addZeroForLessThan10(dateTimeTable.day) .. "-" - .. addZeroForLessThan10(dateTimeTable.hour) .. "-" - .. addZeroForLessThan10(dateTimeTable.min) .. "-" - .. addZeroForLessThan10(dateTimeTable.sec) + local dateTime = dateTimeTable.year .. '-' .. addZeroForLessThan10(dateTimeTable.month) .. '-' .. + addZeroForLessThan10(dateTimeTable.day) .. '-' .. addZeroForLessThan10(dateTimeTable.hour) .. + '-' .. addZeroForLessThan10(dateTimeTable.min) .. '-' .. + addZeroForLessThan10(dateTimeTable.sec) return dateTime end function addZeroForLessThan10(number) - if(number < 10) then + if (number < 10) then return 0 .. number else return number @@ -119,14 +110,13 @@ function addZeroForLessThan10(number) end function saveLogs() - local filename = "logs/" .. generateDateTime() .. ".log" - love.filesystem.write(filename, "") + local filename = 'logs/' .. generateDateTime() .. '.log' + love.filesystem.write(filename, '') for _, message in ipairs(getAllMessages()) do - love.filesystem.append(filename, message:formatted(true) .. "\n") + love.filesystem.append(filename, message:formatted(true) .. '\n') end end - local function clearLogs() for name, logger in pairs(LOGGERS) do logger.messages = {} From cc7760a78d10bbd978904d1f4de242ee8fa5ee7e Mon Sep 17 00:00:00 2001 From: Zei33 Date: Sat, 1 Jun 2024 00:39:22 +1000 Subject: [PATCH 12/15] Fixed console scrolling when there are long strings that wrap. --- src/console.lua | 244 ++++++++++++++++++++++++------------------------ 1 file changed, 120 insertions(+), 124 deletions(-) diff --git a/src/console.lua b/src/console.lua index 146e754..cc81851 100644 --- a/src/console.lua +++ b/src/console.lua @@ -1,21 +1,21 @@ -local logging = require("logging") -local platform = require("platform") -local utf8 = require("utf8") -local math = require("math") +local logging = require('logging') +local platform = require('platform') +local utf8 = require('utf8') +local math = require('math') -local logger = logging.getLogger("console") +local logger = logging.getLogger('console') return { logger = logger, - log_level = "INFO", + log_level = 'INFO', is_open = false, - cmd = "> ", + cmd = '> ', line_height = 20, max_lines = love.graphics.getHeight() / 20, start_line_offset = 1, history_index = 0, command_history = {}, - history_path = "dev_console.history", + history_path = 'dev_console.history', modifiers = { capslock = false, scrolllock = false, @@ -40,13 +40,16 @@ return { end end, longestCommonPrefix = function(self, strings) - if #strings == 0 then return "" end + if #strings == 0 then + return '' + end local prefix = strings[1] for i = 2, #strings do local str = strings[i] local j = 1 - while j <= #prefix and j <= #str and prefix:sub(j, j) == - str:sub(j, j) do j = j + 1 end + while j <= #prefix and j <= #str and prefix:sub(j, j) == str:sub(j, j) do + j = j + 1 + end prefix = prefix:sub(1, j - 1) end return prefix @@ -55,10 +58,12 @@ return { local command = self.cmd:sub(3) -- remove the "> " prefix local cmd = {} -- split command into parts - for part in command:gmatch("%S+") do table.insert(cmd, part) end + for part in command:gmatch('%S+') do + table.insert(cmd, part) + end if #cmd == 0 then -- no command typed, do nothing (no completions possible) - logger:trace("No command typed") + logger:trace('No command typed') return nil end local completions = {} @@ -77,12 +82,10 @@ return { local previousArgs = cmd local current_arg = table.remove(previousArgs) if command then - completions = command.autocomplete(current_arg, previousArgs) or - {} + completions = command.autocomplete(current_arg, previousArgs) or {} end end - logger:trace("Autocomplete matches: " .. #completions .. " " .. - table.concat(completions, ", ")) + logger:trace('Autocomplete matches: ' .. #completions .. ' ' .. table.concat(completions, ', ')) if #completions == 0 then -- no completions found return nil @@ -94,12 +97,24 @@ return { end end, getMessageColor = function(self, message) - if message.level == "PRINT" then return 1, 1, 1 end - if message.level == "INFO" then return 0, 0.9, 1 end - if message.level == "WARN" then return 1, 0.5, 0 end - if message.level == "ERROR" then return 1, 0, 0 end - if message.level == "DEBUG" then return 0.16, 0, 1 end - if message.level == "TRACE" then return 1, 1, 1 end + if message.level == 'PRINT' then + return 1, 1, 1 + end + if message.level == 'INFO' then + return 0, 0.9, 1 + end + if message.level == 'WARN' then + return 1, 0.5, 0 + end + if message.level == 'ERROR' then + return 1, 0, 0 + end + if message.level == 'DEBUG' then + return 0.16, 0, 1 + end + if message.level == 'TRACE' then + return 1, 1, 1 + end return 1, 1, 1 end, getFilteredMessages = function(self) @@ -120,8 +135,7 @@ return { local all_messages = {} for _, message in ipairs(base_messages) do - local wrappedLines = self:wrapText(message.text, - love.graphics.getWidth() - 20) + local wrappedLines = self:wrapText(message.text, love.graphics.getWidth() - 20) for _, line in ipairs(wrappedLines) do table.insert(all_messages, { text = line, @@ -129,14 +143,18 @@ return { name = message.name, time = message.time, level_numeric = message.level_numeric, - formatted = function() return line end + formatted = function() + return line + end }) end end while textLength < self.max_lines do local index = #all_messages - i + self.start_line_offset - if index < 1 then break end + if index < 1 then + break + end local message = all_messages[index] if message then table.insert(text, message) @@ -156,12 +174,14 @@ return { local nLinesToPad = #text - self.max_lines for i = 1, nLinesToPad do table.insert(text, { - text = "", - level = "PRINT", - name = "", + text = '', + level = 'PRINT', + name = '', time = 0, level_numeric = 1000, - formatted = function() return "" end + formatted = function() + return '' + end }) end return text @@ -170,7 +190,7 @@ return { -- disable text input if ctrl or cmd is pressed -- this is to fallback to love.keypressed when a modifier is pressed that can -- link to a command (like ctrl+c, ctrl+v, etc) - self.logger:trace("modifiers", self.modifiers) + self.logger:trace('modifiers', self.modifiers) if self.modifiers.ctrl or self.modifiers.meta then love.textinput = nil else @@ -181,22 +201,20 @@ return { end, typeKey = function(self, key_name) -- cmd+shift+C on mac, ctrl+shift+C on windows/linux - if key_name == "c" and - ((platform.is_mac and self.modifiers.meta and self.modifiers.shift) or - (not platform.is_mac and self.modifiers.ctrl and - self.modifiers.shift)) then + if key_name == 'c' and ((platform.is_mac and self.modifiers.meta and self.modifiers.shift) or + (not platform.is_mac and self.modifiers.ctrl and self.modifiers.shift)) then local messages = self:getFilteredMessages() - local text = "" + local text = '' for _, message in ipairs(messages) do - text = text .. message:formatted() .. "\n" + text = text .. message:formatted() .. '\n' end love.system.setClipboardText(text) return end -- cmd+C on mac, ctrl+C on windows/linux - if key_name == "c" and ((platform.is_mac and self.modifiers.meta) or - (not platform.is_mac and self.modifiers.ctrl)) then - if self.cmd:sub(3) == "" then + if key_name == 'c' and + ((platform.is_mac and self.modifiers.meta) or (not platform.is_mac and self.modifiers.ctrl)) then + if self.cmd:sub(3) == '' then -- do nothing if the buffer is empty return end @@ -204,82 +222,68 @@ return { return end -- cmd+V on mac, ctrl+V on windows/linux - if key_name == "v" and ((platform.is_mac and self.modifiers.meta) or - (not platform.is_mac and self.modifiers.ctrl)) then + if key_name == 'v' and + ((platform.is_mac and self.modifiers.meta) or (not platform.is_mac and self.modifiers.ctrl)) then self.cmd = self.cmd .. love.system.getClipboardText() return end - if key_name == "escape" then + if key_name == 'escape' then -- close the console self:toggle() return end -- Delete the current command, on mac it's cmd+backspace - if key_name == "delete" or - (platform.is_mac and self.modifiers.meta and key_name == "backspace") then - self.cmd = "> " + if key_name == 'delete' or (platform.is_mac and self.modifiers.meta and key_name == 'backspace') then + self.cmd = '> ' return end - if key_name == "end" or - (platform.is_mac and key_name == "right" and self.modifiers.meta) then + if key_name == 'end' or (platform.is_mac and key_name == 'right' and self.modifiers.meta) then -- move text to the most recent (bottom) self.start_line_offset = self.max_lines return end - if key_name == "home" or - (platform.is_mac and key_name == "left" and self.modifiers.meta) then + if key_name == 'home' or (platform.is_mac and key_name == 'left' and self.modifiers.meta) then -- move text to the oldest (top) local messages = self:getFilteredMessages() self.start_line_offset = self.max_lines - #messages return end - if key_name == "pagedown" or - (platform.is_mac and key_name == "down" and self.modifiers.meta) then + if key_name == 'pagedown' or (platform.is_mac and key_name == 'down' and self.modifiers.meta) then -- move text down by max_lines - self.start_line_offset = math.min( - self.start_line_offset + self.max_lines, - self.max_lines) + self.start_line_offset = math.min(self.start_line_offset + self.max_lines, self.max_lines) return end - if key_name == "pageup" or - (platform.is_mac and key_name == "up" and self.modifiers.meta) then + if key_name == 'pageup' or (platform.is_mac and key_name == 'up' and self.modifiers.meta) then -- move text up by max_lines local messages = self:getFilteredMessages() - self.start_line_offset = math.max( - self.start_line_offset - self.max_lines, - self.max_lines - #messages) + self.start_line_offset = math.max(self.start_line_offset - self.max_lines, self.max_lines - #messages) return end - if key_name == "up" then + if key_name == 'up' then -- move to the next command in the history (in reverse order of insertion) - self.history_index = math.min(self.history_index + 1, - #self.command_history) + self.history_index = math.min(self.history_index + 1, #self.command_history) if self.history_index == 0 then - self.cmd = "> " + self.cmd = '> ' return end - self.cmd = "> " .. - self.command_history[#self.command_history - - self.history_index + 1] + self.cmd = '> ' .. self.command_history[#self.command_history - self.history_index + 1] return end - if key_name == "down" then + if key_name == 'down' then -- move to the previous command in the history (in reverse order of insertion) self.history_index = math.max(self.history_index - 1, 0) if self.history_index == 0 then - self.cmd = "> " + self.cmd = '> ' return end - self.cmd = "> " .. - self.command_history[#self.command_history - - self.history_index + 1] + self.cmd = '> ' .. self.command_history[#self.command_history - self.history_index + 1] return end - if key_name == "tab" then + if key_name == 'tab' then local completion = self:tryAutocomplete() if completion then -- get the last part of the console command - local lastPart = self.cmd:match("%S+$") + local lastPart = self.cmd:match('%S+$') if lastPart == nil then -- cmd ends with a space, so we stop the completion return end @@ -288,28 +292,28 @@ return { end return end - if key_name == "lalt" or key_name == "ralt" then + if key_name == 'lalt' or key_name == 'ralt' then self.modifiers.alt = true self:modifiersListener() return end - if key_name == "lctrl" or key_name == "rctrl" then + if key_name == 'lctrl' or key_name == 'rctrl' then self.modifiers.ctrl = true self:modifiersListener() return end - if key_name == "lshift" or key_name == "rshift" then + if key_name == 'lshift' or key_name == 'rshift' then self.modifiers.shift = true self:modifiersListener() return end - if key_name == "lgui" or key_name == "rgui" then + if key_name == 'lgui' or key_name == 'rgui' then -- windows key / meta / cmd key (on macos) self.modifiers.meta = true self:modifiersListener() return end - if key_name == "backspace" then + if key_name == 'backspace' then if #self.cmd > 2 then local byteoffset = utf8.offset(self.cmd, -1) if byteoffset then @@ -320,15 +324,17 @@ return { end return end - if key_name == "return" or key_name == "kpenter" then + if key_name == 'return' or key_name == 'kpenter' then self.logger:print(self.cmd) local cmdName = self.cmd:sub(3) - cmdName = cmdName:match("%S+") - if cmdName == nil then return end + cmdName = cmdName:match('%S+') + if cmdName == nil then + return + end local args = {} local argString = self.cmd:sub(3 + #cmdName + 1) if argString then - for arg in argString:gmatch("%S+") do + for arg in argString:gmatch('%S+') do table.insert(args, arg) end end @@ -336,30 +342,29 @@ return { if self.commands[cmdName] then success = self.commands[cmdName].call(args) else - self.logger:error("Command not found: " .. cmdName) + self.logger:error('Command not found: ' .. cmdName) end if success then -- only add the command to the history if it was successful self:addToHistory(self.cmd:sub(3)) end - self.cmd = "> " + self.cmd = '> ' return end end, addToHistory = function(self, command) - if command == nil or command == "" then return end + if command == nil or command == '' then + return + end table.insert(self.command_history, command) self.history_index = 0 - local success, errormsg = love.filesystem.append(self.history_path, - command .. "\n") + local success, errormsg = love.filesystem.append(self.history_path, command .. '\n') if not success then - self.logger:warn("Error appending ", command, " to history file: ", - errormsg) - success, errormsg = love.filesystem.write(self.history_path, - command .. "\n") + self.logger:warn('Error appending ', command, ' to history file: ', errormsg) + success, errormsg = love.filesystem.write(self.history_path, command .. '\n') if not success then - self.logger:error("Error writing to history file: ", errormsg) + self.logger:error('Error writing to history file: ', errormsg) end end end, @@ -369,69 +374,60 @@ return { -- @param short_description: string, a short description of the command -- @param autocomplete: function(current_arg: string), a function that returns a list of possible completions for the current argument -- @param usage: string, a string describing the usage of the command (longer, more detailed description of the command's usage) - registerCommand = function(self, name, callback, short_description, - autocomplete, usage) + registerCommand = function(self, name, callback, short_description, autocomplete, usage) if name == nil then - self.logger:error("registerCommand -- name is required") + self.logger:error('registerCommand -- name is required') end if callback == nil then - self.logger:error( - "registerCommand -- callback is required on command", name) + self.logger:error('registerCommand -- callback is required on command', name) end - if type(callback) ~= "function" then - self.logger:error( - "registerCommand -- callback must be a function on command", - name) + if type(callback) ~= 'function' then + self.logger:error('registerCommand -- callback must be a function on command', name) end - if name == nil or callback == nil or type(callback) ~= "function" then + if name == nil or callback == nil or type(callback) ~= 'function' then return end if short_description == nil then self.logger:warn( - "registerCommand -- no description provided, please provide a description for the `help` command") - short_description = "No help provided" + 'registerCommand -- no description provided, please provide a description for the `help` command') + short_description = 'No help provided' + end + if usage == nil then + usage = short_description end - if usage == nil then usage = short_description end if autocomplete == nil then autocomplete = function(current_arg, previous_args) return nil end end - if type(autocomplete) ~= "function" then - self.logger:warn( - "registerCommand -- autocomplete must be a function for command: ", - name) + if type(autocomplete) ~= 'function' then + self.logger:warn('registerCommand -- autocomplete must be a function for command: ', name) autocomplete = function(current_arg, previous_args) return nil end end if self.commands[name] then - self.logger:warn("Command " .. name .. " already exists") + self.logger:warn('Command ' .. name .. ' already exists') return end - self.logger:info("Registering command: ", name) - self.commands[name] = { - call = callback, - desc = short_description, - autocomplete = autocomplete, - usage = usage - } + self.logger:info('Registering command: ', name) + self.commands[name] = {call = callback, desc = short_description, autocomplete = autocomplete, usage = usage} end, removeCommand = function(self, cmd_name) if cmd_name == nil then - self.logger:error("removeCommand -- cmd_name is required") + self.logger:error('removeCommand -- cmd_name is required') end if self.commands[cmd_name] == nil then - self.logger:error("removeCommand -- command not found: ", cmd_name) + self.logger:error('removeCommand -- command not found: ', cmd_name) return end self.commands[cmd_name] = nil end, wrapText = function(self, message, screenWidth) local lines = {} - local line = "" - for word in message:gmatch("%S+") do - local testLine = line .. " " .. word + local line = '' + for word in message:gmatch('%S+') do + local testLine = line .. ' ' .. word if love.graphics.getFont():getWidth(testLine) > screenWidth then table.insert(lines, line) line = word From 977aecb6e364b585ff9285e1c42a93229b7f1aef Mon Sep 17 00:00:00 2001 From: Zei33 Date: Sat, 1 Jun 2024 00:39:42 +1000 Subject: [PATCH 13/15] Upgraded booster and enhance functions. --- src/balamod.lua | 855 +++++++++++++++++++++++++----------------------- 1 file changed, 439 insertions(+), 416 deletions(-) diff --git a/src/balamod.lua b/src/balamod.lua index 56c66fa..aca4416 100644 --- a/src/balamod.lua +++ b/src/balamod.lua @@ -9,12 +9,7 @@ local https = require('https') logger = logging.getLogger('balamod') mods = {} -local apis = { - logging = logging, - console = console, - math = math, - platform = platform -} +local apis = {logging = logging, console = console, math = math, platform = platform} is_loaded = false local RESULT = { SUCCESS = 0, @@ -31,9 +26,11 @@ local paths = {} -- Paths to the files that will be loaded local _VERSION = require('balamod_version') local function splitstring(inputstr, sep) - if sep == nil then sep = "%s" end + if sep == nil then + sep = '%s' + end local t = {} - for str in string.gmatch(inputstr, "([^" .. sep .. "]+)") do + for str in string.gmatch(inputstr, '([^' .. sep .. ']+)') do table.insert(t, str) end return t @@ -42,29 +39,42 @@ end function buildPaths(root, ignore) local items = love.filesystem.getDirectoryItems(root) for _, file in ipairs(items) do - if root ~= "" then file = root .. "/" .. file end + if root ~= '' then + file = root .. '/' .. file + end local info = love.filesystem.getInfo(file) if info then - if info.type == "file" and file:match("%.lua$") then + if info.type == 'file' and file:match('%.lua$') then table.insert(paths, file) - elseif info.type == "directory" then + elseif info.type == 'directory' then local valid = true for _, i in ipairs(ignore) do - if i == file then valid = false end + if i == file then + valid = false + end + end + if valid then + buildPaths(file, ignore) end - if valid then buildPaths(file, ignore) end end end end end +function table_contains(table, element) + for _, value in pairs(table) do + if value == element then + return value + end + end + return nil +end + local function request(url) logger:debug('Request made with url: ', url) local code local response - code, response, headers = https.request(url, { - headers = {['User-Agent'] = 'Balamod-Client'} - }) + code, response, headers = https.request(url, {headers = {['User-Agent'] = 'Balamod-Client'}}) if (code == 301 or code == 302) and headers.location then -- follow redirects if necessary code, response = request(headers.location) @@ -73,24 +83,25 @@ local function request(url) end local function extractFunctionBody(path, function_name) - local pattern = "\n?%s*function%s+" .. function_name .. "%(" + local pattern = '\n?%s*function%s+' .. function_name .. '%(' local func_begin, fin = current_game_code[path]:find(pattern) if not func_begin then - return "Can't find function begin " .. function_name + return 'Can\'t find function begin ' .. function_name end - local func_end = current_game_code[path]:find("\n\r?end", fin) + local func_end = current_game_code[path]:find('\n\r?end', fin) -- This is to catch functions that have incorrect ending indentation by catching the next function in line. -- Can be removed once Card:calculate_joker no longer has this typo. - local typocatch_func_end = - current_game_code[path]:find("\n\r?function", fin) + local typocatch_func_end = current_game_code[path]:find('\n\r?function', fin) if typocatch_func_end and typocatch_func_end < func_end then func_end = typocatch_func_end - 3 end - if not func_end then return "Can't find function end " .. function_name end + if not func_end then + return 'Can\'t find function end ' .. function_name + end local func_body = current_game_code[path]:sub(func_begin, func_end + 3) return func_body @@ -100,28 +111,25 @@ local function inject(path, function_name, to_replace, replacement) -- Injects code into a function (replaces a string with another string inside a function) local function_body = extractFunctionBody(path, function_name) local modified_function_code = function_body:gsub(to_replace, replacement) - escaped_function_body = function_body:gsub("([^%w])", "%%%1") -- escape function body for use in gsub - escaped_modified_function_code = modified_function_code:gsub("([^%w])", - "%%%1") - current_game_code[path] = current_game_code[path]:gsub( - escaped_function_body, - escaped_modified_function_code) -- update current game code in memory + escaped_function_body = function_body:gsub('([^%w])', '%%%1') -- escape function body for use in gsub + escaped_modified_function_code = modified_function_code:gsub('([^%w])', '%%%1') + current_game_code[path] = current_game_code[path]:gsub(escaped_function_body, escaped_modified_function_code) -- update current game code in memory local new_function, load_error = load(modified_function_code) -- load modified function if not new_function then - logger:error("Error loading modified function", function_name, ": ", - (load_error or "Unknown error")) + logger:error('Error loading modified function', function_name, ': ', (load_error or 'Unknown error')) logger:error(modified_function_code) end - if setfenv then setfenv(new_function, getfenv(original_testFunction)) end -- Set the environment of the new function to the same as the original function + if setfenv then + setfenv(new_function, getfenv(original_testFunction)) + end -- Set the environment of the new function to the same as the original function local status, result = pcall(new_function) -- Execute the new function if status then testFunction = result -- Overwrite the original function with the result of the new function else - logger:error("Error executing modified function", function_name, ": ", - result) -- Safeguard against errors + logger:error('Error executing modified function', function_name, ': ', result) -- Safeguard against errors logger:error(modified_function_code) end end @@ -129,40 +137,36 @@ end local function injectHead(path, function_name, code) local function_body = extractFunctionBody(path, function_name) - local pattern = "(function%s+" .. function_name .. ".-)\n" - local modified_function_code, number_of_subs = - function_body:gsub(pattern, "%1\n" .. code .. "\n") + local pattern = '(function%s+' .. function_name .. '.-)\n' + local modified_function_code, number_of_subs = function_body:gsub(pattern, '%1\n' .. code .. '\n') if number_of_subs == 0 then - logger:error( - "Error: Function start not found in function body or multiple matches encountered.") + logger:error('Error: Function start not found in function body or multiple matches encountered.') logger:error(modified_function_code) return end - escaped_function_body = function_body:gsub("([^%w])", "%%%1") - escaped_modified_function_code = modified_function_code:gsub("([^%w])", - "%%%1") - current_game_code[path] = current_game_code[path]:gsub( - escaped_function_body, - escaped_modified_function_code) + escaped_function_body = function_body:gsub('([^%w])', '%%%1') + escaped_modified_function_code = modified_function_code:gsub('([^%w])', '%%%1') + current_game_code[path] = current_game_code[path]:gsub(escaped_function_body, escaped_modified_function_code) local new_function, load_error = load(modified_function_code) if not new_function then - logger:error("Error loading modified function ", function_name, - " with head injection: ", (load_error or "Unknown error")) + logger:error('Error loading modified function ', function_name, ' with head injection: ', + (load_error or 'Unknown error')) logger:error(modified_function_code) return end - if setfenv then setfenv(new_function, getfenv(original_testFunction)) end + if setfenv then + setfenv(new_function, getfenv(original_testFunction)) + end local status, result = pcall(new_function) if status then testFunction = result else - logger:error("Error executing modified function ", function_name, - " with head injection: ", result) + logger:error('Error executing modified function ', function_name, ' with head injection: ', result) logger:error(modified_function_code) end end @@ -170,41 +174,38 @@ end local function injectTail(path, function_name, code) local function_body = extractFunctionBody(path, function_name) - local pattern = "(.-)(end[ \t]*\n?)$" - local modified_function_code, number_of_subs = - function_body:gsub(pattern, "%1" .. string.gsub(code, '(.-)%s*$', '%1') .. - "\n" .. "%2") + local pattern = '(.-)(end[ \t]*\n?)$' + local modified_function_code, number_of_subs = function_body:gsub(pattern, '%1' .. + string.gsub(code, '(.-)%s*$', '%1') .. '\n' .. + '%2') if number_of_subs == 0 then - logger:error("Error: 'end' not found in function '", function_name, - "' body or multiple ends encountered.") + logger:error('Error: \'end\' not found in function \'', function_name, '\' body or multiple ends encountered.') logger:error(modified_function_code) return end - escaped_function_body = function_body:gsub("([^%w])", "%%%1") - escaped_modified_function_code = modified_function_code:gsub("([^%w])", - "%%%1") - current_game_code[path] = current_game_code[path]:gsub( - escaped_function_body, - escaped_modified_function_code) + escaped_function_body = function_body:gsub('([^%w])', '%%%1') + escaped_modified_function_code = modified_function_code:gsub('([^%w])', '%%%1') + current_game_code[path] = current_game_code[path]:gsub(escaped_function_body, escaped_modified_function_code) local new_function, load_error = load(modified_function_code) if not new_function then - logger:error("Error loading modified function ", function_name, - " with tail injection: ", (load_error or "Unknown error")) + logger:error('Error loading modified function ', function_name, ' with tail injection: ', + (load_error or 'Unknown error')) logger:error(modified_function_code) return end - if setfenv then setfenv(new_function, getfenv(original_testFunction)) end + if setfenv then + setfenv(new_function, getfenv(original_testFunction)) + end local status, result = pcall(new_function) if status then testFunction = result else - logger:error("Error executing modified function ", function_name, - " with tail injection: ", result) + logger:error('Error executing modified function ', function_name, ' with tail injection: ', result) logger:error(modified_function_code) end end @@ -219,8 +220,7 @@ end local function getRepoMods() local repoMods = {} - local reposIndex = - 'https://raw.githubusercontent.com/UwUDev/balamod/master/repos.index' + local reposIndex = 'https://raw.githubusercontent.com/UwUDev/balamod/master/repos.index' logger:info('Requesting ', reposIndex) local indexCode, indexBody = request(reposIndex) if indexCode ~= 200 then @@ -239,9 +239,8 @@ local function getRepoMods() logger:error('Response: ' .. repoBody) else for modInfo in string.gmatch(repoBody, '([^\n]+)') do - local modId, modVersion, modName, modDesc, modUrl = - string.match(modInfo, - '([^|]+)|([^|]+)|([^|]+)|([^|]+)|([^|]+)') + local modId, modVersion, modName, modDesc, modUrl = string.match(modInfo, + '([^|]+)|([^|]+)|([^|]+)|([^|]+)|([^|]+)') local modPresent = isModPresent(modId) local needUpdate = true local version = modVersion @@ -251,8 +250,7 @@ local function getRepoMods() if mod.version then version = mod.version local modVersion = utils.parseVersion(mod.version) - needUpdate = utils.v2GreaterThanV1(modVersion, - repoVersion) + needUpdate = utils.v2GreaterThanV1(modVersion, repoVersion) end end table.insert(repoMods, { @@ -288,78 +286,67 @@ local function validateManifest(modFolder, manifest) -- check that all manifest expected fields are present for field, required in pairs(expectedFields) do if manifest[field] == nil and required then - logger:error('Manifest in folder ', modFolder, - ' is missing field: ', field) + logger:error('Manifest in folder ', modFolder, ' is missing field: ', field) return false end end -- check that none of the manifest fields are not in the expected fields for key, _ in pairs(manifest) do if expectedFields[key] == nil then - logger:error('Manifest in folder ', modFolder, - ' contains unexpected field: ', key) + logger:error('Manifest in folder ', modFolder, ' contains unexpected field: ', key) return false end end -- check that the load_before, load_after and description fields are arrays if type(manifest.load_before) ~= 'table' then - logger:error('Manifest in folder ', modFolder, - ' has a non-array load_before field') + logger:error('Manifest in folder ', modFolder, ' has a non-array load_before field') return false end if type(manifest.load_after) ~= 'table' then - logger:error('Manifest in folder ', modFolder, - ' has a non-array load_after field') + logger:error('Manifest in folder ', modFolder, ' has a non-array load_after field') return false end if type(manifest.description) ~= 'table' then - logger:error('Manifest in folder ', modFolder, - ' has a non-array description field') + logger:error('Manifest in folder ', modFolder, ' has a non-array description field') return false end -- check that the load_before and load_after fields are strings for _, modId in ipairs(manifest.load_before) do if type(modId) ~= 'string' then - logger:error('Manifest in folder ', modFolder, - ' has a non-string load_before field') + logger:error('Manifest in folder ', modFolder, ' has a non-string load_before field') return false end end for _, modId in ipairs(manifest.load_after) do if type(modId) ~= 'string' then - logger:error('Manifest in folder ', modFolder, - ' has a non-string load_after field') + logger:error('Manifest in folder ', modFolder, ' has a non-string load_after field') return false end end -- check that the version field is a string, matching semantic versioning if not manifest.version:match('%d+%.%d+%.%d+') then - logger:error('Manifest in folder ', modFolder, - ' has a non-semantic versioning version field') + logger:error('Manifest in folder ', modFolder, ' has a non-semantic versioning version field') return false end -- check that the author field is a string if type(manifest.author) ~= 'string' then - logger:error('Manifest in folder ', modFolder, - ' has a non-string author field') + logger:error('Manifest in folder ', modFolder, ' has a non-string author field') return false end -- check that the id field is a string if type(manifest.id) ~= 'string' then - logger:error('Manifest in folder ', modFolder, - ' has a non-string id field') + logger:error('Manifest in folder ', modFolder, ' has a non-string id field') return false end -- check that the name field is a string if type(manifest.name) ~= 'string' then - logger:error('Manifest in folder ', modFolder, - ' has a non-string name field') + logger:error('Manifest in folder ', modFolder, ' has a non-string name field') return false end @@ -367,19 +354,16 @@ local function validateManifest(modFolder, manifest) if manifest.dependencies then local incorrectDependencies = {} if type(manifest.dependencies) ~= 'table' then - logger:error('Manifest in folder ', modFolder, - ' has a non-table dependencies field') + logger:error('Manifest in folder ', modFolder, ' has a non-table dependencies field') return false end for modId, version in pairs(manifest.dependencies) do if type(modId) ~= 'string' then - logger:error('Manifest in folder ', modFolder, - ' has a non-string key in dependencies field') + logger:error('Manifest in folder ', modFolder, ' has a non-string key in dependencies field') return false end if type(version) ~= 'string' then - logger:error('Manifest in folder ', modFolder, - ' has a non-string value in dependencies field') + logger:error('Manifest in folder ', modFolder, ' has a non-string value in dependencies field') return false end local versionConstraintCorrect = false @@ -395,8 +379,7 @@ local function validateManifest(modFolder, manifest) local versionPatterns = {'%d+', '%d+%.%d+', '%d+%.%d+%.%d+'} for _, versionPattern1 in ipairs(versionPatterns) do for _, versionPattern2 in ipairs(versionPatterns) do - table.insert(patterns, '[<>]=?' .. versionPattern1 .. - ', ?[<>]=?' .. versionPattern2) + table.insert(patterns, '[<>]=?' .. versionPattern1 .. ', ?[<>]=?' .. versionPattern2) end end -- check every generated pattern, one at a time, if any of them matches, then the version constraint is correct @@ -412,8 +395,7 @@ local function validateManifest(modFolder, manifest) end if #incorrectDependencies > 0 then -- some of the dependencies are incorrect for the mod, let's log them and return false - logger:error('Manifest in folder ', modFolder, - ' has incorrect dependencies field: ', + logger:error('Manifest in folder ', modFolder, ' has incorrect dependencies field: ', table.concat(incorrectDependencies, ', ')) return false end @@ -430,48 +412,47 @@ local function loadMod(modFolder) return nil end if not love.filesystem.getInfo('mods/' .. modFolder .. '/main.lua', 'file') then - logger:error('Mod folder ', modFolder, - ' does not contain a main.lua file') + logger:error('Mod folder ', modFolder, ' does not contain a main.lua file') return nil end - if not love.filesystem.getInfo('mods/' .. modFolder .. '/manifest.json', - 'file') then - logger:error('Mod folder ', modFolder, - ' does not contain a manifest.json file') + if not love.filesystem.getInfo('mods/' .. modFolder .. '/manifest.json', 'file') then + logger:error('Mod folder ', modFolder, ' does not contain a manifest.json file') return nil end - logger:debug("Loading manifest from: ", - 'mods/' .. modFolder .. '/manifest.json') + logger:debug('Loading manifest from: ', 'mods/' .. modFolder .. '/manifest.json') -- load the manifest - local manifest = json.decode(love.filesystem.read( - 'mods/' .. modFolder .. '/manifest.json')) - if not validateManifest(modFolder, manifest) then return nil end + local manifest = json.decode(love.filesystem.read('mods/' .. modFolder .. '/manifest.json')) + if not validateManifest(modFolder, manifest) then + return nil + end logger:debug('Manifest loaded: ', manifest) -- check that the mod is compatible with the current version of balamod if manifest.min_balamod_version then local minVersion = utils.parseVersion(manifest.min_balamod_version) if not utils.v2GreaterThanV1(minVersion, _VERSION) then - logger:error('Mod ', modFolder, - ' requires a newer version of balamod') + logger:error('Mod ', modFolder, ' requires a newer version of balamod') return nil end end if manifest.max_balamod_version then local maxVersion = utils.parseVersion(manifest.max_balamod_version) if utils.v2GreaterThanV1(maxVersion, _VERSION) then - logger:error('Mod ', modFolder, - ' requires an older version of balamod') + logger:error('Mod ', modFolder, ' requires an older version of balamod') return nil end end -- load the hooks (on_enable, on_game_load, etc...) - logger:debug("Loading hooks from: ", 'mods/' .. modFolder .. '/main.lua') + logger:debug('Loading hooks from: ', 'mods/' .. modFolder .. '/main.lua') local modHooks = require('mods/' .. modFolder .. '/main') - for hookName, hook in pairs(modHooks) do mod[hookName] = hook end - for key, value in pairs(manifest) do mod[key] = value end + for hookName, hook in pairs(modHooks) do + mod[hookName] = hook + end + for key, value in pairs(manifest) do + mod[key] = value + end mod.enabled = true logger:debug('Mod loaded: ', mod.id) - logger:debug("Checking if mod is disabled") + logger:debug('Checking if mod is disabled') if love.filesystem.getInfo('mods/' .. modFolder .. '/disable.it', 'file') then mod.enabled = false end @@ -487,8 +468,7 @@ local function toggleMod(mod) love.filesystem.remove('mods/' .. mod.id .. '/disable.it') end pcall(mod.on_enable) - elseif not mod.enabled and mod.on_disable and type(mod.on_disable) == - 'function' then + elseif not mod.enabled and mod.on_disable and type(mod.on_disable) == 'function' then love.filesystem.write('mods/' .. mod.id .. '/disable.it', '') pcall(mod.on_disable) end @@ -497,19 +477,19 @@ end local function callModCallbacksIfExists(mods, callback_name, should_log, ...) local sorted = utils.values(mods) - table.sort(sorted, function(a, b) return a.order < b.order end) + table.sort(sorted, function(a, b) + return a.order < b.order + end) local mod_returns = {} -- pre loading all mods for _, mod in ipairs(sorted) do - if mod.enabled and mod[callback_name] and type(mod[callback_name]) == - "function" then + if mod.enabled and mod[callback_name] and type(mod[callback_name]) == 'function' then if should_log then - logger:info("Calling mod callback", callback_name, "for", mod.id) + logger:info('Calling mod callback', callback_name, 'for', mod.id) end local status, message = pcall(mod[callback_name], ...) -- Call the on_pre_load function of the mod if it exists if not status then - logger:warn("Callback", callback_name, "for mod ", mod.id, - "failed: ", message) + logger:warn('Callback', callback_name, 'for mod ', mod.id, 'failed: ', message) else table.insert(mod_returns, {modId = mod.id, result = message}) end @@ -534,12 +514,16 @@ local function installMod(modInfo) if modInfo.present then logger:debug('Mod ' .. modInfo.id .. ' is already present') local modVersion = modInfo.newVersion - if not modInfo.needUpdate then return RESULT.SUCCESS end + if not modInfo.needUpdate then + return RESULT.SUCCESS + end -- remove old mod for i, mod in ipairs(mods) do if mod.id == modId then - if mod.on_disable then mod.on_disable() end + if mod.on_disable then + mod.on_disable() + end table.remove(mods, i) break @@ -560,7 +544,7 @@ local function installMod(modInfo) logger:debug('Downloaded tarball with body ', body) -- decompress the archive in memory - local decompressedData = love.data.decompress("data", "gzip", body) + local decompressedData = love.data.decompress('data', 'gzip', body) local success, result = pcall(tar.unpack, decompressedData) if not success then logger:error('Error decompressing tarball') @@ -575,16 +559,22 @@ local function installMod(modInfo) -- type = "file" | "directory" -- } -- sort the result table so that directories are processed first - table.sort(result, function(a, b) return a.type < b.type end) + table.sort(result, function(a, b) + return a.type < b.type + end) -- check that the downloaded mod contains a main.lua file as well as a manifest.json file local mainLua = false local manifestJson = false for _, file in ipairs(result) do - if file.type == "file" then + if file.type == 'file' then -- file.name is a path, we only want the filename - local _, _, filename = file.name:find(".+/(.+)") - if filename == "main.lua" then mainLua = true end - if filename == "manifest.json" then manifestJson = true end + local _, _, filename = file.name:find('.+/(.+)') + if filename == 'main.lua' then + mainLua = true + end + if filename == 'manifest.json' then + manifestJson = true + end end end if not mainLua then @@ -597,22 +587,24 @@ local function installMod(modInfo) end for _, file in ipairs(result) do -- replace the first part of file.name with the modId - file.name = modId .. file.name:sub(file.name:find("/")) - if file.type == "directory" then - love.filesystem.createDirectory("mods/" .. file.name) - elseif file.type == "file" then - love.filesystem.write("mods/" .. file.name, file.data) + file.name = modId .. file.name:sub(file.name:find('/')) + if file.type == 'directory' then + love.filesystem.createDirectory('mods/' .. file.name) + elseif file.type == 'file' then + love.filesystem.write('mods/' .. file.name, file.data) end end local mod = loadMod(modId) - if mod == nil then return RESULT.MOD_FS_LOAD_ERROR end + if mod == nil then + return RESULT.MOD_FS_LOAD_ERROR + end mods[modId] = mod mods = sortMods(mods) return RESULT.SUCCESS end -buildPaths("", {"mods", "apis", "resources", "localization"}) +buildPaths('', {'mods', 'apis', 'resources', 'localization'}) -- current_game_code = love.filesystem.read(path) buildPaths = nil -- prevent rerunning (i think) @@ -621,35 +613,33 @@ for _, path in ipairs(paths) do current_game_code[path] = love.filesystem.read(path) end -if not love.filesystem.getInfo("mods", "directory") then -- Create mods folder if it doesn't exist - love.filesystem.createDirectory("mods") +if not love.filesystem.getInfo('mods', 'directory') then -- Create mods folder if it doesn't exist + love.filesystem.createDirectory('mods') end -if not love.filesystem.getInfo("logs", "directory") then -- Create logs folder if it doesn't exist - love.filesystem.createDirectory("logs") +if not love.filesystem.getInfo('logs', 'directory') then -- Create logs folder if it doesn't exist + love.filesystem.createDirectory('logs') end -if not love.filesystem.getInfo("apis", "directory") then -- Create apis folder if it doesn't exist - love.filesystem.createDirectory("apis") +if not love.filesystem.getInfo('apis', 'directory') then -- Create apis folder if it doesn't exist + love.filesystem.createDirectory('apis') end -- apis will be loaded first, then mods -mods["dev_console"] = { - id = "dev_console", - name = "Dev Console", - version = "0.6.0", - author = "sbordeyne & UwUDev", - description = { - "Press F2 to open/close the console", - "Use command `help` for a list of ", "available commands and shortcuts" - }, +mods['dev_console'] = { + id = 'dev_console', + name = 'Dev Console', + version = '0.6.0', + author = 'sbordeyne & UwUDev', + description = {'Press F2 to open/close the console', 'Use command `help` for a list of ', + 'available commands and shortcuts'}, enabled = true, on_game_load = function(args) - console.logger:info("Game loaded", args) + console.logger:info('Game loaded', args) for _, arg in ipairs(args) do - local split = splitstring(arg, "=") - if split[0] == "--log-level" then + local split = splitstring(arg, '=') + if split[0] == '--log-level' then console.logger.level = split[1]:upper() console.log_level = split[1]:upper() end @@ -657,36 +647,36 @@ mods["dev_console"] = { logging.saveLogs() end, on_game_quit = function() - console.logger:info("Quitting Balatro...") + console.logger:info('Quitting Balatro...') logging.saveLogs() end, on_error = function(message) - console.logger:error("Error: ", message) + console.logger:error('Error: ', message) -- on error, write all messages to a file logging.saveLogs() end, on_enable = function() - console.logger:debug("Dev Console enabled") + console.logger:debug('Dev Console enabled') contents, size = love.filesystem.read(console.history_path) if contents then - console.logger:trace("History file size", size) - for line in contents:gmatch("[^\r\n]+") do - if line and line ~= "" then + console.logger:trace('History file size', size) + for line in contents:gmatch('[^\r\n]+') do + if line and line ~= '' then table.insert(console.command_history, line) end end end - console.logger:debug("Registering commands") - console:registerCommand("help", function() - console.logger:print("Available commands:") + console.logger:debug('Registering commands') + console:registerCommand('help', function() + console.logger:print('Available commands:') for name, cmd in pairs(console.commands) do if cmd.desc then - console.logger:print(name .. ": " .. cmd.desc) + console.logger:print(name .. ': ' .. cmd.desc) end end return true - end, "Prints a list of available commands", function(current_arg) + end, 'Prints a list of available commands', function(current_arg) local completions = {} for name, _ in pairs(console.commands) do if name:find(current_arg, 1, true) == 1 then @@ -694,66 +684,59 @@ mods["dev_console"] = { end end return completions - end, "Usage: help ") + end, 'Usage: help ') - console:registerCommand("shortcuts", function() - console.logger:print("Available shortcuts:") - console.logger:print("F2: Open/Close the console") - console.logger:print("F4: Toggle debug mode") + console:registerCommand('shortcuts', function() + console.logger:print('Available shortcuts:') + console.logger:print('F2: Open/Close the console') + console.logger:print('F4: Toggle debug mode') if platform.is_mac then - console.logger:print( - "Cmd+C: Copy the current command to the clipboard.") - console.logger:print( - "Cmd+Shift+C: Copies all messages to the clipboard") - console.logger:print( - "Cmd+V: Paste the clipboard into the current command") + console.logger:print('Cmd+C: Copy the current command to the clipboard.') + console.logger:print('Cmd+Shift+C: Copies all messages to the clipboard') + console.logger:print('Cmd+V: Paste the clipboard into the current command') else - console.logger:print( - "Ctrl+C: Copy the current command to the clipboard.") - console.logger:print( - "Ctrl+Shift+C: Copies all messages to the clipboard") - console.logger:print( - "Ctrl+V: Paste the clipboard into the current command") + console.logger:print('Ctrl+C: Copy the current command to the clipboard.') + console.logger:print('Ctrl+Shift+C: Copies all messages to the clipboard') + console.logger:print('Ctrl+V: Paste the clipboard into the current command') end return true - end, "Prints a list of available shortcuts", - function(current_arg) return nil end, - "Usage: shortcuts") + end, 'Prints a list of available shortcuts', function(current_arg) + return nil + end, 'Usage: shortcuts') - console:registerCommand("history", function() - console.logger:print("Command history:") + console:registerCommand('history', function() + console.logger:print('Command history:') for i, cmd in ipairs(console.command_history) do - console.logger:print(i .. ": " .. cmd) + console.logger:print(i .. ': ' .. cmd) end return true - end, "Prints the command history") + end, 'Prints the command history') - console.logger:debug("Registering command: clear") - console:registerCommand("clear", function() + console.logger:debug('Registering command: clear') + console:registerCommand('clear', function() logging.clearLogs() return true - end, "Clear the console") + end, 'Clear the console') - console:registerCommand("exit", function() + console:registerCommand('exit', function() console:toggle() return true - end, "Close the console") + end, 'Close the console') - console:registerCommand("give", function(args) + console:registerCommand('give', function(args) local id = args[1] local c1 = nil - if string.sub(id, 1, 2) == "j_" then + if string.sub(id, 1, 2) == 'j_' then c1 = create_card(nil, G.jokers, nil, 1, true, false, id, nil) else - c1 = create_card(nil, G.consumeables, nil, 1, true, false, id, - nil) + c1 = create_card(nil, G.consumeables, nil, 1, true, false, id, nil) end G.E_MANAGER:add_event(Event({ trigger = 'after', delay = 0.1, func = function() c1:add_to_deck() - if string.sub(id, 1, 2) == "j_" then + if string.sub(id, 1, 2) == 'j_' then G.jokers:emplace(c1) else G.consumeables:emplace(c1) @@ -765,7 +748,7 @@ mods["dev_console"] = { end })) return true - end, "Give an item to the player", function(current_arg) + end, 'Give an item to the player', function(current_arg) local ret = {} for k, _ in pairs(G.P_CENTERS) do if string.find(k, current_arg) == 1 then @@ -775,38 +758,35 @@ mods["dev_console"] = { return ret end) - console:registerCommand("money", function(args) + console:registerCommand('money', function(args) if args[1] and args[2] then local amount = tonumber(args[2]) if amount then - if args[1] == "add" then + if args[1] == 'add' then ease_dollars(amount, true) - console.logger:info( - "Added " .. amount .. " money to the player") - elseif args[1] == "remove" then + console.logger:info('Added ' .. amount .. ' money to the player') + elseif args[1] == 'remove' then ease_dollars(-amount, true) - console.logger:info( - "Removed " .. amount .. " money from the player") - elseif args[1] == "set" then + console.logger:info('Removed ' .. amount .. ' money from the player') + elseif args[1] == 'set' then local currentMoney = G.GAME.dollars local diff = amount - currentMoney ease_dollars(diff, true) - console.logger:info("Set player money to " .. amount) + console.logger:info('Set player money to ' .. amount) else - console.logger:error( - "Invalid operation, use add, remove or set") + console.logger:error('Invalid operation, use add, remove or set') end else - console.logger:error("Invalid amount") + console.logger:error('Invalid amount') return false end else - console.logger:warn("Usage: money ") + console.logger:warn('Usage: money ') return false end return true - end, "Change the player's money", function(current_arg) - local subcommands = {"add", "remove", "set"} + end, 'Change the player\'s money', function(current_arg) + local subcommands = {'add', 'remove', 'set'} for i, v in ipairs(subcommands) do if v:find(current_arg, 1, true) == 1 then return {v} @@ -815,40 +795,36 @@ mods["dev_console"] = { return nil end) - console:registerCommand("discards", function(args) + console:registerCommand('discards', function(args) if args[1] and args[2] then local amount = tonumber(args[2]) if amount then - if args[1] == "add" then + if args[1] == 'add' then ease_discard(amount, true) - console.logger:info( - "Added " .. amount .. " discards to the player") - elseif args[1] == "remove" then + console.logger:info('Added ' .. amount .. ' discards to the player') + elseif args[1] == 'remove' then ease_discard(-amount, true) - console.logger:info( - "Removed " .. amount .. " discards from the player") - elseif args[1] == "set" then - local currentDiscards = - G.GAME.current_round.discards_left + console.logger:info('Removed ' .. amount .. ' discards from the player') + elseif args[1] == 'set' then + local currentDiscards = G.GAME.current_round.discards_left local diff = amount - currentDiscards ease_discard(diff, true) - console.logger:info("Set player discards to " .. amount) + console.logger:info('Set player discards to ' .. amount) else - console.logger:error( - "Invalid operation, use add, remove or set") + console.logger:error('Invalid operation, use add, remove or set') return false end else - console.logger:error("Invalid amount") + console.logger:error('Invalid amount') return false end else - console.logger:warn("Usage: discards ") + console.logger:warn('Usage: discards ') return false end return true - end, "Change the player's discards", function(current_arg) - local subcommands = {"add", "remove", "set"} + end, 'Change the player\'s discards', function(current_arg) + local subcommands = {'add', 'remove', 'set'} for i, v in ipairs(subcommands) do if v:find(current_arg, 1, true) == 1 then return {v} @@ -857,39 +833,36 @@ mods["dev_console"] = { return nil end) - console:registerCommand("hands", function(args) + console:registerCommand('hands', function(args) if args[1] and args[2] then local amount = tonumber(args[2]) if amount then - if args[1] == "add" then + if args[1] == 'add' then ease_hands_played(amount, true) - console.logger:info( - "Added " .. amount .. " hands to the player") - elseif args[1] == "remove" then + console.logger:info('Added ' .. amount .. ' hands to the player') + elseif args[1] == 'remove' then ease_hands_played(-amount, true) - console.logger:info( - "Removed " .. amount .. " hands from the player") - elseif args[1] == "set" then + console.logger:info('Removed ' .. amount .. ' hands from the player') + elseif args[1] == 'set' then local currentHands = G.GAME.current_round.hands_left local diff = amount - currentHands ease_hands_played(diff, true) - console.logger:info("Set player hands to " .. amount) + console.logger:info('Set player hands to ' .. amount) else - console.logger:error( - "Invalid operation, use add, remove or set") + console.logger:error('Invalid operation, use add, remove or set') return false end else - console.logger:error("Invalid amount") + console.logger:error('Invalid amount') return false end else - console.logger:warn("Usage: hands ") + console.logger:warn('Usage: hands ') return false end return true - end, "Change the player's remaining hands", function(current_arg) - local subcommands = {"add", "remove", "set"} + end, 'Change the player\'s remaining hands', function(current_arg) + local subcommands = {'add', 'remove', 'set'} for i, v in ipairs(subcommands) do if v:find(current_arg, 1, true) == 1 then return {v} @@ -898,17 +871,15 @@ mods["dev_console"] = { return nil end) - console:registerCommand("luamod", function(args) + console:registerCommand('luamod', function(args) if args[1] then local modId = args[1] if isModPresent(modId) then local mod = mods[modId] - if mod.enabled and mod.on_disable and type(mod.on_disable) == - "function" then + if mod.enabled and mod.on_disable and type(mod.on_disable) == 'function' then local success, result = pcall(mod.on_disable) if not success then - console.logger:error( - "Error disabling mod: " .. modId) + console.logger:error('Error disabling mod: ' .. modId) console.logger:error(result) return false end @@ -922,24 +893,23 @@ mods["dev_console"] = { if mod.on_enable and type(mod.on_enable) == 'function' then local status, message = pcall(mod.on_enable) if not status then - console.logger:error( - "Error enabling mod: " .. modId) + console.logger:error('Error enabling mod: ' .. modId) console.logger:error(message) return false end end end - console.logger:info("Reloaded mod: " .. modId) + console.logger:info('Reloaded mod: ' .. modId) else - console.logger:error("Mod not found: " .. modId) + console.logger:error('Mod not found: ' .. modId) return false end else - console.logger:error("Usage: luamod ") + console.logger:error('Usage: luamod ') return false end return true - end, "Reload a mod using its id", function(current_arg) + end, 'Reload a mod using its id', function(current_arg) local completions = {} for modId, _ in pairs(mods) do if modId:find(current_arg, 1, true) == 1 then @@ -947,73 +917,139 @@ mods["dev_console"] = { end end return completions - end, "Usage: luamod ") + end, 'Usage: luamod ') - console:registerCommand("sandbox", function(args) + console:registerCommand('sandbox', function(args) G:sandbox() return true - end, "Goes to the sandbox stage", function(current_arg) + end, 'Goes to the sandbox stage', function(current_arg) return nil - end, "Usage: sandbox") + end, 'Usage: sandbox') - console:registerCommand("luarun", function(args) - local code = table.concat(args, " ") + console:registerCommand('luarun', function(args) + local code = table.concat(args, ' ') local func, err = load(code) if func then - console.logger:info("Lua code executed successfully") + console.logger:info('Lua code executed successfully') console.logger:print(func()) return true else - console.logger:error("Error loading lua code: ", err) + console.logger:error('Error loading lua code: ', err) return false end - end, "Run lua code in the context of the game", - function(current_arg) return nil end, - "Usage: luarun ") + end, 'Run lua code in the context of the game', function(current_arg) + return nil + end, 'Usage: luarun ') - console:registerCommand("installmod", function(args) + console:registerCommand('installmod', function(args) local url = args[1] - local modInfo = { - id = "testmod", - url = url, - present = false, - needUpdate = true - } + local modInfo = {id = 'testmod', url = url, present = false, needUpdate = true} local result = installModFromTar(modInfo) if result == RESULT.SUCCESS then - console.logger:info("Mod installed successfully") + console.logger:info('Mod installed successfully') return true else - console.logger:error("Error installing mod: ", result) + console.logger:error('Error installing mod: ', result) return false end - end, "Install a mod from a tarball", - function(current_arg) return nil end, - "Usage: installmod ") - - console:registerCommand("booster", function(args) - local pack = get_pack("shop_pack", args[1] or nil) - G.FUNCS.use_card({ - config = {ref_table = Card(0, 0, 0, 0, pack, pack)} - }) - end, - "Generate a booster pack (random if no argument, otherwise specify type)", + end, 'Install a mod from a tarball', function(current_arg) + return nil + end, 'Usage: installmod ') + + local booster_list_shown = false + console:registerCommand('booster', function(args) + booster_list_shown = false + + local function pull_pack(_key, _type) + local cume, it, center = 0, 0, nil + for k, v in ipairs(G.P_CENTER_POOLS['Booster']) do + if (not _type or _type == v.kind) then + cume = cume + (v.weight or 1) + end + end + local poll = pseudorandom(pseudoseed((_key or 'pack_generic') .. G.GAME.round_resets.ante)) * cume + for k, v in ipairs(G.P_CENTER_POOLS['Booster']) do + if not _type or _type == v.kind then + it = it + (v.weight or 1) + end + if it >= poll and it - (v.weight or 1) <= poll then + center = v; + break + end + end + G.FUNCS.use_card({config = {ref_table = Card(0, 0, 0, 0, center, center)}}) + end + + local arg = args[1] or nil + + if arg == nil or arg == '' then + logger:info('Opening random booster pack') + pull_pack('shop_pack', nil) + return true + else + local kinds = {} + for k, v in pairs(G.P_CENTER_POOLS['Booster']) do + if string.lower(v.key) == string.lower(arg) then + logger:info('Opening the ' .. v.key .. ' booster pack.') + -- pull_pack(v.key, v.kind) + G.FUNCS.use_card({ + config = {ref_table = Card(0, 0, 0, 0, G.P_CENTERS[v.key], G.P_CENTERS[v.key])} + }) + return true + end + + if not table_contains(kinds, v.kind) then + table.insert(kinds, v.kind) + end + end + + for k, v in pairs(kinds) do + if (string.lower(v) == string.lower(arg)) then + logger:info('Opening a ' .. v .. ' booster pack.') + pull_pack('shop_pack', v) + return true + end + end + end + + logger:info('No booster pack matched ' .. arg) + end, 'Generate a booster pack from a key or kind (random if no argument, otherwise specify type)', function(current_arg) - local subcommands = { - "Standard", "Spectral", "Arcana", "Celestial", "Buffoon" - } + local subcommands = {} + + local kinds = {} + for k, v in pairs(G.P_CENTER_POOLS['Booster']) do + table.insert(subcommands, v.key) + + if not table_contains(kinds, v.kind) then + table.insert(kinds, v.kind) + end + end + + for i = #kinds, 1, -1 do + table.insert(subcommands, 1, kinds[i]) + end + + if (booster_list_shown == false) then + booster_list_shown = true + logger:info('Booster packs (' .. #G.P_CENTER_POOLS['Booster'] .. ' available)') + for k, v in pairs(G.P_CENTER_POOLS['Booster']) do + logger:info(v.key .. ' (' .. v.kind .. ')') + end + end + for i, v in ipairs(subcommands) do if v:find(current_arg, 1, true) == 1 then return {v} end end return nil - end, "Usage: booster [Standard/Spectral/Arcana/Celestial/Buffoon]") + end, 'Usage: booster [Standard/Spectral/Arcana/Celestial/Buffoon/p_arcana_normal_1/p_celestial_jumbo_2/...]') - console:registerCommand("enhance", function(args) + console:registerCommand('enhance', function(args) local arg = args[1] or nil if arg == nil then - logger:info("No enhancement specified") + logger:info('No enhancement specified') return end @@ -1040,79 +1076,63 @@ mods["dev_console"] = { if G.discard then table.insert(tables, G.discard.highlighted) end - if G.deck then table.insert(tables, G.deck.highlighted) end - if G.hand then table.insert(tables, G.hand.highlighted) end - if G.play then table.insert(tables, G.play.highlighted) end + if G.deck then + table.insert(tables, G.deck.highlighted) + end + if G.hand then + table.insert(tables, G.hand.highlighted) + end + if G.play then + table.insert(tables, G.play.highlighted) + end for i, t in ipairs(tables) do for j, card in ipairs(t) do if card.config then - logger:info("Enhancing " .. card.config.center.key) + logger:info('Enhancing ' .. card.config.center.key) for k, enhancement in pairs(args) do - for k, v in pairs(G.P_CENTER_POOLS["Default"]) do - local name = string.gsub(v.label, " ", "") - if enhancement == v.key or - string.lower(enhancement) == - string.lower(name) then - if (card.config.center.set == "Default" or - card.config.center.set == "Enhanced") then - card:set_ability(G.P_CENTERS[v.key], - nil, false) - logger:info( - "Card set to " .. v.label .. " (" .. - card.config.center.key .. ").") + for k, v in pairs(G.P_CENTER_POOLS['Default']) do + local name = string.gsub(v.label, ' ', '') + if enhancement == v.key or string.lower(enhancement) == string.lower(name) then + if (card.config.center.set == 'Default' or card.config.center.set == 'Enhanced') then + card:set_ability(G.P_CENTERS[v.key], nil, false) + logger:info('Card set to ' .. v.label .. ' (' .. card.config.center.key .. ').') else - logger:info( - "Only standard cards should be set to " .. - v.label .. " (" .. - card.config.center.key .. ").") + logger:info('Only standard cards should be set to ' .. v.label .. ' (' .. + card.config.center.key .. ').') end end end - for k, v in pairs(G.P_CENTER_POOLS["Enhanced"]) do - local name = string.gsub(v.label, " ", "") - if enhancement == v.key or - string.lower(enhancement) == - string.lower(name) then - if (card.config.center.set == "Default" or - card.config.center.set == "Enhanced") then - card:set_ability(G.P_CENTERS[v.key], - nil, false) - logger:info( - "Card set to " .. v.label .. " (" .. - card.config.center.key .. ").") + for k, v in pairs(G.P_CENTER_POOLS['Enhanced']) do + local name = string.gsub(v.label, ' ', '') + if enhancement == v.key or string.lower(enhancement) == string.lower(name) then + if (card.config.center.set == 'Default' or card.config.center.set == 'Enhanced') then + card:set_ability(G.P_CENTERS[v.key], nil, false) + logger:info('Card set to ' .. v.label .. ' (' .. card.config.center.key .. ').') else - logger:info( - "Only standard cards should be set to " .. - v.label .. " (" .. - card.config.center.key .. ").") + logger:info('Only standard cards should be set to ' .. v.label .. ' (' .. + card.config.center.key .. ').') end end end - for k, v in pairs(G.P_CENTER_POOLS["Edition"]) do - local name = string.gsub(v.name, " ", "") - if enhancement == v.key or - string.lower(enhancement) == - string.lower(name) then + for k, v in pairs(G.P_CENTER_POOLS['Edition']) do + local name = string.gsub(v.name, ' ', '') + if enhancement == v.key or string.lower(enhancement) == string.lower(name) then local editionKey = string.sub(v.key, 3) card:set_edition({[editionKey] = true}, true) - logger:info( - "Card set to " .. v.name .. " edition (" .. - card.config.center.key .. ").") + logger:info('Card set to ' .. v.name .. ' edition (' .. card.config.center.key .. + ').') end end - for k, v in pairs(G.P_CENTER_POOLS["Seal"]) do - if string.lower(enhancement) == - string.lower(v.key .. "Seal") then + for k, v in pairs(G.P_CENTER_POOLS['Seal']) do + if string.lower(enhancement) == string.lower(v.key .. 'Seal') then card:set_seal(v.key, true) - logger:info( - "Added " .. v.key .. " Seal to card (" .. - card.config.center.key .. ").") + logger:info('Added ' .. v.key .. ' Seal to card (' .. card.config.center.key .. ').') end end @@ -1121,31 +1141,33 @@ mods["dev_console"] = { end end end + + return true end, - "Add one or more enhancements to selected cards. Can use key or name (e.g. c_base or BaseCard, e_negative or Negative). Must add Seal to seals (e.g. RedSeal, BlueSeal)", + 'Add one or more enhancements to selected cards. Can use key or name (e.g. c_base or BaseCard, e_negative or Negative). Must add Seal to seals (e.g. RedSeal, BlueSeal)', function(current_arg, previous_arg) local subcommands = {} - for k, v in pairs(G.P_CENTER_POOLS["Default"]) do - local name = string.gsub(v.label, " ", "") + for k, v in pairs(G.P_CENTER_POOLS['Default']) do + local name = string.gsub(v.label, ' ', '') table.insert(subcommands, v.key) table.insert(subcommands, name) end - for k, v in pairs(G.P_CENTER_POOLS["Enhanced"]) do - local name = string.gsub(v.label, " ", "") + for k, v in pairs(G.P_CENTER_POOLS['Enhanced']) do + local name = string.gsub(v.label, ' ', '') table.insert(subcommands, v.key) table.insert(subcommands, name) end - for k, v in pairs(G.P_CENTER_POOLS["Edition"]) do - local name = string.gsub(v.name, " ", "") + for k, v in pairs(G.P_CENTER_POOLS['Edition']) do + local name = string.gsub(v.name, ' ', '') table.insert(subcommands, v.key) table.insert(subcommands, name) end - for k, v in pairs(G.P_CENTER_POOLS["Seal"]) do - table.insert(subcommands, v.key .. "Seal") + for k, v in pairs(G.P_CENTER_POOLS['Seal']) do + table.insert(subcommands, v.key .. 'Seal') end local completions = {} @@ -1156,27 +1178,27 @@ mods["dev_console"] = { end return completions end, - "Usage: enhance [arg2] [arg3] ...") + 'Usage: enhance [arg2] [arg3] ...') - console.logger:debug("Dev Console on_enable completed") + console.logger:debug('Dev Console on_enable completed') end, on_disable = function() - console.removeCommand("help") - console.removeCommand("shortcuts") - console.removeCommand("history") - console.removeCommand("clear") - console.removeCommand("exit") - console.removeCommand("quit") - console.removeCommand("give") - console.removeCommand("money") - console.removeCommand("discards") - console.removeCommand("hands") - console.removeCommand("booster") - console.removeCommand("enhance") - console.logger:debug("Dev Console disabled") + console.removeCommand('help') + console.removeCommand('shortcuts') + console.removeCommand('history') + console.removeCommand('clear') + console.removeCommand('exit') + console.removeCommand('quit') + console.removeCommand('give') + console.removeCommand('money') + console.removeCommand('discards') + console.removeCommand('hands') + console.removeCommand('booster') + console.removeCommand('enhance') + console.logger:debug('Dev Console disabled') end, on_key_pressed = function(key_name) - if key_name == "f2" then + if key_name == 'f2' then console:toggle() return true end @@ -1185,24 +1207,22 @@ mods["dev_console"] = { return true end - if key_name == "f4" then + if key_name == 'f4' then G.DEBUG = not G.DEBUG if G.DEBUG then - console.logger:info("Debug mode enabled") + console.logger:info('Debug mode enabled') else - console.logger:info("Debug mode disabled") + console.logger:info('Debug mode disabled') end end return false end, on_post_render = function() - console.max_lines = math.floor(love.graphics.getHeight() / - console.line_height) - 5 -- 5 lines of bottom padding + console.max_lines = math.floor(love.graphics.getHeight() / console.line_height) - 5 -- 5 lines of bottom padding local font = love.graphics.getFont() if console.is_open then love.graphics.setColor(0, 0, 0, 0.3) - love.graphics.rectangle("fill", 0, 0, love.graphics.getWidth(), - love.graphics.getHeight()) + love.graphics.rectangle('fill', 0, 0, love.graphics.getWidth(), love.graphics.getHeight()) local messagesToDisplay = console:getMessagesToDisplay() local i = 1 for _, message in ipairs(messagesToDisplay) do @@ -1210,8 +1230,7 @@ mods["dev_console"] = { love.graphics.setColor(r, g, b, 1) local formattedMessage = message:formatted() if font:getWidth(formattedMessage) > love.graphics.getWidth() then - local lines = console:wrapText(formattedMessage, - love.graphics.getWidth()) + local lines = console:wrapText(formattedMessage, love.graphics.getWidth()) for _, line in ipairs(lines) do love.graphics.print(line, 10, 10 + i * 20) i = i + 1 @@ -1226,37 +1245,37 @@ mods["dev_console"] = { end end, on_key_released = function(key_name) - if key_name == "capslock" then + if key_name == 'capslock' then console.modifiers.capslock = not console.modifiers.capslock console:modifiersListener() return end - if key_name == "scrolllock" then + if key_name == 'scrolllock' then console.modifiers.scrolllock = not console.modifiers.scrolllock console:modifiersListener() return end - if key_name == "numlock" then + if key_name == 'numlock' then console.modifiers.numlock = not console.modifiers.numlock console:modifiersListener() return end - if key_name == "lalt" or key_name == "ralt" then + if key_name == 'lalt' or key_name == 'ralt' then console.modifiers.alt = false console:modifiersListener() return false end - if key_name == "lctrl" or key_name == "rctrl" then + if key_name == 'lctrl' or key_name == 'rctrl' then console.modifiers.ctrl = false console:modifiersListener() return false end - if key_name == "lshift" or key_name == "rshift" then + if key_name == 'lshift' or key_name == 'rshift' then console.modifiers.shift = false console:modifiersListener() return false end - if key_name == "lgui" or key_name == "rgui" then + if key_name == 'lgui' or key_name == 'rgui' then console.modifiers.meta = false console:modifiersListener() return false @@ -1282,14 +1301,14 @@ mods["dev_console"] = { local function sortMods(mods) logger:trace('Sorting mods', utils.keys(mods)) local graph = {} - for modId, mod in pairs(mods) do graph[modId] = {before = {}} end + for modId, mod in pairs(mods) do + graph[modId] = {before = {}} + end logger:trace('Graph generated', graph) for modId, mod in pairs(mods) do for i, before in ipairs(mod.load_before or {}) do -- load_before is a list of mod ids, if its nil, use an empty table to avoid a crash if not graph[before] then - logger:error('Mod ', mod.id, - ' has a load_before field that references a non-existent mod: ', - before) + logger:error('Mod ', mod.id, ' has a load_before field that references a non-existent mod: ', before) return nil end graph[modId].before[before] = true -- we set to true just because we want a table behaving like a set() instead of an array @@ -1299,9 +1318,7 @@ local function sortMods(mods) -- this is equivalent to the other mod being loaded before the current mod -- so we add an edge from the other mod to the current mod if not graph[after] then - logger:error('Mod ', mod.id, - ' has a load_after field that references a non-existent mod: ', - after) + logger:error('Mod ', mod.id, ' has a load_after field that references a non-existent mod: ', after) return nil end graph[after].before[modId] = true -- we set to true just because we want a table behaving like a set() instead of an array @@ -1311,27 +1328,33 @@ local function sortMods(mods) local sorted = {} local visited = {} local function visit(node) - logger:trace("Visiting node ", node) - if visited[node] == "permanent" then - logger:trace("Node ", node, " already visited") + logger:trace('Visiting node ', node) + if visited[node] == 'permanent' then + logger:trace('Node ', node, ' already visited') return true end - if visited[node] == "temporary" then + if visited[node] == 'temporary' then logger:error('Mod ', node, ' has a circular dependency') return false end - visited[node] = "temporary" + visited[node] = 'temporary' for other, _ in pairs(graph[node].before) do - if not visit(other) then return false end + if not visit(other) then + return false + end end table.insert(sorted, node) - logger:trace("Inserted node ", node, " in sorted list", sorted) - logger:trace("Marking node ", node, " as visited") - visited[node] = "permanent" + logger:trace('Inserted node ', node, ' in sorted list', sorted) + logger:trace('Marking node ', node, ' as visited') + visited[node] = 'permanent' return true end - logger:trace("Starting to visit nodes") - for node, _ in pairs(graph) do if not visited[node] then visit(node) end end + logger:trace('Starting to visit nodes') + for node, _ in pairs(graph) do + if not visited[node] then + visit(node) + end + end local sortedMods = {} local modCount = #sorted -- we need to keep the mapping between the mod id and the mod object @@ -1343,7 +1366,7 @@ local function sortMods(mods) mod.order = modCount - i sortedMods[modId] = mod end - logger:trace("Built sorted mods", utils.keys(sortedMods)) + logger:trace('Built sorted mods', utils.keys(sortedMods)) return sortedMods end From 50c98d9e064d4d5247931dd8a686387d051889da Mon Sep 17 00:00:00 2001 From: Zei33 Date: Tue, 11 Jun 2024 17:11:51 +1000 Subject: [PATCH 14/15] Set table_contains to local. --- src/balamod.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/balamod.lua b/src/balamod.lua index aca4416..01599db 100644 --- a/src/balamod.lua +++ b/src/balamod.lua @@ -61,7 +61,7 @@ function buildPaths(root, ignore) end end -function table_contains(table, element) +local function table_contains(table, element) for _, value in pairs(table) do if value == element then return value From 2ca2fbb49710a56a2565d70eab243ccf43d580f5 Mon Sep 17 00:00:00 2001 From: Zei33 Date: Tue, 11 Jun 2024 17:13:15 +1000 Subject: [PATCH 15/15] Removed commented code. --- src/balamod.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/src/balamod.lua b/src/balamod.lua index 01599db..a948cbd 100644 --- a/src/balamod.lua +++ b/src/balamod.lua @@ -991,7 +991,6 @@ mods['dev_console'] = { for k, v in pairs(G.P_CENTER_POOLS['Booster']) do if string.lower(v.key) == string.lower(arg) then logger:info('Opening the ' .. v.key .. ' booster pack.') - -- pull_pack(v.key, v.kind) G.FUNCS.use_card({ config = {ref_table = Card(0, 0, 0, 0, G.P_CENTERS[v.key], G.P_CENTERS[v.key])} })