756 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
			
		
		
	
	
			756 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
local util = require("debugplus.util")
 | 
						|
local utf8 = require("utf8")
 | 
						|
local watcher = require("debugplus.watcher")
 | 
						|
local config = require("debugplus.config")
 | 
						|
local logger = require("debugplus.logger")
 | 
						|
 | 
						|
local global = {}
 | 
						|
 | 
						|
local showTime = 5 -- Amount of time new console messages show up 
 | 
						|
local fadeTime = 1 -- Amount of time it takes for a message to fade
 | 
						|
local consoleOpen = false
 | 
						|
local openNextFrame = false
 | 
						|
local gameKeyRepeat = love.keyboard.hasKeyRepeat()
 | 
						|
local gameTextInput = love.keyboard.hasTextInput()
 | 
						|
local showNewLogs = config.getValue("showNewLogs")
 | 
						|
local firstConsoleRender = nil
 | 
						|
---@type string[]
 | 
						|
local history = {}
 | 
						|
---@type {index: number, val: string}
 | 
						|
local currentHistory = nil
 | 
						|
---@type {name: string, source: string, desc: string, shortDesc: string, exec: fun(args: string[], rawArgs: string, dp: table): string, string?, table?}[]
 | 
						|
local commands = nil
 | 
						|
local logOffset = 0
 | 
						|
 | 
						|
