First project-specific commit.
Hardly a game yet, but extremely basic ball movements work!
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))
|
||||||
|
|
1088
lib/luaunit.lua
|
@ -175,14 +175,14 @@ 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:
|
||||||
|
@ -217,7 +217,7 @@ local function formatBytes(n)
|
||||||
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
|
||||||
|
@ -246,9 +246,13 @@ local loadLuaFile = (
|
||||||
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
|
||||||
|
@ -270,59 +274,46 @@ 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
|
||||||
elseif not (processOptions and arg:find"^%-.") then
|
|
||||||
local paths = (hasOutputPaths and #pathsOut < #pathsIn) and pathsOut or pathsIn
|
local paths = (hasOutputPaths and #pathsOut < #pathsIn) and pathsOut or pathsIn
|
||||||
table.insert(paths, arg)
|
table.insert(paths, arg)
|
||||||
|
|
||||||
if arg == "-" and (not hasOutputPaths or paths == pathsOut) then
|
if arg == "-" and (not hasOutputPaths or paths == pathsOut) then
|
||||||
silent = true
|
silent = true
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif arg == "--" then
|
elseif arg == "--" then
|
||||||
processOptions = false
|
processOptions = false
|
||||||
|
elseif arg:find("^%-%-data=") or arg:find("^%-d=") then
|
||||||
elseif arg:find"^%-%-data=" or arg:find"^%-d=" then
|
|
||||||
customData = arg:gsub("^.-=", "")
|
customData = arg:gsub("^.-=", "")
|
||||||
|
|
||||||
elseif arg == "--backtickstrings" then
|
elseif arg == "--backtickstrings" then
|
||||||
allowBacktickStrings = true
|
allowBacktickStrings = true
|
||||||
|
|
||||||
elseif arg == "--debug" then
|
elseif arg == "--debug" then
|
||||||
isDebug = true
|
isDebug = true
|
||||||
outputMeta = outputMeta or true
|
outputMeta = outputMeta or true
|
||||||
|
elseif arg:find("^%-%-handler=") or arg:find("^%-h=") then
|
||||||
elseif arg:find"^%-%-handler=" or arg:find"^%-h=" then
|
|
||||||
messageHandlerPath = arg:gsub("^.-=", "")
|
messageHandlerPath = arg:gsub("^.-=", "")
|
||||||
|
|
||||||
elseif arg == "--jitsyntax" then
|
elseif arg == "--jitsyntax" then
|
||||||
allowJitSyntax = true
|
allowJitSyntax = true
|
||||||
|
|
||||||
elseif arg == "--linenumbers" then
|
elseif arg == "--linenumbers" then
|
||||||
addLineNumbers = true
|
addLineNumbers = true
|
||||||
|
|
||||||
elseif arg == "--meta" then
|
elseif arg == "--meta" then
|
||||||
outputMeta = true
|
outputMeta = true
|
||||||
elseif arg:find"^%-%-meta=" then
|
elseif arg:find("^%-%-meta=") then
|
||||||
outputMeta = arg:gsub("^.-=", "")
|
outputMeta = arg:gsub("^.-=", "")
|
||||||
|
|
||||||
elseif arg == "--nonil" then
|
elseif arg == "--nonil" then
|
||||||
canOutputNil = false
|
canOutputNil = false
|
||||||
|
|
||||||
elseif arg == "--novalidate" then
|
elseif arg == "--novalidate" then
|
||||||
validate = false
|
validate = false
|
||||||
|
elseif arg:find("^%-%-outputextension=") then
|
||||||
elseif arg:find"^%-%-outputextension=" then
|
|
||||||
if hasOutputPaths then
|
if hasOutputPaths then
|
||||||
errorLine("Cannot specify both --outputextension and --outputpaths")
|
errorLine("Cannot specify both --outputextension and --outputpaths")
|
||||||
end
|
end
|
||||||
hasOutputExtension = true
|
hasOutputExtension = true
|
||||||
outputExtension = arg:gsub("^.-=", "")
|
outputExtension = arg:gsub("^.-=", "")
|
||||||
|
|
||||||
elseif arg == "--outputpaths" or arg == "-o" then
|
elseif arg == "--outputpaths" or arg == "-o" then
|
||||||
if hasOutputExtension then
|
if hasOutputExtension then
|
||||||
errorLine("Cannot specify both --outputpaths and --outputextension")
|
errorLine("Cannot specify both --outputpaths and --outputextension")
|
||||||
|
@ -330,38 +321,27 @@ for _, arg in ipairs(args) do
|
||||||
errorLine(arg .. " must appear before any input path.")
|
errorLine(arg .. " must appear before any input path.")
|
||||||
end
|
end
|
||||||
hasOutputPaths = true
|
hasOutputPaths = true
|
||||||
|
elseif arg:find("^%-%-saveinfo=") or arg:find("^%-i=") then
|
||||||
elseif arg:find"^%-%-saveinfo=" or arg:find"^%-i=" then
|
|
||||||
processingInfoPath = arg:gsub("^.-=", "")
|
processingInfoPath = arg:gsub("^.-=", "")
|
||||||
|
|
||||||
elseif arg == "--silent" then
|
elseif arg == "--silent" then
|
||||||
silent = true
|
silent = true
|
||||||
|
|
||||||
elseif arg == "--faststrings" then
|
elseif arg == "--faststrings" then
|
||||||
fastStrings = true
|
fastStrings = true
|
||||||
|
|
||||||
elseif arg == "--nogc" then
|
elseif arg == "--nogc" then
|
||||||
collectgarbage("stop")
|
collectgarbage("stop")
|
||||||
|
elseif arg:find("^%-%-macroprefix=") then
|
||||||
elseif arg:find"^%-%-macroprefix=" then
|
|
||||||
macroPrefix = arg:gsub("^.-=", "")
|
macroPrefix = arg:gsub("^.-=", "")
|
||||||
|
elseif arg:find("^%-%-macrosuffix=") then
|
||||||
elseif arg:find"^%-%-macrosuffix=" then
|
|
||||||
macroSuffix = arg:gsub("^.-=", "")
|
macroSuffix = arg:gsub("^.-=", "")
|
||||||
|
|
||||||
elseif arg == "--release" then
|
elseif arg == "--release" then
|
||||||
releaseMode = true
|
releaseMode = true
|
||||||
|
elseif arg:find("^%-%-loglevel=") then
|
||||||
elseif arg:find"^%-%-loglevel=" then
|
|
||||||
maxLogLevel = arg:gsub("^.-=", "")
|
maxLogLevel = arg:gsub("^.-=", "")
|
||||||
|
|
||||||
elseif arg == "--version" then
|
elseif arg == "--version" then
|
||||||
io.stdout:write(pp.VERSION)
|
io.stdout:write(pp.VERSION)
|
||||||
os.exit()
|
os.exit()
|
||||||
|
|
||||||
elseif arg == "--nostrictmacroarguments" then
|
elseif arg == "--nostrictmacroarguments" then
|
||||||
strictMacroArguments = false
|
strictMacroArguments = false
|
||||||
|
|
||||||
else
|
else
|
||||||
errorLine("Unknown option '" .. arg:gsub("=.*", "") .. "'.")
|
errorLine("Unknown option '" .. arg:gsub("=.*", "") .. "'.")
|
||||||
end
|
end
|
||||||
|
@ -380,26 +360,19 @@ 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
|
elseif type(messageHandler) == "table" then
|
||||||
return messageHandler[message] ~= nil
|
return messageHandler[message] ~= nil
|
||||||
|
|
||||||
else
|
else
|
||||||
assert(false)
|
assert(false)
|
||||||
end
|
end
|
||||||
|
@ -408,18 +381,17 @@ end
|
||||||
local function sendMessage(message, ...)
|
local function sendMessage(message, ...)
|
||||||
if not messageHandler then
|
if not messageHandler then
|
||||||
return
|
return
|
||||||
|
|
||||||
elseif type(messageHandler) == "function" then
|
elseif type(messageHandler) == "function" then
|
||||||
local returnValues = pp.pack(messageHandler(message, ...))
|
local returnValues = pp.pack(messageHandler(message, ...))
|
||||||
return pp.unpack(returnValues, 1, returnValues.n)
|
return pp.unpack(returnValues, 1, returnValues.n)
|
||||||
|
|
||||||
elseif type(messageHandler) == "table" then
|
elseif type(messageHandler) == "table" then
|
||||||
local _messageHandler = messageHandler[message]
|
local _messageHandler = messageHandler[message]
|
||||||
if not _messageHandler then return end
|
if not _messageHandler then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
local returnValues = pp.pack(_messageHandler(...))
|
local returnValues = pp.pack(_messageHandler(...))
|
||||||
return pp.unpack(returnValues, 1, returnValues.n)
|
return pp.unpack(returnValues, 1, returnValues.n)
|
||||||
|
|
||||||
else
|
else
|
||||||
assert(false)
|
assert(false)
|
||||||
end
|
end
|
||||||
|
@ -450,8 +422,6 @@ if messageHandlerPath ~= "" then
|
||||||
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?
|
||||||
|
|
||||||
|
@ -471,17 +441,23 @@ 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.
|
||||||
|
|
||||||
|
@ -507,7 +483,7 @@ for i, pathIn in ipairs(pathsIn) do
|
||||||
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,
|
||||||
|
@ -549,17 +525,20 @@ for i, pathIn in ipairs(pathsIn) do
|
||||||
sendMessage("beforemeta", pathIn, lua)
|
sendMessage("beforemeta", pathIn, lua)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
onAfterMeta = messageHandler and function(lua)
|
onAfterMeta = messageHandler
|
||||||
|
and function(lua)
|
||||||
local luaModified = sendMessage("aftermeta", pathIn, 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
|
elseif luaModified ~= nil then
|
||||||
error(F(
|
error(
|
||||||
|
F(
|
||||||
"%s: Message handler did not return a string for 'aftermeta'. (Got %s)",
|
"%s: Message handler did not return a string for 'aftermeta'. (Got %s)",
|
||||||
messageHandlerPath, type(luaModified)
|
messageHandlerPath,
|
||||||
))
|
type(luaModified)
|
||||||
|
)
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
return lua
|
return lua
|
||||||
|
@ -577,7 +556,7 @@ for i, pathIn in ipairs(pathsIn) do
|
||||||
end)
|
end)
|
||||||
os.exit(1)
|
os.exit(1)
|
||||||
end,
|
end,
|
||||||
}
|
})
|
||||||
assert(info, err) -- The onError() handler above should have been called and we should have exited already.
|
assert(info, err) -- The onError() handler above should have been called and we should have exited already.
|
||||||
|
|
||||||
byteCount = byteCount + info.processedByteCount
|
byteCount = byteCount + info.processedByteCount
|
||||||
|
@ -586,18 +565,14 @@ for i, pathIn in ipairs(pathsIn) do
|
||||||
tokenCount = tokenCount + info.tokenCount
|
tokenCount = tokenCount + info.tokenCount
|
||||||
|
|
||||||
if processingInfoPath ~= "" then
|
if processingInfoPath ~= "" then
|
||||||
|
|
||||||
-- :SavedInfo
|
-- :SavedInfo
|
||||||
table.insert(processingInfo.files, info) -- See 'ProcessInfo' in preprocess.lua for what more 'info' contains.
|
table.insert(processingInfo.files, info) -- See 'ProcessInfo' in preprocess.lua for what more 'info' contains.
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
printfNoise("Processing '%s' successful! (%.3fs)", pathIn, os.clock() - startClockForPath)
|
printfNoise("Processing '%s' successful! (%.3fs)", pathIn, os.clock() - startClockForPath)
|
||||||
printfNoise(("-"):rep(#header))
|
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)
|
||||||
|
@ -614,17 +589,18 @@ 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,
|
||||||
|
(#pathsIn == 1) and "" or "s",
|
||||||
lineCountCode,
|
lineCountCode,
|
||||||
lineCount, (lineCount == 1) and "" or "s",
|
lineCount,
|
||||||
tokenCount, (tokenCount == 1) and "" or "s",
|
(lineCount == 1) and "" or "s",
|
||||||
|
tokenCount,
|
||||||
|
(tokenCount == 1) and "" or "s",
|
||||||
formatBytes(byteCount)
|
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.
|
||||||
|
|
||||||
==============================================================]]
|
==============================================================]]
|
||||||
|
|
||||||
|
|
1497
lib/preprocess.lua
67
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)
|
||||||
|
|
|
@ -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",
|
||||||
|
{ position = T.XyPair, drawAsSprite = T.Drawable, rotation = Maybe(T.number) },
|
||||||
|
function(e)
|
||||||
|
if not e.drawAsSprite then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
e.drawAsSprite:draw(e.position.x, e.position.y)
|
local width, height = e.drawAsSprite:getDimensions()
|
||||||
end)
|
gfx.draw(e.drawAsSprite, 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
|
||||||
|
|
|
@ -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 = {}
|
||||||
|
|