-- debug -- if os.getenv("LOCAL_LUA_DEBUGGER_VSCODE") == "1" then DEBUG = true require("lldebugger").start() else DEBUG = false end -- libraries -- ---@type Object Object = require 'lib/classic/classic' ---@type Baton Baton = require 'lib/baton/baton' ---@type Timer Timer = require 'lib/hump/timer' HC = require 'lib/hc' Camera = require 'lib/cameramod/camera' require 'lib/cameramod/Shake' -- generic objects -- Room = require 'obj/Room' Area = require 'obj/Area' GameObject = require 'obj/GameObject' function love.load() -- screen setup love.graphics.setDefaultFilter("nearest") love.graphics.setLineStyle("rough") resize(2) ---@type Room|nil current_room = nil -- load game objects local game_objects = {} print('loading game objects:') recursiveEnumerate('obj/game', game_objects) for _,v in ipairs(game_objects) do print('',v) end requireFiles(game_objects, true) -- load rooms local room_files = {} print('loading rooms:') recursiveEnumerate('rooms', room_files) for _,v in ipairs(room_files) do print('',v) end requireFiles(room_files, true) -- load input input = Baton.new { controls = { left = {'key:left', 'key:a'}, right = {'key:right', 'key:d'}, up = {'key:up', 'key:w'}, down = {'key:down', 'key:s'}, action = {'key:z', 'key:space'}, f1 = {'key:f1'}, f2 = {'key:f2'}, f3 = {'key:f3'}, f12 = {'key:f12'} }, pairs = { move = {'left', 'right', 'up', 'down'} } } camera = Camera() gotoRoom('Stage') end function love.update(dt) if current_room then current_room:update(dt) end input:update(dt) if input:pressed('f1') then gotoRoom('CircleRoom') end if input:pressed('f3') then camera:shake(4, 60, 1) end if input:pressed('f12') then print("-------------------------------------") print("Before collection: " .. collectgarbage("count")/1024) collectgarbage() print("After collection: " .. collectgarbage("count")/1024) print("Object count: ") local counts = type_count() for k, v in pairs(counts) do print(k, v) end print("-------------------------------------") end camera:update(dt) end function love.draw() if current_room then current_room:draw() end end ---Enumerates files in a folder and inserts them to a provided table. ---@param folder string path to folder to scan ---@param file_list table table to be inserted to function recursiveEnumerate(folder, file_list) local items = love.filesystem.getDirectoryItems(folder) for _, item in ipairs(items) do local file = folder .. '/' .. item local info = love.filesystem.getInfo(file) if info then -- exists (covers both files and directories) if info.type == "file" then table.insert(file_list, file) elseif info.type == "directory" then recursiveEnumerate(file, file_list) end end end end ---Load files table as lua modules. ---@param files table table containing files to be loaded ---@param should_return boolean|nil whether required files return a table/object function requireFiles(files, should_return) local returns = should_return or false for _, file in ipairs(files) do local module = file:sub(1, -5) if not returns then require(module) else local filename = module:match("([^/]+)$") _G[filename] = require(module) end end end ---Go to a specific room. ---@param room_type string the name of the room as registered ---@param ... any arguments passed to the room constructor function gotoRoom(room_type, ...) local Class = _G[room_type] if not Class then error("room '"..room_type.."' not found in _G") end if type(Class) ~= "table" and type(Class) ~= "function" then error("room '"..room_type.."' is not callable (got "..type(Class)..", does room return itself?)") end if current_room and current_room.destroy then current_room:destroy() end current_room = _G[room_type](...) end ---Generates and returns random UUID string ---@return string function UUID() local fn = function(x) local r = math.random(16) - 1 r = (x == "x") and (r + 1) or (r % 4) + 9 return ("0123456789abcdef"):sub(r, r) end return (("xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx"):gsub("[xy]", fn)) end ---Resize window ---@param s number scale setting function resize(s) love.window.setMode(s*gw, s*gh) sx, sy = s, s end ---Returns random number ---@param min number minimum of range ---@param max number maximum of range ---@return number function random(min, max) local min, max = min or 0, max or 1 return (min > max and (love.math.random()*(min - max) + max)) or (love.math.random()*(max - min) + min) end ------------------------ -- garbage collection -- ------------------------ ---Counts all globals and applies function ---@param f function function count_all(f) local seen = {} local count_table count_table = function(t) if seen[t] then return end f(t) seen[t] = true for k,v in pairs(t) do if type(v) == "table" then count_table(v) elseif type(v) == "userdata" then f(v) end end end count_table(_G) end ---Count all types of globals ---@return table function type_count() local counts = {} local enumerate = function (o) local t = type_name(o) counts[t] = (counts[t] or 0) + 1 end count_all(enumerate) return counts end global_type_table = nil ---Resolves an object's true class name via metatable lookup. ---Caches results in `global_type_table` for speed. ---Fallbacks: "table" (no metatable), "Unknown" (uncached). ---@param o any Any object (table, userdata, etc.) ---@return string Class name (e.g., "Player", "Camera", "ImageData") function type_name(o) if global_type_table == nil then global_type_table = {} for k,v in pairs(_G) do global_type_table[v] = k end global_type_table[0] = "table" end return global_type_table[getmetatable(o) or 0] or "Unknown" end