commands = {{
 | 
						|
    name = "echo",
 | 
						|
    source = "debugplus",
 | 
						|
    shortDesc = "Repeat's what you say",
 | 
						|
    desc = "Mostly just a testing command. Outputs what you input.",
 | 
						|
    exec = function(args, rawArgs, dp)
 | 
						|
        return rawArgs
 | 
						|
    end
 | 
						|
}, {
 | 
						|
    name = "help",
 | 
						|
    source = "debugplus",
 | 
						|
    shortDesc = "Get command info",
 | 
						|
    desc = "Get's help about commands. When run without args, lists all commands and their short descriptions. When run with a command name, shows info about that command.",
 | 
						|
    exec = function(args, rawArgs, dp)
 | 
						|
        local toLookup = args[1]
 | 
						|
        if not toLookup then
 | 
						|
            local out = "Help:\nBelow is a list of commands.\n"
 | 
						|
            for k, v in ipairs(commands) do
 | 
						|
                out = out .. v.name .. ": " .. v.shortDesc .. "\n"
 | 
						|
            end
 | 
						|
            out = out .. "\nFor more information about a specific command, run 'help <commandName>'"
 | 
						|
            return out
 | 
						|
        end
 | 
						|
        local cmdName = string.lower(string.gsub(toLookup, "^(%S+).*", "%1"))
 | 
						|
        local cmd
 | 
						|
        for i, c in ipairs(commands) do
 | 
						|
            if c.source .. ":" .. c.name == cmdName then
 | 
						|
                cmd = c
 | 
						|
                break
 | 
						|
            end
 | 
						|
            if c.name == cmdName then
 | 
						|
                cmd = c
 | 
						|
                break
 | 
						|
            end
 | 
						|
        end
 | 
						|
        if not cmd then
 | 
						|
            return '"' .. cmdName .. '" could not be found. To see a list of all commands, run "help" without any args',
 | 
						|
                "ERROR"
 | 
						|
        end
 | 
						|
        return cmd.name .. ":\n" .. cmd.desc .. "\n\nThis command can be run by typing '" .. cmd.name .. "' or '" ..
 | 
						|
                   cmd.source .. ":" .. cmd.name .. "'."
 | 
						|
    end
 | 
						|
}, {
 | 
						|
    name = "eval",
 | 
						|
    source = "debugplus",
 | 
						|
    shortDesc = "Evaluate lua code",
 | 
						|
    desc = "Execute's lua code. This code has access to all the globals that the game has, as well as a dp object, with some DebugPlus specific stuff.",
 | 
						|
    exec = function(args, rawArgs, dp)
 | 
						|
        local env = {}
 | 
						|
        for k, v in pairs(_G) do
 | 
						|
            env[k] = v
 | 
						|
        end
 | 
						|
        env.dp = dp
 | 
						|
        local func, err = load("return " .. rawArgs, "DebugPlus Eval", "t", env)
 | 
						|
        if not func then
 | 
						|
            func, err = load(rawArgs, "DebugPlus Eval", "t", env)
 | 
						|
        end
 | 
						|
        if not func then
 | 
						|
            return "Syntax Error: " .. err, "ERROR"
 | 
						|
        end
 | 
						|
        local res = util.pack(pcall(func))
 | 
						|
        local success = table.remove(res, 1)
 | 
						|
        res.n = res.n - 1
 | 
						|
        local resString = ""
 | 
						|
        if res.n > 1 then
 | 
						|
            for k = 1, res.n do
 | 
						|
                local v = res[k]
 | 
						|
                if k ~= 1 then
 | 
						|
                    resString = resString .. ", "
 | 
						|
                end
 | 
						|
                resString = resString .. util.stringifyTable(v)
 | 
						|
            end
 | 
						|
        else
 | 
						|
            resString = util.stringifyTable(res[1])
 | 
						|
        end
 | 
						|
        if not success then
 | 
						|
            return "Error: " .. resString, "ERROR"
 | 
						|
        end
 | 
						|
        return resString
 | 
						|
    end
 | 
						|
}, {
 | 
						|
    name = "money",
 | 
						|
    source = "debugplus",
 | 
						|
    shortDesc = "Set or add money",
 | 
						|
    desc = "Set or add to your money. Usage:\nmoney set [amount] - Set your money to the given amount\nmoney add [amount] - Adds the given amount to your money.",
 | 
						|
    exec = function(args, rawArgs, dp)
 | 
						|
        if G.STAGE ~= G.STAGES.RUN then
 | 
						|
            return "This command must be run during a run.", "ERROR"
 | 
						|
        end
 | 
						|
        local subCmd = args[1]
 | 
						|
        local amount = tonumber(args[2])
 | 
						|
        if subCmd == "set" then
 | 
						|
            if not amount then
 | 
						|
                return "Please provide a valid number to set/add.", "ERROR"
 | 
						|
            end
 | 
						|
            G.GAME.dollars = amount
 | 
						|
        elseif subCmd == "add" then
 | 
						|
            if not amount then
 | 
						|
                return "Please provide a valid number to set/add.", "ERROR"
 | 
						|
            end
 | 
						|
            G.GAME.dollars = G.GAME.dollars + amount
 | 
						|
        else
 | 
						|
            return "Please choose whether you want to add or set. For more info, run 'help money'"
 | 
						|
        end
 | 
						|
        return "Money is now $" .. G.GAME.dollars
 | 
						|
    end
 | 
						|
}, {
 | 
						|
    name = "round",
 | 
						|
    source = "debugplus",
 | 
						|
    shortDesc = "Set or add to your round",
 | 
						|
    desc = "Set or add to your round. Usage:\nround set [amount] - Set the current round to the given amount\nround add [amount] - Adds the given number of rounds.",
 | 
						|
    exec = function(args, rawArgs, dp)
 | 
						|
        if G.STAGE ~= G.STAGES.RUN then
 | 
						|
            return "This command must be run during a run.", "ERROR"
 | 
						|
        end
 | 
						|
        local subCmd = args[1]
 | 
						|
        local amount = tonumber(args[2])
 | 
						|
        if subCmd == "set" then
 | 
						|
            if not amount then
 | 
						|
                return "Please provide a valid number to set/add.", "ERROR"
 | 
						|
            end
 | 
						|
            G.GAME.round = amount
 | 
						|
        elseif subCmd == "add" then
 | 
						|
            if not amount then
 | 
						|
                return "Please provide a valid number to set/add.", "ERROR"
 | 
						|
            end
 | 
						|
            G.GAME.round = G.GAME.round + amount
 | 
						|
        else
 | 
						|
            return "Please choose whether you want to add or set. For more info, run 'help round'"
 | 
						|
        end
 | 
						|
        return "Round is now " .. G.GAME.round
 | 
						|
    end
 | 
						|
}, {
 | 
						|
    name = "ante",
 | 
						|
    source = "debugplus",
 | 
						|
    shortDesc = "Set or add to your ante",
 | 
						|
    desc = "Set or add to your ante. Usage:\nante set [amount] - Set the current ante to the given amount\nante add [amount] - Adds the given number of antes.",
 | 
						|
    exec = function(args, rawArgs, dp)
 | 
						|
        if G.STAGE ~= G.STAGES.RUN then
 | 
						|
            return "This command must be run during a run.", "ERROR"
 | 
						|
        end
 | 
						|
        local subCmd = args[1]
 | 
						|
        local amount = tonumber(args[2])
 | 
						|
        if subCmd == "set" then
 | 
						|
            if not amount then
 | 
						|
                return "Please provide a valid number to set/add.", "ERROR"
 | 
						|
            end
 | 
						|
            G.GAME.round_resets.ante = amount
 | 
						|
        elseif subCmd == "add" then
 | 
						|
            if not amount then
 | 
						|
                return "Please provide a valid number to set/add.", "ERROR"
 | 
						|
            end
 | 
						|
            G.GAME.round_resets.ante = G.GAME.round_resets.ante + amount
 | 
						|
        else
 | 
						|
            return "Please choose whether you want to add or set. For more info, run 'help ante'"
 | 
						|
        end
 | 
						|
        return "Ante is now " .. G.GAME.round_resets.ante
 | 
						|
    end
 | 
						|
}, {
 | 
						|
    name = "discards",
 | 
						|
    source = "debugplus",
 | 
						|
    shortDesc = "Set or add to your hand",
 | 
						|
    desc = "Set or add to your hand. Usage:\ndiscards set [amount] - Set the current hand to the given amount\ndiscards add [amount] - Adds the given number of discards.",
 | 
						|
    exec = function(args, rawArgs, dp)
 | 
						|
        if G.STAGE ~= G.STAGES.RUN then
 | 
						|
            return "This command must be run during a run.", "ERROR"
 | 
						|
        end
 | 
						|
        local subCmd = args[1]
 | 
						|
        local amount = tonumber(args[2])
 | 
						|
        if subCmd == "set" then
 | 
						|
            if not amount then
 | 
						|
                return "Please provide a valid number to set/add.", "ERROR"
 | 
						|
            end
 | 
						|
            G.GAME.current_round.discards_left = amount
 | 
						|
        elseif subCmd == "add" then
 | 
						|
            if not amount then
 | 
						|
                return "Please provide a valid number to set/add.", "ERROR"
 | 
						|
            end
 | 
						|
            G.GAME.current_round.discards_left = G.GAME.current_round.discards_left + amount
 | 
						|
        else
 | 
						|
            return "Please choose whether you want to add or set. For more info, run 'help hand'"
 | 
						|
        end
 | 
						|
        return "Discards are now " .. G.GAME.current_round.discards_left
 | 
						|
    end
 | 
						|
}, {
 | 
						|
    name = "hands",
 | 
						|
    source = "debugplus",
 | 
						|
    shortDesc = "Set or add to your hand",
 | 
						|
    desc = "Set or add to your hand. Usage:\nhands set [amount] - Set the current hand to the given amount\nhands add [amount] - Adds the given number of hands.",
 | 
						|
    exec = function(args, rawArgs, dp)
 | 
						|
        if G.STAGE ~= G.STAGES.RUN then
 | 
						|
            return "This command must be run during a run.", "ERROR"
 | 
						|
        end
 | 
						|
        local subCmd = args[1]
 | 
						|
        local amount = tonumber(args[2])
 | 
						|
        if subCmd == "set" then
 | 
						|
            if not amount then
 | 
						|
                return "Please provide a valid number to set/add.", "ERROR"
 | 
						|
            end
 | 
						|
            G.GAME.current_round.hands_left = amount
 | 
						|
        elseif subCmd == "add" then
 | 
						|
            if not amount then
 | 
						|
                return "Please provide a valid number to set/add.", "ERROR"
 | 
						|
            end
 | 
						|
            G.GAME.current_round.hands_left = G.GAME.current_round.hands_left + amount
 | 
						|
        else
 | 
						|
            return "Please choose whether you want to add or set. For more info, run 'help hand'"
 | 
						|
        end
 | 
						|
        return "Hands are now " .. G.GAME.current_round.hands_left
 | 
						|
    end
 | 
						|
}, {
 | 
						|
    name = "watch",
 | 
						|
    source = "debugplus",
 | 
						|
    shortDesc = "Watch and execute a file when it changes.",
 | 
						|
    desc = "Watch and execute a file when it changes. Usage:\nwatch stop - Stop's watching files.\n".. watcher.subCommandDesc .."Files should be a relative path to a file in the save directory (e.g. `Mods/Example/test.lua`)",
 | 
						|
    exec = function(args, rawArgs, dp)
 | 
						|
        local subCmd = args[1]
 | 
						|
        local file = args[2]
 | 
						|
        if subCmd == "stop" then
 | 
						|
            watcher.stopWatching()
 | 
						|
            return "I will stop watching for file changes."
 | 
						|
        elseif watcher.types[subCmd] then
 | 
						|
            local succ, err = watcher.startWatching(file, subCmd)
 | 
						|
            if not succ then return err, "ERROR" end
 | 
						|
            return "Started watching " .. file
 | 
						|
        else
 | 
						|
            return "Please provide a valid sub command. For more info, run 'help watch'"
 | 
						|
        end
 | 
						|
    end
 | 
						|
}, {
 | 
						|
    name = "tutorial",
 | 
						|
    source = "debugplus",
 | 
						|
    shortDesc = "Modify the tutorial state.",
 | 
						|
    desc = "Modify the tutorial state. Usage:\ntutorial finish - Finish the tutorial.\ntutorial reset - Reset the tutorial progress to a fresh state.\ntutorial new - Starts a new tutorial run (like hitting play for the first time)",
 | 
						|
    exec = function(args, rawArgs, dp)
 | 
						|
        local subCmd = args[1]
 | 
						|
        if subCmd == "finish" then
 | 
						|
            if G.OVERLAY_TUTORIAL then
 | 
						|
                G.FUNCS.skip_tutorial_section()
 | 
						|
            end
 | 
						|
            G.SETTINGS.tutorial_complete = true
 | 
						|
            G.SETTINGS.tutorial_progress = nil
 | 
						|
            return "Tutorial finished."
 | 
						|
        elseif subCmd == "reset" then
 | 
						|
            G.SETTINGS.tutorial_complete = false
 | 
						|
            G.SETTINGS.tutorial_progress = {
 | 
						|
                forced_shop = {'j_joker', 'c_empress'},
 | 
						|
                forced_voucher = 'v_grabber',
 | 
						|
                forced_tags = {'tag_handy', 'tag_garbage'},
 | 
						|
                hold_parts = {},
 | 
						|
                completed_parts = {}
 | 
						|
            }
 | 
						|
            return "Tutorial reset."
 | 
						|
        elseif subCmd == "new" then
 | 
						|
            G.FUNCS.start_tutorial()
 | 
						|
            return "Starting a new run."
 | 
						|
        else
 | 
						|
            return "Please provide a valid sub command. For more info, run 'help tutorial'"
 | 
						|
        end
 | 
						|
    end
 | 
						|
}, {
 | 
						|
    name = "resetshop",
 | 
						|
    source = "debugplus",
 | 
						|
    shortDesc = "Reset the shop.",
 | 
						|
    desc = "Resets the shop.",
 | 
						|
    exec = function(args, rawArgs, dp)
 | 
						|
        if G.STATE ~= G.STATES.SHOP then
 | 
						|
            return "This command can only be run in a shop.", 'ERROR'
 | 
						|
        end
 | 
						|
        G.shop:remove()
 | 
						|
        G.shop = nil
 | 
						|
        G.SHOP_SIGN:remove()
 | 
						|
        G.SHOP_SIGN = nil
 | 
						|
        G.GAME.current_round.used_packs = nil
 | 
						|
        G.STATE_COMPLETE = false
 | 
						|
        G:update_shop()
 | 
						|
        return "Reset shop."
 | 
						|
    end
 | 
						|
}, {
 | 
						|
    name = "value",
 | 
						|
    source = "debugplus",
 | 
						|
    shortDesc = "Get and modify highlighted card values",
 | 
						|
    desc = "Retrives or modifies the values of the currently hovered card. Usage:\nvalue get - Gets all detected values on the hovered card.\nvalue set [keys] [value] - Modifies a value of hovered card. The format of keys should match the 'get' command.\nvalue set_center [keys] [value] - Modifies a value on the center of the hovered card. This will modify future versions of the card.",
 | 
						|
    exec = function (args, rawArgs, dp)
 | 
						|
        local unmodified_vals = {
 | 
						|
            bonus = 0,
 | 
						|
            perma_bonus = 0,
 | 
						|
            extra_value = 0,
 | 
						|
            p_dollars = 0,
 | 
						|
            h_mult = 0,
 | 
						|
            h_x_mult = 0,
 | 
						|
            h_dollars = 0,
 | 
						|
            h_size = 0,
 | 
						|
            d_size = 0,
 | 
						|
            hands_played_at_create = 0,
 | 
						|
            mult = 0,
 | 
						|
            x_mult = 1,
 | 
						|
            e_mult = 0,
 | 
						|
            ee_mult = 0,
 | 
						|
            eee_mult = 0,
 | 
						|
            x_chips = 0,
 | 
						|
            e_chips = 0,
 | 
						|
            ee_chips = 0,
 | 
						|
            eee_chips = 0,
 | 
						|
            t_mult = 0,
 | 
						|
            t_chips = 0,
 | 
						|
        }
 | 
						|
        local ignore_vals = {
 | 
						|
            name = true,
 | 
						|
            set = true,
 | 
						|
            order = true,
 | 
						|
            consumeable = true
 | 
						|
        }
 | 
						|
        if dp.hovered:is(Card) then
 | 
						|
            if args[1] == "get" then
 | 
						|
                local values = "Values:"
 | 
						|
                for k, v in pairs(dp.hovered.ability) do
 | 
						|
                    if (not ignore_vals[k]) and (not unmodified_vals[k] or unmodified_vals[k] ~= dp.hovered.ability[k]) then
 | 
						|
                        if k == "hyper_chips" or k == "hyper_mult" then
 | 
						|
                            if dp.hovered.ability[k][1] ~= 0 or dp.hovered.ability[k][2] ~= 0 then
 | 
						|
                                values = values .. "\n" .. tostring(k) .. " " .. tostring(dp.hovered.ability[k][1]) .. " " .. tostring(dp.hovered.ability[k][2])
 | 
						|
                            end
 | 
						|
                        elseif type(dp.hovered.ability[k]) == "table" then
 | 
						|
                            for kk, vv in pairs(dp.hovered.ability[k]) do
 | 
						|
                                values = values .. "\n" .. tostring(k) .. " " .. tostring(kk) .. " " .. tostring(vv)
 | 
						|
                            end
 | 
						|
                        elseif dp.hovered.ability[k] ~= "" then
 | 
						|
                            values = values .. "\n" .. tostring(k) .. " " .. tostring(dp.hovered.ability[k])
 | 
						|
                        end
 | 
						|
                    end
 | 
						|
                end
 | 
						|
                return values
 | 
						|
            elseif args[1] == "set" or args[1] == "set_center" then
 | 
						|
                local root = dp.hovered.ability
 | 
						|
                if args[1] == "set_center" then
 | 
						|
                    root = dp.hovered.config.center.config
 | 
						|
                end
 | 
						|
                local rootC
 | 
						|
                if dp.hovered.ability.consumeable then
 | 
						|
                    rootC = root.consumeable
 | 
						|
                end
 | 
						|
                if #args < 2 then
 | 
						|
                    return "Please provide a key to set", "ERROR"
 | 
						|
                end
 | 
						|
                if #args < 3 then
 | 
						|
                    return "Please provide a value to set", "ERROR"
 | 
						|
                end
 | 
						|
                for i = 2, #args-2 do
 | 
						|
                    root = root[args[i]]
 | 
						|
                    if rootC then rootC = rootC[args[i]] end
 | 
						|
                end
 | 
						|
                if tonumber(args[#args]) then --number
 | 
						|
                    root[args[#args-1]] = tonumber(args[#args])
 | 
						|
                    if rootC then rootC[args[#args-1]] = tonumber(args[#args]) end
 | 
						|
                elseif args[#args] == "true" then --bool
 | 
						|
                    root[args[#args-1]] = true
 | 
						|
                    if rootC then rootC[args[#args-1]] = true end
 | 
						|
                elseif args[#args] == "false" then
 | 
						|
                    root[args[#args-1]] = false
 | 
						|
                    if rootC then rootC[args[#args-1]] = false end
 | 
						|
                else
 | 
						|
                    root[args[#args-1]] = args[#args]
 | 
						|
                    if rootC then rootC[args[#args-1]] = args[#args] end
 | 
						|
                end
 | 
						|
                return "Value set successfully."
 | 
						|
            else
 | 
						|
                return "Invalid argument. Use 'get' or 'set' or 'set_center'.", "ERROR"
 | 
						|
            end
 | 
						|
        else
 | 
						|
            return "This command only works while hovering over a card. Rerun it while hovering over a card.", "ERROR"
 | 
						|
        end
 | 
						|
    end
 | 
						|
}}
 | 
						|
local inputText = ""
 | 
						|
 | 
						|
local function runCommand()
 | 
						|
    inputText = util.trim(inputText)
 | 
						|
    if inputText == "" then
 | 
						|
        return
 | 
						|
    end
 | 
						|
 | 
						|
    logger.handleLog({1, 0, 1}, "INFO", "> " .. inputText)
 | 
						|
    if history[1] ~= inputText then
 | 
						|
        table.insert(history, 1, inputText)
 | 
						|
    end
 | 
						|
 | 
						|
    local cmdName = string.lower(string.gsub(inputText, "^(%S+).*", "%1"))
 | 
						|
    local rawArgs = string.gsub(inputText, "^%S+%s*(.*)", "%1")
 | 
						|
    local args = {}
 | 
						|
    for w in string.gmatch(rawArgs, "%S+") do
 | 
						|
        table.insert(args, w)
 | 
						|
    end
 | 
						|
 | 
						|
    inputText = ""
 | 
						|
    consoleOpen = false
 | 
						|
	love.keyboard.setKeyRepeat(gameKeyRepeat)
 | 
						|
	love.keyboard.setTextInput(gameTextInput)
 | 
						|
 | 
						|
    local cmd
 | 
						|
    for i, c in ipairs(commands) do
 | 
						|
        if c.source .. ":" .. c.name == cmdName then
 | 
						|
            cmd = c
 | 
						|
            break
 | 
						|
        end
 | 
						|
        if c.name == cmdName then
 | 
						|
            cmd = c
 | 
						|
            break
 | 
						|
        end
 | 
						|
    end
 | 
						|
    if not cmd then
 | 
						|
        return logger.handleLog({1, 0, 0}, "ERROR", "< ERROR: Command '" .. cmdName .. "' not found.")
 | 
						|
    end
 | 
						|
    local dp = {
 | 
						|
        hovered = G and G.CONTROLLER and G.CONTROLLER.hovering.target,
 | 
						|
        handleLog = logger.handleLog
 | 
						|
    }
 | 
						|
    local success, result, loglevel, colourOverride = pcall(cmd.exec, args, rawArgs, dp)
 | 
						|
    if not success then
 | 
						|
        return logger.handleLog({1, 0, 0}, "ERROR", "< An error occurred processing the command:", result)
 | 
						|
    end
 | 
						|
    local level = loglevel or "INFO"
 | 
						|
    if not logger.levelMeta[level] then
 | 
						|
        level = "INFO"
 | 
						|
        logger.handleLogAdvanced({
 | 
						|
            level = "WARN",
 | 
						|
        }, "[DebugPlus] Command ".. cmdName.. " returned an invalid log level. Defaulting to INFO.")
 | 
						|
    end
 | 
						|
    local colour = colourOverride or logger.levelMeta[level].colour
 | 
						|
    if success and success ~= "" then
 | 
						|
        return logger.handleLog(colour, level, "<", result)
 | 
						|
    else
 | 
						|
        return logger.handleLog(colour, level, "< Command exited without a response.")
 | 
						|
    end
 | 
						|
end
 | 
						|
 | 
						|
function global.consoleHandleKey(key)
 | 
						|
    if not consoleOpen then
 | 
						|
        if key == '/' or key == 'kp/' then
 | 
						|
            if util.isShiftDown() then
 | 
						|
                showNewLogs = not showNewLogs
 | 
						|
            else
 | 
						|
                openNextFrame = true -- This is to prevent the keyboard handler from typing this key
 | 
						|
            end
 | 
						|
        end
 | 
						|
        return true
 | 
						|
    end
 | 
						|
 | 
						|
    if key == "escape" then
 | 
						|
        consoleOpen = false
 | 
						|
		love.keyboard.setKeyRepeat(gameKeyRepeat)
 | 
						|
		love.keyboard.setTextInput(gameTextInput)
 | 
						|
		inputText = ""
 | 
						|
	end
 | 
						|
    -- This bit stolen from https://love2d.org/wiki/love.textinput
 | 
						|
    if key == "backspace" then
 | 
						|
        -- get the byte offset to the last UTF-8 character in the string.
 | 
						|
        local byteoffset = utf8.offset(inputText, -1)
 | 
						|
 | 
						|
        if byteoffset then
 | 
						|
            -- remove the last UTF-8 character.
 | 
						|
            -- string.sub operates on bytes rather than UTF-8 characters, so we couldn't do string.sub(text, 1, -2).
 | 
						|
            inputText = string.sub(inputText, 1, byteoffset - 1)
 | 
						|
        end
 | 
						|
    end
 | 
						|
 | 
						|
    if key == "return" then
 | 
						|
        if util.isShiftDown() then
 | 
						|
            inputText = inputText .. "\n"
 | 
						|
        else
 | 
						|
            runCommand()
 | 
						|
        end
 | 
						|
    end
 | 
						|
 | 
						|
    if key == "v" and util.isCtrlDown() then
 | 
						|
        inputText = inputText .. love.system.getClipboardText()
 | 
						|
    end
 | 
						|
 | 
						|
    if key == "up" then
 | 
						|
        if currentHistory.index >= #history then
 | 
						|
            return
 | 
						|
        end
 | 
						|
        if currentHistory.index == 0 then
 | 
						|
            currentHistory.val = inputText
 | 
						|
        end
 | 
						|
        currentHistory.index = currentHistory.index + 1
 | 
						|
        inputText = history[currentHistory.index]
 | 
						|
    end
 | 
						|
 | 
						|
    if key == "down" then
 | 
						|
        if currentHistory.index <= 0 then
 | 
						|
            return
 | 
						|
        end
 | 
						|
        currentHistory.index = currentHistory.index - 1
 | 
						|
        if currentHistory.index == 0 then
 | 
						|
            inputText = currentHistory.val
 | 
						|
        else
 | 
						|
            inputText = history[currentHistory.index]
 | 
						|
        end
 | 
						|
    end
 | 
						|
 | 
						|
end
 | 
						|
 | 
						|
local orig_textinput
 | 
						|
local function textinput(t)
 | 
						|
    if not consoleOpen then
 | 
						|
        if orig_textinput then
 | 
						|
            orig_textinput(t)
 | 
						|
        end -- That way if another mod uses this, I don't clobber it's implementation
 | 
						|
        return
 | 
						|
    end
 | 
						|
    inputText = inputText .. t
 | 
						|
end
 | 
						|
 | 
						|
local orig_wheelmoved
 | 
						|
local function wheelmoved(x, y)
 | 
						|
    if not consoleOpen then
 | 
						|
        if orig_wheelmoved then
 | 
						|
            orig_wheelmoved(x, y)
 | 
						|
        end
 | 
						|
        return
 | 
						|
    end
 | 
						|
    logOffset = math.min(math.max(logOffset + y, 0), #logger.logs - 1)
 | 
						|
end
 | 
						|
 | 
						|
local function hookStuffs()
 | 
						|
    orig_textinput = love.textinput
 | 
						|
    love.textinput = textinput
 | 
						|
 | 
						|
    orig_wheelmoved = love.wheelmoved
 | 
						|
    love.wheelmoved = wheelmoved
 | 
						|
end
 | 
						|
 | 
						|
local function calcHeight(text, width)
 | 
						|
    local font = love.graphics.getFont()
 | 
						|
    local rw, lines = font:getWrap(text, width)
 | 
						|
    local lineHeight = font:getHeight()
 | 
						|
 | 
						|
    return #lines * lineHeight, rw, lineHeight
 | 
						|
end
 | 
						|
 | 
						|
local function hyjackErrorHandler()
 | 
						|
    local orig = love.errorhandler
 | 
						|
    if not orig then -- Vanilla
 | 
						|
        return -- Doesn't work with love.errhand (need love.errorhandler)
 | 
						|
    end
 | 
						|
    local function safeCall(func, ...)
 | 
						|
        local succ, res = pcall(func, ...)
 | 
						|
        if not succ then print("ERROR", res)
 | 
						|
        else return res end
 | 
						|
    end
 | 
						|
    function love.errorhandler(msg)
 | 
						|
        local ret = orig(msg)
 | 
						|
        orig_wheelmoved = nil
 | 
						|
        orig_textinput = nil
 | 
						|
        consoleOpen = false
 | 
						|
        inputText = ""
 | 
						|
        love.keyboard.setKeyRepeat(gameKeyRepeat)
 | 
						|
        love.keyboard.setTextInput(gameTextInput)
 | 
						|
        local justCrashed = true
 | 
						|
 | 
						|
        local present = love.graphics.present
 | 
						|
        function love.graphics.present()
 | 
						|
            local r, g, b, a = love.graphics.getColor()
 | 
						|
            if justCrashed then
 | 
						|
                firstConsoleRender = love.timer.getTime()
 | 
						|
                justCrashed = false
 | 
						|
            end
 | 
						|
            safeCall(global.doConsoleRender)
 | 
						|
            love.graphics.setColor(r,g,b,a)
 | 
						|
            present()
 | 
						|
        end
 | 
						|
 | 
						|
        return function()
 | 
						|
            love.event.pump()
 | 
						|
 | 
						|
            local evts = {}
 | 
						|
 | 
						|
            for e, a, b, c in love.event.poll() do
 | 
						|
                if consoleOpen and e == "textinput" then
 | 
						|
                    safeCall(textinput, a)
 | 
						|
                elseif consoleOpen and e == "wheelmoved" then
 | 
						|
                    safeCall(wheelmoved, a, b)
 | 
						|
                elseif e == "keypressed" then
 | 
						|
                    if safeCall(global.consoleHandleKey, a) then
 | 
						|
                        table.insert(evts, {e,a,b,c})
 | 
						|
                    end
 | 
						|
                else
 | 
						|
                    table.insert(evts, {e,a,b,c})
 | 
						|
                end
 | 
						|
            end
 | 
						|
            for _,v in ipairs(evts) do -- Add back for the original handler
 | 
						|
                love.event.push(unpack(v))
 | 
						|
            end
 | 
						|
            return ret()
 | 
						|
        end
 | 
						|
    end
 | 
						|
end
 | 
						|
 | 
						|
function global.doConsoleRender()
 | 
						|
    if openNextFrame then
 | 
						|
        consoleOpen = true
 | 
						|
        openNextFrame = false
 | 
						|
        currentHistory = {
 | 
						|
            index = 0,
 | 
						|
            val = ""
 | 
						|
        }
 | 
						|
        logOffset = 0
 | 
						|
		gameKeyRepeat = love.keyboard.hasKeyRepeat()
 | 
						|
		gameTextInput = love.keyboard.hasTextInput()
 | 
						|
		love.keyboard.setKeyRepeat(true)
 | 
						|
		love.keyboard.setTextInput(true)
 | 
						|
    end
 | 
						|
    if not consoleOpen and not showNewLogs then
 | 
						|
        return
 | 
						|
    end
 | 
						|
    -- Setup
 | 
						|
    local width, height = love.graphics.getDimensions()
 | 
						|
    local padding = 10
 | 
						|
    local lineWidth = width - padding * 2
 | 
						|
    local bottom = height - padding * 2
 | 
						|
    local now = love.timer.getTime()
 | 
						|
    if firstConsoleRender == nil then
 | 
						|
        if config.getValue("hyjackErrorHandler") then hyjackErrorHandler() end
 | 
						|
        hookStuffs()
 | 
						|
        firstConsoleRender = now
 | 
						|
        logger.log("Press [/] to toggle console and press [shift] + [/] to toggle new log previews")
 | 
						|
    end
 | 
						|
    -- Input Box
 | 
						|
    love.graphics.setColor(0, 0, 0, .5)
 | 
						|
    if consoleOpen then
 | 
						|
        bottom = bottom - padding * 2
 | 
						|
        local text = "> " .. inputText
 | 
						|
        local lineHeight, realWidth, singleLineHeight = calcHeight(text, lineWidth)
 | 
						|
        love.graphics.rectangle("fill", padding, bottom - lineHeight + padding, lineWidth, lineHeight + padding * 2)
 | 
						|
        love.graphics.setColor(1, 1, 1, 1)
 | 
						|
        love.graphics.printf(text, padding * 2, bottom - lineHeight + singleLineHeight, lineWidth - padding * 2)
 | 
						|
 | 
						|
        bottom = bottom - lineHeight - padding * 2
 | 
						|
    end
 | 
						|
 | 
						|
    -- Main window
 | 
						|
    if consoleOpen then
 | 
						|
        love.graphics.setColor(0, 0, 0, .5)
 | 
						|
        love.graphics.rectangle("fill", padding, padding, lineWidth, bottom)
 | 
						|
    end
 | 
						|
    for i = #logger.logs, 1, -1 do
 | 
						|
        local v = logger.logs[i]
 | 
						|
        if consoleOpen and #logger.logs - logOffset < i then -- TODO: could this be more efficent?
 | 
						|
            goto finishrender
 | 
						|
        end
 | 
						|
        if not consoleOpen and v.time < firstConsoleRender then
 | 
						|
            break
 | 
						|
        end
 | 
						|
        local age = now - v.time
 | 
						|
        if not consoleOpen and age > showTime + fadeTime then
 | 
						|
            break
 | 
						|
        end
 | 
						|
        if not logger.levelMeta[v.level].shouldShow and not v.command then
 | 
						|
            goto finishrender
 | 
						|
        end
 | 
						|
        if not v.command and config.getValue("onlyCommands") then
 | 
						|
            goto finishrender
 | 
						|
        end
 | 
						|
        local msg = v.str
 | 
						|
        if consoleOpen and not v.hack_no_prefix then
 | 
						|
            msg = "[" .. string.sub(v.level, 1, 1) .. "] " .. msg
 | 
						|
        end
 | 
						|
        local lineHeight, realWidth = calcHeight(msg, lineWidth)
 | 
						|
        bottom = bottom - lineHeight
 | 
						|
        if bottom < padding then
 | 
						|
            break
 | 
						|
        end
 | 
						|
 | 
						|
        local opacityPercent = 1
 | 
						|
        if not consoleOpen and age > showTime then
 | 
						|
            opacityPercent = (fadeTime - (age - showTime)) / fadeTime
 | 
						|
        end
 | 
						|
 | 
						|
        if not consoleOpen then
 | 
						|
            love.graphics.setColor(0, 0, 0, .5 * opacityPercent)
 | 
						|
            love.graphics.rectangle("fill", padding, bottom, lineWidth, lineHeight)
 | 
						|
        end
 | 
						|
        love.graphics.setColor(v.colour[1], v.colour[2], v.colour[3], opacityPercent)
 | 
						|
 | 
						|
        love.graphics.printf(msg, padding * 2, bottom, lineWidth - padding * 2)
 | 
						|
        ::finishrender::
 | 
						|
    end
 | 
						|
end
 | 
						|
 | 
						|
function global.registerCommand(id, options)
 | 
						|
    if not options then
 | 
						|
        error("Options must be provided")
 | 
						|
    end
 | 
						|
    if not options.name and not string.match(options.name, "^[%l%d_-]$") then
 | 
						|
        error("Options.name must be provided and match pattern `^[%l%d_-]$`.")
 | 
						|
    end
 | 
						|
    if not options.exec or type(options.exec) ~= "function" then
 | 
						|
        error("Options.exec must be a function")
 | 
						|
    end
 | 
						|
    if not options.shortDesc or type(options.shortDesc) ~= "string" then
 | 
						|
        error("Options.shortDesc must be a string")
 | 
						|
    end
 | 
						|
    if not options.desc or type(options.desc) ~= "string" then
 | 
						|
        error("Options.desc must be a string")
 | 
						|
    end
 | 
						|
    local cmd = {
 | 
						|
        source = id,
 | 
						|
        name = options.name,
 | 
						|
        exec = options.exec,
 | 
						|
        shortDesc = options.shortDesc,
 | 
						|
        desc = options.desc
 | 
						|
    }
 | 
						|
    for k, v in ipairs(commands) do
 | 
						|
        if v.source == cmd.source and v.name == cmd.name then
 | 
						|
            error("This command already exists")
 | 
						|
        end
 | 
						|
    end
 | 
						|
    table.insert(commands, cmd)
 | 
						|
end
 | 
						|
 | 
						|
local function handleLogsChange(added)
 | 
						|
    added = added or 0
 | 
						|
    logOffset = math.min(logOffset + added, #logger.logs)
 | 
						|
end
 | 
						|
 | 
						|
logger.handleLogsChange = handleLogsChange
 | 
						|
config.configDefinition.showNewLogs.onUpdate = function(v)
 | 
						|
    showNewLogs = v
 | 
						|
end
 | 
						|
 | 
						|
return global
 |