Shining-in-the-Darkness-Lua.../lovedebug.lua

358 lines
11 KiB
Lua
Raw Permalink Normal View History

2024-06-05 14:16:51 +03:00
--[[
The ISC License
Copyright (c) Varun Ramesh
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
]]--
local console = {}
-- Utilty functions for manipulating tables.
local function map(tbl, f)
local t = {}
for k,v in pairs(tbl) do t[k] = f(v) end
return t
end
local function filter(tbl, f)
local t, i = {}, 1
for _, v in ipairs(tbl) do
if f(v) then t[i], i = v, i + 1 end
end
return t
end
local function push(tbl, ...)
for _, v in ipairs({...}) do table.insert(tbl, v) end
end
console.HORIZONTAL_MARGIN = 10 -- Horizontal margin between the text and window.
console.VERTICAL_MARGIN = 0 -- Vertical margins between components.
console.PROMPT = "> " -- The prompt symbol.
console.MAX_LINES = 2000 -- How many lines to store in the buffer.
console.HISTORY_SIZE = 2000 -- How much of history to store.
-- Color configurations.
console.BACKGROUND_COLOR = {0, 0, 0, 0.4}
console.TEXT_COLOR = {1, 1, 1, 1}
console.ERROR_COLOR = {1, 0, 0, 1}
console.FONT_SIZE = 12
console.FONT = love.graphics.newFont(console.FONT_SIZE)
-- The scope in which lines in the console are executed.
console.ENV = setmetatable({}, {__index = _G})
-- The default help text shown.
console.HELP_TEXT = [[==== Welcome to the In-Game Console ====
- Type any expression or statement to evaluate it.
- Type a built-in command to run it (type `commands` to list all commands).]]
-- Builtin commands.
console.COMMANDS = {
clear = function() console.clear() end,
quit = function() love.event.quit(0) end,
exit = function() love.event.quit(0) end,
help = function() print(console.HELP_TEXT) end,
commands = function()
print ("=== Available Commands ===")
for k, _ in pairs(console.COMMANDS) do
if console.COMMAND_HELP[k] then
print(k .. " - " .. console.COMMAND_HELP[k])
else
print(k)
end
end
end
}
console.COMMAND_HELP = {
clear = "Clear the sceen.",
quit = "Quit the game.",
exit = "Quit the game.",
help = "Print help text.",
commands = "List all commands."
}
function console.inspect(val)
if type(val) == "table" then
-- If this table has a tostring function, just use that.
local mt = getmetatable(val)
if mt and mt.__tostring then return tostring(val) end
local result = "{ "
-- First print out array-like keys, keeping track of which keys we've seen.
local seen = {}
for k, v in ipairs(val) do
result = result .. tostring(v) .. ", "
seen[k] = true
end
-- Now print out the reset of the keys.
for k, v in pairs(val) do
if seen[k] ~= true then
result = result .. tostring(k) .. " = " .. tostring(v) .. ", "
end
end
result = result .. "}"
return result
else
return tostring(val)
end
end
-- Overrideable function that is used for formatting return values.
console.INSPECT_FUNCTION = function(...)
local args = {...}
if #args == 0 then
return "nil"
else
return table.concat(map(args, console.inspect), "\t")
end
end
-- Store global state for whether or not the console is enabled / disabled.
local enabled = false
function console.isEnabled() return enabled end
-- Store the printed lines in a buffer.
local lines = {}
function console.clear() lines = {} end
-- Store previously executed commands in a history buffer.
local history = {}
function console.addHistory(command)
table.insert(history, 1, command)
end
-- Print a colored text to the console. Colored text is simply represented
-- as a table of values that alternate between an {r, g, b, a} object and a
-- string value.
function console.colorprint(coloredtext) table.insert(lines, coloredtext) end
-- Wrap the print function and redirect it to store into the line buffer.
local normal_print = print
_G.print = function(...)
normal_print(...) -- Call original print function.
local args = {...}
local line = table.concat(map({...}, tostring), "\t")
push(lines, line)
while #lines > console.MAX_LINES do
table.remove(lines, 1)
end
end
-- Helper object that encapuslates operations on the current command.
local command = {
clear = function(self)
-- Clear the current command.
self.text, self.cursor, self.history_index = "", 0, 0
end,
insert = function(self, input)
-- Inert text at the cursor.
self.text = self.text:sub(0, self.cursor) ..
input .. self.text:sub(self.cursor + 1)
self.cursor = self.cursor + 1
end,
delete_backward = function(self)
-- Delete the character before the cursor.
if self.cursor > 0 then
self.text = self.text:sub(0, self.cursor - 1) ..
self.text:sub(self.cursor + 1)
self.cursor = self.cursor - 1
end
end,
forward_character = function(self)
self.cursor = math.min(self.cursor + 1, self.text:len())
end,
backward_character = function(self)
self.cursor = math.max(self.cursor - 1, 0)
end,
beginning_of_line = function(self) self.cursor = 0 end,
end_of_line = function(self) self.cursor = self.text:len() end,
forward_word = function(self)
local word = self.text:match('%W*%w*', self.cursor + 1)
self.cursor = math.min(self.cursor + word:len())
end,
backward_word = function(self)
local word = self.text:reverse():match('%W*%w*', self.text:len() - self.cursor + 1)
self.cursor = math.max(self.cursor - word:len(), 0)
end,
previous = function(self)
-- If there is no more history, don't do anything.
if self.history_index + 1 > #history then return end
-- If this is the first time, then save the command in case the user
-- navigates back to the present command.
if self.history_index == 0 then self.saved_command = self.text end
self.history_index = math.min(self.history_index + 1, #history)
self.text = history[self.history_index]
self.cursor = self.text:len()
end,
next = function(self)
-- If there is no more history, don't do anything.
if self.history_index - 1 < 0 then return end
self.history_index = math.max(self.history_index - 1, 0)
if self.history_index == 0 then self.text = self.saved_command
else self.text = history[self.history_index] end
self.cursor = self.text:len()
end
}
command:clear()
function console.draw()
-- Only draw the console if enabled.
if not enabled then return end
-- Fill the background color.
love.graphics.setColor(unpack(console.BACKGROUND_COLOR))
love.graphics.rectangle('fill', 0, 0, love.graphics.getWidth(),
love.graphics.getHeight())
love.graphics.setColor(255, 255, 255, 255)
love.graphics.setFont(console.FONT)
local line_start = love.graphics.getHeight() - console.VERTICAL_MARGIN*3 - console.FONT:getHeight()
local wraplimit = love.graphics.getWidth() - console.HORIZONTAL_MARGIN*2
for i = #lines, 1, -1 do
local textonly = lines[i]
if type(lines[i]) == "table" then
textonly = table.concat(filter(lines[i], function(val)
return type(val) == "string"
end), "")
end
width, wrapped = console.FONT:getWrap(textonly, wraplimit)
love.graphics.printf(
lines[i], console.HORIZONTAL_MARGIN,
line_start - #wrapped * console.FONT:getHeight(),
wraplimit, "left")
line_start = line_start - #wrapped * console.FONT:getHeight()
end
--[[
love.graphics.setLineWidth(1)
love.graphics.line(0,
love.graphics.getHeight() - console.VERTICAL_MARGIN
- console.FONT:getHeight() - console.VERTICAL_MARGIN,
love.graphics.getWidth(),
love.graphics.getHeight() - console.VERTICAL_MARGIN
- console.FONT:getHeight() - console.VERTICAL_MARGIN)
love.graphics.printf(
console.PROMPT .. command.text,
console.HORIZONTAL_MARGIN,
love.graphics.getHeight() - console.VERTICAL_MARGIN - console.FONT:getHeight(),
love.graphics.getWidth() - console.HORIZONTAL_MARGIN*2, "left")
if love.timer.getTime() % 1 > 0.5 then
local cursorx = console.HORIZONTAL_MARGIN +
console.FONT:getWidth(console.PROMPT .. command.text:sub(0, command.cursor))
love.graphics.line(
cursorx,
love.graphics.getHeight() - console.VERTICAL_MARGIN - console.FONT:getHeight(),
cursorx,
love.graphics.getHeight() - console.VERTICAL_MARGIN)
end ]]
end
function console.textinput(input)
-- Use the "~" key to enable / disable the console.
-- If disabled, ignore the input, otherwise insert at the cursor.
if not enabled then return end
command:insert(input)
end
function console.execute(command)
-- If this is a builtin command, execute it and return immediately.
if console.COMMANDS[command] then
console.COMMANDS[command]()
return
end
-- Reprint the command + the prompt string.
print(console.PROMPT .. command)
local chunk, error = load("return " .. command)
if not chunk then
chunk, error = load(command)
end
if chunk then
setfenv(chunk, console.ENV)
local values = { pcall(chunk) }
if values[1] then
table.remove(values, 1)
print(console.INSPECT_FUNCTION(unpack(values)))
-- Bind '_' to the first returned value, and bind 'last' to a list
-- of returned values.
console.ENV._ = values[1]
console.ENV.last = values
else
console.colorprint({console.ERROR_COLOR, values[2]})
end
else
console.colorprint({console.ERROR_COLOR, error})
end
end
function console.keypressed(key, scancode, isrepeat)
if love.keyboard.isDown("f10") then
enabled = not enabled
return
end
-- Ignore if the console isn't enabled.
if not enabled then return end
local ctrl = love.keyboard.isDown("lctrl", "lgui")
local shift = love.keyboard.isDown("lshift")
local alt = love.keyboard.isDown("lalt")
if key == 'backspace' then command:delete_backward()
elseif key == "up" then command:previous()
elseif key == "down" then command:next()
elseif alt and key == "left" then command:backward_word()
elseif alt and key == "right" then command:forward_word()
elseif ctrl and key == "left" then command:beginning_of_line()
elseif ctrl and key == "right" then command:end_of_line()
elseif key == "left" then command:backward_character()
elseif key == "right" then command:forward_character()
elseif key == "c" and ctrl then command:clear()
elseif key == "=" and shift and ctrl then
console.FONT_SIZE = console.FONT_SIZE + 1
console.FONT = love.graphics.newFont(console.FONT_SIZE)
elseif key == "-" and ctrl then
console.FONT_SIZE = math.max(console.FONT_SIZE - 1, 1)
console.FONT = love.graphics.newFont(console.FONT_SIZE)
elseif key == "return" then
console.addHistory(command.text)
console.execute(command.text)
command:clear()
end
end
return console