First project-specific commit.
Hardly a game yet, but extremely basic ball movements work!
|
@ -1,4 +1,4 @@
|
||||||
std = "lua54+love"
|
std = "lua54+love"
|
||||||
stds.project = {
|
stds.project = {
|
||||||
globals = {"tiny"},
|
globals = {"tiny"},
|
||||||
}
|
}
|
||||||
|
|
After Width: | Height: | Size: 772 B |
After Width: | Height: | Size: 925 B |
After Width: | Height: | Size: 901 B |
After Width: | Height: | Size: 889 B |
After Width: | Height: | Size: 126 KiB |
After Width: | Height: | Size: 857 B |
After Width: | Height: | Size: 857 B |
After Width: | Height: | Size: 915 B |
After Width: | Height: | Size: 726 B |
|
@ -5,4 +5,5 @@ require("../systems/decay")
|
||||||
require("../systems/draw")
|
require("../systems/draw")
|
||||||
require("../systems/gravity")
|
require("../systems/gravity")
|
||||||
require("../systems/input")
|
require("../systems/input")
|
||||||
|
require("../systems/rounds")
|
||||||
require("../systems/velocity")
|
require("../systems/velocity")
|
||||||
|
|
|
@ -1,11 +1,47 @@
|
||||||
-- GENERATED FILE - DO NOT EDIT
|
-- GENERATED FILE - DO NOT EDIT
|
||||||
-- Instead, edit the source file directly: assets.lua2p.
|
-- Instead, edit the source file directly: assets.lua2p.
|
||||||
|
|
||||||
|
-- luacheck: ignore
|
||||||
|
---@type love.Texture
|
||||||
|
Ball = love.graphics.newImage("assets/images/Ball.png")
|
||||||
|
|
||||||
|
-- luacheck: ignore
|
||||||
|
---@type love.Texture
|
||||||
|
Flag = love.graphics.newImage("assets/images/Flag.png")
|
||||||
|
|
||||||
|
-- luacheck: ignore
|
||||||
|
---@type love.Texture
|
||||||
|
GolferRightActivated = love.graphics.newImage("assets/images/GolferRightActivated.png")
|
||||||
|
|
||||||
|
-- luacheck: ignore
|
||||||
|
---@type love.Texture
|
||||||
|
GolferRight = love.graphics.newImage("assets/images/GolferRight.png")
|
||||||
|
|
||||||
|
-- luacheck: ignore
|
||||||
|
---@type love.Texture
|
||||||
|
Grass = love.graphics.newImage("assets/images/Grass.png")
|
||||||
|
|
||||||
|
-- luacheck: ignore
|
||||||
|
---@type love.Texture
|
||||||
|
SandTrap = love.graphics.newImage("assets/images/SandTrap.png")
|
||||||
|
|
||||||
|
-- luacheck: ignore
|
||||||
|
---@type love.Texture
|
||||||
|
SmallSandTrapActivated = love.graphics.newImage("assets/images/SmallSandTrapActivated.png")
|
||||||
|
|
||||||
|
-- luacheck: ignore
|
||||||
|
---@type love.Texture
|
||||||
|
SmallSandTrap = love.graphics.newImage("assets/images/SmallSandTrap.png")
|
||||||
|
|
||||||
|
-- luacheck: ignore
|
||||||
|
---@type love.Texture
|
||||||
|
StartButton = love.graphics.newImage("assets/images/StartButton.png")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- luacheck: ignore
|
-- luacheck: ignore
|
||||||
---@type FontData
|
---@type fun(fontSize: number | nil): love.Font
|
||||||
EtBt7001Z0xa = function(fontSize)
|
EtBt7001Z0xa = function(fontSize)
|
||||||
return love.graphics.newFont("assets/fonts/EtBt7001Z0xa.ttf", fontSize)
|
return love.graphics.newFont("assets/fonts/EtBt7001Z0xa.ttf", fontSize)
|
||||||
end
|
end
|
||||||
|
|
|
@ -25,9 +25,11 @@ function generatedFileWarning()
|
||||||
return "-- GENERATED FILE - DO NOT EDIT\n-- Instead, edit the source file directly: assets.lua2p."
|
return "-- GENERATED FILE - DO NOT EDIT\n-- Instead, edit the source file directly: assets.lua2p."
|
||||||
end)!!(generatedFileWarning())
|
end)!!(generatedFileWarning())
|
||||||
|
|
||||||
!!(dirLookup('assets/images', 'png', 'love.graphics.newImage', 'Image'))
|
!!(dirLookup('assets/images', 'png', 'love.graphics.newImage', 'love.Texture'))
|
||||||
!!(dirLookup('assets/sounds', 'wav', 'love.sound.newSoundData', 'SoundData'))
|
!!(dirLookup('assets/sounds', 'ogg', 'love.audio.newSource', 'love.Source', function(varName, newFunc, file)
|
||||||
!!(dirLookup('assets/music', 'wav', 'love.sound.newSoundData', 'SoundData'))
|
return varName .. ' = ' .. newFunc .. '("' .. file .. '", "static")'
|
||||||
!!(dirLookup('assets/fonts', 'ttf', 'love.graphics.newFont', 'FontData', function(varName, newFunc, file)
|
end))
|
||||||
|
!!(dirLookup('assets/music', 'wav', 'love.sound.newSoundData', 'love.SoundData'))
|
||||||
|
!!(dirLookup('assets/fonts', 'ttf', 'love.graphics.newFont', 'fun(fontSize: number | nil): love.Font', function(varName, newFunc, file)
|
||||||
return varName .. ' = function(fontSize)\n return ' .. newFunc .. '("' .. file .. '", fontSize)\nend'
|
return varName .. ' = function(fontSize)\n return ' .. newFunc .. '("' .. file .. '", fontSize)\nend'
|
||||||
end))
|
end))
|
||||||
|
|
2968
lib/luaunit.lua
|
@ -1,7 +1,7 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
_=[[
|
_ = [[
|
||||||
exec lua "$0" "$@"
|
exec lua "$0" "$@"
|
||||||
]]and nil
|
]] and nil
|
||||||
--==============================================================
|
--==============================================================
|
||||||
--=
|
--=
|
||||||
--= LuaPreprocess command line program
|
--= LuaPreprocess command line program
|
||||||
|
@ -175,35 +175,35 @@ Handler messages:
|
||||||
]]
|
]]
|
||||||
--==============================================================
|
--==============================================================
|
||||||
|
|
||||||
|
local startTime = os.time()
|
||||||
|
|
||||||
local startTime = os.time()
|
|
||||||
local startClock = os.clock()
|
local startClock = os.clock()
|
||||||
|
|
||||||
local args = arg
|
local args = arg
|
||||||
|
|
||||||
if not args[0] then error("Expected to run from the Lua interpreter.") end
|
if not args[0] then
|
||||||
|
error("Expected to run from the Lua interpreter.")
|
||||||
|
end
|
||||||
local pp = dofile((args[0]:gsub("[^/\\]+$", "preprocess.lua")))
|
local pp = dofile((args[0]:gsub("[^/\\]+$", "preprocess.lua")))
|
||||||
|
|
||||||
-- From args:
|
-- From args:
|
||||||
local addLineNumbers = false
|
local addLineNumbers = false
|
||||||
local allowBacktickStrings = false
|
local allowBacktickStrings = false
|
||||||
local allowJitSyntax = false
|
local allowJitSyntax = false
|
||||||
local canOutputNil = true
|
local canOutputNil = true
|
||||||
local customData = nil
|
local customData = nil
|
||||||
local fastStrings = false
|
local fastStrings = false
|
||||||
local hasOutputExtension = false
|
local hasOutputExtension = false
|
||||||
local hasOutputPaths = false
|
local hasOutputPaths = false
|
||||||
local isDebug = false
|
local isDebug = false
|
||||||
local outputExtension = "lua"
|
local outputExtension = "lua"
|
||||||
local outputMeta = false -- flag|path
|
local outputMeta = false -- flag|path
|
||||||
local processingInfoPath = ""
|
local processingInfoPath = ""
|
||||||
local silent = false
|
local silent = false
|
||||||
local validate = true
|
local validate = true
|
||||||
local macroPrefix = ""
|
local macroPrefix = ""
|
||||||
local macroSuffix = ""
|
local macroSuffix = ""
|
||||||
local releaseMode = false
|
local releaseMode = false
|
||||||
local maxLogLevel = "trace"
|
local maxLogLevel = "trace"
|
||||||
local strictMacroArguments = true
|
local strictMacroArguments = true
|
||||||
|
|
||||||
--==============================================================
|
--==============================================================
|
||||||
|
@ -212,46 +212,50 @@ local strictMacroArguments = true
|
||||||
local F = string.format
|
local F = string.format
|
||||||
|
|
||||||
local function formatBytes(n)
|
local function formatBytes(n)
|
||||||
if n >= 1024*1024*1024 then
|
if n >= 1024 * 1024 * 1024 then
|
||||||
return F("%.2f GiB", n/(1024*1024*1024))
|
return F("%.2f GiB", n / (1024 * 1024 * 1024))
|
||||||
elseif n >= 1024*1024 then
|
elseif n >= 1024 * 1024 then
|
||||||
return F("%.2f MiB", n/(1024*1024))
|
return F("%.2f MiB", n / (1024 * 1024))
|
||||||
elseif n >= 1024 then
|
elseif n >= 1024 then
|
||||||
return F("%.2f KiB", n/(1024))
|
return F("%.2f KiB", n / 1024)
|
||||||
elseif n == 1 then
|
elseif n == 1 then
|
||||||
return F("1 byte", n)
|
return F("1 byte", n)
|
||||||
else
|
else
|
||||||
return F("%d bytes", n)
|
return F("%d bytes", n)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function printfNoise(s, ...)
|
local function printfNoise(s, ...)
|
||||||
print(s:format(...))
|
print(s:format(...))
|
||||||
end
|
end
|
||||||
local function printError(s)
|
local function printError(s)
|
||||||
io.stderr:write(s, "\n")
|
io.stderr:write(s, "\n")
|
||||||
end
|
end
|
||||||
local function printfError(s, ...)
|
local function printfError(s, ...)
|
||||||
io.stderr:write(s:format(...), "\n")
|
io.stderr:write(s:format(...), "\n")
|
||||||
end
|
end
|
||||||
|
|
||||||
local function errorLine(err)
|
local function errorLine(err)
|
||||||
printError(pp.tryToFormatError(err))
|
printError(pp.tryToFormatError(err))
|
||||||
os.exit(1)
|
os.exit(1)
|
||||||
end
|
end
|
||||||
|
|
||||||
local loadLuaFile = (
|
local loadLuaFile = (
|
||||||
(_VERSION >= "Lua 5.2" or jit) and function(path, env)
|
(_VERSION >= "Lua 5.2" or jit) and function(path, env)
|
||||||
return loadfile(path, "bt", env)
|
return loadfile(path, "bt", env)
|
||||||
end
|
end
|
||||||
or function(path, env)
|
or function(path, env)
|
||||||
local chunk, err = loadfile(path)
|
local chunk, err = loadfile(path)
|
||||||
if not chunk then return nil, err end
|
if not chunk then
|
||||||
|
return nil, err
|
||||||
|
end
|
||||||
|
|
||||||
if env then setfenv(chunk, env) end
|
if env then
|
||||||
|
setfenv(chunk, env)
|
||||||
|
end
|
||||||
|
|
||||||
return chunk
|
return chunk
|
||||||
end
|
end
|
||||||
)
|
)
|
||||||
|
|
||||||
--==============================================================
|
--==============================================================
|
||||||
|
@ -264,367 +268,339 @@ io.stderr:setvbuf("no")
|
||||||
math.randomseed(os.time()) -- In case math.random() is used anywhere.
|
math.randomseed(os.time()) -- In case math.random() is used anywhere.
|
||||||
math.random() -- Must kickstart...
|
math.random() -- Must kickstart...
|
||||||
|
|
||||||
local processOptions = true
|
local processOptions = true
|
||||||
local messageHandlerPath = ""
|
local messageHandlerPath = ""
|
||||||
local pathsIn = {}
|
local pathsIn = {}
|
||||||
local pathsOut = {}
|
local pathsOut = {}
|
||||||
|
|
||||||
for _, arg in ipairs(args) do
|
for _, arg in ipairs(args) do
|
||||||
if processOptions and (arg:find"^%-%-?help$" or arg == "/?" or arg:find"^/[Hh][Ee][Ll][Pp]$") then
|
if processOptions and (arg:find("^%-%-?help$") or arg == "/?" or arg:find("^/[Hh][Ee][Ll][Pp]$")) then
|
||||||
print("LuaPreprocess v"..pp.VERSION)
|
print("LuaPreprocess v" .. pp.VERSION)
|
||||||
print((help:gsub("\t", " ")))
|
print((help:gsub("\t", " ")))
|
||||||
os.exit()
|
os.exit()
|
||||||
|
elseif not (processOptions and arg:find("^%-.")) then
|
||||||
|
local paths = (hasOutputPaths and #pathsOut < #pathsIn) and pathsOut or pathsIn
|
||||||
|
table.insert(paths, arg)
|
||||||
|
|
||||||
elseif not (processOptions and arg:find"^%-.") then
|
if arg == "-" and (not hasOutputPaths or paths == pathsOut) then
|
||||||
local paths = (hasOutputPaths and #pathsOut < #pathsIn) and pathsOut or pathsIn
|
silent = true
|
||||||
table.insert(paths, arg)
|
end
|
||||||
|
elseif arg == "--" then
|
||||||
if arg == "-" and (not hasOutputPaths or paths == pathsOut) then
|
processOptions = false
|
||||||
silent = true
|
elseif arg:find("^%-%-data=") or arg:find("^%-d=") then
|
||||||
end
|
customData = arg:gsub("^.-=", "")
|
||||||
|
elseif arg == "--backtickstrings" then
|
||||||
elseif arg == "--" then
|
allowBacktickStrings = true
|
||||||
processOptions = false
|
elseif arg == "--debug" then
|
||||||
|
isDebug = true
|
||||||
elseif arg:find"^%-%-data=" or arg:find"^%-d=" then
|
outputMeta = outputMeta or true
|
||||||
customData = arg:gsub("^.-=", "")
|
elseif arg:find("^%-%-handler=") or arg:find("^%-h=") then
|
||||||
|
messageHandlerPath = arg:gsub("^.-=", "")
|
||||||
elseif arg == "--backtickstrings" then
|
elseif arg == "--jitsyntax" then
|
||||||
allowBacktickStrings = true
|
allowJitSyntax = true
|
||||||
|
elseif arg == "--linenumbers" then
|
||||||
elseif arg == "--debug" then
|
addLineNumbers = true
|
||||||
isDebug = true
|
elseif arg == "--meta" then
|
||||||
outputMeta = outputMeta or true
|
outputMeta = true
|
||||||
|
elseif arg:find("^%-%-meta=") then
|
||||||
elseif arg:find"^%-%-handler=" or arg:find"^%-h=" then
|
outputMeta = arg:gsub("^.-=", "")
|
||||||
messageHandlerPath = arg:gsub("^.-=", "")
|
elseif arg == "--nonil" then
|
||||||
|
canOutputNil = false
|
||||||
elseif arg == "--jitsyntax" then
|
elseif arg == "--novalidate" then
|
||||||
allowJitSyntax = true
|
validate = false
|
||||||
|
elseif arg:find("^%-%-outputextension=") then
|
||||||
elseif arg == "--linenumbers" then
|
if hasOutputPaths then
|
||||||
addLineNumbers = true
|
errorLine("Cannot specify both --outputextension and --outputpaths")
|
||||||
|
end
|
||||||
elseif arg == "--meta" then
|
hasOutputExtension = true
|
||||||
outputMeta = true
|
outputExtension = arg:gsub("^.-=", "")
|
||||||
elseif arg:find"^%-%-meta=" then
|
elseif arg == "--outputpaths" or arg == "-o" then
|
||||||
outputMeta = arg:gsub("^.-=", "")
|
if hasOutputExtension then
|
||||||
|
errorLine("Cannot specify both --outputpaths and --outputextension")
|
||||||
elseif arg == "--nonil" then
|
elseif pathsIn[1] then
|
||||||
canOutputNil = false
|
errorLine(arg .. " must appear before any input path.")
|
||||||
|
end
|
||||||
elseif arg == "--novalidate" then
|
hasOutputPaths = true
|
||||||
validate = false
|
elseif arg:find("^%-%-saveinfo=") or arg:find("^%-i=") then
|
||||||
|
processingInfoPath = arg:gsub("^.-=", "")
|
||||||
elseif arg:find"^%-%-outputextension=" then
|
elseif arg == "--silent" then
|
||||||
if hasOutputPaths then
|
silent = true
|
||||||
errorLine("Cannot specify both --outputextension and --outputpaths")
|
elseif arg == "--faststrings" then
|
||||||
end
|
fastStrings = true
|
||||||
hasOutputExtension = true
|
elseif arg == "--nogc" then
|
||||||
outputExtension = arg:gsub("^.-=", "")
|
collectgarbage("stop")
|
||||||
|
elseif arg:find("^%-%-macroprefix=") then
|
||||||
elseif arg == "--outputpaths" or arg == "-o" then
|
macroPrefix = arg:gsub("^.-=", "")
|
||||||
if hasOutputExtension then
|
elseif arg:find("^%-%-macrosuffix=") then
|
||||||
errorLine("Cannot specify both --outputpaths and --outputextension")
|
macroSuffix = arg:gsub("^.-=", "")
|
||||||
elseif pathsIn[1] then
|
elseif arg == "--release" then
|
||||||
errorLine(arg.." must appear before any input path.")
|
releaseMode = true
|
||||||
end
|
elseif arg:find("^%-%-loglevel=") then
|
||||||
hasOutputPaths = true
|
maxLogLevel = arg:gsub("^.-=", "")
|
||||||
|
elseif arg == "--version" then
|
||||||
elseif arg:find"^%-%-saveinfo=" or arg:find"^%-i=" then
|
io.stdout:write(pp.VERSION)
|
||||||
processingInfoPath = arg:gsub("^.-=", "")
|
os.exit()
|
||||||
|
elseif arg == "--nostrictmacroarguments" then
|
||||||
elseif arg == "--silent" then
|
strictMacroArguments = false
|
||||||
silent = true
|
else
|
||||||
|
errorLine("Unknown option '" .. arg:gsub("=.*", "") .. "'.")
|
||||||
elseif arg == "--faststrings" then
|
end
|
||||||
fastStrings = true
|
|
||||||
|
|
||||||
elseif arg == "--nogc" then
|
|
||||||
collectgarbage("stop")
|
|
||||||
|
|
||||||
elseif arg:find"^%-%-macroprefix=" then
|
|
||||||
macroPrefix = arg:gsub("^.-=", "")
|
|
||||||
|
|
||||||
elseif arg:find"^%-%-macrosuffix=" then
|
|
||||||
macroSuffix = arg:gsub("^.-=", "")
|
|
||||||
|
|
||||||
elseif arg == "--release" then
|
|
||||||
releaseMode = true
|
|
||||||
|
|
||||||
elseif arg:find"^%-%-loglevel=" then
|
|
||||||
maxLogLevel = arg:gsub("^.-=", "")
|
|
||||||
|
|
||||||
elseif arg == "--version" then
|
|
||||||
io.stdout:write(pp.VERSION)
|
|
||||||
os.exit()
|
|
||||||
|
|
||||||
elseif arg == "--nostrictmacroarguments" then
|
|
||||||
strictMacroArguments = false
|
|
||||||
|
|
||||||
else
|
|
||||||
errorLine("Unknown option '"..arg:gsub("=.*", "").."'.")
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if silent then
|
if silent then
|
||||||
printfNoise = function()end
|
printfNoise = function() end
|
||||||
end
|
end
|
||||||
|
|
||||||
local header = "= LuaPreprocess v"..pp.VERSION..os.date(", %Y-%m-%d %H:%M:%S =", startTime)
|
local header = "= LuaPreprocess v" .. pp.VERSION .. os.date(", %Y-%m-%d %H:%M:%S =", startTime)
|
||||||
printfNoise(("="):rep(#header))
|
printfNoise(("="):rep(#header))
|
||||||
printfNoise("%s", header)
|
printfNoise("%s", header)
|
||||||
printfNoise(("="):rep(#header))
|
printfNoise(("="):rep(#header))
|
||||||
|
|
||||||
if hasOutputPaths and #pathsOut < #pathsIn then
|
if hasOutputPaths and #pathsOut < #pathsIn then
|
||||||
errorLine("Missing output path for "..pathsIn[#pathsIn])
|
errorLine("Missing output path for " .. pathsIn[#pathsIn])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- Prepare metaEnvironment.
|
-- Prepare metaEnvironment.
|
||||||
pp.metaEnvironment.dataFromCommandLine = customData -- May be nil.
|
pp.metaEnvironment.dataFromCommandLine = customData -- May be nil.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- Load message handler.
|
-- Load message handler.
|
||||||
local messageHandler = nil
|
local messageHandler = nil
|
||||||
|
|
||||||
local function hasMessageHandler(message)
|
local function hasMessageHandler(message)
|
||||||
if not messageHandler then
|
if not messageHandler then
|
||||||
return false
|
return false
|
||||||
|
elseif type(messageHandler) == "function" then
|
||||||
elseif type(messageHandler) == "function" then
|
return true
|
||||||
return true
|
elseif type(messageHandler) == "table" then
|
||||||
|
return messageHandler[message] ~= nil
|
||||||
elseif type(messageHandler) == "table" then
|
else
|
||||||
return messageHandler[message] ~= nil
|
assert(false)
|
||||||
|
end
|
||||||
else
|
|
||||||
assert(false)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local function sendMessage(message, ...)
|
local function sendMessage(message, ...)
|
||||||
if not messageHandler then
|
if not messageHandler then
|
||||||
return
|
return
|
||||||
|
elseif type(messageHandler) == "function" then
|
||||||
|
local returnValues = pp.pack(messageHandler(message, ...))
|
||||||
|
return pp.unpack(returnValues, 1, returnValues.n)
|
||||||
|
elseif type(messageHandler) == "table" then
|
||||||
|
local _messageHandler = messageHandler[message]
|
||||||
|
if not _messageHandler then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
elseif type(messageHandler) == "function" then
|
local returnValues = pp.pack(_messageHandler(...))
|
||||||
local returnValues = pp.pack(messageHandler(message, ...))
|
return pp.unpack(returnValues, 1, returnValues.n)
|
||||||
return pp.unpack(returnValues, 1, returnValues.n)
|
else
|
||||||
|
assert(false)
|
||||||
elseif type(messageHandler) == "table" then
|
end
|
||||||
local _messageHandler = messageHandler[message]
|
|
||||||
if not _messageHandler then return end
|
|
||||||
|
|
||||||
local returnValues = pp.pack(_messageHandler(...))
|
|
||||||
return pp.unpack(returnValues, 1, returnValues.n)
|
|
||||||
|
|
||||||
else
|
|
||||||
assert(false)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if messageHandlerPath ~= "" then
|
if messageHandlerPath ~= "" then
|
||||||
-- Make the message handler and the metaprogram share the same environment.
|
-- Make the message handler and the metaprogram share the same environment.
|
||||||
-- This way the message handler can easily define globals that the metaprogram uses.
|
-- This way the message handler can easily define globals that the metaprogram uses.
|
||||||
local mainChunk, err = loadLuaFile(messageHandlerPath, pp.metaEnvironment)
|
local mainChunk, err = loadLuaFile(messageHandlerPath, pp.metaEnvironment)
|
||||||
if not mainChunk then
|
if not mainChunk then
|
||||||
errorLine("Could not load message handler...\n"..pp.tryToFormatError(err))
|
errorLine("Could not load message handler...\n" .. pp.tryToFormatError(err))
|
||||||
end
|
end
|
||||||
|
|
||||||
messageHandler = mainChunk()
|
messageHandler = mainChunk()
|
||||||
|
|
||||||
if type(messageHandler) == "function" then
|
if type(messageHandler) == "function" then
|
||||||
-- void
|
-- void
|
||||||
elseif type(messageHandler) == "table" then
|
elseif type(messageHandler) == "table" then
|
||||||
for message, _messageHandler in pairs(messageHandler) do
|
for message, _messageHandler in pairs(messageHandler) do
|
||||||
if type(message) ~= "string" then
|
if type(message) ~= "string" then
|
||||||
errorLine(messageHandlerPath..": Table of handlers must only contain messages as keys.")
|
errorLine(messageHandlerPath .. ": Table of handlers must only contain messages as keys.")
|
||||||
elseif type(_messageHandler) ~= "function" then
|
elseif type(_messageHandler) ~= "function" then
|
||||||
errorLine(messageHandlerPath..": Table of handlers must only contain functions as values.")
|
errorLine(messageHandlerPath .. ": Table of handlers must only contain functions as values.")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
errorLine(messageHandlerPath..": File did not return a table or a function.")
|
errorLine(messageHandlerPath .. ": File did not return a table or a function.")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- Init stuff.
|
-- Init stuff.
|
||||||
sendMessage("init", pathsIn, (hasOutputPaths and pathsOut or nil)) -- @Incomplete: Use pcall and format error message better?
|
sendMessage("init", pathsIn, (hasOutputPaths and pathsOut or nil)) -- @Incomplete: Use pcall and format error message better?
|
||||||
|
|
||||||
if not hasOutputPaths then
|
if not hasOutputPaths then
|
||||||
for i, pathIn in ipairs(pathsIn) do
|
for i, pathIn in ipairs(pathsIn) do
|
||||||
pathsOut[i] = (pathIn == "-") and "-" or pathIn:gsub("%.%w+$", "").."."..outputExtension
|
pathsOut[i] = (pathIn == "-") and "-" or pathIn:gsub("%.%w+$", "") .. "." .. outputExtension
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if not pathsIn[1] then
|
if not pathsIn[1] then
|
||||||
errorLine("No path(s) specified.")
|
errorLine("No path(s) specified.")
|
||||||
elseif #pathsIn ~= #pathsOut then
|
elseif #pathsIn ~= #pathsOut then
|
||||||
errorLine(F("Number of input and output paths differ. (%d in, %d out)", #pathsIn, #pathsOut))
|
errorLine(F("Number of input and output paths differ. (%d in, %d out)", #pathsIn, #pathsOut))
|
||||||
end
|
end
|
||||||
|
|
||||||
local pathsSetIn = {}
|
local pathsSetIn = {}
|
||||||
local pathsSetOut = {}
|
local pathsSetOut = {}
|
||||||
|
|
||||||
for i = 1, #pathsIn do
|
for i = 1, #pathsIn do
|
||||||
if pathsSetIn [pathsIn [i]] then errorLine("Duplicate input path: " ..pathsIn [i]) end
|
if pathsSetIn[pathsIn[i]] then
|
||||||
if pathsSetOut[pathsOut[i]] then errorLine("Duplicate output path: "..pathsOut[i]) end
|
errorLine("Duplicate input path: " .. pathsIn[i])
|
||||||
|
end
|
||||||
|
if pathsSetOut[pathsOut[i]] then
|
||||||
|
errorLine("Duplicate output path: " .. pathsOut[i])
|
||||||
|
end
|
||||||
|
|
||||||
pathsSetIn [pathsIn [i]] = true
|
pathsSetIn[pathsIn[i]] = true
|
||||||
pathsSetOut[pathsOut[i]] = true
|
pathsSetOut[pathsOut[i]] = true
|
||||||
|
|
||||||
if pathsIn [i] ~= "-" and pathsSetOut[pathsIn [i]] then errorLine("Path is both input and output: "..pathsIn [i]) end
|
if pathsIn[i] ~= "-" and pathsSetOut[pathsIn[i]] then
|
||||||
if pathsOut[i] ~= "-" and pathsSetIn [pathsOut[i]] then errorLine("Path is both input and output: "..pathsOut[i]) end
|
errorLine("Path is both input and output: " .. pathsIn[i])
|
||||||
|
end
|
||||||
|
if pathsOut[i] ~= "-" and pathsSetIn[pathsOut[i]] then
|
||||||
|
errorLine("Path is both input and output: " .. pathsOut[i])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- Process files.
|
-- Process files.
|
||||||
|
|
||||||
-- :SavedInfo
|
-- :SavedInfo
|
||||||
local processingInfo = {
|
local processingInfo = {
|
||||||
date = os.date("%Y-%m-%d %H:%M:%S", startTime),
|
date = os.date("%Y-%m-%d %H:%M:%S", startTime),
|
||||||
files = {},
|
files = {},
|
||||||
}
|
}
|
||||||
|
|
||||||
local byteCount = 0
|
local byteCount = 0
|
||||||
local lineCount = 0
|
local lineCount = 0
|
||||||
local lineCountCode = 0
|
local lineCountCode = 0
|
||||||
local tokenCount = 0
|
local tokenCount = 0
|
||||||
|
|
||||||
for i, pathIn in ipairs(pathsIn) do
|
for i, pathIn in ipairs(pathsIn) do
|
||||||
local startClockForPath = os.clock()
|
local startClockForPath = os.clock()
|
||||||
printfNoise("Processing '%s'...", pathIn)
|
printfNoise("Processing '%s'...", pathIn)
|
||||||
|
|
||||||
local pathOut = pathsOut[i]
|
local pathOut = pathsOut[i]
|
||||||
local pathMeta = (type(outputMeta) == "string") and outputMeta or pathOut:gsub("%.%w+$", "")..".meta.lua"
|
local pathMeta = (type(outputMeta) == "string") and outputMeta or pathOut:gsub("%.%w+$", "") .. ".meta.lua"
|
||||||
|
|
||||||
if not outputMeta or pathOut == "-" then
|
if not outputMeta or pathOut == "-" then
|
||||||
pathMeta = nil
|
pathMeta = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
local info, err = pp.processFile{
|
local info, err = pp.processFile({
|
||||||
pathIn = pathIn,
|
pathIn = pathIn,
|
||||||
pathMeta = pathMeta,
|
pathMeta = pathMeta,
|
||||||
pathOut = pathOut,
|
pathOut = pathOut,
|
||||||
|
|
||||||
debug = isDebug,
|
debug = isDebug,
|
||||||
addLineNumbers = addLineNumbers,
|
addLineNumbers = addLineNumbers,
|
||||||
|
|
||||||
backtickStrings = allowBacktickStrings,
|
backtickStrings = allowBacktickStrings,
|
||||||
jitSyntax = allowJitSyntax,
|
jitSyntax = allowJitSyntax,
|
||||||
canOutputNil = canOutputNil,
|
canOutputNil = canOutputNil,
|
||||||
fastStrings = fastStrings,
|
fastStrings = fastStrings,
|
||||||
validate = validate,
|
validate = validate,
|
||||||
strictMacroArguments = strictMacroArguments,
|
strictMacroArguments = strictMacroArguments,
|
||||||
|
|
||||||
macroPrefix = macroPrefix,
|
macroPrefix = macroPrefix,
|
||||||
macroSuffix = macroSuffix,
|
macroSuffix = macroSuffix,
|
||||||
|
|
||||||
release = releaseMode,
|
release = releaseMode,
|
||||||
logLevel = maxLogLevel,
|
logLevel = maxLogLevel,
|
||||||
|
|
||||||
onInsert = (hasMessageHandler("insert") or nil) and function(name)
|
onInsert = (hasMessageHandler("insert") or nil) and function(name)
|
||||||
local lua = sendMessage("insert", pathIn, name)
|
local lua = sendMessage("insert", pathIn, name)
|
||||||
|
|
||||||
-- onInsert() is expected to return a Lua code string and so is the message
|
-- onInsert() is expected to return a Lua code string and so is the message
|
||||||
-- handler. However, if the handler is a single catch-all function we allow
|
-- handler. However, if the handler is a single catch-all function we allow
|
||||||
-- the message to not be handled and we fall back to the default behavior of
|
-- the message to not be handled and we fall back to the default behavior of
|
||||||
-- treating 'name' as a path to a file to be inserted. If we didn't allow this
|
-- treating 'name' as a path to a file to be inserted. If we didn't allow this
|
||||||
-- then it would be required for the "insert" message to be handled. I think
|
-- then it would be required for the "insert" message to be handled. I think
|
||||||
-- it's better if the user can choose whether to handle a message or not!
|
-- it's better if the user can choose whether to handle a message or not!
|
||||||
--
|
--
|
||||||
if lua == nil and type(messageHandler) == "function" then
|
if lua == nil and type(messageHandler) == "function" then
|
||||||
return assert(pp.readFile(name))
|
return assert(pp.readFile(name))
|
||||||
end
|
end
|
||||||
|
|
||||||
return lua
|
return lua
|
||||||
end,
|
end,
|
||||||
|
|
||||||
onBeforeMeta = messageHandler and function(lua)
|
onBeforeMeta = messageHandler and function(lua)
|
||||||
sendMessage("beforemeta", pathIn, lua)
|
sendMessage("beforemeta", pathIn, lua)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
onAfterMeta = messageHandler and function(lua)
|
onAfterMeta = messageHandler
|
||||||
local luaModified = sendMessage("aftermeta", pathIn, lua)
|
and function(lua)
|
||||||
|
local luaModified = sendMessage("aftermeta", pathIn, lua)
|
||||||
|
|
||||||
if type(luaModified) == "string" then
|
if type(luaModified) == "string" then
|
||||||
lua = luaModified
|
lua = luaModified
|
||||||
|
elseif luaModified ~= nil then
|
||||||
|
error(
|
||||||
|
F(
|
||||||
|
"%s: Message handler did not return a string for 'aftermeta'. (Got %s)",
|
||||||
|
messageHandlerPath,
|
||||||
|
type(luaModified)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
elseif luaModified ~= nil then
|
return lua
|
||||||
error(F(
|
end,
|
||||||
"%s: Message handler did not return a string for 'aftermeta'. (Got %s)",
|
|
||||||
messageHandlerPath, type(luaModified)
|
|
||||||
))
|
|
||||||
end
|
|
||||||
|
|
||||||
return lua
|
onDone = messageHandler and function(info)
|
||||||
end,
|
sendMessage("filedone", pathIn, pathOut, info)
|
||||||
|
end,
|
||||||
|
|
||||||
onDone = messageHandler and function(info)
|
onError = function(err)
|
||||||
sendMessage("filedone", pathIn, pathOut, info)
|
xpcall(function()
|
||||||
end,
|
sendMessage("fileerror", pathIn, err)
|
||||||
|
end, function(err)
|
||||||
|
printfError("Additional error in 'fileerror' message handler...\n%s", pp.tryToFormatError(err))
|
||||||
|
end)
|
||||||
|
os.exit(1)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
assert(info, err) -- The onError() handler above should have been called and we should have exited already.
|
||||||
|
|
||||||
onError = function(err)
|
byteCount = byteCount + info.processedByteCount
|
||||||
xpcall(function()
|
lineCount = lineCount + info.lineCount
|
||||||
sendMessage("fileerror", pathIn, err)
|
lineCountCode = lineCountCode + info.linesOfCode
|
||||||
end, function(err)
|
tokenCount = tokenCount + info.tokenCount
|
||||||
printfError("Additional error in 'fileerror' message handler...\n%s", pp.tryToFormatError(err))
|
|
||||||
end)
|
|
||||||
os.exit(1)
|
|
||||||
end,
|
|
||||||
}
|
|
||||||
assert(info, err) -- The onError() handler above should have been called and we should have exited already.
|
|
||||||
|
|
||||||
byteCount = byteCount + info.processedByteCount
|
if processingInfoPath ~= "" then
|
||||||
lineCount = lineCount + info.lineCount
|
-- :SavedInfo
|
||||||
lineCountCode = lineCountCode + info.linesOfCode
|
table.insert(processingInfo.files, info) -- See 'ProcessInfo' in preprocess.lua for what more 'info' contains.
|
||||||
tokenCount = tokenCount + info.tokenCount
|
end
|
||||||
|
|
||||||
if processingInfoPath ~= "" then
|
printfNoise("Processing '%s' successful! (%.3fs)", pathIn, os.clock() - startClockForPath)
|
||||||
|
printfNoise(("-"):rep(#header))
|
||||||
-- :SavedInfo
|
|
||||||
table.insert(processingInfo.files, info) -- See 'ProcessInfo' in preprocess.lua for what more 'info' contains.
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
printfNoise("Processing '%s' successful! (%.3fs)", pathIn, os.clock()-startClockForPath)
|
|
||||||
printfNoise(("-"):rep(#header))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- Finalize stuff.
|
-- Finalize stuff.
|
||||||
if processingInfoPath ~= "" then
|
if processingInfoPath ~= "" then
|
||||||
printfNoise("Saving processing info to '%s'.", processingInfoPath)
|
printfNoise("Saving processing info to '%s'.", processingInfoPath)
|
||||||
|
|
||||||
local luaParts = {"return"}
|
local luaParts = { "return" }
|
||||||
assert(pp.serialize(luaParts, processingInfo))
|
assert(pp.serialize(luaParts, processingInfo))
|
||||||
local lua = table.concat(luaParts)
|
local lua = table.concat(luaParts)
|
||||||
|
|
||||||
local file = assert(io.open(processingInfoPath, "wb"))
|
local file = assert(io.open(processingInfoPath, "wb"))
|
||||||
file:write(lua)
|
file:write(lua)
|
||||||
file:close()
|
file:close()
|
||||||
end
|
end
|
||||||
|
|
||||||
printfNoise(
|
printfNoise(
|
||||||
"All done! (%.3fs, %.0f file%s, %.0f LOC, %.0f line%s, %.0f token%s, %s)",
|
"All done! (%.3fs, %.0f file%s, %.0f LOC, %.0f line%s, %.0f token%s, %s)",
|
||||||
os.clock()-startClock,
|
os.clock() - startClock,
|
||||||
#pathsIn, (#pathsIn == 1) and "" or "s",
|
#pathsIn,
|
||||||
lineCountCode,
|
(#pathsIn == 1) and "" or "s",
|
||||||
lineCount, (lineCount == 1) and "" or "s",
|
lineCountCode,
|
||||||
tokenCount, (tokenCount == 1) and "" or "s",
|
lineCount,
|
||||||
formatBytes(byteCount)
|
(lineCount == 1) and "" or "s",
|
||||||
|
tokenCount,
|
||||||
|
(tokenCount == 1) and "" or "s",
|
||||||
|
formatBytes(byteCount)
|
||||||
)
|
)
|
||||||
|
|
||||||
sendMessage("alldone") -- @Incomplete: Use pcall and format error message better?
|
sendMessage("alldone") -- @Incomplete: Use pcall and format error message better?
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--[[!===========================================================
|
--[[!===========================================================
|
||||||
|
|
||||||
Copyright © 2018-2022 Marcus 'ReFreezed' Thunström
|
Copyright © 2018-2022 Marcus 'ReFreezed' Thunström
|
||||||
|
@ -648,4 +624,3 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
|
|
||||||
==============================================================]]
|
==============================================================]]
|
||||||
|
|
||||||
|
|
5819
lib/preprocess.lua
69
lib/tiny.lua
|
@ -30,7 +30,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
---@field indices table<any, any> field is a table of Entity keys to their indices in the entities list. Most Systems can ignore this.
|
---@field indices table<any, any> field is a table of Entity keys to their indices in the entities list. Most Systems can ignore this.
|
||||||
---@field modified boolean indicator for if the System has been modified in the last update. If so, the onModify callback will be called on the System in the next update, if it has one. This is usually managed by tiny-ecs, so users should mostly ignore this, too.
|
---@field modified boolean indicator for if the System has been modified in the last update. If so, the onModify callback will be called on the System in the next update, if it has one. This is usually managed by tiny-ecs, so users should mostly ignore this, too.
|
||||||
|
|
||||||
|
|
||||||
--- @module tiny-ecs
|
--- @module tiny-ecs
|
||||||
-- @author Calvin Rose
|
-- @author Calvin Rose
|
||||||
-- @license MIT
|
-- @license MIT
|
||||||
|
@ -103,14 +102,13 @@ local filterJoin
|
||||||
-- A helper function to filters from string
|
-- A helper function to filters from string
|
||||||
local filterBuildString
|
local filterBuildString
|
||||||
|
|
||||||
|
|
||||||
local function filterJoinRaw(invert, joining_op, ...)
|
local function filterJoinRaw(invert, joining_op, ...)
|
||||||
local _args = {...}
|
local _args = { ... }
|
||||||
|
|
||||||
return function(system, e)
|
return function(system, e)
|
||||||
local acc
|
local acc
|
||||||
local args = _args
|
local args = _args
|
||||||
if joining_op == 'or' then
|
if joining_op == "or" then
|
||||||
acc = false
|
acc = false
|
||||||
for i = 1, #args do
|
for i = 1, #args do
|
||||||
local v = args[i]
|
local v = args[i]
|
||||||
|
@ -119,7 +117,7 @@ local function filterJoinRaw(invert, joining_op, ...)
|
||||||
elseif type(v) == "function" then
|
elseif type(v) == "function" then
|
||||||
acc = acc or v(system, e)
|
acc = acc or v(system, e)
|
||||||
else
|
else
|
||||||
error 'Filter token must be a string or a filter function.'
|
error("Filter token must be a string or a filter function.")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
|
@ -131,7 +129,7 @@ local function filterJoinRaw(invert, joining_op, ...)
|
||||||
elseif type(v) == "function" then
|
elseif type(v) == "function" then
|
||||||
acc = acc and v(system, e)
|
acc = acc and v(system, e)
|
||||||
else
|
else
|
||||||
error 'Filter token must be a string or a filter function.'
|
error("Filter token must be a string or a filter function.")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -146,69 +144,68 @@ local function filterJoinRaw(invert, joining_op, ...)
|
||||||
end
|
end
|
||||||
|
|
||||||
do
|
do
|
||||||
|
|
||||||
function filterJoin(...)
|
function filterJoin(...)
|
||||||
local state, value = pcall(filterJoinRaw, ...)
|
local state, value = pcall(filterJoinRaw, ...)
|
||||||
if state then return value else return nil, value end
|
if state then
|
||||||
|
return value
|
||||||
|
else
|
||||||
|
return nil, value
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function buildPart(str)
|
local function buildPart(str)
|
||||||
local accum = {}
|
local accum = {}
|
||||||
local subParts = {}
|
local subParts = {}
|
||||||
str = str:gsub('%b()', function(p)
|
str = str:gsub("%b()", function(p)
|
||||||
subParts[#subParts + 1] = buildPart(p:sub(2, -2))
|
subParts[#subParts + 1] = buildPart(p:sub(2, -2))
|
||||||
return ('\255%d'):format(#subParts)
|
return ("\255%d"):format(#subParts)
|
||||||
end)
|
end)
|
||||||
for invert, part, sep in str:gmatch('(%!?)([^%|%&%!]+)([%|%&]?)') do
|
for invert, part, sep in str:gmatch("(%!?)([^%|%&%!]+)([%|%&]?)") do
|
||||||
if part:match('^\255%d+$') then
|
if part:match("^\255%d+$") then
|
||||||
local partIndex = tonumber(part:match(part:sub(2)))
|
local partIndex = tonumber(part:match(part:sub(2)))
|
||||||
accum[#accum + 1] = ('%s(%s)')
|
accum[#accum + 1] = ("%s(%s)"):format(invert == "" and "" or "not", subParts[partIndex])
|
||||||
:format(invert == '' and '' or 'not', subParts[partIndex])
|
|
||||||
else
|
else
|
||||||
accum[#accum + 1] = ("(e[%s] %s nil)")
|
accum[#accum + 1] = ("(e[%s] %s nil)"):format(make_safe(part), invert == "" and "~=" or "==")
|
||||||
:format(make_safe(part), invert == '' and '~=' or '==')
|
|
||||||
end
|
end
|
||||||
if sep ~= '' then
|
if sep ~= "" then
|
||||||
accum[#accum + 1] = (sep == '|' and ' or ' or ' and ')
|
accum[#accum + 1] = (sep == "|" and " or " or " and ")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return table.concat(accum)
|
return table.concat(accum)
|
||||||
end
|
end
|
||||||
|
|
||||||
function filterBuildString(str)
|
function filterBuildString(str)
|
||||||
local source = ("return function(_, e) return %s end")
|
local source = ("return function(_, e) return %s end"):format(buildPart(str))
|
||||||
:format(buildPart(str))
|
|
||||||
local loader, err = loadstring(source)
|
local loader, err = loadstring(source)
|
||||||
if err then
|
if err then
|
||||||
error(err)
|
error(err)
|
||||||
end
|
end
|
||||||
return loader()
|
return loader()
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Makes a Filter that selects Entities with all specified Components and
|
--- Makes a Filter that selects Entities with all specified Components and
|
||||||
-- Filters.
|
-- Filters.
|
||||||
function tiny.requireAll(...)
|
function tiny.requireAll(...)
|
||||||
return filterJoin(false, 'and', ...)
|
return filterJoin(false, "and", ...)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Makes a Filter that selects Entities with at least one of the specified
|
--- Makes a Filter that selects Entities with at least one of the specified
|
||||||
-- Components and Filters.
|
-- Components and Filters.
|
||||||
function tiny.requireAny(...)
|
function tiny.requireAny(...)
|
||||||
return filterJoin(false, 'or', ...)
|
return filterJoin(false, "or", ...)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Makes a Filter that rejects Entities with all specified Components and
|
--- Makes a Filter that rejects Entities with all specified Components and
|
||||||
-- Filters, and selects all other Entities.
|
-- Filters, and selects all other Entities.
|
||||||
function tiny.rejectAll(...)
|
function tiny.rejectAll(...)
|
||||||
return filterJoin(true, 'and', ...)
|
return filterJoin(true, "and", ...)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Makes a Filter that rejects Entities with at least one of the specified
|
--- Makes a Filter that rejects Entities with at least one of the specified
|
||||||
-- Components and Filters, and selects all other Entities.
|
-- Components and Filters, and selects all other Entities.
|
||||||
function tiny.rejectAny(...)
|
function tiny.rejectAny(...)
|
||||||
return filterJoin(true, 'or', ...)
|
return filterJoin(true, "or", ...)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Makes a Filter from a string. Syntax of `pattern` is as follows.
|
--- Makes a Filter from a string. Syntax of `pattern` is as follows.
|
||||||
|
@ -224,7 +221,11 @@ end
|
||||||
-- @param pattern
|
-- @param pattern
|
||||||
function tiny.filter(pattern)
|
function tiny.filter(pattern)
|
||||||
local state, value = pcall(filterBuildString, pattern)
|
local state, value = pcall(filterBuildString, pattern)
|
||||||
if state then return value else return nil, value end
|
if state then
|
||||||
|
return value
|
||||||
|
else
|
||||||
|
return nil, value
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- System functions.
|
--- System functions.
|
||||||
|
@ -463,8 +464,7 @@ function tiny.world(...)
|
||||||
entities = {},
|
entities = {},
|
||||||
|
|
||||||
-- List of Systems
|
-- List of Systems
|
||||||
systems = {}
|
systems = {},
|
||||||
|
|
||||||
}, worldMetaTable)
|
}, worldMetaTable)
|
||||||
|
|
||||||
tiny_add(ret, ...)
|
tiny_add(ret, ...)
|
||||||
|
@ -673,7 +673,6 @@ end
|
||||||
|
|
||||||
-- Adds, removes, and changes Entities that have been marked.
|
-- Adds, removes, and changes Entities that have been marked.
|
||||||
function tiny_manageEntities(world)
|
function tiny_manageEntities(world)
|
||||||
|
|
||||||
local e2r = world.entitiesToRemove
|
local e2r = world.entitiesToRemove
|
||||||
local e2c = world.entitiesToChange
|
local e2c = world.entitiesToChange
|
||||||
|
|
||||||
|
@ -793,7 +792,6 @@ end
|
||||||
-- Systems. If `filter` is not supplied, all Systems are updated. Put this
|
-- Systems. If `filter` is not supplied, all Systems are updated. Put this
|
||||||
-- function in your main loop.
|
-- function in your main loop.
|
||||||
function tiny.update(world, dt, filter)
|
function tiny.update(world, dt, filter)
|
||||||
|
|
||||||
tiny_manageSystems(world)
|
tiny_manageSystems(world)
|
||||||
tiny_manageEntities(world)
|
tiny_manageEntities(world)
|
||||||
|
|
||||||
|
@ -809,8 +807,7 @@ function tiny.update(world, dt, filter)
|
||||||
onModify(system, dt)
|
onModify(system, dt)
|
||||||
end
|
end
|
||||||
local preWrap = system.preWrap
|
local preWrap = system.preWrap
|
||||||
if preWrap and
|
if preWrap and ((not filter) or filter(world, system)) then
|
||||||
((not filter) or filter(world, system)) then
|
|
||||||
preWrap(system, dt)
|
preWrap(system, dt)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -853,12 +850,10 @@ function tiny.update(world, dt, filter)
|
||||||
for i = 1, #systems do
|
for i = 1, #systems do
|
||||||
local system = systems[i]
|
local system = systems[i]
|
||||||
local postWrap = system.postWrap
|
local postWrap = system.postWrap
|
||||||
if postWrap and system.active and
|
if postWrap and system.active and ((not filter) or filter(world, system)) then
|
||||||
((not filter) or filter(world, system)) then
|
|
||||||
postWrap(system, dt)
|
postWrap(system, dt)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Removes all Entities from the World.
|
--- Removes all Entities from the World.
|
||||||
|
@ -924,11 +919,11 @@ worldMetaTable = {
|
||||||
clearSystems = tiny.clearSystems,
|
clearSystems = tiny.clearSystems,
|
||||||
getEntityCount = tiny.getEntityCount,
|
getEntityCount = tiny.getEntityCount,
|
||||||
getSystemCount = tiny.getSystemCount,
|
getSystemCount = tiny.getSystemCount,
|
||||||
setSystemIndex = tiny.setSystemIndex
|
setSystemIndex = tiny.setSystemIndex,
|
||||||
},
|
},
|
||||||
__tostring = function()
|
__tostring = function()
|
||||||
return "<tiny-ecs_World>"
|
return "<tiny-ecs_World>"
|
||||||
end
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
_G.tiny = tiny
|
_G.tiny = tiny
|
||||||
|
|
190
main.lua
|
@ -9,25 +9,177 @@ require("generated/filter-types")
|
||||||
require("generated/assets")
|
require("generated/assets")
|
||||||
require("generated/all-systems")
|
require("generated/all-systems")
|
||||||
|
|
||||||
local scenarios = {
|
local width, height = love.graphics.getWidth(), love.graphics.getHeight()
|
||||||
default = function()
|
|
||||||
-- TODO: Add default entities
|
local squareSize = 80
|
||||||
end,
|
local tileSize = math.floor(squareSize * 1.2)
|
||||||
textTestScenario = function()
|
local marginSize = 10
|
||||||
|
|
||||||
|
CursorMask = 1
|
||||||
|
BallMask = 2
|
||||||
|
|
||||||
|
local function emptyTile(unhighlightSiblings, gridPosition)
|
||||||
|
local size = { x = squareSize, y = squareSize }
|
||||||
|
return {
|
||||||
|
unhighlightSiblings = unhighlightSiblings,
|
||||||
|
canBeCollidedBy = CursorMask,
|
||||||
|
highlightOnMouseOver = T.marker,
|
||||||
|
size = size,
|
||||||
|
gridPosition = gridPosition,
|
||||||
|
drawAsRectangle = { size = size },
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
local function replaceAt(grid, y, x, entity)
|
||||||
|
local current = grid[y][x]
|
||||||
|
grid[y][x] = entity
|
||||||
|
|
||||||
|
current.unhighlightSiblings[entity] = true
|
||||||
|
current.unhighlightSiblings[current] = nil
|
||||||
|
entity.unhighlightSiblings = current.unhighlightSiblings
|
||||||
|
|
||||||
|
-- entity.position = current.position
|
||||||
|
entity.gridPosition = current.gridPosition
|
||||||
|
entity.toLeft = current.toLeft
|
||||||
|
entity.toRight = current.toRight
|
||||||
|
entity.above = current.above
|
||||||
|
entity.below = current.below
|
||||||
|
|
||||||
|
current.toLeft.toRight = current.toRight
|
||||||
|
current.toRight.toLeft = current.toLeft
|
||||||
|
current.above.below = current.below
|
||||||
|
current.below.above = current.above
|
||||||
|
|
||||||
|
World:removeEntity(current)
|
||||||
|
World:addEntity(entity)
|
||||||
|
end
|
||||||
|
|
||||||
|
function PositionAtGridXy(x, y)
|
||||||
|
return {
|
||||||
|
x = 20 + ((x - 1) * tileSize),
|
||||||
|
y = 60 + ((y - 1) * tileSize),
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
Scenarios = {
|
||||||
|
---@return table xyGrid Where grid[y][x] is an Entity
|
||||||
|
emptyGrid = function()
|
||||||
|
local cursorSize = { x = 5, y = 5 }
|
||||||
World:addEntity({
|
World:addEntity({
|
||||||
position = { x = 0, y = 600 },
|
size = cursorSize,
|
||||||
drawAsText = {
|
moveWithCursor = T.marker,
|
||||||
text = "Hello, world!",
|
canCollideWith = bit.bor(CursorMask, BallMask),
|
||||||
style = TextStyle.Inverted,
|
position = { x = -999, y = -999 },
|
||||||
|
highlightsCollided = T.marker,
|
||||||
|
drawAsRectangle = {
|
||||||
|
size = cursorSize,
|
||||||
|
mode = "fill",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
World:addEntity({
|
||||||
|
position = { x = 0, y = 0 },
|
||||||
|
drawAsSprite = Grass,
|
||||||
|
z = 0,
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Temporary storages for connecting grid items
|
||||||
|
local previous
|
||||||
|
local unhighlightSiblings = {}
|
||||||
|
local yxGrid = {}
|
||||||
|
|
||||||
|
local xCount = 8
|
||||||
|
local yCount = 5
|
||||||
|
|
||||||
|
for y = 1, yCount do
|
||||||
|
yxGrid[y] = {}
|
||||||
|
for x = 1, xCount do
|
||||||
|
local current = World:addEntity(emptyTile(unhighlightSiblings, { x = x, y = y }))
|
||||||
|
yxGrid[y][x] = current
|
||||||
|
unhighlightSiblings[current] = true
|
||||||
|
current.toLeft = previous
|
||||||
|
if previous ~= nil then
|
||||||
|
if previous.toRight == nil then
|
||||||
|
previous.toRight = current
|
||||||
|
end
|
||||||
|
else
|
||||||
|
current.canReceiveButtons = T.marker
|
||||||
|
current.highlighted = T.marker
|
||||||
|
end
|
||||||
|
if yxGrid[y - 1] and yxGrid[y - 1][x] then
|
||||||
|
current.above = yxGrid[y - 1][x]
|
||||||
|
yxGrid[y - 1][x].below = current
|
||||||
|
end
|
||||||
|
if y == yCount then
|
||||||
|
-- Connect last row to first row
|
||||||
|
yxGrid[1][x].above = current
|
||||||
|
current.below = yxGrid[1][x]
|
||||||
|
end
|
||||||
|
if x == xCount then
|
||||||
|
-- Connect last entry to first Entry
|
||||||
|
current.toRight = yxGrid[y][1]
|
||||||
|
yxGrid[y][1].toLeft = current
|
||||||
|
end
|
||||||
|
previous = current
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return yxGrid
|
||||||
|
end,
|
||||||
|
|
||||||
|
firstLevel = function()
|
||||||
|
local yxGrid = Scenarios.emptyGrid()
|
||||||
|
replaceAt(yxGrid, 4, 7, {
|
||||||
|
drawAsSprite = Flag,
|
||||||
|
z = 1,
|
||||||
|
effectsToApply = {
|
||||||
|
endOfRound = T.marker,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
replaceAt(yxGrid, 2, 2, {
|
||||||
|
drawAsSprite = SmallSandTrap,
|
||||||
|
z = 1,
|
||||||
|
spriteAfterEffect = SmallSandTrapActivated,
|
||||||
|
effectsToApply = {
|
||||||
|
slowsBy = { x = 1, y = 1 },
|
||||||
|
movement = { x = 0, y = 0, },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
replaceAt(yxGrid, 1, 1, {
|
||||||
|
pickedUpOnClick = T.marker,
|
||||||
|
drawAsSprite = GolferRight,
|
||||||
|
z = 1,
|
||||||
|
spriteAfterEffect = GolferRightActivated,
|
||||||
|
effectsToApply = {
|
||||||
|
movement = { x = 6, y = 0, },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
replaceAt(yxGrid, 1, 7, {
|
||||||
|
pickedUpOnClick = T.marker,
|
||||||
|
drawAsSprite = GolferRight,
|
||||||
|
z = 1,
|
||||||
|
spriteAfterEffect = GolferRightActivated,
|
||||||
|
effectsToApply = {
|
||||||
|
movement = { x = 0, y = 3 },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
World:addEntity({
|
||||||
|
ballEffects = {},
|
||||||
|
drawAsSprite = Ball,
|
||||||
|
gridPosition = { x = 1, y = 1 },
|
||||||
|
z = 2,
|
||||||
|
})
|
||||||
|
-- TODO: Make the start button start
|
||||||
|
World:addEntity({
|
||||||
|
drawAsSprite = StartButton,
|
||||||
|
z = 3,
|
||||||
|
position = {
|
||||||
|
x = width - 120,
|
||||||
|
y = height - 50,
|
||||||
},
|
},
|
||||||
velocity = { x = 240, y = -500 },
|
|
||||||
mass = 1,
|
|
||||||
decayAfterSeconds = 10,
|
|
||||||
})
|
})
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
scenarios.textTestScenario()
|
Scenarios.firstLevel()
|
||||||
|
|
||||||
function love.load()
|
function love.load()
|
||||||
love.graphics.setBackgroundColor(1, 1, 1)
|
love.graphics.setBackgroundColor(1, 1, 1)
|
||||||
|
@ -35,5 +187,15 @@ function love.load()
|
||||||
end
|
end
|
||||||
|
|
||||||
function love.draw()
|
function love.draw()
|
||||||
World:update(love.timer.getDelta())
|
local dt = love.timer.getDelta()
|
||||||
|
if love.keyboard.isDown("r") then
|
||||||
|
World:clearEntities()
|
||||||
|
Scenarios.firstLevel()
|
||||||
|
end
|
||||||
|
World:update(dt, function(_, system)
|
||||||
|
return not system.isDrawSystem
|
||||||
|
end)
|
||||||
|
World:update(dt, function(_, system)
|
||||||
|
return system.isDrawSystem
|
||||||
|
end)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,11 +1,24 @@
|
||||||
collidingEntities = filteredSystem("collidingEntitites", {
|
collidingEntities = filteredSystem("collidingEntitites", {
|
||||||
velocity = T.XyPair,
|
|
||||||
position = T.XyPair,
|
position = T.XyPair,
|
||||||
size = T.XyPair,
|
size = T.XyPair,
|
||||||
canCollideWith = T.BitMask,
|
canCollideWith = T.BitMask,
|
||||||
isSolid = Maybe(T.bool),
|
isSolid = Maybe(T.bool),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
local function intersects(rect, rectOther)
|
||||||
|
local left = rect.position.x
|
||||||
|
local right = rect.position.x + rect.size.x
|
||||||
|
local top = rect.position.y
|
||||||
|
local bottom = rect.position.y + rect.size.y
|
||||||
|
|
||||||
|
local leftOther = rectOther.position.x
|
||||||
|
local rightOther = rectOther.position.x + rectOther.size.x
|
||||||
|
local topOther = rectOther.position.y
|
||||||
|
local bottomOther = rectOther.position.y + rectOther.size.y
|
||||||
|
|
||||||
|
return leftOther < right and left < rightOther and topOther < bottom and top < bottomOther
|
||||||
|
end
|
||||||
|
|
||||||
filteredSystem(
|
filteredSystem(
|
||||||
"collisionDetection",
|
"collisionDetection",
|
||||||
{ position = T.XyPair, size = T.XyPair, canBeCollidedBy = T.BitMask, isSolid = Maybe(T.bool) },
|
{ position = T.XyPair, size = T.XyPair, canBeCollidedBy = T.BitMask, isSolid = Maybe(T.bool) },
|
||||||
|
@ -18,19 +31,7 @@ filteredSystem(
|
||||||
and e.canBeCollidedBy
|
and e.canBeCollidedBy
|
||||||
and bit.band(collider.canCollideWith, e.canBeCollidedBy) ~= 0
|
and bit.band(collider.canCollideWith, e.canBeCollidedBy) ~= 0
|
||||||
then
|
then
|
||||||
local colliderTop = collider.position.y
|
if intersects(e, collider) then
|
||||||
local colliderBottom = collider.position.y + collider.size.y
|
|
||||||
local entityTop = e.position.y
|
|
||||||
local entityBottom = entityTop + e.size.y
|
|
||||||
|
|
||||||
local withinY = (entityTop > colliderTop and entityTop < colliderBottom)
|
|
||||||
or (entityBottom > colliderTop and entityBottom < colliderBottom)
|
|
||||||
|
|
||||||
if
|
|
||||||
withinY
|
|
||||||
and collider.position.x < e.position.x + e.size.x
|
|
||||||
and collider.position.x + collider.size.x > e.position.x
|
|
||||||
then
|
|
||||||
system.world:addEntity({ collisionBetween = { e, collider } })
|
system.world:addEntity({ collisionBetween = { e, collider } })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,4 +1,23 @@
|
||||||
filteredSystem("collisionResolution", { collisionBetween = T.Collision }, function(e, _, system)
|
Collisions = filteredSystem("collisionResolution", { collisionBetween = T.Collision }, function(e, _, system)
|
||||||
local collidedInto, collider = e.collisionBetween[1], e.collisionBetween[2]
|
local collidedInto, collider = e.collisionBetween[1], e.collisionBetween[2]
|
||||||
|
if collider.highlightsCollided then
|
||||||
|
if collidedInto.unhighlightSiblings then
|
||||||
|
for sibling in pairs(collidedInto.unhighlightSiblings) do
|
||||||
|
sibling.highlighted = nil
|
||||||
|
sibling.canReceiveButtons = nil
|
||||||
|
system.world:addEntity(sibling)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if collidedInto.highlightOnMouseOver ~= nil then
|
||||||
|
collidedInto.highlighted = T.marker
|
||||||
|
collidedInto.canReceiveButtons = T.marker
|
||||||
|
system.world:addEntity(collidedInto)
|
||||||
|
end
|
||||||
|
if collidedInto.pickedUpOnClick ~= nil then -- and #HeldByCursor.entities == 0 and love.mouse.isDown(1) then
|
||||||
|
print("Click!")
|
||||||
|
collidedInto.moveWithCursor = T.marker
|
||||||
|
system.world:addEntity(collidedInto)
|
||||||
|
end
|
||||||
|
end
|
||||||
system.world:removeEntity(e)
|
system.world:removeEntity(e)
|
||||||
end)
|
end)
|
||||||
|
|
|
@ -3,4 +3,4 @@ filteredSystem("decay", { decayAfterSeconds = T.number }, function(e, dt, system
|
||||||
if e.decayAfterSeconds <= 0 then
|
if e.decayAfterSeconds <= 0 then
|
||||||
system.world:removeEntity(e)
|
system.world:removeEntity(e)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
|
@ -1,23 +1,52 @@
|
||||||
|
local floor = math.floor
|
||||||
local gfx = love.graphics
|
local gfx = love.graphics
|
||||||
|
---
|
||||||
|
---@generic T
|
||||||
|
---@param shape T | fun()
|
||||||
|
---@param process fun(entity: T, dt: number, system: System) | nil
|
||||||
|
---@return System | { entities: T[] }
|
||||||
|
local function drawSystem(name, shape, process)
|
||||||
|
local system = filteredSystem(name, shape, process, function(_, a, b)
|
||||||
|
if a.z ~= nil and b.z ~= nil then
|
||||||
|
return a.z < b.z
|
||||||
|
end
|
||||||
|
if a.z ~= nil then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end)
|
||||||
|
system.isDrawSystem = true
|
||||||
|
return system
|
||||||
|
end
|
||||||
|
|
||||||
filteredSystem("drawRectangles", { position = T.XyPair, drawAsRectangle = { size = T.XyPair } }, function(e, _, _)
|
filteredSystem("mapGridPositionToRealPosition", { gridPosition = T.XyPair }, function(e, _, system)
|
||||||
gfx.fillRect(e.position.x, e.position.y, e.drawAsRectangle.size.x, e.drawAsRectangle.size.y)
|
e.position = PositionAtGridXy(e.gridPosition.x, e.gridPosition.y)
|
||||||
|
system.world:addEntity(e)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
filteredSystem("drawSprites", { position = T.XyPair, drawAsSprite = T.pd_image }, function(e)
|
local spriteDrawSystem = drawSystem(
|
||||||
if e.position.y < Camera.pan.y - 240 or e.position.y > Camera.pan.y + 480 then
|
"drawSprites",
|
||||||
return
|
{ position = T.XyPair, drawAsSprite = T.Drawable, rotation = Maybe(T.number) },
|
||||||
|
function(e)
|
||||||
|
if not e.drawAsSprite then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local width, height = e.drawAsSprite:getDimensions()
|
||||||
|
gfx.draw(e.drawAsSprite, e.position.x, e.position.y)
|
||||||
end
|
end
|
||||||
e.drawAsSprite:draw(e.position.x, e.position.y)
|
)
|
||||||
end)
|
|
||||||
|
function spriteDrawSystem:preProcess()
|
||||||
|
gfx.setColor(1, 1, 1)
|
||||||
|
end
|
||||||
|
|
||||||
local margin = 8
|
local margin = 8
|
||||||
|
|
||||||
filteredSystem(
|
drawSystem(
|
||||||
"drawText",
|
"drawText",
|
||||||
{ position = T.XyPair, drawAsText = { text = T.str, style = Maybe(T.str), font = Maybe(T.pd_font) } },
|
{ position = T.XyPair, drawAsText = { text = T.str, style = Maybe(T.str), font = Maybe(T.pd_font) } },
|
||||||
function(e)
|
function(e)
|
||||||
local font = gfx.getFont() -- e.drawAsText.font or AshevilleSans14Bold
|
local font = e.font or gfx.getFont() -- e.drawAsText.font or AshevilleSans14Bold
|
||||||
local textHeight = font:getHeight()
|
local textHeight = font:getHeight()
|
||||||
local textWidth = font:getWidth(e.drawAsText.text)
|
local textWidth = font:getWidth(e.drawAsText.text)
|
||||||
|
|
||||||
|
@ -40,3 +69,9 @@ filteredSystem(
|
||||||
gfx.print(e.drawAsText.text, bgLeftEdge + margin, bgTopEdge + margin)
|
gfx.print(e.drawAsText.text, bgLeftEdge + margin, bgTopEdge + margin)
|
||||||
end
|
end
|
||||||
)
|
)
|
||||||
|
|
||||||
|
drawSystem("drawRectangles", { position = T.XyPair, drawAsRectangle = { size = T.XyPair } }, function(e, _, _)
|
||||||
|
gfx.setColor(1, 1, 1, 0.5)
|
||||||
|
local mode = e.drawAsRectangle.mode or (e.highlighted and "fill" or "line")
|
||||||
|
gfx.rectangle(mode, e.position.x, e.position.y, e.drawAsRectangle.size.x, e.drawAsRectangle.size.y)
|
||||||
|
end)
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
local isDown = love.keyboard.isDown
|
||||||
|
|
||||||
---@type ButtonState
|
---@type ButtonState
|
||||||
local buttonState = {}
|
local buttonState = {}
|
||||||
|
|
||||||
|
@ -6,8 +8,105 @@ buttonInputSystem = filteredSystem("buttonInput", { canReceiveButtons = T.marker
|
||||||
system.world:addEntity(e)
|
system.world:addEntity(e)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
HeldByCursor = filteredSystem("HeldByCursor", { pickedUpOnClick = T.marker, moveWithCursor = T.marker })
|
||||||
|
|
||||||
function buttonInputSystem:preProcess()
|
function buttonInputSystem:preProcess()
|
||||||
if #self.entities == 0 then
|
if #self.entities == 0 then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function love.keypressed(key, _, _)
|
||||||
|
buttonState[key] = true
|
||||||
|
end
|
||||||
|
|
||||||
|
function ClearButtonState()
|
||||||
|
for key in pairs(buttonState) do
|
||||||
|
buttonState[key] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---@type System
|
||||||
|
local cursorTracking
|
||||||
|
|
||||||
|
local mouseX, mouseY = -9999, -9999
|
||||||
|
local mouseInControl = false
|
||||||
|
|
||||||
|
function love.mousemoved(x, y)
|
||||||
|
mouseInControl = true
|
||||||
|
mouseX, mouseY = x, y
|
||||||
|
end
|
||||||
|
|
||||||
|
local keyDebounceSec = 0.1
|
||||||
|
local delay = 0
|
||||||
|
|
||||||
|
local menuSystem = filteredSystem(
|
||||||
|
"menu",
|
||||||
|
{ canReceiveButtons = T.marker, highlighted = T.marker },
|
||||||
|
function(e, dt, system)
|
||||||
|
if delay > 0 then
|
||||||
|
delay = delay - dt
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local function tryShiftMenu(target, keys)
|
||||||
|
if target == nil then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
for _, key in ipairs(keys) do
|
||||||
|
if isDown(key) then
|
||||||
|
e.highlighted = nil
|
||||||
|
e.canReceiveButtons = nil
|
||||||
|
target.canReceiveButtons = T.marker
|
||||||
|
target.highlighted = T.marker
|
||||||
|
system.world:addEntity(e)
|
||||||
|
system.world:addEntity(target)
|
||||||
|
e = target
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local pressed = tryShiftMenu(e.toRight, { "right", "d" })
|
||||||
|
pressed = tryShiftMenu(e.toLeft, { "left", "a" }) or pressed
|
||||||
|
pressed = tryShiftMenu(e.below, { "down", "s" }) or pressed
|
||||||
|
pressed = tryShiftMenu(e.above, { "up", "w" }) or pressed
|
||||||
|
|
||||||
|
if isDown("return") then
|
||||||
|
pressed = true
|
||||||
|
system.world:addEntity({
|
||||||
|
round = "start",
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
if pressed then
|
||||||
|
mouseInControl = false
|
||||||
|
mouseX, mouseY = -9999, -9999
|
||||||
|
for _, tracker in pairs(cursorTracking.entities) do
|
||||||
|
tracker.position.x = -9999
|
||||||
|
tracker.position.y = -9999
|
||||||
|
end
|
||||||
|
delay = keyDebounceSec
|
||||||
|
end
|
||||||
|
end
|
||||||
|
)
|
||||||
|
|
||||||
|
cursorTracking = filteredSystem("cursorTracking", { moveWithCursor = T.marker, position = T.XyPair }, function(e)
|
||||||
|
if mouseInControl then
|
||||||
|
e.position.x = mouseX
|
||||||
|
e.position.y = mouseY
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- local allHighlighted = filteredSystem("allHighlighted", { highlighted = T.marker })
|
||||||
|
|
||||||
|
function cursorTracking:postProcess()
|
||||||
|
-- if mouseInControl and #Collisions.entities == 0 then
|
||||||
|
-- -- If the cursor is not colliding with anything, wipe all highlighted components
|
||||||
|
-- for _, highlighted in pairs(allHighlighted.entities) do
|
||||||
|
-- highlighted.highlighted = nil
|
||||||
|
-- self.world:addEntity(highlighted)
|
||||||
|
-- end
|
||||||
|
-- end
|
||||||
|
end
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
local gridElements = filteredSystem("gridElements", { gridPosition = T.XyPair, effectsToApply = T.AnyComponent })
|
||||||
|
|
||||||
|
local roundRunning = false
|
||||||
|
|
||||||
|
filteredSystem("timers", { timerSec = T.number, callback = T.SelfFunction }, function(e, dt, system)
|
||||||
|
e.timerSec = e.timerSec - dt
|
||||||
|
if e.timerSec < 0 then
|
||||||
|
e:callback()
|
||||||
|
system.world:removeEntity(e)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
local function sign(n)
|
||||||
|
return n > 0 and 1 or n < 0 and -1 or 0
|
||||||
|
end
|
||||||
|
|
||||||
|
local roundSystem = filteredSystem("rounds", { round = T.str })
|
||||||
|
|
||||||
|
local activeBallEffects = filteredSystem("activeBallEffects", { ballEffects = T.AnyComponent, gridPosition = T.XyPair }, function(e, dt, system)
|
||||||
|
local roundActive = false
|
||||||
|
for _, state in pairs(roundSystem.entities) do
|
||||||
|
if state.round == "start" then
|
||||||
|
roundActive = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not roundActive then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local gridPosition, effects = e.gridPosition, e.ballEffects
|
||||||
|
|
||||||
|
-- Search for new effects from the current tile
|
||||||
|
for _, gridElement in pairs(gridElements.entities) do
|
||||||
|
if
|
||||||
|
gridPosition.x == gridElement.gridPosition.x
|
||||||
|
and gridPosition.y == gridElement.gridPosition.y
|
||||||
|
then
|
||||||
|
-- More direct-mutation-y than I'd like,
|
||||||
|
-- but offers a simple way to overwrite existing effects.
|
||||||
|
-- We're "setting InRelations" :D
|
||||||
|
for key, value in pairs(gridElement.effectsToApply) do
|
||||||
|
effects[key] = value
|
||||||
|
end
|
||||||
|
if gridElement.spriteAfterEffect then
|
||||||
|
gridElement.drawAsSprite = gridElement.spriteAfterEffect
|
||||||
|
gridElement.spriteAfterEffect = nil
|
||||||
|
end
|
||||||
|
gridElement.effectsToApply = nil
|
||||||
|
system.world:addEntity(gridElement)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Apply any effects currently connected to this ball
|
||||||
|
if effects.movement ~= nil then
|
||||||
|
gridPosition.x = gridPosition.x + sign(effects.movement.x)
|
||||||
|
gridPosition.y = gridPosition.y + sign(effects.movement.y)
|
||||||
|
|
||||||
|
effects.movement.x = effects.movement.x - sign(effects.movement.x)
|
||||||
|
effects.movement.y = effects.movement.y - sign(effects.movement.y)
|
||||||
|
|
||||||
|
if effects.movement.x == 0 and effects.movement.y == 0 then
|
||||||
|
effects.movement = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- TODO: Trigger the round end
|
||||||
|
-- for _, _ in pairs(effects) do
|
||||||
|
-- -- Return if there are any effects left
|
||||||
|
-- return
|
||||||
|
-- end
|
||||||
|
|
||||||
|
-- system.world:addEntity({ round = "end" })
|
||||||
|
end)
|
||||||
|
|
||||||
|
local stepTimeSec = 0.3
|
||||||
|
local stepTimer = stepTimeSec
|
||||||
|
|
||||||
|
function activeBallEffects:preProcess(dt)
|
||||||
|
stepTimer = stepTimer - dt
|
||||||
|
if stepTimer <= 0 then
|
||||||
|
stepTimer = stepTimeSec
|
||||||
|
return
|
||||||
|
end
|
||||||
|
return tiny.SKIP_PROCESS
|
||||||
|
end
|
||||||
|
|
|
@ -13,4 +13,4 @@ filteredSystem("drag", { velocity = T.XyPair, drag = T.number }, function(e, dt,
|
||||||
local currentDrag = e.drag * dt
|
local currentDrag = e.drag * dt
|
||||||
e.velocity.x = e.velocity.x - (e.velocity.x * currentDrag * dt)
|
e.velocity.x = e.velocity.x - (e.velocity.x * currentDrag * dt)
|
||||||
e.velocity.y = e.velocity.y - (e.velocity.y * currentDrag * dt)
|
e.velocity.y = e.velocity.y - (e.velocity.y * currentDrag * dt)
|
||||||
end)
|
end)
|
||||||
|
|
|
@ -39,4 +39,4 @@ if tinyWarnWhenNonDataOnEntities then
|
||||||
return valType
|
return valType
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,13 +1,15 @@
|
||||||
---@generic T
|
---@generic T
|
||||||
---@param shape T | fun()
|
---@param shape T | fun()
|
||||||
---@param process fun(entity: T, dt: number, system: System) | nil
|
---@param process fun(entity: T, dt: number, system: System) | nil
|
||||||
|
---@param compare nil | fun(system: System, entityA: T, entityB: T): boolean
|
||||||
---@return System | { entities: T[] }
|
---@return System | { entities: T[] }
|
||||||
function filteredSystem(name, shape, process)
|
function filteredSystem(name, shape, process, compare)
|
||||||
assert(type(name) == "string")
|
assert(type(name) == "string")
|
||||||
assert(type(shape) == "table" or type(shape) == "function")
|
assert(type(shape) == "table" or type(shape) == "function")
|
||||||
assert(process == nil or type(process) == "function")
|
assert(process == nil or type(process) == "function")
|
||||||
|
|
||||||
local system = tiny.processingSystem()
|
local system = compare and tiny.sortedProcessingSystem() or tiny.processingSystem()
|
||||||
|
system.compare = compare
|
||||||
system.name = name
|
system.name = name
|
||||||
if type(shape) == "table" then
|
if type(shape) == "table" then
|
||||||
local keys = {}
|
local keys = {}
|
||||||
|
|