Compare commits
69 Commits
Author | SHA1 | Date |
---|---|---|
Pascal Abresch | 42b0e04312 | |
Pascal Abresch | f76f59015d | |
Pascal Abresch | 0d1b808848 | |
Pascal Abresch | 5b5f21dcce | |
Pascal Abresch | 0e0faf0d41 | |
Pascal Abresch | 4f15220f0a | |
Pascal Abresch | dca9be008b | |
Pascal Abresch | 3ccbb66b2d | |
Pascal Abresch | 609e7ed5fe | |
Pascal Abresch | d1e253a8e1 | |
Pascal Abresch | 31a6234ed2 | |
Pascal Abresch | 52413ab1b1 | |
Pascal Abresch | 7a8ca2ef1e | |
Pascal Abresch | ac349ae0fa | |
Pascal Abresch | 87248d5139 | |
Pascal Abresch | 8b2a9e4b30 | |
Pascal Abresch | 1a64cc4334 | |
Pascal Abresch | 1501ee13b1 | |
Pascal Abresch | 72c301c6b4 | |
Pascal Abresch | cbe4755d35 | |
Pascal Abresch | 9ab9d0669c | |
Pascal Abresch | 25c9bf62fb | |
Pascal Abresch | 1d87524d40 | |
Pascal Abresch | 6b50f9a6ca | |
Pascal Abresch | 0a512bf243 | |
Pascal Abresch | c78a94d550 | |
Pascal Abresch | 0ae47972a3 | |
Pascal Abresch | 26265cfabf | |
Pascal Abresch | c813f5e745 | |
Pascal Abresch | 2a20018ff6 | |
Pascal Abresch | e17fa512be | |
Pascal Abresch | 51cb7aab16 | |
Pascal Abresch | 963bdd4f42 | |
Pascal Abresch | c36ca674df | |
Pascal Abresch | 0a680ff7a5 | |
Pascal Abresch | bce53de48c | |
Pascal Abresch | 92ea3fe781 | |
Pascal Abresch | ae6e7171a8 | |
Pascal Abresch | eeb1756052 | |
Pascal Abresch | 5118472ef4 | |
Pascal Abresch | c513087c7a | |
Pascal Abresch | d6015ef155 | |
Pascal Abresch | 761fa00cbd | |
Pascal Abresch | 388964b2c3 | |
Pascal Abresch | a5649210a5 | |
Pascal Abresch | 2306586af4 | |
Pascal Abresch | 03f18b8c8a | |
Pascal Abresch | 126ae0e127 | |
Pascal Abresch | 91fbc72297 | |
Pascal Abresch | 0ef7436318 | |
Pascal Abresch | 39bce76863 | |
Pascal Abresch | 540b25379f | |
Pascal Abresch | 4de4a5eb9f | |
Pascal Abresch | 2f2a098ca6 | |
Pascal Abresch | cf2821152e | |
Pascal Abresch | 7499e6dc40 | |
Pascal Abresch | dc5397331b | |
Pascal Abresch | a195005f91 | |
Pascal Abresch | fb0c3357dc | |
Pascal Abresch | 4835007941 | |
Pascal Abresch | 926448965c | |
Pascal Abresch | 522e6f6026 | |
Pascal Abresch | e3e516fee4 | |
Pascal Abresch | 7c8fef10bd | |
Pascal Abresch | df21735a7b | |
Pascal Abresch | b076b5c613 | |
Pascal Abresch | e5f7d21bfc | |
Pascal Abresch | 594b1d2651 | |
Pascal Abresch | ab40ec1b34 |
|
@ -1,3 +0,0 @@
|
||||||
[submodule "server"]
|
|
||||||
path = server
|
|
||||||
url = https://git.gryphno.de/nephele/poppy-server
|
|
30
FixMime
|
@ -1,16 +1,42 @@
|
||||||
#!sh
|
#!sh
|
||||||
#adjust the next line... not sure how to figure this out from a shellscript, sorry :)
|
# This mechanism is fairly unreliable, but it is the only "okay" way currently :/
|
||||||
cd ~/proj/poppy-client-loeve
|
# realpath is also not part of any POSIX standard
|
||||||
|
|
||||||
|
# This script exists because mimetypes on Haiku are not derived from pathnames (.lua)
|
||||||
|
# but git does not store the mimetype (extended attribute) either
|
||||||
|
cd $(dirname $(realpath "$0"))
|
||||||
if test $? -eq 1; then
|
if test $? -eq 1; then
|
||||||
alert --stop "cant figure out where this script is" "Ill adjust it!"
|
alert --stop "cant figure out where this script is" "Ill adjust it!"
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
touch log
|
||||||
for i in lua/*.lua; do
|
for i in lua/*.lua; do
|
||||||
|
echo "$i" >> log
|
||||||
addattr -t mime BEOS:TYPE text/x-source-code $i;
|
addattr -t mime BEOS:TYPE text/x-source-code $i;
|
||||||
done
|
done
|
||||||
|
|
||||||
for i in server/*.lua; do
|
for i in server/*.lua; do
|
||||||
|
echo "$i" >> log
|
||||||
addattr -t mime BEOS:TYPE text/x-source-code $i;
|
addattr -t mime BEOS:TYPE text/x-source-code $i;
|
||||||
done
|
done
|
||||||
|
|
||||||
|
for i in server/commands/*.lua; do
|
||||||
|
echo "$i" >> log
|
||||||
|
addattr -t mime BEOS:TYPE text/x-source-code $i;
|
||||||
|
done
|
||||||
|
|
||||||
|
for i in server/unpriviliged_commands/*.lua; do
|
||||||
|
echo "$i" >> log
|
||||||
|
addattr -t mime BEOS:TYPE text/x-source-code $i;
|
||||||
|
done
|
||||||
|
|
||||||
|
for i in shared/*.lua; do
|
||||||
|
echo "$i" >> log
|
||||||
|
addattr -t mime BEOS:TYPE text/x-source-code $i;
|
||||||
|
done
|
||||||
|
|
||||||
|
alert --info "Fixed mime types
|
||||||
|
|
||||||
|
$(cat log)"
|
||||||
|
rm log
|
||||||
|
|
32
Readme.md
|
@ -2,7 +2,6 @@
|
||||||
This is the Poppy game\
|
This is the Poppy game\
|
||||||
A server is required to play the game, if you do not have one running then\
|
A server is required to play the game, if you do not have one running then\
|
||||||
run ```love .``` in the ```server/``` directory for a local one for now.\
|
run ```love .``` in the ```server/``` directory for a local one for now.\
|
||||||
(per default the client will connect to this server, it is currently a hardcoded constant in lua/networkThread.lua :/ )\
|
|
||||||
|
|
||||||
|
|
||||||
The performance is currently not so great on older computers,\
|
The performance is currently not so great on older computers,\
|
||||||
|
@ -15,7 +14,7 @@ I have not investigated this much yet. Safe to say it is not intended to run so
|
||||||
* Loading textures
|
* Loading textures
|
||||||
* drawing on canvases
|
* drawing on canvases
|
||||||
* physics calculation for collison and movement
|
* physics calculation for collison and movement
|
||||||
* drawing physics collisions (on the physics layer)
|
* drawing physics collisions (on the physics layer) (debug)
|
||||||
* Basic networking
|
* Basic networking
|
||||||
* Get clientID
|
* Get clientID
|
||||||
* Draw all players
|
* Draw all players
|
||||||
|
@ -25,17 +24,28 @@ I have not investigated this much yet. Safe to say it is not intended to run so
|
||||||
* Send disconnect on client error
|
* Send disconnect on client error
|
||||||
* Chat support
|
* Chat support
|
||||||
|
|
||||||
|
* Retrieve initial game state (partial)
|
||||||
|
* Get users name
|
||||||
|
* Get users avatar
|
||||||
|
* Get world size
|
||||||
|
|
||||||
* Drawing
|
* Drawing
|
||||||
* Draw with custom color
|
* Draw with custom color
|
||||||
* Re-do base textures as gray scale to color in arbitrarily
|
* Re-do base textures as gray scale to color in arbitrarily
|
||||||
|
|
||||||
|
* UI
|
||||||
|
* UI to connect to specific server
|
||||||
|
* Reconnect to server
|
||||||
|
* connect to other server during a session
|
||||||
## Not implemented (rough goals anyhow)
|
## Not implemented (rough goals anyhow)
|
||||||
* UI to connect to specific server
|
* Remember visited servers (save the list)
|
||||||
* Reconnect to server
|
* Add new servers to the list by user input
|
||||||
* connect to other server during a session
|
* Add new servers at runtime via DNS-SD mDNS discovery
|
||||||
* Open second window? Open second instance instead? (does love2d allow that?)
|
|
||||||
* Saving the game (server stuff?)
|
* Saving the map
|
||||||
* Loading the game/map (server stuff?)
|
* Loading the map from disk (and send it to the server)
|
||||||
|
* Loading the map from server on connect
|
||||||
|
|
||||||
* confirmation before clearing canvas (that's quite destructive :D)
|
* confirmation before clearing canvas (that's quite destructive :D)
|
||||||
* Make a way to add dialogs and stuff
|
* Make a way to add dialogs and stuff
|
||||||
|
|
||||||
|
@ -72,7 +82,9 @@ I have not investigated this much yet. Safe to say it is not intended to run so
|
||||||
|
|
||||||
* Networking support
|
* Networking support
|
||||||
* Figure out why and how physics is tied to framerate, why does jump height differ?
|
* Figure out why and how physics is tied to framerate, why does jump height differ?
|
||||||
* Implement initial state sync (mainly server work)
|
* Implement initial state sync
|
||||||
|
* Server remembers plays last position and tells you
|
||||||
|
* Server remembers drawn stuff and tells you
|
||||||
* Add ack que, resend lost packets (but only for stuff like drawing and such, movement may drop)
|
* Add ack que, resend lost packets (but only for stuff like drawing and such, movement may drop)
|
||||||
* Tell the user if the server doesn't support the networking version requested
|
* Tell the user if the server doesn't support the networking version requested
|
||||||
|
|
||||||
|
|
7
conf.lua
|
@ -1,11 +1,12 @@
|
||||||
love.conf = function(conf)
|
love.conf = function(conf)
|
||||||
conf.window.height = 480
|
conf.identity = "Poppy"
|
||||||
|
conf.window.height = 612
|
||||||
conf.window.minheight = 480
|
conf.window.minheight = 480
|
||||||
conf.window.width = 640
|
conf.window.width = 1024
|
||||||
conf.window.minwidth = 640
|
conf.window.minwidth = 640
|
||||||
conf.window.resizable = true
|
conf.window.resizable = true
|
||||||
conf.window.vsync = -1
|
conf.window.vsync = -1
|
||||||
conf.window.title = "poppy"
|
conf.window.title = "Poppy"
|
||||||
conf.version = "11.0"
|
conf.version = "11.0"
|
||||||
|
|
||||||
conf.modules.joystick = false
|
conf.modules.joystick = false
|
||||||
|
|
|
@ -2,14 +2,16 @@ local CAM_SPEED = 10
|
||||||
|
|
||||||
camera = {}
|
camera = {}
|
||||||
camera.target = 0
|
camera.target = 0
|
||||||
camera.offset = { x = 0, y = 300 }
|
camera.offset = { x = 0, y = 0 }
|
||||||
local fullscreen = false
|
local fullscreen = false
|
||||||
|
|
||||||
local altPressed, enterPressed = false, false
|
local altPressed, enterPressed = false, false
|
||||||
function camera.keypressed(key, _)
|
function camera.keypressed(key, _)
|
||||||
if key == "return" then enterPressed = true end
|
if key == "return" then enterPressed = true end
|
||||||
if key == "lalt" then altPressed = true end
|
if key == "lalt" then altPressed = true end
|
||||||
if key == "f11" or (altPressed and key == "return") or (enterPressed and key == "lalt") then
|
if key == "f11"
|
||||||
|
or (altPressed and key == "return")
|
||||||
|
or (enterPressed and key == "lalt") then
|
||||||
fullscreen = not fullscreen
|
fullscreen = not fullscreen
|
||||||
love.window.setFullscreen(fullscreen, "desktop")
|
love.window.setFullscreen(fullscreen, "desktop")
|
||||||
end
|
end
|
||||||
|
@ -93,7 +95,7 @@ end
|
||||||
|
|
||||||
function camera.setplayer()
|
function camera.setplayer()
|
||||||
if camera.target == 0 then return end
|
if camera.target == 0 then return end
|
||||||
player = players[camera.target]
|
player = participants[camera.target]
|
||||||
camera.offsetclamp()
|
camera.offsetclamp()
|
||||||
local perfectx = math.floor(player.x * -1) + ((window.x -300)/2 -((player.avatar:getWidth() /2) / scale)) / scale + camera.offset.x
|
local perfectx = math.floor(player.x * -1) + ((window.x -300)/2 -((player.avatar:getWidth() /2) / scale)) / scale + camera.offset.x
|
||||||
local perfecty = math.floor(player.y * -1) + (window.y/2 -((player.avatar:getHeight() /2) / scale)) / scale + camera.offset.y
|
local perfecty = math.floor(player.y * -1) + (window.y/2 -((player.avatar:getHeight() /2) / scale)) / scale + camera.offset.y
|
||||||
|
@ -105,7 +107,7 @@ end
|
||||||
|
|
||||||
function camera.update()
|
function camera.update()
|
||||||
if camera.target == 0 then return end
|
if camera.target == 0 then return end
|
||||||
player = players[camera.target]
|
player = participants[camera.target]
|
||||||
--[[ Update camera to track player ]]--
|
--[[ Update camera to track player ]]--
|
||||||
local perfectx = math.floor(player.x * -1) + ((window.x -300)/2 -(player.avatar:getWidth() /2)) / scale + camera.offset.x
|
local perfectx = math.floor(player.x * -1) + ((window.x -300)/2 -(player.avatar:getWidth() /2)) / scale + camera.offset.x
|
||||||
local perfecty = math.floor(player.y * -1) + (window.y/2 -(player.avatar:getHeight() /2)) / scale + camera.offset.y
|
local perfecty = math.floor(player.y * -1) + (window.y/2 -(player.avatar:getHeight() /2)) / scale + camera.offset.y
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
local player = require("lua.player")
|
||||||
|
|
||||||
|
local utils = require("shared.utils")
|
||||||
|
local fonts = require("shared.fonts")
|
||||||
|
|
||||||
|
|
||||||
|
return function(commands)
|
||||||
|
-- arg1 here is the clientID, but the server suplies 0
|
||||||
|
-- as this is the "origin"
|
||||||
|
-- don't want to overload/specialcase this, so the clientID is passed
|
||||||
|
-- as first argument instead.
|
||||||
|
commands.init = function(_, clientID, nickname, avatarHash, worldID, x, y)
|
||||||
|
print("Connected as " .. clientID .. " with name " .. nickname)
|
||||||
|
print("New world is " .. worldID .. " Size is [" .. x .. "|" .. y .. "]")
|
||||||
|
|
||||||
|
participants[clientID] = player.newPlayer(
|
||||||
|
{name = nickname, avatar = levelloop.avatars[avatarHash].image, y=y})
|
||||||
|
|
||||||
|
participants[clientID].y = y - participants[clientID].avatar:getHeight()
|
||||||
|
camera.target = clientID
|
||||||
|
levelloop.clientID = clientID
|
||||||
|
|
||||||
|
local world = { id = worldID, x = x, y = y }
|
||||||
|
levelloop.loadworld(world)
|
||||||
|
camera.setplayer()
|
||||||
|
end
|
||||||
|
|
||||||
|
commands.serverQuit = function(clientID, message)
|
||||||
|
menu.init(message)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
commands.ping = function(clientID)
|
||||||
|
levelloop.networkSend("pong")
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
commands.saveWorld = function(clientID, status, savename)
|
||||||
|
if status == false then
|
||||||
|
ui.addChatEntry("SERVER", "Failed to save world!")
|
||||||
|
elseif status == "sucess" then
|
||||||
|
ui.addChatEntry("SERVER", "saved world. " .. savename)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
commands.playerJoin = function(_, clientID, nickname, avatarHash)
|
||||||
|
ui.addChatEntry("", nickname .. " Joined the game")
|
||||||
|
local avatar = { image = love.graphics.newImage("textures/player/nephele/fallback.png") }
|
||||||
|
if levelloop.avatars[avatarHash] then
|
||||||
|
avatar = levelloop.avatars[avatarHash]
|
||||||
|
end
|
||||||
|
participants[clientID] = player.newPlayer({name = nickname, avatar = avatar.image})
|
||||||
|
participants[clientID].label = love.graphics.newText(fonts.smallFont, nickname)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
commands.playerLeave = function(_, clientID)
|
||||||
|
if participants[clientID] then
|
||||||
|
local name = participants[clientID].name
|
||||||
|
ui.addChatEntry("", name .. " Left the game")
|
||||||
|
participants[clientID] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
commands.deletePhysics = function(clientID, x, y, width, height)
|
||||||
|
if clientID == camera.target then return end
|
||||||
|
if not participants[clientID] then return end
|
||||||
|
physics.remove_solid(x, y, width, height)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
commands.drawPhysics = function(clientID, x, y, width, height)
|
||||||
|
if clientID == camera.target then return end
|
||||||
|
if not participants[clientID] then return end
|
||||||
|
physics.draw_solid(x, y, width, height)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
commands.chatMessage = function(clientID, message)
|
||||||
|
local name
|
||||||
|
if clientID == "0" then
|
||||||
|
name = ""
|
||||||
|
else
|
||||||
|
if participants[clientID] and participants[clientID].name then
|
||||||
|
name = participants[clientID].name
|
||||||
|
else
|
||||||
|
name = "UNKNOWN?"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
ui.addChatEntry(name, message)
|
||||||
|
end
|
||||||
|
end
|
|
@ -3,60 +3,66 @@ function alignToGrid(num, thing)
|
||||||
return num - (num % thing)
|
return num - (num % thing)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local utils = require("shared.utils")
|
||||||
|
|
||||||
function drawing.clearAllCanvases()
|
function drawing.clearAllCanvases()
|
||||||
love.graphics.setCanvas(gameloop.buffer.pixel.fg)
|
love.graphics.setCanvas(levelloop.Canvas.fg)
|
||||||
love.graphics.clear()
|
love.graphics.clear()
|
||||||
love.graphics.setCanvas(gameloop.buffer.pixel.bg)
|
love.graphics.setCanvas(levelloop.Canvas.bg)
|
||||||
love.graphics.clear()
|
love.graphics.clear()
|
||||||
love.graphics.setCanvas(gameloop.buffer.pixel.dbg)
|
love.graphics.setCanvas(levelloop.Canvas.dbg)
|
||||||
love.graphics.clear()
|
love.graphics.clear()
|
||||||
love.graphics.setCanvas()
|
love.graphics.setCanvas()
|
||||||
gameloop.buffer.physics = {}
|
levelloop.Canvas.physics = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function drawing.clearAllCanvasesNetwork()
|
function drawing.clearAllCanvasesNetwork()
|
||||||
gameloop.networkSend("clearCanvas")
|
levelloop.remoteCall("clearCanvas")
|
||||||
drawing.clearAllCanvases()
|
drawing.clearAllCanvases()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function drawing.fillCanvas(layer, hash)
|
function drawing.fillCanvas(layer, hash, color)
|
||||||
love.graphics.setCanvas(gameloop.buffer.pixel[layer])
|
love.graphics.setCanvas(levelloop.Canvas[layer])
|
||||||
love.graphics.clear()
|
love.graphics.clear()
|
||||||
for x = 0, gameloop.world.x -gameloop.textures[hash].image:getPixelWidth(),
|
utils.color_push()
|
||||||
gameloop.textures[hash].image:getPixelWidth() do
|
love.graphics.setColor(color)
|
||||||
for y = 0, gameloop.world.y -gameloop.textures[hash].image:getPixelHeight(),
|
for x = 0, levelloop.world.x -levelloop.textures[hash].image:getPixelWidth(),
|
||||||
gameloop.textures[hash].image:getPixelHeight() do
|
levelloop.textures[hash].image:getPixelWidth() do
|
||||||
love.graphics.draw(gameloop.textures[hash].image, x, y)
|
for y = 0, levelloop.world.y -levelloop.textures[hash].image:getPixelHeight(),
|
||||||
end
|
levelloop.textures[hash].image:getPixelHeight() do
|
||||||
|
love.graphics.draw(levelloop.textures[hash].image, x, y)
|
||||||
end
|
end
|
||||||
love.graphics.setCanvas()
|
end
|
||||||
|
utils.color_pop()
|
||||||
|
love.graphics.setCanvas()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function drawing.fillCanvasNetwork(layer, hash)
|
function drawing.fillCanvasNetwork(layer, hash, color)
|
||||||
gameloop.networkSend(unit("fillCanvas", layer, love.data.encode("string", "base64", hash)))
|
color.nwType = "color"
|
||||||
drawing.fillCanvas(layer, hash)
|
levelloop.remoteCall("fillCanvas", layer, hash, color)
|
||||||
|
drawing.fillCanvas(layer, hash, color)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function drawing.keyreleased(key, _)
|
function drawing.keyreleased(key, _)
|
||||||
|
assert(drawing.color)
|
||||||
if key == "c" then
|
if key == "c" then
|
||||||
drawing.clearAllCanvasesNetwork()
|
drawing.clearAllCanvasesNetwork()
|
||||||
end
|
end
|
||||||
if key == "f" then
|
if key == "f" then
|
||||||
drawing.fillCanvasNetwork("fg", drawing.cursorHash)
|
drawing.fillCanvasNetwork("fg", drawing.cursorHash, drawing.color)
|
||||||
end
|
end
|
||||||
if key == "b" then
|
if key == "b" then
|
||||||
drawing.fillCanvasNetwork("bg", drawing.cursorHash)
|
drawing.fillCanvasNetwork("bg", drawing.cursorHash, drawing.color)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local drawmouse = {}
|
local drawmouse = {}
|
||||||
function drawing.input(dt)
|
function drawing.input(dt)
|
||||||
local textures = gameloop.textures
|
local textures = levelloop.textures
|
||||||
local cursor = drawing.cursor
|
local cursor = drawing.cursor
|
||||||
if not cursor or not cursor.data then return end
|
if not cursor or not cursor.data then return end
|
||||||
assert(cursor, "Failed to find cursor")
|
assert(cursor, "Failed to find cursor")
|
||||||
|
@ -73,8 +79,8 @@ function drawing.input(dt)
|
||||||
end
|
end
|
||||||
mousex = mousex / scale - view.x
|
mousex = mousex / scale - view.x
|
||||||
mousey = mousey / scale - view.y
|
mousey = mousey / scale - view.y
|
||||||
if mousex > gameloop.world.x or
|
if mousex > levelloop.world.x or
|
||||||
mousey > gameloop.world.y or
|
mousey > levelloop.world.y or
|
||||||
mousex < 0 or
|
mousex < 0 or
|
||||||
mousey < 0 then
|
mousey < 0 then
|
||||||
return
|
return
|
||||||
|
@ -99,18 +105,17 @@ function drawing.input(dt)
|
||||||
if not (thisx == thisx) or not (thisy == thisy) then return end
|
if not (thisx == thisx) or not (thisy == thisy) then return end
|
||||||
|
|
||||||
local ctrlDown = love.keyboard.isDown("lctrl") or love.keyboard.isDown("rctrl")
|
local ctrlDown = love.keyboard.isDown("lctrl") or love.keyboard.isDown("rctrl")
|
||||||
love.graphics.setBlendMode("replace")
|
|
||||||
if ctrlDown then
|
if ctrlDown then
|
||||||
love.graphics.setCanvas(gameloop.buffer.pixel.bg)
|
love.graphics.setCanvas(levelloop.Canvas.bg)
|
||||||
else
|
else
|
||||||
love.graphics.setCanvas(gameloop.buffer.pixel.fg)
|
love.graphics.setCanvas(levelloop.Canvas.fg)
|
||||||
end
|
end
|
||||||
|
|
||||||
if love.mouse.isDown(1) then -- only delete
|
if love.mouse.isDown(1) then -- only delete
|
||||||
color_push()
|
utils.color_push()
|
||||||
love.graphics.setColor(drawing.color)
|
love.graphics.setColor(drawing.color)
|
||||||
love.graphics.draw(cursor.image, thisx, thisy)
|
love.graphics.draw(cursor.image, thisx, thisy)
|
||||||
color_pop()
|
utils.color_pop()
|
||||||
if ctrlDown then
|
if ctrlDown then
|
||||||
drawing.draw_texture(drawing.cursorHash, thisx, thisy, "bg", drawing.color)
|
drawing.draw_texture(drawing.cursorHash, thisx, thisy, "bg", drawing.color)
|
||||||
else
|
else
|
||||||
|
@ -120,7 +125,8 @@ function drawing.input(dt)
|
||||||
cursor.image:getPixelHeight())
|
cursor.image:getPixelHeight())
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
color_push()
|
love.graphics.setBlendMode("replace")
|
||||||
|
utils.color_push()
|
||||||
love.graphics.setColor(0,0,0,0)
|
love.graphics.setColor(0,0,0,0)
|
||||||
love.graphics.rectangle("fill", thisx, thisy,
|
love.graphics.rectangle("fill", thisx, thisy,
|
||||||
cursor.image:getPixelWidth(),
|
cursor.image:getPixelWidth(),
|
||||||
|
@ -137,9 +143,9 @@ function drawing.input(dt)
|
||||||
cursor.image:getPixelWidth(),
|
cursor.image:getPixelWidth(),
|
||||||
cursor.image:getPixelHeight())
|
cursor.image:getPixelHeight())
|
||||||
end
|
end
|
||||||
color_pop()
|
utils.color_pop()
|
||||||
|
love.graphics.setBlendMode("alpha")
|
||||||
end
|
end
|
||||||
love.graphics.setBlendMode("alpha")
|
|
||||||
love.graphics.setCanvas()
|
love.graphics.setCanvas()
|
||||||
end
|
end
|
||||||
drawmouse.lastX = mousex
|
drawmouse.lastX = mousex
|
||||||
|
@ -155,8 +161,8 @@ function drawing.draw_texture(hash, x, y, layer, color)
|
||||||
if not color[4] then color[4] = 1 end
|
if not color[4] then color[4] = 1 end
|
||||||
local last = drawing.lastTexture
|
local last = drawing.lastTexture
|
||||||
if hash ~= last.hash or x ~= last.x or y ~= last.y or layer ~= last.layer then
|
if hash ~= last.hash or x ~= last.x or y ~= last.y or layer ~= last.layer then
|
||||||
gameloop.networkSend(unit("drawTexture", x, y,
|
color.nwType = "color"
|
||||||
layer, love.data.encode("string", "base64", hash), color[1], color[2], color[3], color[4]))
|
levelloop.remoteCall("drawTexture", x, y, layer, hash, color)
|
||||||
end
|
end
|
||||||
last.hash, last.x, last.y, last.layer = hash, x, y, layer
|
last.hash, last.x, last.y, last.layer = hash, x, y, layer
|
||||||
end
|
end
|
||||||
|
@ -168,7 +174,7 @@ function drawing.del_texture(x, y, width, height, layer)
|
||||||
local last = drawing.lastTextureDel
|
local last = drawing.lastTextureDel
|
||||||
if x ~= last.x or y ~= last.y or width ~= last.width
|
if x ~= last.x or y ~= last.y or width ~= last.width
|
||||||
or height ~= last.height or layer ~= last.layer then
|
or height ~= last.height or layer ~= last.layer then
|
||||||
gameloop.networkSend(unit("deleteTexture", x, y, width, height, layer))
|
levelloop.remoteCall("deleteTexture", x, y, width, height, layer)
|
||||||
last.x, last.y, last.width, last.height, last.layer = x, y, width, height, layer
|
last.x, last.y, last.width, last.height, last.layer = x, y, width, height, layer
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -177,7 +183,7 @@ end
|
||||||
local lastDWP = { x = 0, y = 0, width = 0, height = 0}
|
local lastDWP = { x = 0, y = 0, width = 0, height = 0}
|
||||||
function drawing.draw_solid(x, y, width, height)
|
function drawing.draw_solid(x, y, width, height)
|
||||||
if x ~= lastDWP.x or y ~= lastDWP.y or width ~= lastDWP.width or height ~= lastDWP.height then
|
if x ~= lastDWP.x or y ~= lastDWP.y or width ~= lastDWP.width or height ~= lastDWP.height then
|
||||||
gameloop.networkSend(unit("drawPhysics", x, y, width, height))
|
levelloop.remoteCall("drawPhysics", x, y, width, height)
|
||||||
physics.draw_solid(x, y, width, height)
|
physics.draw_solid(x, y, width, height)
|
||||||
lastDWP.x, lastDWP.y, lastDWP.width, lastDWP.height = x, y, width, height
|
lastDWP.x, lastDWP.y, lastDWP.width, lastDWP.height = x, y, width, height
|
||||||
end
|
end
|
||||||
|
@ -187,14 +193,15 @@ end
|
||||||
local lastRMP = { x = 0, y = 0, width = 0, height = 0}
|
local lastRMP = { x = 0, y = 0, width = 0, height = 0}
|
||||||
function drawing.remove_solid(x, y, width, height)
|
function drawing.remove_solid(x, y, width, height)
|
||||||
if x ~= lastRMP.x or y ~= lastRMP.y or width ~= lastRMP.width or height ~= lastRMP.height then
|
if x ~= lastRMP.x or y ~= lastRMP.y or width ~= lastRMP.width or height ~= lastRMP.height then
|
||||||
gameloop.networkSend(unit("deletePhysics", x, y, width, height))
|
levelloop.remoteCall("deletePhysics", x, y, width, height)
|
||||||
physics.remove_solid(x, y, width, height)
|
physics.remove_solid(x, y, width, height)
|
||||||
lastRMP.x, lastRMP.y, lastRMP.width, lastRMP.height = x, y, width, height
|
lastRMP.x, lastRMP.y, lastRMP.width, lastRMP.height = x, y, width, height
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function drawing.init()
|
function drawing.init()
|
||||||
local path = "textures/blocks/krock/Glass/3.png"
|
local path = "textures/blocks/krock/Glass/03.png"
|
||||||
local texture = {data = love.image.newImageData(path),
|
local texture = {data = love.image.newImageData(path),
|
||||||
image = love.graphics.newImage(path)}
|
image = love.graphics.newImage(path)}
|
||||||
drawing.color = {1, 1, 1, 1}
|
drawing.color = {1, 1, 1, 1}
|
||||||
|
@ -203,7 +210,6 @@ function drawing.init()
|
||||||
drawing.cursor = textures[drawing.cursorHash]
|
drawing.cursor = textures[drawing.cursorHash]
|
||||||
|
|
||||||
texture, path = nil, nil
|
texture, path = nil, nil
|
||||||
local clientChannel = love.thread.getChannel ( "clientChannel" );
|
|
||||||
|
|
||||||
mouse.lastpos = {}
|
mouse.lastpos = {}
|
||||||
mouse.lastpos.x = 0
|
mouse.lastpos.x = 0
|
||||||
|
|
315
lua/gameloop.lua
|
@ -1,26 +1,44 @@
|
||||||
gameloop = {}
|
levelloop = {}
|
||||||
gameloop.world = nil
|
levelloop.CLIENT = true
|
||||||
gameloop.textures = {}
|
levelloop.world = nil
|
||||||
local rpc = require("server.rpc")
|
levelloop.textures = {}
|
||||||
local v2_message = require("lua.network")
|
levelloop.avatars = {}
|
||||||
local socket = require "socket"
|
commands = {}
|
||||||
local util = require("server.utils")
|
require("lua.commands")(commands)
|
||||||
|
require("shared.commands")(commands)
|
||||||
|
|
||||||
|
local protocolVersion = "PPY\x03"
|
||||||
|
|
||||||
|
local socket = require "socket"
|
||||||
|
|
||||||
|
local utils = require("shared.utils")
|
||||||
|
local fonts = require("shared.fonts")
|
||||||
|
local errorHandler = require("shared.error")
|
||||||
|
|
||||||
|
local rpc = require("server.rpc")
|
||||||
|
local constants = require("server.constants")
|
||||||
|
|
||||||
|
local format = require("shared.networkFormat")
|
||||||
|
|
||||||
|
levelloop.nwChecklist = {}
|
||||||
|
levelloop.Canvas = {}
|
||||||
|
local count = 1
|
||||||
local function normalDraw()
|
local function normalDraw()
|
||||||
|
if count == 1 then print("use canvas!") count = 2 end
|
||||||
local layerTransform = love.math.newTransform(math.floor(view.x) * scale, math.floor(view.y) * scale, 0, scale, scale)
|
local layerTransform = love.math.newTransform(math.floor(view.x) * scale, math.floor(view.y) * scale, 0, scale, scale)
|
||||||
|
|
||||||
-- Draw background
|
-- Draw background
|
||||||
if drawBackground then
|
if drawBackground then
|
||||||
color_push()
|
utils.color_push()
|
||||||
love.graphics.setColor(0.05, 0.05, 0.05)
|
love.graphics.setColor(0.05, 0.05, 0.05)
|
||||||
love.graphics.rectangle("fill", math.floor(view.x) * scale, math.floor(view.y) * scale, gameloop.world.x * scale, gameloop.world.y * scale)
|
love.graphics.rectangle("fill", math.floor(view.x) * scale, math.floor(view.y) * scale, levelloop.world.x * scale, levelloop.world.y * scale)
|
||||||
love.graphics.setColor(1, 1, 1, 1)
|
love.graphics.setColor(1, 1, 1, 1)
|
||||||
love.graphics.draw(gameloop.buffer.pixel.bg, layerTransform)
|
love.graphics.draw(levelloop.Canvas.bg, layerTransform)
|
||||||
color_pop()
|
utils.color_pop()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- draw player
|
-- draw player
|
||||||
for i, player in pairs(players) do
|
for i, player in pairs(participants) do
|
||||||
local playerTransform = love.math.newTransform(
|
local playerTransform = love.math.newTransform(
|
||||||
math.floor(player.x) * scale + math.floor(view.x) * scale,
|
math.floor(player.x) * scale + math.floor(view.x) * scale,
|
||||||
math.floor(player.y) * scale + math.floor(view.y) * scale,
|
math.floor(player.y) * scale + math.floor(view.y) * scale,
|
||||||
|
@ -29,7 +47,7 @@ local function normalDraw()
|
||||||
love.graphics.draw(player.avatar, playerTransform)
|
love.graphics.draw(player.avatar, playerTransform)
|
||||||
if player.name then
|
if player.name then
|
||||||
if not player.label then
|
if not player.label then
|
||||||
player.label = love.graphics.newText(ui.smallFont, player.name)
|
player.label = love.graphics.newText(fonts.smallFont, player.name)
|
||||||
end
|
end
|
||||||
local labelTransform = love.math.newTransform(
|
local labelTransform = love.math.newTransform(
|
||||||
math.floor(player.x - (player.label:getWidth() / 2) +
|
math.floor(player.x - (player.label:getWidth() / 2) +
|
||||||
|
@ -41,7 +59,7 @@ local function normalDraw()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Draw foreground
|
-- Draw foreground
|
||||||
love.graphics.draw(gameloop.buffer.pixel.fg, layerTransform)
|
love.graphics.draw(levelloop.Canvas.fg, layerTransform)
|
||||||
|
|
||||||
-- Draw UI
|
-- Draw UI
|
||||||
love.graphics.draw(ui.buffer)
|
love.graphics.draw(ui.buffer)
|
||||||
|
@ -51,184 +69,156 @@ end
|
||||||
local function debugDraw()
|
local function debugDraw()
|
||||||
normalDraw()
|
normalDraw()
|
||||||
local layerTransform = love.math.newTransform(math.floor(view.x) * scale, math.floor(view.y) * scale, 0, scale, scale)
|
local layerTransform = love.math.newTransform(math.floor(view.x) * scale, math.floor(view.y) * scale, 0, scale, scale)
|
||||||
|
for i = 30, 120, 30 do
|
||||||
|
love.graphics.draw(ui.speedText[i/30], 400, i-30)
|
||||||
|
end
|
||||||
|
love.graphics.draw(levelloop.Canvas.dbg, layerTransform)
|
||||||
|
love.graphics.draw(levelloop.Canvas.dbg2, layerTransform)
|
||||||
|
|
||||||
love.graphics.draw(gameloop.buffer.pixel.dbg, layerTransform)
|
love.graphics.setCanvas(levelloop.Canvas.dbg2)
|
||||||
love.graphics.draw(gameloop.buffer.pixel.dbg2, layerTransform)
|
|
||||||
|
|
||||||
love.graphics.setCanvas(gameloop.buffer.pixel.dbg2)
|
|
||||||
love.graphics.clear()
|
love.graphics.clear()
|
||||||
love.graphics.setCanvas()
|
love.graphics.setCanvas()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
local function normalNetworkSend(args)
|
local function remoteCall(name, ...)
|
||||||
if clientID == "0" then print("Invalid clientID?") end
|
local request = protocolVersion .. format.encodeFunction(name, levelloop.clientID or 0, ...)
|
||||||
local request = "poppyV002" ..US.. clientID ..US.. args
|
levelloop.client:send(request)
|
||||||
gameloop.client:send(request)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
local function debugNetworkSend(args)
|
local function networkSync()
|
||||||
assert(clientID ~= 0)
|
while true do
|
||||||
local request = "poppyV002" ..US.. clientID ..US.. args
|
local message, fault = levelloop.client:receive()
|
||||||
print("=> " .. util.pprint(request))
|
|
||||||
gameloop.client:send(request)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function normalNetworkSync()
|
|
||||||
local message, errorString = nil, false
|
|
||||||
while not errorString do
|
|
||||||
local message, errorString = gameloop.client:receive()
|
|
||||||
if not message then
|
if not message then
|
||||||
if errorString == "timeout" then return else
|
if fault == "timeout" then
|
||||||
error(errorString)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local response = rpc.validate(message)
|
|
||||||
if not response.errorMsg then
|
|
||||||
rpc.execute(v2_message, response.clientID, response.record)
|
|
||||||
message = nil
|
|
||||||
else
|
|
||||||
error(errorMsg)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function debugNetworkSync()
|
|
||||||
local message, errorString = nil, false
|
|
||||||
while not errorString do
|
|
||||||
local message, errorString = gameloop.client:receive()
|
|
||||||
if message then print("<= " .. util.pprint(message)) end
|
|
||||||
if not message then
|
|
||||||
if errorString == "timeout" then return else
|
|
||||||
menu.init()
|
|
||||||
return
|
return
|
||||||
|
else
|
||||||
|
error(fault)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
local response = rpc.validate(message)
|
local protocolVersion, index = string.unpack("<c4", message, 1)
|
||||||
if not response.errorMsg then
|
if protocolVersion ~= "PPY\x03" then
|
||||||
rpc.execute(v2_message, response.clientID, response.record)
|
print("Dropping packet with invalid protocol " .. protocolVersion)
|
||||||
message = nil
|
|
||||||
else
|
else
|
||||||
error(errorMsg)
|
print("got poppy package")
|
||||||
|
local DecoderType, index = string.unpack("<I1", message, index)
|
||||||
|
if (DecoderType == 0x81) then
|
||||||
|
name, clientID, args = format.decodeFunction(message, index)
|
||||||
|
print(name)
|
||||||
|
print(#args)
|
||||||
|
for i, val in pairs(args) do
|
||||||
|
print(i, val)
|
||||||
|
end
|
||||||
|
commands[name](clientID, table.unpack(args))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
local function errorhandlerNetwork(msg)
|
local function errorhandlerNetwork(msg)
|
||||||
gameloop.networkSend(unit("playerLeave"))
|
levelloop.remoteCall("playerLeave")
|
||||||
return errorhandler(msg)
|
return errorHandler.redscreen(msg)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function errorhandler(msg)
|
function levelloop.mousepressed(mousex, mousey)
|
||||||
local errorfont
|
|
||||||
if love.filesystem.getInfo("fonts/font.ttf") then
|
|
||||||
errorfont = love.graphics.newFont("fonts/font.ttf", 45)
|
|
||||||
else
|
|
||||||
errorfont = love.graphics.getFont(45)
|
|
||||||
end
|
|
||||||
love.graphics.reset()
|
|
||||||
love.graphics.origin()
|
|
||||||
love.graphics.clear(.4, 0, 0)
|
|
||||||
love.graphics.setColor(1, 1, 1)
|
|
||||||
local message = love.graphics.newText(errorfont, "Poppy has encountered a critical fault")
|
|
||||||
local message2 = love.graphics.newText(errorfont, "Press q to exit")
|
|
||||||
local message3 = love.graphics.newText(errorfont, msg)
|
|
||||||
love.graphics.draw(message, 40, 40, 0, 1, 1)
|
|
||||||
love.graphics.draw(message2, 40, 95, 0, 1, 1)
|
|
||||||
love.graphics.draw(message3, 40, 145, 0, 1, 1)
|
|
||||||
local indent = 0
|
|
||||||
local messageline
|
|
||||||
for line in debug.traceback():gmatch("([^\n]*)\n?") do
|
|
||||||
messageline = love.graphics.newText(errorfont, line)
|
|
||||||
love.graphics.draw(messageline, 40, 255 + indent, 0, 1, 1)
|
|
||||||
indent = indent + 55
|
|
||||||
end
|
|
||||||
love.graphics.present()
|
|
||||||
return function()
|
|
||||||
love.event.pump()
|
|
||||||
for event, action in love.event.poll() do
|
|
||||||
if event == "quit" then return 1 end
|
|
||||||
if event == "keypressed" then
|
|
||||||
if action == "q" then
|
|
||||||
love.event.quit( 1 )
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
love.timer.sleep(0.1)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function gameloop.mousepressed(mousex, mousey)
|
|
||||||
ui.mousepressed(mousex, mousey)
|
ui.mousepressed(mousex, mousey)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function gameloop.init(server, nickname)
|
function levelloop.loadAvatars()
|
||||||
love.errorhandler = errorhandler
|
local container = {}
|
||||||
assert(server, "Gameloop called without server to connect to?")
|
local entries = love.filesystem.getDirectoryItems("textures/player")
|
||||||
assert(nickname, "Gameloop called without nickname?")
|
if #entries == 0 then return error("no avatars found") end
|
||||||
gameloop.server = server
|
for i, file in pairs(entries) do
|
||||||
|
local path = "textures/player" .."/".. file
|
||||||
|
local entry = love.filesystem.getInfo(path)
|
||||||
|
if entry.type == "directory" then
|
||||||
|
local textures = love.filesystem.getDirectoryItems(path)
|
||||||
|
for i, texture in pairs(textures) do
|
||||||
|
print(path)
|
||||||
|
local imageData = love.image.newImageData(path .. "/" .. texture)
|
||||||
|
local image = love.graphics.newImage(path .. "/" .. texture)
|
||||||
|
local imageHash = love.data.hash("sha512", imageData:getString())
|
||||||
|
container[imageHash] = {data = imageData, image = image}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
levelloop.avatars = container
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function levelloop.init(server, nickname, avatarHash)
|
||||||
|
love.errorhandler = errorHandler.redscreen
|
||||||
|
assert(server, "levelloop called without server to connect to?")
|
||||||
|
assert(nickname, "levelloop called without nickname?")
|
||||||
|
assert(avatarHash, "levelloop called without avatar?")
|
||||||
|
levelloop.server = server
|
||||||
|
|
||||||
scale = 1
|
scale = 1
|
||||||
|
|
||||||
love.graphics.setDefaultFilter("nearest", "nearest", 0)
|
love.graphics.setDefaultFilter("nearest", "nearest", 0)
|
||||||
|
|
||||||
layer = true
|
layer = true
|
||||||
|
|
||||||
grid = 16
|
grid = 16
|
||||||
color = {}
|
color = {}
|
||||||
mouse = {}
|
mouse = {}
|
||||||
|
|
||||||
window = {}
|
window = {}
|
||||||
window.x, window.y = love.graphics.getDimensions()
|
window.x, window.y = love.graphics.getDimensions()
|
||||||
|
|
||||||
players = {}
|
participants = {}
|
||||||
textures = {}
|
textures = {}
|
||||||
|
|
||||||
view = {}
|
view = {}
|
||||||
view.x = 0
|
view.x = 0
|
||||||
view.y = 0
|
view.y = 0
|
||||||
clientID = "0"
|
clientID = "0"
|
||||||
|
|
||||||
|
-- Must be done now because it has to be ready before "init" returns from the server
|
||||||
gameloop.client = socket.udp()
|
-- which is /before/ the game world is loaded
|
||||||
gameloop.client:setpeername(server.adress, server.port)
|
levelloop.loadAvatars()
|
||||||
gameloop.client:settimeout(0)
|
|
||||||
|
|
||||||
print("Socketnname: " .. gameloop.client:getsockname())
|
levelloop.client = socket.udp()
|
||||||
|
levelloop.client:setpeername(server.adress, server.port)
|
||||||
|
levelloop.client:settimeout(0)
|
||||||
|
|
||||||
|
CLIENT = true
|
||||||
|
SERVER = false
|
||||||
|
print("Socketnname: " .. levelloop.client:getsockname())
|
||||||
print("Connecting to [" .. server.adress .."]:" .. server.port)
|
print("Connecting to [" .. server.adress .."]:" .. server.port)
|
||||||
|
|
||||||
local request = "poppyV002" ..US.. "0" ..US.. unit("init", nickname)
|
remoteCall("init", nickname, avatarHash)
|
||||||
gameloop.client:send(request)
|
|
||||||
if NETWORK_DEBUG then print("=> " .. util.pprint(request)) end
|
|
||||||
|
|
||||||
if not NETWORK_DEBUG then
|
levelloop.remoteCall = remoteCall
|
||||||
gameloop.networkSend = normalNetworkSend
|
levelloop.networkSync = networkSync
|
||||||
gameloop.networkSync = normalNetworkSync
|
|
||||||
else
|
|
||||||
gameloop.networkSend = debugNetworkSend
|
|
||||||
gameloop.networkSync = debugNetworkSync
|
|
||||||
end
|
|
||||||
|
|
||||||
love.update = function(dt)
|
love.update = function(dt)
|
||||||
gameloop.networkSync(dt)
|
networkSync(dt)
|
||||||
love.timer.sleep((1/60) -dt)
|
love.timer.sleep((1/60) -dt)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local infoFont
|
||||||
|
if love.filesystem.getInfo("fonts/font.ttf") then
|
||||||
|
infoFont = love.graphics.newFont("fonts/font.ttf", 45)
|
||||||
|
else
|
||||||
|
infoFont = love.graphics.newFont(45)
|
||||||
|
end
|
||||||
|
local message = love.graphics.newText(infoFont, "Connecting...")
|
||||||
|
love.graphics.draw(message, 40, 40, 0, 1, 1)
|
||||||
love.draw = function(dt)
|
love.draw = function(dt)
|
||||||
love.graphics.present()
|
love.graphics.setColor(0.05, 0.05, 0.05)
|
||||||
|
love.graphics.rectangle("fill", 0, 0, window.x, window.y)
|
||||||
|
love.graphics.setColor(1, 1, 1)
|
||||||
|
love.graphics.draw(message, 40, 40, 0, 1, 1)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function gameloop.normalVisible(dt)
|
function levelloop.normalVisible(dt)
|
||||||
gameloop.networkSync(dt)
|
networkSync(dt)
|
||||||
camera.input(dt)
|
camera.input(dt)
|
||||||
physics.input(dt)
|
physics.input(dt)
|
||||||
drawing.input(dt)
|
drawing.input(dt)
|
||||||
|
@ -238,20 +228,20 @@ function gameloop.normalVisible(dt)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function gameloop.reducedVisible(dt)
|
function levelloop.reducedVisible(dt)
|
||||||
gameloop.networkSync(dt)
|
networkSync(dt)
|
||||||
physics.update(dt)
|
physics.update(dt)
|
||||||
love.timer.sleep((1/60) -dt)
|
love.timer.sleep((1/60) -dt)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function gameloop.keypressed(key, _)
|
function levelloop.keypressed(key, _)
|
||||||
physics.keypressed(key, _)
|
physics.keypressed(key, _)
|
||||||
camera.keypressed(key, _)
|
camera.keypressed(key, _)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function gameloop.keyreleased(key, _)
|
function levelloop.keyreleased(key, _)
|
||||||
if key == "r" then
|
if key == "r" then
|
||||||
drawBackground = not drawBackground
|
drawBackground = not drawBackground
|
||||||
end
|
end
|
||||||
|
@ -261,53 +251,52 @@ function gameloop.keyreleased(key, _)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function gameloop.loadworld(world)
|
function levelloop.loadworld(world)
|
||||||
-- Game connected!
|
-- Game connected!
|
||||||
love.errorhandler = errorhandlerNetwork
|
--love.errorhandler = errorhandlerNetwork
|
||||||
local buffer = {}
|
print("make canvas!")
|
||||||
buffer.pixel = {}
|
levelloop.Canvas.fg = love.graphics.newCanvas(world.x, world.y)
|
||||||
buffer.pixel.fg = love.graphics.newCanvas(world.x, world.y)
|
levelloop.Canvas.bg = love.graphics.newCanvas(world.x, world.y)
|
||||||
buffer.pixel.bg = love.graphics.newCanvas(world.x, world.y)
|
|
||||||
if PHYSICS_DEBUG then
|
if PHYSICS_DEBUG then
|
||||||
buffer.pixel.dbg = love.graphics.newCanvas(world.x, world.y)
|
levelloop.Canvas.dbg = love.graphics.newCanvas(world.x, world.y)
|
||||||
buffer.pixel.dbg2 = love.graphics.newCanvas(world.x, world.y)
|
levelloop.Canvas.dbg2 = love.graphics.newCanvas(world.x, world.y)
|
||||||
end
|
end
|
||||||
buffer.physics = {}
|
levelloop.Canvas.physics = {}
|
||||||
gameloop.buffer = buffer
|
levelloop.world = world
|
||||||
|
|
||||||
|
|
||||||
ui.init()
|
ui.init()
|
||||||
drawing.init()
|
drawing.init()
|
||||||
love.update = gameloop.normalVisible
|
love.update = levelloop.normalVisible
|
||||||
love.visible = function(visible)
|
love.visible = function(visible)
|
||||||
if visible then
|
if visible then
|
||||||
love.update = gameloop.normalVisible
|
love.update = levelloop.normalVisible
|
||||||
else
|
else
|
||||||
love.update = gameloop.reducedVisible
|
love.update = levelloop.reducedVisible
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
drawBackground = true
|
drawBackground = true
|
||||||
|
|
||||||
love.keyreleased = gameloop.keyreleased
|
love.keyreleased = levelloop.keyreleased
|
||||||
love.keypressed = gameloop.keypressed
|
love.keypressed = levelloop.keypressed
|
||||||
love.mousepressed = gameloop.mousepressed
|
love.mousepressed = levelloop.mousepressed
|
||||||
if not PHYSICS_DEBUG then
|
if not PHYSICS_DEBUG then
|
||||||
love.draw = normalDraw
|
love.draw = normalDraw
|
||||||
else
|
else
|
||||||
love.draw = debugDraw
|
love.draw = debugDraw
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
love.resize = function()
|
love.resize = function()
|
||||||
|
normalDraw()
|
||||||
ui.resize()
|
ui.resize()
|
||||||
camera.resize()
|
camera.resize()
|
||||||
end
|
end
|
||||||
|
|
||||||
love.quit = function()
|
love.quit = function()
|
||||||
gameloop.networkSend(unit("playerLeave"))
|
levelloop.remoteCall("playerLeave")
|
||||||
end
|
end
|
||||||
|
|
||||||
gameloop.world = world
|
levelloop.world = world
|
||||||
end
|
end
|
||||||
|
|
18
lua/init.lua
|
@ -1,18 +0,0 @@
|
||||||
setmetatable(_G, {
|
|
||||||
__index = function(self, idx)
|
|
||||||
if rawget(self, idx) == nil then
|
|
||||||
error("attempted to read implicit global:" .. idx)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
})
|
|
||||||
|
|
||||||
PHYSICS_DEBUG = true
|
|
||||||
NETWORK_DEBUG = true
|
|
||||||
|
|
||||||
constants = require("server.constants")
|
|
||||||
US = string.char(31)
|
|
||||||
|
|
||||||
local menu = require("lua.menuloop")
|
|
||||||
function love.load()
|
|
||||||
menu.init()
|
|
||||||
end
|
|
253
lua/layout.lua
|
@ -1,25 +1,36 @@
|
||||||
layout = {}
|
local layout = {}
|
||||||
uiState = {}
|
layout.uiState = {}
|
||||||
|
layout.uiState.Menu = {}
|
||||||
|
layout.uiState.GameUI = {}
|
||||||
|
layout.uiState.MainMenu = {}
|
||||||
|
|
||||||
local debugPrint = function(string)
|
local fonts = require("shared.fonts")
|
||||||
|
local utils = require("shared.utils")
|
||||||
|
local asserts = require("shared.asserts")
|
||||||
|
local catalog = require("shared.catalog")
|
||||||
|
|
||||||
|
function layout.debugPrint(string)
|
||||||
--print(string)
|
--print(string)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function layout.canvasAndColor(container, x, y, simulate)
|
function layout.canvasAndColor(container, x, y, simulate)
|
||||||
|
asserts.number("layout.canvasAndColor", 2, x, y)
|
||||||
|
|
||||||
if simulate then return layout.handle(container[1], x, y, simulate) end
|
if simulate then return layout.handle(container[1], x, y, simulate) end
|
||||||
color_push()
|
utils.color_push()
|
||||||
love.graphics.setCanvas(container.canvas)
|
love.graphics.setCanvas(container.canvas)
|
||||||
x, y = layout.handle(container[1], x, y, simulate)
|
x, y = layout.handle(container[1], x, y, simulate)
|
||||||
love.graphics.setCanvas()
|
love.graphics.setCanvas()
|
||||||
color_pop()
|
utils.color_pop()
|
||||||
return x, y
|
return x, y
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function layout.rotate(container, x, y, simulate)
|
function layout.rotate(container, x, y, simulate)
|
||||||
assert_num("layout.rotate", 2, x, y)
|
asserts.number("layout.rotate", 2, x, y)
|
||||||
assert_num("layout.rotate (container)", container.rotation)
|
asserts.number("layout.rotate (container)", container.rotation)
|
||||||
|
|
||||||
local transform = love.math.newTransform(0, 0, container.rotation)
|
local transform = love.math.newTransform(0, 0, container.rotation)
|
||||||
local noTransform = love.math.newTransform(0, 0, 0)
|
local noTransform = love.math.newTransform(0, 0, 0)
|
||||||
love.graphics.replaceTransform(transform)
|
love.graphics.replaceTransform(transform)
|
||||||
|
@ -30,7 +41,8 @@ end
|
||||||
|
|
||||||
|
|
||||||
function layout.frame(container, x, y, simulate)
|
function layout.frame(container, x, y, simulate)
|
||||||
assert_num("layout.frame", 2, x, y)
|
asserts.number("layout.frame", 2, x, y)
|
||||||
|
|
||||||
x2, y2 = layout.handle(container[1], x +1, y +1, simulate)
|
x2, y2 = layout.handle(container[1], x +1, y +1, simulate)
|
||||||
if not simulate then
|
if not simulate then
|
||||||
love.graphics.rectangle("line", x, y, x2 -x, y2 -y)
|
love.graphics.rectangle("line", x, y, x2 -x, y2 -y)
|
||||||
|
@ -41,31 +53,22 @@ end
|
||||||
|
|
||||||
function layout.cursorSelect(container, x, y, simulate)
|
function layout.cursorSelect(container, x, y, simulate)
|
||||||
x2, y2 = layout.handle(container[1], x, y, simulate)
|
x2, y2 = layout.handle(container[1], x, y, simulate)
|
||||||
|
assert(container.uiSelect)
|
||||||
if not simulate then
|
if not simulate then
|
||||||
table.insert(uiState, { identifier = container.identifier, kind=container.kind,
|
table.insert(layout.uiState[container.uiSelect], { identifier = container.identifier, kind=container.kind,
|
||||||
x=x, x2=x2, y=y, y2=y2 })
|
x=x, x2=x2, y=y, y2=y2 })
|
||||||
end
|
end
|
||||||
return x2, y2
|
return x2, y2
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function layout.linewidth(container, x, y, simulate)
|
|
||||||
if simulate then return layout.handle(container[1], x, y, simulate) end
|
|
||||||
assert(container.width, "layout.linewidth (container) no container.width provided!")
|
|
||||||
local oldWidth = love.graphics.getLineWidth()
|
|
||||||
love.graphics.setLineWidth(container.width)
|
|
||||||
x, y = layout.handle(container[1], x, y, simulate)
|
|
||||||
love.graphics.setLineWidth(oldWidth)
|
|
||||||
return x, y
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function layout.spacer(container, x, y, simulate)
|
function layout.spacer(container, x, y, simulate)
|
||||||
assert(container.width or container.height, "No arguments for layout.spacer!")
|
assert(container.width or container.height, "No arguments for layout.spacer!")
|
||||||
assert(container.width == container.width,
|
assert(container.width == container.width,
|
||||||
"Argument container.width to layout.spacer must not be NaN!")
|
"Argument container.width to layout.spacer must not be NaN!")
|
||||||
assert(container.height == container.height,
|
assert(container.height == container.height,
|
||||||
"Argument container.height to layout.spacer must not be NaN!")
|
"Argument container.height to layout.spacer must not be NaN!")
|
||||||
|
|
||||||
local width = container.width or 0
|
local width = container.width or 0
|
||||||
local height = container.height or 0
|
local height = container.height or 0
|
||||||
if container[1] then
|
if container[1] then
|
||||||
|
@ -82,12 +85,12 @@ function layout.drawTexture(container, x, y, simulate)
|
||||||
if not simulate then
|
if not simulate then
|
||||||
love.graphics.draw(texture, x, y, 0, scale, scale)
|
love.graphics.draw(texture, x, y, 0, scale, scale)
|
||||||
end
|
end
|
||||||
return x + texture:getWidth()* scale, y + texture:getHeight() * scale
|
return x + (texture:getWidth() * scale), y + texture:getHeight() * scale
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function layout.drawHash(container, x, y, simulate)
|
function layout.drawHash(container, x, y, simulate)
|
||||||
local texture = gameloop.textures[container.hash]
|
local texture = levelloop.textures[container.hash]
|
||||||
local scale = container.scale or 1
|
local scale = container.scale or 1
|
||||||
if not texture.image then
|
if not texture.image then
|
||||||
texture.image = love.graphics.newImage(texture.data)
|
texture.image = love.graphics.newImage(texture.data)
|
||||||
|
@ -99,42 +102,80 @@ function layout.drawHash(container, x, y, simulate)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function layout.bwColorPicker(container, x, y, simulate)
|
||||||
|
asserts.number("layout.bwColorPicker", 2, x, y)
|
||||||
|
|
||||||
|
local startx, starty = x, y
|
||||||
|
granularity = container.granularity or 0.04
|
||||||
|
local points = {}
|
||||||
|
local myx = x
|
||||||
|
local endx = 0
|
||||||
|
local pointSize = container.pointSize or 8
|
||||||
|
|
||||||
|
if not simulate then
|
||||||
|
utils.color_push()
|
||||||
|
love.graphics.setPointSize(pointSize)
|
||||||
|
|
||||||
|
for c=0, 1.0, granularity do
|
||||||
|
love.graphics.setColor(c, c, c, 1)
|
||||||
|
love.graphics.points(myx+(pointSize/2), y +(pointSize/2))
|
||||||
|
myx = myx + pointSize
|
||||||
|
end
|
||||||
|
utils.color_pop()
|
||||||
|
|
||||||
|
local iterationCount = (1.0 / granularity)
|
||||||
|
maxx = x + pointSize * iterationCount
|
||||||
|
table.insert(layout.uiState.GameUI, { granularity = granularity, pointSize = pointSize,
|
||||||
|
kind=container.kind,
|
||||||
|
x=x, x2=maxx, y=y, y2=y + pointSize })
|
||||||
|
else
|
||||||
|
local iterationCount = (1.0 / granularity)
|
||||||
|
maxx = x + pointSize * iterationCount
|
||||||
|
maxy = y + pointSize
|
||||||
|
return maxx, maxy
|
||||||
|
end
|
||||||
|
return maxx, y + pointSize
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
function layout.colorPicker(container, x, y, simulate)
|
function layout.colorPicker(container, x, y, simulate)
|
||||||
assert(x and y)
|
asserts.number("layout.colorPicker", 2, x, y)
|
||||||
|
|
||||||
local startx, starty = x, y
|
local startx, starty = x, y
|
||||||
granularity = container.granularity or 0.2
|
granularity = container.granularity or 0.2
|
||||||
local points = {}
|
local points = {}
|
||||||
local myx, myy = x, y
|
local myx, myy = x, y
|
||||||
local endx = 0
|
local endx = 0
|
||||||
local pointsize = 16
|
local pointSize = container.pointSize or 8
|
||||||
|
|
||||||
if not simulate then
|
if not simulate then
|
||||||
color_push()
|
utils.color_push()
|
||||||
love.graphics.setPointSize(pointsize)
|
love.graphics.setPointSize(pointSize)
|
||||||
|
|
||||||
for r=0, 1.0, granularity do
|
for r=0, 1.0, granularity do
|
||||||
for g=0, 1.0, granularity do
|
for g=0, 1.0, granularity do
|
||||||
for b=0, 1.0, granularity do
|
for b=0, 1.0, granularity do
|
||||||
love.graphics.setColor(r, g, b, 1)
|
love.graphics.setColor(r, g, b, 1)
|
||||||
myx=myx+16
|
myx=myx+pointSize
|
||||||
love.graphics.points(myx-4.5, myy+4.5)
|
love.graphics.points(myx-(pointSize/2) -.5, myy +(pointSize/2))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
myy = myy + 16
|
myy = myy + pointSize
|
||||||
if myx > endx then endx = myx end
|
if myx > endx then endx = myx end
|
||||||
myx = x
|
myx = x
|
||||||
end
|
end
|
||||||
color_pop()
|
utils.color_pop()
|
||||||
|
|
||||||
local iterationCount = (1.0 / granularity) + 1
|
local iterationCount = (1.0 / granularity) + 1
|
||||||
maxx = x + pointsize * iterationCount * iterationCount
|
maxx = x + pointSize * iterationCount * iterationCount
|
||||||
table.insert(uiState, { granularity = granularity, pointsize = pointsize,
|
table.insert(layout.uiState.GameUI, { granularity = granularity, pointSize = pointSize,
|
||||||
kind=container.kind,
|
kind=container.kind,
|
||||||
x=x, x2=maxx, y=y, y2=myy })
|
x=x, x2=maxx, y=y, y2=myy })
|
||||||
else
|
else
|
||||||
local iterationCount = (1.0 / granularity) + 1
|
local iterationCount = (1.0 / granularity) + 1
|
||||||
maxx = x + pointsize * iterationCount * iterationCount
|
maxx = x + pointSize * iterationCount * iterationCount
|
||||||
maxy = y + pointsize * iterationCount
|
maxy = y + pointSize * iterationCount
|
||||||
|
return maxx, maxy
|
||||||
end
|
end
|
||||||
return maxx, myy
|
return maxx, myy
|
||||||
end
|
end
|
||||||
|
@ -142,34 +183,64 @@ end
|
||||||
|
|
||||||
function layout.color(container, x, y, simulate)
|
function layout.color(container, x, y, simulate)
|
||||||
if simulate then return layout.handle(container[1], x, y, simulate) end
|
if simulate then return layout.handle(container[1], x, y, simulate) end
|
||||||
|
assert(container.color, "layout.color Color!")
|
||||||
assert(#container.color == 3 or #container.color == 4)
|
assert(#container.color == 3 or #container.color == 4)
|
||||||
color_push()
|
utils.color_push()
|
||||||
love.graphics.setColor(container.color)
|
love.graphics.setColor(container.color)
|
||||||
x, y = layout.handle(container[1], x, y, simulate)
|
x, y = layout.handle(container[1], x, y, simulate)
|
||||||
color_pop()
|
utils.color_pop()
|
||||||
return x, y
|
return x, y
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function layout.rect(container, x, y, simulate)
|
function layout.rect(container, x, y, simulate)
|
||||||
assert_num("layout.rect", 2, x, y)
|
asserts.number("layout.rect", 2, x, y)
|
||||||
assert_num("layout.rect (container)", 2, container.width, container.height)
|
asserts.number("layout.rect (container)", 2, container.width, container.height)
|
||||||
if not simulate then
|
|
||||||
if container.color then
|
if simulate then return x + container.width, y + container.height end
|
||||||
color_push()
|
|
||||||
love.graphics.setColor(container.color.r, container.color.g, container.color.b, container.color.a or 1)
|
if container.color then
|
||||||
|
utils.color_push()
|
||||||
|
love.graphics.setColor(container.color.r, container.color.g, container.color.b, container.color.a or 1)
|
||||||
|
love.graphics.rectangle(container.fill or "fill", x, y, container.width, container.height)
|
||||||
|
utils.color_pop()
|
||||||
|
else
|
||||||
love.graphics.rectangle(container.fill or "fill", x, y, container.width, container.height)
|
love.graphics.rectangle(container.fill or "fill", x, y, container.width, container.height)
|
||||||
color_pop()
|
|
||||||
else
|
|
||||||
love.graphics.rectangle(container.fill or "fill", x, y, container.width, container.height)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return x + container.width, y + container.height
|
return x + container.width, y + container.height
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function layout.button(container, x, y, simulate)
|
||||||
|
local textObject = love.graphics.newText(fonts.headerFont, container.text)
|
||||||
|
local uiSelect = layout.uiState[container.uiSelect] or error("layout.button passed invalid uiSelect!" .. container.uiSelect or "no value!")
|
||||||
|
assert(container.identifier, "layout.button no identifier passed!")
|
||||||
|
local buttonColor = catalog.buttonColors[container.buttonColor or "blue"]
|
||||||
|
local width = textObject:getWidth()
|
||||||
|
local height = textObject:getHeight()
|
||||||
|
local factor = 1.15
|
||||||
|
if not simulate then
|
||||||
|
utils.color_push()
|
||||||
|
love.graphics.setColor(buttonColor[1])
|
||||||
|
love.graphics.rectangle("fill", x, y, width* factor, height* factor,
|
||||||
|
height* factor/4, height* factor/4)
|
||||||
|
love.graphics.setColor(buttonColor[2])
|
||||||
|
love.graphics.rectangle("line", x, y, width* factor, height* factor,
|
||||||
|
height* factor/4, height* factor/4)
|
||||||
|
love.graphics.setColor(buttonColor[3])
|
||||||
|
love.graphics.draw(textObject, x + width * 0.075 , y + height * 0.075)
|
||||||
|
utils.color_pop()
|
||||||
|
end
|
||||||
|
table.insert(uiSelect, { identifier = container.identifier, kind="button",
|
||||||
|
x=x, x2=x+(width *factor), y=y, y2=y + (height * factor) })
|
||||||
|
return x+ (width *factor), y + (height * factor)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
function layout.overlayRect(container, x, y, simulate)
|
function layout.overlayRect(container, x, y, simulate)
|
||||||
assert_num("layout.overlayRect", 2, x, y)
|
asserts.number("layout.overlayRect", 2, x, y)
|
||||||
|
|
||||||
winx, winy = layout.handle(container[1], x, y, simulate)
|
winx, winy = layout.handle(container[1], x, y, simulate)
|
||||||
width = winx -x
|
width = winx -x
|
||||||
height = winy -y
|
height = winy -y
|
||||||
|
@ -180,11 +251,33 @@ function layout.overlayRect(container, x, y, simulate)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function layout.labelWShadow(container, x, y, simulate)
|
||||||
|
-- TODO: Replace this with a text shadow shader FFS
|
||||||
|
if not container.font then container.font = fonts.normalFont end
|
||||||
|
local text = love.graphics.newText( container.font, container.text )
|
||||||
|
if not text then text = love.graphics.newText( container.font, "Unset text!!") end
|
||||||
|
if not simulate then
|
||||||
|
utils.color_push()
|
||||||
|
love.graphics.setColor(0,0,0)
|
||||||
|
love.graphics.draw(text, x, y +4)
|
||||||
|
love.graphics.draw(text, x, y +2)
|
||||||
|
love.graphics.draw(text, x, y +0)
|
||||||
|
love.graphics.draw(text, x +2, y +2)
|
||||||
|
love.graphics.draw(text, x +2, y +0)
|
||||||
|
love.graphics.draw(text, x +4, y +4)
|
||||||
|
love.graphics.draw(text, x +4, y +2)
|
||||||
|
love.graphics.draw(text, x +4, y +0)
|
||||||
|
utils.color_pop()
|
||||||
|
love.graphics.draw(text, x +2, y +2)
|
||||||
|
end
|
||||||
|
return x + text:getWidth() + 2, y + text:getHeight() +2
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
function layout.label(container, x, y, simulate)
|
function layout.label(container, x, y, simulate)
|
||||||
if not container.font then container.font = ui.font end
|
if not container.font then container.font = fonts.normalFont end
|
||||||
local text = love.graphics.newText( container.font, container.text )
|
local text = love.graphics.newText( container.font, container.text )
|
||||||
if not text then text = "Unset text!!" end
|
if not text then text = love.graphics.newText( container.font, "Unset text!!") end
|
||||||
if not simulate then
|
if not simulate then
|
||||||
love.graphics.draw(text, x, y)
|
love.graphics.draw(text, x, y)
|
||||||
end
|
end
|
||||||
|
@ -214,7 +307,40 @@ function layout.horizontal(container, x, y, simulate)
|
||||||
if ret.y > bigy then bigy = ret.y end
|
if ret.y > bigy then bigy = ret.y end
|
||||||
x = ret.x
|
x = ret.x
|
||||||
end
|
end
|
||||||
return x,bigy
|
return x, bigy
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function layout.naiveGrid(container, startX, startY, simulate)
|
||||||
|
-- This is naiveGrid and not grid because the splitting alghorythm splits
|
||||||
|
-- *after* the threshold is reached, thus individual lines can be quite a bit longer
|
||||||
|
-- depending on the passed content.
|
||||||
|
-- todo: rewrite this with a (possibly) more expensive grid function that
|
||||||
|
-- simulates everything before doing it
|
||||||
|
local maxX, maxY = startX, startY
|
||||||
|
local workX, workY = startX, startY -- The current "pointer" for where to draw
|
||||||
|
local retX, retY = startX, startY -- the values returned by the newly drawn shape
|
||||||
|
local width = container.width
|
||||||
|
if not simulate then
|
||||||
|
assert(container[1], "naiveGrid called without arguments!")
|
||||||
|
else
|
||||||
|
if not container[1] then return startX, startY end
|
||||||
|
end
|
||||||
|
|
||||||
|
for i, v in ipairs(container) do
|
||||||
|
retX, retY = layout.handle(v, workX, workY, simulate)
|
||||||
|
if retY > maxY then maxY = retY end
|
||||||
|
|
||||||
|
if retX > startX + width then
|
||||||
|
workY = maxY
|
||||||
|
workX = startX
|
||||||
|
if retX > maxX then maxX = retX end
|
||||||
|
else
|
||||||
|
workX = retX
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return maxX, maxY
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -247,13 +373,22 @@ function layout.vertical(container, x, y, simulate)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function layout.skip(container, x, y, simulate)
|
||||||
|
return x, y
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
local depth = -1
|
local depth = -1
|
||||||
function layout.handle(container, x, y, simulate)
|
function layout.handle(container, x, y, simulate)
|
||||||
assert_num("layout.handle", 2, x, y)
|
|
||||||
|
asserts.number("layout.handle", 2, x, y)
|
||||||
assert(container, "layout.handle called without container")
|
assert(container, "layout.handle called without container")
|
||||||
|
|
||||||
local name = container["name"]
|
local name = container["name"]
|
||||||
|
if not name then return end
|
||||||
assert(name, "layout.handle container without name")
|
assert(name, "layout.handle container without name")
|
||||||
assert(layout[name], "Unknown layout function " .. name .. " called")
|
assert(layout[name], "Unknown layout function " .. name .. " called")
|
||||||
|
|
||||||
depth = depth +1
|
depth = depth +1
|
||||||
local depthCursor = ""
|
local depthCursor = ""
|
||||||
for i=0, depth do
|
for i=0, depth do
|
||||||
|
@ -261,22 +396,24 @@ function layout.handle(container, x, y, simulate)
|
||||||
end
|
end
|
||||||
if depth == 0 then
|
if depth == 0 then
|
||||||
if simulate then
|
if simulate then
|
||||||
debugPrint("Starting simulation")
|
layout.debugPrint("Starting simulation")
|
||||||
else
|
else
|
||||||
debugPrint("Starting rendering")
|
layout.debugPrint("Starting rendering")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
debugPrint(depthCursor.. ">" .. container.name)
|
layout.debugPrint(depthCursor.. ">" .. container.name)
|
||||||
x, y = layout[name](container, x, y, simulate)
|
x, y = layout[name](container, x, y, simulate)
|
||||||
assert_num("layout.handle" .. container.name .. " is not returning properly!", 2, x, y)
|
asserts.number("layout.handle" .. container.name .. " is not returning properly!", 2, x, y)
|
||||||
debugPrint(depthCursor.."<" .. container.name)
|
layout.debugPrint(depthCursor.."<" .. container.name)
|
||||||
depth = depth -1
|
depth = depth -1
|
||||||
if depth == -1 then
|
if depth == -1 then
|
||||||
if simulate then
|
if simulate then
|
||||||
debugPrint("simulation ended\n")
|
layout.debugPrint("simulation ended\n")
|
||||||
else
|
else
|
||||||
debugPrint("rendering ended\n")
|
layout.debugPrint("rendering ended\n")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return x, y
|
return x, y
|
||||||
end
|
end
|
||||||
|
|
||||||
|
return layout
|
||||||
|
|
311
lua/menuloop.lua
|
@ -1,9 +1,16 @@
|
||||||
menu = {}
|
menu = {}
|
||||||
menu.serverlist = {
|
menu.serverlist = {
|
||||||
{description = "A localhost server [ipv4]", host = "127.0.0.1", port = "11150"},
|
{description = "A localhost server", host = "::1", port = "11150"},
|
||||||
{description = "A localhost server [ipv6]", host = "::1", port = "11150"}
|
{description = "A localhost server [ipv4]", host = "127.0.0.1", port = "11150"}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
local utils = require("shared.utils")
|
||||||
|
local fonts = require("shared.fonts")
|
||||||
|
local catalog = require("shared.catalog")
|
||||||
|
local icon = require("shared.icon")
|
||||||
|
|
||||||
|
local layout = require("lua.layout")
|
||||||
|
|
||||||
local altPressed, enterPressed = false, false
|
local altPressed, enterPressed = false, false
|
||||||
function menu.keyreleased(key, _)
|
function menu.keyreleased(key, _)
|
||||||
if key == "lalt" then
|
if key == "lalt" then
|
||||||
|
@ -25,11 +32,6 @@ function menu.keypressed(key, _)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function menu.update(dt)
|
|
||||||
love.timer.sleep((1/60) -dt)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function menu.resize()
|
function menu.resize()
|
||||||
menu.width, menu.height = love.window.getMode()
|
menu.width, menu.height = love.window.getMode()
|
||||||
menu.Canvas = love.graphics.newCanvas(width, height)
|
menu.Canvas = love.graphics.newCanvas(width, height)
|
||||||
|
@ -40,20 +42,50 @@ function menu.resize()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local firstClient = true
|
||||||
function menu.mousepressed(mousex, mousey)
|
function menu.mousepressed(mousex, mousey)
|
||||||
local textures = gameloop.textures
|
for i, v in ipairs(layout.uiState.MainMenu) do
|
||||||
for i, v in ipairs(uiState) do
|
if mousex >= v.x and mousex <= v.x2 and mousey >= v.y and mousey <= v.y2 then
|
||||||
if mousex >= v.x and mousex <= v.x2 then
|
if v.kind == "localServer" then
|
||||||
if mousey >= v.y and mousey <= v.y2 then
|
menuMusic[menuMusic.trac]:stop()
|
||||||
if v.kind == "server" then
|
love.graphics.reset()
|
||||||
local names = { "Herbert", "Thorben", "Martin", "Heinrich", "Dietrich", "Markus", "Florian", "Helmut", "Willhelm", "Fritz", "Gustav", "Konrad", "Berta", "Charlotte", "Hildegard", "Lieselotte", "Gudrun", "Giesela", "Margarete", "Antonia", "Friederike", "Clotilde", "Marlies", "Hedwig" }
|
love.graphics.origin()
|
||||||
local lastNames = { "Müller", "Schmidt", "Meier", "Bauer", "Werner", "Schumacher", "Bergmann", "Eisenhauer", "Heisenberg" }
|
love.graphics.clear(.6, 0, .6)
|
||||||
local fullName = names[love.math.random(1, #names)] .. " " .. lastNames[love.math.random(1, #lastNames)]
|
love.graphics.setColor(1, 1, 1)
|
||||||
local serverID = v.identifier
|
love.graphics.present()
|
||||||
local serverEntry = menu.serverlist[serverID]
|
local serverloop = require("server.main")
|
||||||
gameloop.init({ adress = serverEntry.host, port = serverEntry.port }, fullName)
|
serverloop.init(v.identifier)
|
||||||
uiState = {}
|
elseif v.kind == "server" then
|
||||||
|
if firstClient then
|
||||||
|
menuMusic[menuMusic.trac]:stop()
|
||||||
|
dofile "lua/gameloop.lua"
|
||||||
|
dofile "lua/layout.lua"
|
||||||
|
dofile "lua/ui.lua"
|
||||||
|
dofile "lua/camera.lua"
|
||||||
|
dofile "lua/physics.lua"
|
||||||
|
dofile "lua/drawing.lua"
|
||||||
|
setmetatable(_G, {
|
||||||
|
__index = function(self, idx)
|
||||||
|
if rawget(self, idx) == nil then
|
||||||
|
error("attempted to read implicit global:" .. idx)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
})
|
||||||
|
|
||||||
|
PHYSICS_DEBUG = true
|
||||||
|
NETWORK_DEBUG = true
|
||||||
|
firstclient = false
|
||||||
end
|
end
|
||||||
|
local textures = levelloop.textures
|
||||||
|
|
||||||
|
local fullName = catalog.firstNames[love.math.random(1, #catalog.firstNames)]
|
||||||
|
.. " " .. catalog.surNames[love.math.random(1, #catalog.surNames)]
|
||||||
|
local serverID = v.identifier
|
||||||
|
local serverEntry = menu.serverlist[serverID]
|
||||||
|
local avatar = love.image.newImageData(catalog.default.avatar)
|
||||||
|
local avatarHash = love.data.hash("sha512", avatar:getString())
|
||||||
|
levelloop.init({ adress = serverEntry.host, port = serverEntry.port }, fullName, avatarHash)
|
||||||
|
layout.uiState.MainMenu = {}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -61,68 +93,245 @@ end
|
||||||
|
|
||||||
|
|
||||||
menu.serverentry = function(id, description, host, port)
|
menu.serverentry = function(id, description, host, port)
|
||||||
return {name = "horizontal",
|
return {name = "cursorSelect", kind = "server", identifier = id, uiSelect = "MainMenu",
|
||||||
{name = "label", text = description },
|
{name = "horizontal",
|
||||||
{name = "spacer", width = 20 },
|
{name = "drawTexture", texture = icon.joinServer},
|
||||||
{name = "label", text = "[".. host .."]"},
|
{name = "spacer", width = 10 },
|
||||||
{name = "label", text = ":".. port },
|
{name = "labelWShadow", text = description },
|
||||||
{name = "spacer", width = 25 },
|
{name = "spacer", width = 20 },
|
||||||
{name = "cursorSelect", kind = "server", identifier = id,
|
{name = "labelWShadow", text = "host: ".. host, font = fonts.smallFont},
|
||||||
{name = "drawTexture", texture = love.graphics.newImage("textures/menu/serverPlay.png")}}}
|
{name = "spacer", width = 10 },
|
||||||
|
{name = "labelWShadow", text = "port:".. port, font = fonts.smallFont},
|
||||||
|
}
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
menu.layoutServerList = function()
|
menu.layoutServerList = function()
|
||||||
local list = {name = "vertical"}
|
local list = {}
|
||||||
for i, v in ipairs(menu.serverlist) do
|
for i, v in ipairs(menu.serverlist) do
|
||||||
table.insert(list, menu.serverentry(i, v.description, v.host, v.port))
|
table.insert(list, menu.serverentry(i, v.description, v.host, v.port))
|
||||||
end
|
end
|
||||||
return list
|
return unpack(list)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
menu.layout = {name ="vertical",
|
menu.layoutLocalServer = function()
|
||||||
{name = "spacer", height = 20},
|
return {name = "vertical",
|
||||||
{name = "horizontal",
|
{name = "cursorSelect", kind = "localServer",
|
||||||
{name = "spacer", width = 20},
|
identifier = {"::1", "11150"}, uiSelect = "MainMenu",
|
||||||
{name = "drawTexture", texture = love.graphics.newImage("ressources/Poppy.png")},
|
{name = "horizontal",
|
||||||
{name = "label", text = "Welcome to poppy!"},
|
{name = "spacer", width = 30},
|
||||||
},
|
{name = "drawTexture",
|
||||||
{name = "horizontal",
|
texture = icon.startServer
|
||||||
{name = "spacer", width = 60},
|
},
|
||||||
{name = "vertical",
|
{name = "spacer", width = 5},
|
||||||
{name = "spacer", height = 60},
|
{name = "labelWShadow", text = "Create a local server"},
|
||||||
{name = "label", text = "Pick a server to join!"},
|
}
|
||||||
{name = "spacer", height = 20},
|
},
|
||||||
menu.layoutServerList()
|
{name = "cursorSelect", kind = "localServer",
|
||||||
|
identifier = {"0.0.0.0", "11150"}, uiSelect = "MainMenu",
|
||||||
|
{name = "horizontal",
|
||||||
|
{name = "spacer", width = 30},
|
||||||
|
{name = "drawTexture",
|
||||||
|
texture = icon.startServer
|
||||||
|
},
|
||||||
|
{name = "spacer", width = 5},
|
||||||
|
{name = "labelWShadow", text = "Create a local server [IPV4]"},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{name = "spacer", height = 20},
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
menu.layout = function(message)
|
||||||
|
return {name ="vertical",
|
||||||
|
{name = "spacer", height = 20},
|
||||||
|
{name = "horizontal",
|
||||||
|
{name = "spacer", width = 20},
|
||||||
|
{name = "drawTexture", texture = icon.logo},
|
||||||
|
{name = "labelWShadow", font = fonts.bigFont, text = "Welcome to poppy!"},
|
||||||
|
},
|
||||||
|
{name = "horizontal",
|
||||||
|
{name = "spacer", width = 50},
|
||||||
|
{name = "vertical",
|
||||||
|
{name = "labelWShadow", text = "Pick a server to join!", font = fonts.headerFont},
|
||||||
|
{name = "spacer", height = 20},
|
||||||
|
{name = "horizontal",
|
||||||
|
{name = "spacer", width = 10},
|
||||||
|
{name = "vertical",
|
||||||
|
{name = "vertical",
|
||||||
|
menu.layoutServerList(),
|
||||||
|
},
|
||||||
|
{name = "vertical",
|
||||||
|
{name = "spacer", height = 10},
|
||||||
|
{name = "horizontal",
|
||||||
|
{name = "drawTexture", texture = icon.addServer},
|
||||||
|
{name = "spacer", width = 10 },
|
||||||
|
{name = "labelWShadow", text = "placeholder (new server)" },
|
||||||
|
{name = "spacer", width = 20 },
|
||||||
|
{name = "labelWShadow", text = "host: newhost", font = fonts.smallFont},
|
||||||
|
{name = "spacer", width = 10 },
|
||||||
|
{name = "labelWShadow", text = "port: newport", font = fonts.smallFont},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
end
|
||||||
|
|
||||||
|
|
||||||
function drawMenu()
|
function drawMenu(message)
|
||||||
color_push()
|
utils.color_push()
|
||||||
love.graphics.setCanvas(menu.Canvas)
|
love.graphics.setCanvas(menu.Canvas)
|
||||||
uiState = {}
|
layout.uiState.MainMenu = {}
|
||||||
love.graphics.clear( )
|
love.graphics.clear( )
|
||||||
layout.handle(menu.layout, 0, 0)
|
layout.handle(menu.layout(message), 0, 0)
|
||||||
|
|
||||||
|
local sizex, sizey = layout.handle(menu.layoutLocalServer(), 0, 0, true)
|
||||||
|
layout.handle(menu.layoutLocalServer(), 0, menu.height - sizey)
|
||||||
love.graphics.setCanvas()
|
love.graphics.setCanvas()
|
||||||
color_pop()
|
utils.color_pop()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function menu.draw(w, h)
|
local bubbles = {}
|
||||||
|
function menu.draw()
|
||||||
|
for i=1, #bubbles do
|
||||||
|
drawBubble(bubbles[i])
|
||||||
|
end
|
||||||
love.graphics.draw(menu.Canvas)
|
love.graphics.draw(menu.Canvas)
|
||||||
|
layout.handle(debugAudio(), 600, 300)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function menu.init()
|
menuMusic = {}
|
||||||
|
menuMusic[1] = love.audio.newSource("sound/Poppy Main Menu theme_2.ogg", "stream")
|
||||||
|
menuMusic[2] = love.audio.newSource("sound/Poppy Main Menu theme_2.ogg", "stream")
|
||||||
|
menuMusic.duration = menuMusic[1]:getDuration("seconds")
|
||||||
|
menuMusic.samples = menuMusic[1]:getDuration("samples")
|
||||||
|
menuMusic.fading = false
|
||||||
|
menuMusic.trac = 1
|
||||||
|
menuMusic.nextTrac = 2
|
||||||
|
|
||||||
|
menuMusic.fadeStart = 0
|
||||||
|
menuMusic.fadeEnd = 0
|
||||||
|
menuMusic.fadeTime = 0
|
||||||
|
function musicUpdate(dt)
|
||||||
|
if not menuMusic.fading then
|
||||||
|
if menuMusic[menuMusic.trac]:tell("seconds") >= menuMusic.duration - 20 then
|
||||||
|
menuMusic.fading = true
|
||||||
|
menuMusic.fadeStart = menuMusic[menuMusic.trac]:tell("samples")
|
||||||
|
menuMusic.fadeTime = menuMusic.samples - menuMusic.fadeStart
|
||||||
|
menuMusic[menuMusic.nextTrac]:setVolume(1)
|
||||||
|
menuMusic[menuMusic.nextTrac]:play()
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local fadefraction = ( menuMusic.fadeStart - menuMusic[menuMusic.trac]:tell("samples") )
|
||||||
|
/ menuMusic.fadeTime
|
||||||
|
menuMusic[menuMusic.trac]:setVolume(fadefraction)
|
||||||
|
menuMusic[menuMusic.nextTrac]:setVolume(fadefraction - 1)
|
||||||
|
if fadefraction > 0.95 then
|
||||||
|
menuMusic[menuMusic.trac]:stop()
|
||||||
|
menuMusic[menuMusic.nextTrac]:setVolume(1)
|
||||||
|
local trac = menuMusic.trac
|
||||||
|
menuMusic.trac = menuMusic.nextTrac
|
||||||
|
menuMusic.nextTrac = trac
|
||||||
|
menuMusic.fading = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function debugAudio()
|
||||||
|
return {name = "vertical",
|
||||||
|
{name = "labelWShadow", text = "CUR: " .. menuMusic.trac, font = fonts.smallFont},
|
||||||
|
{name = "labelWShadow", text = "CUR " .. menuMusic.duration, font = fonts.smallFont},
|
||||||
|
{name = "labelWShadow", text = "CUR " .. menuMusic[menuMusic.trac]:tell("seconds"), font = fonts.smallFont},
|
||||||
|
{name = "labelWShadow", text = "CUR " .. menuMusic.samples, font = fonts.smallFont},
|
||||||
|
{name = "labelWShadow", text = "CUR " .. menuMusic[menuMusic.trac]:tell("samples"), font = fonts.smallFont},
|
||||||
|
{name = "labelWShadow", font = fonts.smallFont, text = "CUR volume:" .. menuMusic[menuMusic.trac]:getVolume()},
|
||||||
|
{name = "spacer", height = 20},
|
||||||
|
{name = "labelWShadow", text = "NXT: " .. menuMusic.nextTrac, font = fonts.smallFont},
|
||||||
|
{name = "labelWShadow", text = "NXT " .. menuMusic.duration, font = fonts.smallFont},
|
||||||
|
{name = "labelWShadow", text = "NXT " .. menuMusic[menuMusic.nextTrac]:tell("seconds"), font = fonts.smallFont},
|
||||||
|
{name = "labelWShadow", text = "NXT " .. menuMusic.samples, font = fonts.smallFont},
|
||||||
|
{name = "labelWShadow", text = "NXT " .. menuMusic[menuMusic.nextTrac]:tell("samples"), font = fonts.smallFont},
|
||||||
|
{name = "labelWShadow", font = fonts.smallFont, text = "NXT volume:" .. menuMusic[menuMusic.nextTrac]:getVolume()},
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
local elapsed = 0
|
||||||
|
function menu.update(dt)
|
||||||
|
if elapsed > 1 / 60 then
|
||||||
|
for i=1, #bubbles do
|
||||||
|
local bubble = bubbles[i]
|
||||||
|
local sx = bubble.sx * dt
|
||||||
|
local sy = bubble.sy * dt
|
||||||
|
local bubble = bubbles[i]
|
||||||
|
bubble.x = bubble.x + sx
|
||||||
|
bubble.y = bubble.y + sy
|
||||||
|
if bubble.x + bubble.radius >= menu.width then
|
||||||
|
bubble.x = menu.width - bubble.radius
|
||||||
|
bubble.sx = -bubble.sx
|
||||||
|
end
|
||||||
|
if bubble.x - bubble.radius <= 0 then
|
||||||
|
bubble.x = bubble.radius
|
||||||
|
bubble.sx = -bubble.sx
|
||||||
|
end
|
||||||
|
if bubble.y - bubble.radius <= 0 then
|
||||||
|
bubble.y = bubble.radius
|
||||||
|
bubble.sy = -bubble.sy
|
||||||
|
end
|
||||||
|
if bubble.y + bubble.radius >= menu.height then
|
||||||
|
bubble.y = menu.height - bubble.radius
|
||||||
|
bubble.sy = -bubble.sy
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elapsed = 0
|
||||||
|
end
|
||||||
|
elapsed = elapsed + dt
|
||||||
|
musicUpdate(dt)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function drawBubble(bubble)
|
||||||
|
utils.color_push()
|
||||||
|
love.graphics.setColor(bubble.color[1]* .6, bubble.color[2] * .6, bubble.color[3] * .6, bubble.color[4])
|
||||||
|
love.graphics.circle("fill", bubble.x, bubble.y, bubble.radius )
|
||||||
|
love.graphics.setColor(bubble.color[1]* .9, bubble.color[2] * .9, bubble.color[3] * .9, bubble.color[4])
|
||||||
|
love.graphics.circle("fill", bubble.x, bubble.y, bubble.radius -3 )
|
||||||
|
--love.graphics.setColor(bubble.color)
|
||||||
|
--love.graphics.circle("fill", bubble.x, bubble.y, bubble.radius/2 )
|
||||||
|
utils.color_pop()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function menu.init(message)
|
||||||
menu.width, menu.height = love.window.getMode()
|
menu.width, menu.height = love.window.getMode()
|
||||||
|
math.randomseed(os.time())
|
||||||
|
for i=1,24 do
|
||||||
|
local bubble = {}
|
||||||
|
bubble.radius = 10 + math.floor(math.random() * 30)
|
||||||
|
bubble.color = catalog.pastelColors[math.floor(1 + math.random() * #catalog.pastelColors - 0.001)]
|
||||||
|
bubble.y = bubble.radius + math.floor(math.random() * (menu.height - (2* bubble.radius)))
|
||||||
|
bubble.x = bubble.radius + math.floor(math.random() * (menu.width - (2* bubble.radius)))
|
||||||
|
bubble.sx = 20 - math.random() * 40
|
||||||
|
bubble.sy = 20 - math.random() * 40
|
||||||
|
table.insert(bubbles, bubble)
|
||||||
|
end
|
||||||
|
menuMusic[menuMusic.trac]:play()
|
||||||
menu.Canvas = love.graphics.newCanvas(menu.width, menu.height)
|
menu.Canvas = love.graphics.newCanvas(menu.width, menu.height)
|
||||||
drawMenu()
|
drawMenu()
|
||||||
love.draw = menu.draw
|
love.draw = menu.draw
|
||||||
|
love.update = menu.update
|
||||||
love.keyreleased = menu.keyreleased
|
love.keyreleased = menu.keyreleased
|
||||||
love.keypressed = menu.keypressed
|
love.keypressed = menu.keypressed
|
||||||
love.mousepressed = menu.mousepressed
|
love.mousepressed = menu.mousepressed
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
return menu
|
return menu
|
||||||
|
|
157
lua/network.lua
|
@ -1,157 +0,0 @@
|
||||||
local player = require("lua.player")
|
|
||||||
local util = require("server.utils")
|
|
||||||
local v2_message = {}
|
|
||||||
|
|
||||||
v2_message.init = function(clientID, args)
|
|
||||||
local nickname, args = util.nextStringRecord(args)
|
|
||||||
local worldID, args = util.nextStringRecord(args)
|
|
||||||
local x, args = util.nextIntRecord(args)
|
|
||||||
local y = tonumber(args)
|
|
||||||
print("Connected as " .. clientID .. " with name " .. nickname)
|
|
||||||
print("New world is " .. worldID .. " Size is [" .. x .. "|" .. y .. "]")
|
|
||||||
players[clientID] = player.newPlayer()
|
|
||||||
camera.target = clientID
|
|
||||||
_G.clientID = clientID
|
|
||||||
|
|
||||||
players[clientID].name = nickname
|
|
||||||
|
|
||||||
local world = { id = worldID, x = x, y = y }
|
|
||||||
gameloop.loadworld(world)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
v2_message.ping = function(clientID)
|
|
||||||
gameloop.networkSend("pong")
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
v2_message.playerJoin = function(clientID, name)
|
|
||||||
ui.addChatEntry("", name .. " Joined the game")
|
|
||||||
players[clientID] = player.newPlayer()
|
|
||||||
players[clientID].label = love.graphics.newText(ui.smallFont, name)
|
|
||||||
players[clientID].name = name
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
v2_message.playerLeave = function(clientID)
|
|
||||||
if players[clientID] then
|
|
||||||
local name = players[clientID].name
|
|
||||||
ui.addChatEntry("", name .. " Left the game")
|
|
||||||
players[clientID] = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
v2_message.moveUpdate = function(clientID, args)
|
|
||||||
if not players[clientID] then return end
|
|
||||||
local x, args = util.nextIntRecord(args)
|
|
||||||
local y, args = util.nextIntRecord(args)
|
|
||||||
local speedx, args = util.nextIntRecord(args)
|
|
||||||
local speedy = util.clean(args:gsub("^.-\31",""))
|
|
||||||
|
|
||||||
players[clientID].x = x
|
|
||||||
players[clientID].y = y
|
|
||||||
players[clientID].speed.x = speedx
|
|
||||||
players[clientID].speed.y = speedy
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
v2_message.moveFlying = function(clientID, args)
|
|
||||||
if not players[clientID] then return end
|
|
||||||
players[clientID].flying = (args == "true")
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
v2_message.deletePhysics = function(clientID, args)
|
|
||||||
if clientID == camera.target then return end
|
|
||||||
if not players[clientID] then return end
|
|
||||||
physics.remove_solid(util.getIntRecords(4, args))
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
v2_message.drawPhysics = function(clientID, args)
|
|
||||||
if clientID == camera.target then return end
|
|
||||||
if not players[clientID] then return end
|
|
||||||
physics.draw_solid(util.getIntRecords(4, args))
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
v2_message.drawTexture = function(clientID, args)
|
|
||||||
if clientID == camera.target then return end
|
|
||||||
local x, args = util.nextIntRecord(args)
|
|
||||||
local y, args = util.nextIntRecord(args)
|
|
||||||
local layer, args = util.nextStringRecord(args)
|
|
||||||
local hash, args = util.nextStringRecord(args)
|
|
||||||
hash = love.data.decode("string", "base64", hash)
|
|
||||||
local r, args = util.nextIntRecord(args)
|
|
||||||
local g, args = util.nextIntRecord(args)
|
|
||||||
local b, a = util.nextIntRecord(args)
|
|
||||||
a = tonumber(a)
|
|
||||||
if not r or not g or not b then
|
|
||||||
r, g, b = 1, 1, 1
|
|
||||||
end
|
|
||||||
if not a then
|
|
||||||
a = 1
|
|
||||||
end
|
|
||||||
if not gameloop.textures[hash] then print("Failed to find hash!") return end
|
|
||||||
love.graphics.setBlendMode("replace")
|
|
||||||
if layer == "fg" then
|
|
||||||
love.graphics.setCanvas(gameloop.buffer.pixel.fg)
|
|
||||||
elseif layer == "bg" then
|
|
||||||
love.graphics.setCanvas(gameloop.buffer.pixel.bg)
|
|
||||||
end
|
|
||||||
if not gameloop.textures[hash].image then love.graphics.newImage(gameloop.textures[hash].data) end
|
|
||||||
color_push()
|
|
||||||
love.graphics.setColor(r, g, b ,a)
|
|
||||||
love.graphics.draw(gameloop.textures[hash].image, x, y)
|
|
||||||
color_pop()
|
|
||||||
love.graphics.setBlendMode("alpha")
|
|
||||||
love.graphics.setCanvas()
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
v2_message.deleteTexture = function(clientID, args)
|
|
||||||
if clientID == camera.target then return end
|
|
||||||
local x, args = util.nextIntRecord(args)
|
|
||||||
local y, args = util.nextIntRecord(args)
|
|
||||||
local width, args = util.nextIntRecord(args)
|
|
||||||
local height, layer = util.nextIntRecord(args)
|
|
||||||
if layer == "fg" then
|
|
||||||
love.graphics.setCanvas(gameloop.buffer.pixel.fg)
|
|
||||||
elseif layer == "bg" then
|
|
||||||
love.graphics.setCanvas(gameloop.buffer.pixel.bg)
|
|
||||||
end
|
|
||||||
love.graphics.setBlendMode("replace")
|
|
||||||
color_push()
|
|
||||||
love.graphics.setColor(0,0,0,0)
|
|
||||||
love.graphics.rectangle("fill", x, y, width, height)
|
|
||||||
color_pop()
|
|
||||||
love.graphics.setBlendMode("alpha")
|
|
||||||
love.graphics.setCanvas()
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
v2_message.chatMessage = function(clientID, args)
|
|
||||||
local name
|
|
||||||
if clientID == "0" then
|
|
||||||
name = ""
|
|
||||||
else
|
|
||||||
name = players[clientID].name
|
|
||||||
end
|
|
||||||
ui.addChatEntry(players[clientID].name, args)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
v2_message.clearCanvas = function(clientID)
|
|
||||||
drawing.clearAllCanvases()
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
v2_message.fillCanvas = function(clientID, args)
|
|
||||||
local layer, hash = util.nextStringRecord(args)
|
|
||||||
hash = love.data.decode("string", "base64", hash)
|
|
||||||
drawing.fillCanvas(layer, hash)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
return v2_message
|
|
267
lua/physics.lua
|
@ -1,5 +1,8 @@
|
||||||
physics = {}
|
physics = {}
|
||||||
|
|
||||||
|
local asserts = require("shared.asserts")
|
||||||
|
local utils = require("shared.utils")
|
||||||
|
|
||||||
function physics.keyreleased_debug(key, _)
|
function physics.keyreleased_debug(key, _)
|
||||||
if key == "kp4" then
|
if key == "kp4" then
|
||||||
player.x = player.x -1
|
player.x = player.x -1
|
||||||
|
@ -17,30 +20,30 @@ end
|
||||||
|
|
||||||
|
|
||||||
function physics.remove_solid(x, y, sizex, sizey)
|
function physics.remove_solid(x, y, sizex, sizey)
|
||||||
assert_num("physics.remove_solid", 4, x, y, sizex, sizey)
|
asserts.number("physics.remove_solid", 4, x, y, sizex, sizey)
|
||||||
for coord_x=x, x + sizex-1 do
|
for coord_x=x, x + sizex-1 do
|
||||||
for coord_y=y, y + sizey-1 do
|
for coord_y=y, y + sizey-1 do
|
||||||
gameloop.buffer.physics[coord_x*gameloop.world.x+coord_y] = nil
|
levelloop.Canvas.physics[coord_x*levelloop.world.x+coord_y] = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function physics.draw_solid(x, y, sizex, sizey)
|
function physics.draw_solid(x, y, sizex, sizey)
|
||||||
assert_num("physics.draw_solid", 4, x, y, sizex, sizey)
|
asserts.number("physics.draw_solid", 4, x, y, sizex, sizey)
|
||||||
for coord_x=x, x + sizex-1 do
|
for coord_x=x, x + sizex-1 do
|
||||||
for coord_y=y, y + sizey-1 do
|
for coord_y=y, y + sizey-1 do
|
||||||
gameloop.buffer.physics[coord_x*gameloop.world.x+coord_y] = {0, 0, 0, true }
|
levelloop.Canvas.physics[coord_x*levelloop.world.x+coord_y] = {0, 0, 0, true }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function draw_colision(x, y, x2, y2, pixelx, pixely)
|
function draw_colision(x, y, x2, y2, pixelx, pixely)
|
||||||
color_push()
|
utils.color_push()
|
||||||
love.graphics.setLineWidth(1)
|
love.graphics.setLineWidth(1)
|
||||||
love.graphics.setDefaultFilter("nearest", "nearest", 0)
|
love.graphics.setDefaultFilter("nearest", "nearest", 0)
|
||||||
love.graphics.setCanvas(gameloop.buffer.pixel.dbg2)
|
love.graphics.setCanvas(levelloop.Canvas.dbg2)
|
||||||
if pixelx then
|
if pixelx then
|
||||||
love.graphics.setColor(.6, 0, .6, 1)
|
love.graphics.setColor(.6, 0, .6, 1)
|
||||||
love.graphics.rectangle("fill", x, y, x2, y2)
|
love.graphics.rectangle("fill", x, y, x2, y2)
|
||||||
|
@ -51,15 +54,15 @@ function draw_colision(x, y, x2, y2, pixelx, pixely)
|
||||||
love.graphics.rectangle("fill", x, y, x2, y2)
|
love.graphics.rectangle("fill", x, y, x2, y2)
|
||||||
end
|
end
|
||||||
love.graphics.setCanvas()
|
love.graphics.setCanvas()
|
||||||
color_pop()
|
utils.color_pop()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function physics.is_blocked_x(x, y, sizex)
|
function physics.is_blocked_x(x, y, sizex)
|
||||||
x, y = math.floor(x), math.floor(y)
|
x, y = math.floor(x), math.floor(y)
|
||||||
assert_num("physics.is_blocked_x", 3, x, y, sizex)
|
asserts.number("physics.is_blocked_x", 3, x, y, sizex)
|
||||||
for coord_x=x ,x + sizex -1 do
|
for coord_x=x ,x + sizex -1 do
|
||||||
if gameloop.buffer.physics[coord_x * gameloop.world.x+y] and gameloop.buffer.physics[coord_x*gameloop.world.x+y][4] then
|
if levelloop.Canvas.physics[coord_x * levelloop.world.x+y] and levelloop.Canvas.physics[coord_x*levelloop.world.x+y][4] then
|
||||||
if PHYSICS_DEBUG then draw_colision(x, y, sizex, 1, coord_x, y) end
|
if PHYSICS_DEBUG then draw_colision(x, y, sizex, 1, coord_x, y) end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
@ -71,9 +74,9 @@ end
|
||||||
|
|
||||||
function physics.is_blocked_y(x, y, sizey)
|
function physics.is_blocked_y(x, y, sizey)
|
||||||
x, y = math.floor(x), math.floor(y)
|
x, y = math.floor(x), math.floor(y)
|
||||||
assert_num("physics.is_blocked_y", 3, x, y, sizey)
|
asserts.number("physics.is_blocked_y", 3, x, y, sizey)
|
||||||
for coord_y=y, y + sizey -1 do
|
for coord_y=y, y + sizey -1 do
|
||||||
if gameloop.buffer.physics[x*gameloop.world.x+coord_y] and gameloop.buffer.physics[x*gameloop.world.x+coord_y][4] then
|
if levelloop.Canvas.physics[x*levelloop.world.x+coord_y] and levelloop.Canvas.physics[x*levelloop.world.x+coord_y][4] then
|
||||||
if PHYSICS_DEBUG then draw_colision(x, y, 1, sizey, x, coord_y) end
|
if PHYSICS_DEBUG then draw_colision(x, y, 1, sizey, x, coord_y) end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
@ -85,16 +88,14 @@ end
|
||||||
|
|
||||||
function physics.keypressed( key, code, isrepeat)
|
function physics.keypressed( key, code, isrepeat)
|
||||||
if isrepeat then return end
|
if isrepeat then return end
|
||||||
|
|
||||||
if camera.target == 0 then return end
|
if camera.target == 0 then return end
|
||||||
player = players[camera.target]
|
local player = participants[levelloop.clientID]
|
||||||
if key == "p" then
|
if key == "p" then
|
||||||
player.flying = not player.flying
|
player.flying = not player.flying
|
||||||
|
levelloop.remoteCall("moveFlying", player.flying)
|
||||||
if player.flying then
|
if player.flying then
|
||||||
player.speed.y = 0
|
player.speed.y = 0
|
||||||
gameloop.networkSend(unit("moveFlying", "true"))
|
|
||||||
else
|
|
||||||
gameloop.networkSend(unit("moveFlying", "false"))
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -102,7 +103,7 @@ end
|
||||||
|
|
||||||
function physics.input(dt)
|
function physics.input(dt)
|
||||||
if camera.target == 0 then return end
|
if camera.target == 0 then return end
|
||||||
player = players[camera.target]
|
local player = participants[levelloop.clientID]
|
||||||
local speedModified = dt * 20
|
local speedModified = dt * 20
|
||||||
if love.keyboard.isDown("rshift") or love.keyboard.isDown("lshift")
|
if love.keyboard.isDown("rshift") or love.keyboard.isDown("lshift")
|
||||||
then
|
then
|
||||||
|
@ -124,7 +125,7 @@ function physics.input(dt)
|
||||||
local posx = player.x + player.avatar:getWidth()
|
local posx = player.x + player.avatar:getWidth()
|
||||||
local posy = player.y + player.avatar:getHeight()
|
local posy = player.y + player.avatar:getHeight()
|
||||||
if physics.is_blocked_x(posx - player.avatar:getWidth(), posy, player.avatar:getWidth()) or
|
if physics.is_blocked_x(posx - player.avatar:getWidth(), posy, player.avatar:getWidth()) or
|
||||||
player.y == gameloop.world.y - player.avatar:getHeight() then
|
player.y == levelloop.world.y - player.avatar:getHeight() then
|
||||||
player.speed.y = -25
|
player.speed.y = -25
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -132,16 +133,16 @@ end
|
||||||
|
|
||||||
|
|
||||||
function physics.cap_world(width, height, newx, newy, new_sx, new_sy)
|
function physics.cap_world(width, height, newx, newy, new_sx, new_sy)
|
||||||
if newx > gameloop.world.x - width then
|
if newx > levelloop.world.x - width then
|
||||||
newx = gameloop.world.x - width
|
newx = levelloop.world.x - width
|
||||||
new_sx = 0
|
new_sx = 0
|
||||||
end
|
end
|
||||||
if newx < 0 then
|
if newx < 0 then
|
||||||
newx = 0
|
newx = 0
|
||||||
new_sx = 0
|
new_sx = 0
|
||||||
end
|
end
|
||||||
if newy > gameloop.world.y - height then
|
if newy > levelloop.world.y - height then
|
||||||
newy = gameloop.world.y - height
|
newy = levelloop.world.y - height
|
||||||
new_sy = 0
|
new_sy = 0
|
||||||
end
|
end
|
||||||
if newy < 0 then
|
if newy < 0 then
|
||||||
|
@ -151,129 +152,123 @@ function physics.cap_world(width, height, newx, newy, new_sx, new_sy)
|
||||||
return newx, newy, new_sx, new_sy
|
return newx, newy, new_sx, new_sy
|
||||||
end
|
end
|
||||||
|
|
||||||
local SKIP = false
|
|
||||||
function physics.update(dt)
|
|
||||||
SKIP = false
|
|
||||||
for i, player in pairs(players) do
|
|
||||||
assert(player.x and player.y and player.speed.y and player.speed.x, "Player " .. i .. " has an invalid position!")
|
|
||||||
|
|
||||||
--friction
|
function physics.updatePlayer(dt, player, localplayer)
|
||||||
local new_sx = player.speed.x - player.speed.x / 16
|
local new_sx, new_sy
|
||||||
local new_sy
|
if player.flying then
|
||||||
if player.flying then
|
new_sy = player.speed.y - player.speed.y / 6
|
||||||
new_sy = player.speed.y - player.speed.y / 16
|
new_sx = player.speed.x - player.speed.x / 6
|
||||||
else
|
else
|
||||||
new_sy = player.speed.y - player.speed.y / 6
|
new_sx = player.speed.x - player.speed.x / 16
|
||||||
|
new_sy = player.speed.y - player.speed.y / 16
|
||||||
|
end
|
||||||
|
|
||||||
|
if not new_sx then new_sx = 0 end
|
||||||
|
if not new_sy then new_sy = 0 end
|
||||||
|
if (new_sx < 0.001 and new_sx > -0.001) and (new_sy < 0.001 and new_sy > -0.001) then return end
|
||||||
|
--falling
|
||||||
|
if not player.flying then
|
||||||
|
new_sy = new_sy +dt * 100
|
||||||
|
end
|
||||||
|
|
||||||
|
--new positions
|
||||||
|
local futureX = player.x + player.speed.x
|
||||||
|
local futureY = player.y + player.speed.y
|
||||||
|
assert(futureX and futureY)
|
||||||
|
|
||||||
|
-- cap boundaries
|
||||||
|
futureX, futureY, new_sx, new_sy = physics.cap_world(player.avatar:getWidth(), player.avatar:getHeight(), futureX, futureY, new_sx, new_sy)
|
||||||
|
|
||||||
|
local granularity = math.sqrt(math.abs(player.speed.x * player.speed.x)+ math.abs(player.speed.y * player.speed.y)) + 1
|
||||||
|
local stepsizeX = player.speed.x / granularity
|
||||||
|
local stepsizeY = player.speed.y / granularity
|
||||||
|
assert(granularity and stepsizeX and stepsizeY)
|
||||||
|
|
||||||
|
local currentX = player.x
|
||||||
|
local currentY = player.y
|
||||||
|
if granularity < 1 then granularity = 1 end
|
||||||
|
|
||||||
|
|
||||||
|
if player.flying then
|
||||||
|
player.x, player.y = futureX, futureY
|
||||||
|
|
||||||
|
player.speed.x = new_sx
|
||||||
|
player.speed.y = new_sy
|
||||||
|
if localplayer then
|
||||||
|
physics.send_update(player, dt)
|
||||||
end
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local blocked_x, blocked_y = false, false
|
||||||
|
for iteration=1, granularity do
|
||||||
|
intermediateX = currentX + (stepsizeX * iteration)
|
||||||
|
intermediateY = currentY + (stepsizeY * iteration)
|
||||||
|
|
||||||
if not new_sx then new_sx = 0 end
|
if intermediateY > futureY then -- UP
|
||||||
if not new_sy then new_sy = 0 end
|
if physics.is_blocked_x(intermediateX, intermediateY, player.avatar:getWidth()) then
|
||||||
--falling
|
intermediateY = currentY
|
||||||
if not player.flying then
|
blocked_y = true
|
||||||
new_sy = new_sy +dt * 100
|
|
||||||
end
|
|
||||||
|
|
||||||
--new positions
|
|
||||||
local futureX = player.x + player.speed.x
|
|
||||||
local futureY = player.y + player.speed.y
|
|
||||||
assert(futureX and futureY)
|
|
||||||
|
|
||||||
-- cap boundaries
|
|
||||||
futureX, futureY, new_sx, new_sy = physics.cap_world(player.avatar:getWidth(), player.avatar:getHeight(), futureX, futureY, new_sx, new_sy)
|
|
||||||
|
|
||||||
local granularity = math.sqrt(math.abs(player.speed.x * player.speed.x)+ math.abs(player.speed.y * player.speed.y)) + 1
|
|
||||||
local stepsizeX = player.speed.x / granularity
|
|
||||||
local stepsizeY = player.speed.y / granularity
|
|
||||||
assert(granularity and stepsizeX and stepsizeY)
|
|
||||||
|
|
||||||
local currentX = player.x
|
|
||||||
local currentY = player.y
|
|
||||||
if not granularity or not stepsizeX and not stepSizeY then SKIP = true end
|
|
||||||
if granularity < 1 then granularity = 1 end
|
|
||||||
assert(currentX and currentY)
|
|
||||||
|
|
||||||
if player.flying then -- skip physics
|
|
||||||
player.x, player.y = futureX, futureY
|
|
||||||
|
|
||||||
player.speed.x = new_sx
|
|
||||||
player.speed.y = new_sy
|
|
||||||
if camera.target == i then -- FIXME: uses camera.target
|
|
||||||
physics.send_update(player, dt)
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if intermediateY < futureY then --DOWN
|
||||||
|
local lowerY = intermediateY + player.avatar:getHeight() -1
|
||||||
|
if physics.is_blocked_x(intermediateX, lowerY, player.avatar:getWidth()) then
|
||||||
|
intermediateY = currentY
|
||||||
|
blocked_y = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if intermediateX > futureX then -- ONLY LEFT
|
||||||
|
if physics.is_blocked_y(intermediateX, intermediateY, player.avatar:getHeight()) then
|
||||||
|
blocked_x = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if intermediateX < futureX then -- ONLY RIGHT
|
||||||
|
local lowerX = intermediateX + player.avatar:getWidth() -1
|
||||||
|
if physics.is_blocked_y(lowerX, intermediateY, player.avatar:getHeight()) then
|
||||||
|
blocked_x = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
intermediateX, intermediateY, new_sx, new_sy = physics.cap_world(player.avatar:getWidth(), player.avatar:getHeight(), intermediateX, intermediateY, new_sx, new_sy)
|
||||||
|
if blocked_x and not blocked_y then -- X blocked
|
||||||
|
player.y = intermediateY
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if blocked_y and not blocked_x then
|
||||||
|
player.x = intermediateX
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
do -- SCOPE
|
if blocked_x and blocked_y then
|
||||||
local blocked_x, blocked_y = false, false
|
return
|
||||||
if not SKIP then
|
|
||||||
for iteration=1, granularity do
|
|
||||||
intermediateX = currentX + (stepsizeX * iteration)
|
|
||||||
intermediateY = currentY + (stepsizeY * iteration)
|
|
||||||
|
|
||||||
|
|
||||||
if intermediateY > futureY then -- UP
|
|
||||||
if physics.is_blocked_x(intermediateX, intermediateY, player.avatar:getWidth()) then
|
|
||||||
intermediateY = currentY
|
|
||||||
blocked_y = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if intermediateY < futureY then --DOWN
|
|
||||||
local lowerY = intermediateY + player.avatar:getHeight() -1
|
|
||||||
if physics.is_blocked_x(intermediateX, lowerY, player.avatar:getWidth()) then
|
|
||||||
intermediateY = currentY
|
|
||||||
blocked_y = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if intermediateX > futureX then -- ONLY LEFT
|
|
||||||
if physics.is_blocked_y(intermediateX, intermediateY, player.avatar:getHeight()) then
|
|
||||||
blocked_x = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if intermediateX < futureX then -- ONLY RIGHT
|
|
||||||
local lowerX = intermediateX + player.avatar:getWidth() -1
|
|
||||||
if physics.is_blocked_y(lowerX, intermediateY, player.avatar:getHeight()) then
|
|
||||||
blocked_x = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
intermediateX, intermediateY, new_sx, new_sy = physics.cap_world(player.avatar:getWidth(), player.avatar:getHeight(), intermediateX, intermediateY, new_sx, new_sy)
|
|
||||||
if blocked_x and not blocked_y then -- X blocked
|
|
||||||
--new_sy = 0
|
|
||||||
player.y = intermediateY
|
|
||||||
end
|
|
||||||
|
|
||||||
if blocked_y and not blocked_x then
|
|
||||||
--new_sx = 0
|
|
||||||
player.x = intermediateX
|
|
||||||
end
|
|
||||||
|
|
||||||
if blocked_x and blocked_y then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
|
|
||||||
if not blocked_x and not blocked_y then
|
|
||||||
player.x = intermediateX
|
|
||||||
player.y = intermediateY
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end -- SCOPE
|
|
||||||
end
|
end
|
||||||
-- cap low speed
|
|
||||||
if new_sx > -.1 and new_sx < .1 then
|
if not blocked_x and not blocked_y then
|
||||||
new_sx = 0
|
player.x = intermediateX
|
||||||
end
|
player.y = intermediateY
|
||||||
if new_sy > -.1 and new_sy < .1 then
|
|
||||||
new_sy = 0
|
|
||||||
end
|
|
||||||
player.speed.x = new_sx
|
|
||||||
player.speed.y = new_sy
|
|
||||||
if i == camera.target then -- FIXME: uses camera.target
|
|
||||||
physics.send_update(player, dt)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
player.speed.x = new_sx
|
||||||
|
player.speed.y = new_sy
|
||||||
|
if localplayer then
|
||||||
|
physics.send_update(player, dt)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function physics.update(dt)
|
||||||
|
for i, player in pairs(participants) do
|
||||||
|
assert(player.x and player.y and player.speed.y and player.speed.x, "Player has an invalid position!")
|
||||||
|
physics.updatePlayer(dt, player, i == levelloop.clientID)
|
||||||
|
end
|
||||||
|
local player = participants[levelloop.clientID]
|
||||||
|
ui.updateSpeed(player.speed.x, player.speed.y, player.x, player.y)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -282,7 +277,7 @@ last.packet, last.x, last.y, last.speedx, last.speedy = 0, 0, 0, 0, 0
|
||||||
function physics.send_update(player, dt)
|
function physics.send_update(player, dt)
|
||||||
if last.packet > 0.03 then
|
if last.packet > 0.03 then
|
||||||
if last.x ~= player.x or last.y ~= player.y then
|
if last.x ~= player.x or last.y ~= player.y then
|
||||||
gameloop.networkSend(unit("moveUpdate", player.x, player.y, player.speed.x, player.speed.y))
|
levelloop.remoteCall("moveUpdate", player.x, player.y, player.speed.x, player.speed.y)
|
||||||
last.packet = 0
|
last.packet = 0
|
||||||
last.x, last.y = player.x, player.y
|
last.x, last.y = player.x, player.y
|
||||||
last.speedx, last.speedy = player.speedx, player.speedy
|
last.speedx, last.speedy = player.speedx, player.speedy
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
local playerfuncs = {}
|
local playerfuncs = {}
|
||||||
playerfuncs.newPlayer = function(arg)
|
playerfuncs.newPlayer = function(arg)
|
||||||
|
assert(arg.avatar)
|
||||||
|
assert(arg.name)
|
||||||
local playerTable = {}
|
local playerTable = {}
|
||||||
playerTable.avatar = love.graphics.newImage("textures/player/andysphinx/personb.png")
|
playerTable.avatar = arg.avatar
|
||||||
|
playerTable.name = arg.name
|
||||||
playerTable.speed = {}
|
playerTable.speed = {}
|
||||||
playerTable.speed.x = 0
|
playerTable.speed.x = 0
|
||||||
playerTable.speed.y = 0
|
playerTable.speed.y = 0
|
||||||
playerTable.x = 16
|
playerTable.x = 16
|
||||||
playerTable.y = 16
|
playerTable.y = 16
|
||||||
playerTable.flying = false
|
playerTable.flying = false
|
||||||
return playerTable
|
return playerTable
|
||||||
|
|
395
lua/ui.lua
|
@ -1,40 +1,83 @@
|
||||||
ui = {}
|
ui = {}
|
||||||
ui.textureTree = nil
|
ui.textureTree = nil
|
||||||
if love.filesystem.getInfo("fonts/font.ttf") then
|
|
||||||
ui.smallFont = love.graphics.newFont("fonts/font.ttf", 14)
|
|
||||||
ui.font = love.graphics.newFont("fonts/font.ttf", 20)
|
|
||||||
ui.bigFont = love.graphics.newFont("fonts/font.ttf", 45)
|
|
||||||
else
|
|
||||||
ui.smallFont = love.graphics.getFont(14)
|
|
||||||
ui.font = love.graphics.getFont(20)
|
|
||||||
ui.bigFont = love.graphics.getFont(45)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
local layout = require("lua.layout")
|
||||||
|
local utils = require("shared.utils")
|
||||||
|
local fonts = require("shared.fonts")
|
||||||
|
|
||||||
|
ui.speedText = {
|
||||||
|
love.graphics.newText(fonts.normalFont, "0000"),
|
||||||
|
love.graphics.newText(fonts.normalFont, "0000"),
|
||||||
|
love.graphics.newText(fonts.normalFont, "0000"),
|
||||||
|
love.graphics.newText(fonts.normalFont, "0000")
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.sidebarScale = 1
|
||||||
|
ui.sidebarWidth = 13 * 16
|
||||||
function ui.update_scale(p_scale)
|
function ui.update_scale(p_scale)
|
||||||
scale = p_scale
|
scale = p_scale
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function ui.updateSpeed(xspeed, yspeed, xpos, ypos)
|
||||||
|
ui.speedText = {
|
||||||
|
love.graphics.newText(fonts.normalFont, "XSpeed: " .. xspeed),
|
||||||
|
love.graphics.newText(fonts.normalFont, "YSpeed: " .. yspeed),
|
||||||
|
love.graphics.newText(fonts.normalFont, "X: " .. xpos),
|
||||||
|
love.graphics.newText(fonts.normalFont, "X: " .. ypos)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
ui.helptext = [[[P] toggle fly
|
ui.helptext = [[[P] toggle fly
|
||||||
[A/D] Move
|
{A/D} Move
|
||||||
[Hold Shift] Move faster
|
{Shift} Move faster
|
||||||
[Space] Jump
|
[Space] Jump
|
||||||
[A/S/DW] Move (flying)
|
{A/S/DW} Move (flying)
|
||||||
[C] Clear canvas
|
[C] Clear canvas
|
||||||
[F] Fill foreground
|
[F] Fill foreground
|
||||||
[G] Fill background
|
[G] Fill background
|
||||||
[Mouse1] Draw
|
{Mouse1} Draw
|
||||||
[Mouse2] Delete
|
{Mouse2} Delete
|
||||||
[Hold ctrl] Draw on Background
|
{ctrl} Background draw
|
||||||
[+/-/PGUP/PGDWN] Zoom canvas
|
[PGUP/PGDWN] Zoom canvas
|
||||||
[Cursor keys] Move camera
|
{Cursor keys} Move camera
|
||||||
]]
|
]]
|
||||||
|
|
||||||
|
local menuVisible = false
|
||||||
local textEnabled = false
|
local textEnabled = false
|
||||||
local textEntry = ""
|
local textEntry = ""
|
||||||
function ui.keyreleased(key, _)
|
function ui.keyreleased(key, _)
|
||||||
|
if key == "escape" then
|
||||||
|
menuVisible = not menuVisible
|
||||||
|
if menuVisible then
|
||||||
|
love.visible = function(visible)
|
||||||
|
if visible then
|
||||||
|
love.update = function(dt)
|
||||||
|
levelloop.networkSync(dt)
|
||||||
|
physics.update(dt)
|
||||||
|
camera.update(dt)
|
||||||
|
love.timer.sleep((1/60) -dt)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
love.update = function(dt)
|
||||||
|
levelloop.networkSync(dt)
|
||||||
|
physics.update(dt)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
ui.drawMenu(window.x, window.y)
|
||||||
|
else
|
||||||
|
love.visible = function(visible)
|
||||||
|
if visible then
|
||||||
|
love.update = levelloop.normalVisible
|
||||||
|
else
|
||||||
|
love.update = levelloop.reducedVisible
|
||||||
|
end
|
||||||
|
end
|
||||||
|
ui.draw(window.x, window.y)
|
||||||
|
end
|
||||||
|
love.visible(true)
|
||||||
|
return
|
||||||
|
end
|
||||||
if key == "return" and not love.keyboard.isDown("lalt") then
|
if key == "return" and not love.keyboard.isDown("lalt") then
|
||||||
textEnabled = true
|
textEnabled = true
|
||||||
love.keyboard.setTextInput(true)
|
love.keyboard.setTextInput(true)
|
||||||
|
@ -44,14 +87,14 @@ function ui.keyreleased(key, _)
|
||||||
love.visible = function(visible)
|
love.visible = function(visible)
|
||||||
if visible then
|
if visible then
|
||||||
love.update = function(dt)
|
love.update = function(dt)
|
||||||
gameloop.networkSync(dt)
|
levelloop.networkSync(dt)
|
||||||
drawing.input(dt)
|
drawing.input(dt)
|
||||||
physics.update(dt)
|
physics.update(dt)
|
||||||
camera.update(dt)
|
camera.update(dt)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
love.update = function(dt)
|
love.update = function(dt)
|
||||||
gameloop.networkSync(dt)
|
levelloop.networkSync(dt)
|
||||||
physics.update(dt)
|
physics.update(dt)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -61,14 +104,14 @@ function ui.keyreleased(key, _)
|
||||||
if key == "return" and not love.keyboard.isDown("lalt") then
|
if key == "return" and not love.keyboard.isDown("lalt") then
|
||||||
textEnabled = false
|
textEnabled = false
|
||||||
if textEntry ~= "" then
|
if textEntry ~= "" then
|
||||||
gameloop.networkSend(unit("chatMessage", textEntry))
|
levelloop.remoteCall("chatMessage", textEntry)
|
||||||
textEntry = ""
|
textEntry = ""
|
||||||
end
|
end
|
||||||
ui.draw(window.x, window.y)
|
ui.draw(window.x, window.y)
|
||||||
love.keyboard.setTextInput(false)
|
love.keyboard.setTextInput(false)
|
||||||
love.keyreleased = gameloop.keyreleased
|
love.keyreleased = levelloop.keyreleased
|
||||||
love.keypressed = gameloop.keypressed
|
love.keypressed = levelloop.keypressed
|
||||||
love.update = gameloop.normalVisible
|
love.update = levelloop.normalVisible
|
||||||
elseif key == "backspace" then
|
elseif key == "backspace" then
|
||||||
textEntry = string.sub(textEntry, 0, #textEntry -1)
|
textEntry = string.sub(textEntry, 0, #textEntry -1)
|
||||||
ui.draw(window.x, window.y)
|
ui.draw(window.x, window.y)
|
||||||
|
@ -76,8 +119,8 @@ function ui.keyreleased(key, _)
|
||||||
end
|
end
|
||||||
love.keypressed = function(key, _) return end
|
love.keypressed = function(key, _) return end
|
||||||
else
|
else
|
||||||
love.keyreleased = gameloop.keyreleased
|
love.keyreleased = levelloop.keyreleased
|
||||||
love.keypressed = gameloop.keypressed
|
love.keypressed = levelloop.keypressed
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -97,11 +140,11 @@ function ui.chatEntry()
|
||||||
fill = "fill",
|
fill = "fill",
|
||||||
color = { r = .1, g = .1, b = .1, .7 }
|
color = { r = .1, g = .1, b = .1, .7 }
|
||||||
},
|
},
|
||||||
{name = "frame",
|
{name = "frame",
|
||||||
{name = "label",
|
{name = "label",
|
||||||
text = passedEntry
|
text = passedEntry
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return chatEntry
|
return chatEntry
|
||||||
end
|
end
|
||||||
|
@ -125,22 +168,22 @@ function ui.addChatEntry(user, message)
|
||||||
end
|
end
|
||||||
local entry = {name = "rotate",
|
local entry = {name = "rotate",
|
||||||
rotation = 0, -- OKAY DIEGO >:(
|
rotation = 0, -- OKAY DIEGO >:(
|
||||||
{name = "copySize",
|
{name = "copySize",
|
||||||
{name = "rect",
|
{name = "rect",
|
||||||
fill = "fill",
|
fill = "fill",
|
||||||
color = { r = .1, g = .1, b = .1, a = .6 }
|
color = { r = .1, g = .1, b = .1, a = .6 }
|
||||||
|
},
|
||||||
|
{name = "vertical",
|
||||||
|
{name = "label",
|
||||||
|
text = chatText,
|
||||||
|
font = fonts.smallFont
|
||||||
},
|
},
|
||||||
{name = "vertical",
|
{name = "spacer",
|
||||||
{name = "label",
|
height = 2
|
||||||
text = chatText,
|
|
||||||
font = ui.smallFont
|
|
||||||
},
|
|
||||||
{name = "spacer",
|
|
||||||
height = 2
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
table.insert(chatentries, entry)
|
table.insert(chatentries, entry)
|
||||||
ui.draw(window.x, window.y)
|
ui.draw(window.x, window.y)
|
||||||
end
|
end
|
||||||
|
@ -153,18 +196,16 @@ function ui.loadTextures(directory, container)
|
||||||
for i, file in pairs(entries) do
|
for i, file in pairs(entries) do
|
||||||
local path = directory .."/".. file
|
local path = directory .."/".. file
|
||||||
local entry = love.filesystem.getInfo(path)
|
local entry = love.filesystem.getInfo(path)
|
||||||
if entry.type == "directory" then
|
if entry.type == "directory" and file ~= ".git" then
|
||||||
container[file] = {}
|
container[file] = {}
|
||||||
container[file] = ui.loadTextures(path, container[file])
|
container[file] = ui.loadTextures(path, container[file])
|
||||||
elseif entry.type == "file" or entry.type == "symlink" then
|
elseif entry.type == "file" or entry.type == "symlink" then
|
||||||
if file ~= "license.txt" then
|
local status, imageData = pcall(love.image.newImageData, path);
|
||||||
local imageData = love.image.newImageData(path)
|
if status then
|
||||||
local hash = love.data.hash("sha512", imageData:getString())
|
local hash = love.data.hash("sha512", imageData:getString())
|
||||||
gameloop.textures[hash] = {data = imageData}
|
levelloop.textures[hash] = {data = imageData}
|
||||||
container[file] = hash
|
container[file] = hash
|
||||||
end
|
end
|
||||||
elseif entry.type == "other" then
|
|
||||||
error("Can't figure out what " .. path .. " is!")
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return container
|
return container
|
||||||
|
@ -172,31 +213,29 @@ end
|
||||||
|
|
||||||
|
|
||||||
function textureEntry(hash)
|
function textureEntry(hash)
|
||||||
local texture = gameloop.textures[hash]
|
local texture = levelloop.textures[hash]
|
||||||
assert(texture, "No texture found for textureentry?")
|
assert(texture, "No texture found for textureentry?")
|
||||||
assert(texture.data, "No imagedata found for textureentry?")
|
assert(texture.data, "No imagedata found for textureentry?")
|
||||||
if not texture.image then
|
if not texture.image then
|
||||||
texture.image = love.graphics.newImage(texture.data)
|
texture.image = love.graphics.newImage(texture.data)
|
||||||
end
|
end
|
||||||
local width = texture.image:getWidth()
|
local width = texture.image:getWidth()
|
||||||
local scale = 1
|
local height = texture.image:getHeight()
|
||||||
if width <= 16 then scale = 2 end
|
if drawing and drawing.cursorHash and hash == drawing.cursorHash then
|
||||||
if width <= 8 then scale = 4 end
|
return {name = "overlayRect",
|
||||||
if drawing and drawing.cursor and hash == drawing.cursor then
|
|
||||||
return{name = "overlayRect",
|
|
||||||
fill = "line",
|
fill = "line",
|
||||||
{name = "drawHash",
|
{name = "drawHash",
|
||||||
hash = hash,
|
hash = hash,
|
||||||
scale = scale
|
scale = ui.sidebarScale
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return{name = "cursorSelect",
|
return {name = "cursorSelect", uiSelect = "GameUI",
|
||||||
identifier = hash,
|
identifier = hash,
|
||||||
kind = "picker",
|
kind = "picker",
|
||||||
{name = "drawHash",
|
{name = "drawHash",
|
||||||
hash = hash,
|
hash = hash,
|
||||||
scale = scale
|
scale = ui.sidebarScale
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
@ -204,52 +243,54 @@ end
|
||||||
|
|
||||||
|
|
||||||
function textureRun(entryTable, width)
|
function textureRun(entryTable, width)
|
||||||
local textures = gameloop.textures
|
local textures = levelloop.textures
|
||||||
local superEntry = {name = "vertical"}
|
local entries = {name = "naiveGrid", width = width - 20}
|
||||||
local entries = {name = "horizontal"}
|
|
||||||
local entryTableSorted = {}
|
local entryTableSorted = {}
|
||||||
|
|
||||||
|
-- Sort the entries alphapetically (or well, probably more... "byte compared") :)
|
||||||
|
-- This implies that in <author>/<packname>/<fileName> the fileName controls where
|
||||||
|
-- the texture lands in the block drawer, smaller lands further left.
|
||||||
for iter, entry in pairs(entryTable) do
|
for iter, entry in pairs(entryTable) do
|
||||||
table.insert(entryTableSorted, {name = iter, entry = entry})
|
table.insert(entryTableSorted, {name = iter, entry = entry})
|
||||||
end
|
end
|
||||||
table.sort(entryTableSorted, function(k1, k2) return k1.name < k2.name end)
|
table.sort(entryTableSorted, function(k1, k2) return k1.name < k2.name end)
|
||||||
|
|
||||||
for iter, entry in pairs(entryTableSorted) do
|
for iter, entry in pairs(entryTableSorted) do
|
||||||
if #entries > 2 then
|
if not textures[entry.entry].image then
|
||||||
local NextX, nextY = layout.handle(entries, 0, 0, true)
|
textures[entry.entry].image = love.graphics.newImage(textures[entry.entry].data)
|
||||||
if not textures[entry.entry] then textures[entry.entry] = {} end
|
|
||||||
if not textures[entry.entry].image then
|
|
||||||
textures[entry.entry].image = love.graphics.newImage(textures[entry.entry].data)
|
|
||||||
end
|
|
||||||
if textures[entry.entry].image:getWidth() + NextX < width then
|
|
||||||
table.insert(entries, textureEntry(entry.entry))
|
|
||||||
else
|
|
||||||
table.insert(superEntry, entries)
|
|
||||||
entries = {name = "horizontal"}
|
|
||||||
end
|
|
||||||
else
|
|
||||||
table.insert(entries, textureEntry(entry.entry))
|
|
||||||
end
|
end
|
||||||
|
table.insert(entries, textureEntry(entry.entry))
|
||||||
end
|
end
|
||||||
if #entries > 1 then table.insert(superEntry, entries) end
|
return entries
|
||||||
if #superEntry > 1 then
|
|
||||||
return superEntry
|
|
||||||
else
|
|
||||||
return entries
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function ui.blockDrawer(textureTree, w, h, container)
|
function ui.blockDrawer(textureTree, w, h, container)
|
||||||
if not textureTree then return {name = "label", text = "Loading textures..."} end
|
if not textureTree then return {name = "label", text = "Loading textures..."} end
|
||||||
|
local pointSize = 8
|
||||||
|
if ui.sidebarScale == 2 then pointSize = 16 end
|
||||||
if not container then container = {name="vertical",
|
if not container then container = {name="vertical",
|
||||||
{name = "colorPicker", kind = "colorpicker", granularity = 0.25}}
|
{name = "colorPicker", kind = "colorpicker",
|
||||||
|
granularity = 0.25, pointSize = pointSize},
|
||||||
|
{name = "spacer", height = 5 * ui.sidebarScale},
|
||||||
|
{name = "bwColorPicker", kind = "bwColorPicker",
|
||||||
|
pointSize = pointSize}}
|
||||||
end
|
end
|
||||||
|
local textureTreeSorted = {}
|
||||||
for author, tree in pairs(textureTree) do
|
for author, tree in pairs(textureTree) do
|
||||||
for pack, subTree in pairs(tree) do
|
table.insert(textureTreeSorted, {author = author, tree = tree})
|
||||||
|
end
|
||||||
|
table.sort(textureTreeSorted, function(k1, k2) return k1.author < k2.author end)
|
||||||
|
for author, tree in pairs(textureTreeSorted) do
|
||||||
|
for pack, subTree in pairs(tree.tree) do
|
||||||
container.name = "vertical"
|
container.name = "vertical"
|
||||||
table.insert(container, {name = "label", text = pack})
|
table.insert(container, {name = "label", text = pack})
|
||||||
table.insert(container,
|
table.insert(container,
|
||||||
{name = "color",
|
{name = "color",
|
||||||
color = drawing.color,
|
color = drawing.color or {1, 1, 1},
|
||||||
|
--[[ HACK! remove the 1, 1, 1.
|
||||||
|
drawing.color should be set!
|
||||||
|
]]--
|
||||||
textureRun(subTree, w)
|
textureRun(subTree, w)
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
@ -258,107 +299,111 @@ function ui.blockDrawer(textureTree, w, h, container)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function ui.helpDrawer(w, h)
|
function ui.menuDrawer(w, h)
|
||||||
return {name = "label",
|
return {name = "horizontal",
|
||||||
text = ui.helptext
|
{name = "vertical",
|
||||||
}
|
{name = "button",
|
||||||
end
|
identifier = "save",
|
||||||
|
uiSelect = "Menu",
|
||||||
|
buttonColor = "greenTranslucent",
|
||||||
function ui.testDrawer(w, h)
|
text = "Save world"
|
||||||
local drawer = {name = "vertical"}
|
|
||||||
return drawer
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local selectedTab = "help"
|
|
||||||
function ui.tabDrawer(textureTree, w, h)
|
|
||||||
local labels
|
|
||||||
local helpTab = {name = "label",
|
|
||||||
text = ui.helptext
|
|
||||||
}
|
|
||||||
local hWidth, hHeight = layout.handle(helpTab, 0, 0, true)
|
|
||||||
local blockTab = ui.blockDrawer(textureTree, w, h)
|
|
||||||
local bWidth, bHeight = layout.handle(blockTab, 0, 0, true)
|
|
||||||
labels = {name = "horizontal",
|
|
||||||
{name = "cursorSelect",
|
|
||||||
identifier = "help",
|
|
||||||
kind = "tab",
|
|
||||||
{name = "label",
|
|
||||||
text = " [Help] "
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{name = "cursorSelect",
|
|
||||||
identifier = "blocks",
|
|
||||||
kind = "tab",
|
|
||||||
{name = "label",
|
|
||||||
text = " [Blocks] "
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{name = "cursorSelect",
|
{name = "spacer",
|
||||||
|
height = 10,
|
||||||
|
},
|
||||||
|
{name = "button",
|
||||||
identifier = "disconnect",
|
identifier = "disconnect",
|
||||||
kind = "button",
|
uiSelect = "Menu",
|
||||||
{name = "label",
|
buttonColor = "pinkTranslucent",
|
||||||
text = " Disconnect. "
|
text = "Disconnect"
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if selectedTab == "blocks" then
|
|
||||||
tab = blockTab
|
|
||||||
elseif selectedTab == "help" then
|
|
||||||
tab = helpTab
|
|
||||||
end
|
|
||||||
local drawer = {
|
|
||||||
name = "vertical",
|
|
||||||
{name = "copySize",
|
|
||||||
{name = "rect",
|
|
||||||
fill = "fill",
|
|
||||||
color = { r = .2, g = 0, b = .2 }
|
|
||||||
},
|
},
|
||||||
{name = "horizontal",
|
{name = "spacer",
|
||||||
labels,
|
height = 25,
|
||||||
{name = "spacer",
|
},
|
||||||
height = 40,
|
{name = "button",
|
||||||
},
|
identifier = "sideBarScaleTwo",
|
||||||
|
uiSelect = "Menu",
|
||||||
|
buttonColor = "grayTranslucent",
|
||||||
|
text = "Ui scale x2"
|
||||||
|
},
|
||||||
|
{name = "spacer",
|
||||||
|
height = 4,
|
||||||
|
},
|
||||||
|
{name = "button",
|
||||||
|
identifier = "sideBarScaleOne",
|
||||||
|
uiSelect = "Menu",
|
||||||
|
buttonColor = "grayTranslucent",
|
||||||
|
text = "Ui scale x1"
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{name = "copySize",
|
{name = "spacer",
|
||||||
{name = "rect",
|
width = 80,
|
||||||
fill = "fill",
|
},
|
||||||
color = { r = .1, g = .1, b = .1 }
|
{name = "label",
|
||||||
},
|
text = ui.helptext,
|
||||||
tab
|
font = fonts.normallFont
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return drawer, math.max(hWidth, bWidth)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function ui.draw(w, h)
|
function ui.draw(w, h)
|
||||||
window.x, window.y = w, h
|
window.x, window.y = w, h
|
||||||
color_push()
|
|
||||||
love.graphics.setCanvas(ui.buffer)
|
love.graphics.setCanvas(ui.buffer)
|
||||||
uiState = {}
|
layout.uiState.GameUI = {}
|
||||||
love.graphics.clear( )
|
love.graphics.clear( )
|
||||||
local sidebar, width = ui.tabDrawer(ui.textureTree, w, h)
|
local blockDrawer = ui.blockDrawer(ui.textureTree, w, h)
|
||||||
layout.handle(sidebar, window.x -width, 0)
|
blockMaxX, blockMaxY = layout.handle(blockDrawer, 0, 0, true)
|
||||||
ui.space = width
|
local blockDrawerAdjusted = ui.blockDrawer(ui.textureTree, blockMaxX, blockMaxY)
|
||||||
if textEnabled then
|
layout.handle(blockDrawerAdjusted, window.x - blockMaxX, 0)
|
||||||
local chatLayout = ui.chatEntry()
|
|
||||||
local sizex, sizey = layout.handle(chatLayout, 0, 0, true)
|
|
||||||
layout.handle(chatLayout, 0, window.y - sizey)
|
|
||||||
end
|
|
||||||
layout.handle(ui.chatlog(), 25, 90)
|
layout.handle(ui.chatlog(), 25, 90)
|
||||||
color_pop()
|
love.graphics.setCanvas()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function ui.drawMenu(w, h)
|
||||||
|
window.x, window.y = w, h
|
||||||
|
layout.uiState.Menu = {}
|
||||||
|
love.graphics.setCanvas(ui.buffer)
|
||||||
|
love.graphics.clear()
|
||||||
|
local menuDrawer = ui.menuDrawer(w, h)
|
||||||
|
menux, menuy = layout.handle(menuDrawer, 0, 0, true)
|
||||||
|
layout.handle(menuDrawer, (window.x - menux) /2, (window.y - menuy)/2)
|
||||||
love.graphics.setCanvas()
|
love.graphics.setCanvas()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function ui.mousepressed(mousex, mousey)
|
function ui.mousepressed(mousex, mousey)
|
||||||
local textures = gameloop.textures
|
local textures = levelloop.textures
|
||||||
for i, v in ipairs(uiState) do
|
if menuVisible then
|
||||||
if mousex >= v.x and mousex <= v.x2 then
|
for i, v in ipairs(layout.uiState.Menu) do
|
||||||
if mousey >= v.y and mousey <= v.y2 then
|
if mousex >= v.x and mousex <= v.x2 and --hit testing
|
||||||
|
mousey >= v.y and mousey <= v.y2 then
|
||||||
|
if v.kind == "button" then
|
||||||
|
if v.identifier == "save" then
|
||||||
|
levelloop.networkSend("saveWorld")
|
||||||
|
ui.keyreleased("escape")
|
||||||
|
elseif v.identifier == "disconnect" then
|
||||||
|
levelloop.remoteCall("playerLeave")
|
||||||
|
|
||||||
|
menu.init()
|
||||||
|
elseif v.identifier == "sideBarScaleTwo" then
|
||||||
|
ui.sidebarScale = 2
|
||||||
|
ui.draw(window.x, window.y)
|
||||||
|
ui.keyreleased("escape")
|
||||||
|
elseif v.identifier == "sideBarScaleOne" then
|
||||||
|
ui.sidebarScale = 1
|
||||||
|
ui.draw(window.x, window.y)
|
||||||
|
ui.keyreleased("escape")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return -- Return after the first hit...
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
for i, v in ipairs(layout.uiState.GameUI) do
|
||||||
|
if mousex >= v.x and mousex <= v.x2 and
|
||||||
|
mousey >= v.y and mousey <= v.y2 then
|
||||||
if v.kind == "picker" then
|
if v.kind == "picker" then
|
||||||
assert(v.identifier, "No identifier in picker!")
|
assert(v.identifier, "No identifier in picker!")
|
||||||
drawing.cursorHash = v.identifier
|
drawing.cursorHash = v.identifier
|
||||||
|
@ -371,19 +416,23 @@ function ui.mousepressed(mousex, mousey)
|
||||||
ui.draw(window.x, window.y)
|
ui.draw(window.x, window.y)
|
||||||
return
|
return
|
||||||
elseif v.kind == "colorpicker" then
|
elseif v.kind == "colorpicker" then
|
||||||
local scale = 1.0 / granularity
|
print("Picker hit :D")
|
||||||
local red = math.floor((mousey - v.y) / v.pointsize) / scale
|
local scale = ui.sidebarScale / granularity
|
||||||
local green = math.floor((mousex - v.x) / v.pointsize / (scale + 1)) / scale
|
local red = math.floor((mousey - v.y) / v.pointSize) / scale
|
||||||
local blue = (math.floor((mousex - v.x) / v.pointsize) % (scale + 1)) / scale
|
local green = math.floor((mousex - v.x) / v.pointSize / (scale + 1)) / scale
|
||||||
|
local blue = (math.floor((mousex - v.x) / v.pointSize) % (scale + 1)) / scale
|
||||||
|
print("I think the color is " .. red .. ":" .. green .. ":" .. blue)
|
||||||
drawing.color = {red, green, blue, 1}
|
drawing.color = {red, green, blue, 1}
|
||||||
ui.draw(window.x, window.y)
|
ui.draw(window.x, window.y)
|
||||||
return
|
return
|
||||||
elseif v.kind == "button" then
|
elseif v.kind == "bwColorPicker" then
|
||||||
if v.identifier == "disconnect" then
|
local scale = 1.0 / granularity
|
||||||
gameloop.networkSend(unit("playerLeave"))
|
local saturation = math.floor((mousex - v.x) / v.pointSize) / scale
|
||||||
menu.init()
|
drawing.color = {saturation, saturation, saturation, 1}
|
||||||
end
|
ui.draw(window.x, window.y)
|
||||||
|
return
|
||||||
end
|
end
|
||||||
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,60 +0,0 @@
|
||||||
local colorstack = {}
|
|
||||||
|
|
||||||
function color_push()
|
|
||||||
local depth = #colorstack +1
|
|
||||||
colorstack[depth] = {}
|
|
||||||
colorstack[depth].r, colorstack[depth].g, colorstack[depth].b,
|
|
||||||
colorstack[depth].a = love.graphics.getColor()
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function color_pop()
|
|
||||||
love.graphics.setColor(colorstack[#colorstack].r, colorstack[#colorstack].g,
|
|
||||||
colorstack[#colorstack].b, colorstack[#colorstack].a)
|
|
||||||
table.remove(colorstack, #colorstack)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function assert_num(name, count, ...)
|
|
||||||
local args = { ... }
|
|
||||||
for i = 1, count do
|
|
||||||
assert(args[i] ~= nil, "Argument " .. i .. " to " .. name .. " may not be nil and must be an int!")
|
|
||||||
assert(type(args[i]) == "number", "Argument " .. i .. " to " .. name .. " must be an int!")
|
|
||||||
assert(args[i] == args[i], "Argument " .. i .. " to " .. name .. " must not be NaN!")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function assert_num_or_nil(name, count, ...)
|
|
||||||
local args = { ... }
|
|
||||||
for i = 1, count do
|
|
||||||
if (args[i] ~= nil) then
|
|
||||||
assert(type(args[i]) == "number", "Argument " .. i .. " to " .. name .. " can only be of type int!")
|
|
||||||
assert(args[i] == args[i], "Argument " .. i .. " to " .. name .. " may not be NaN!")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local recordSeperator = string.char(30)
|
|
||||||
local unitSeperator = string.char(31)
|
|
||||||
|
|
||||||
|
|
||||||
function unit(...)
|
|
||||||
local args = { ... }
|
|
||||||
local string = args[1]
|
|
||||||
for i=2, #args do
|
|
||||||
string = string .. unitSeperator .. args[i]
|
|
||||||
end
|
|
||||||
return string
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function record(...)
|
|
||||||
local args = { ... }
|
|
||||||
local string = args[1]
|
|
||||||
for i=2, #args do
|
|
||||||
string = string .. recordSeperator .. args[i]
|
|
||||||
end
|
|
||||||
return string
|
|
||||||
end
|
|
71
main.lua
|
@ -2,11 +2,66 @@ function dofile(fileString)
|
||||||
local chunk = love.filesystem.load(fileString)
|
local chunk = love.filesystem.load(fileString)
|
||||||
chunk()
|
chunk()
|
||||||
end
|
end
|
||||||
dofile "lua/gameloop.lua"
|
|
||||||
dofile "lua/utils.lua"
|
|
||||||
dofile "lua/layout.lua"
|
function love.load(args)
|
||||||
dofile "lua/init.lua"
|
if args[1] == "--nwTest" then
|
||||||
dofile "lua/ui.lua"
|
local format = require("shared.networkFormat")
|
||||||
dofile "lua/camera.lua"
|
format.testEncoder()
|
||||||
dofile "lua/physics.lua"
|
elseif args[1] == "--server" then
|
||||||
dofile "lua/drawing.lua"
|
if not args[2]then
|
||||||
|
print("Missing server adress!")
|
||||||
|
love.event.quit()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local host = args[2]
|
||||||
|
local port = "11150"
|
||||||
|
if args[3] then port = args[3] end
|
||||||
|
|
||||||
|
local serverloop = require("server.main")
|
||||||
|
print("Starting server! [" .. host .. "] <" .. port .. ">")
|
||||||
|
serverloop.init({host, port})
|
||||||
|
elseif args[1] == "--client" then
|
||||||
|
if not args[2]then
|
||||||
|
print("Missing server adress!")
|
||||||
|
love.event.quit()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
dofile "lua/gameloop.lua"
|
||||||
|
dofile "lua/layout.lua"
|
||||||
|
dofile "lua/ui.lua"
|
||||||
|
dofile "lua/camera.lua"
|
||||||
|
dofile "lua/physics.lua"
|
||||||
|
dofile "lua/drawing.lua"
|
||||||
|
setmetatable(_G, {
|
||||||
|
__index = function(self, idx)
|
||||||
|
if rawget(self, idx) == nil then
|
||||||
|
error("attempted to read implicit global:" .. idx)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
})
|
||||||
|
|
||||||
|
PHYSICS_DEBUG = false
|
||||||
|
NETWORK_DEBUG = true
|
||||||
|
|
||||||
|
local host = args[2]
|
||||||
|
local port = "11150"
|
||||||
|
if args[3] then port = args[3] end
|
||||||
|
local avatar = love.image.newImageData("textures/player/nephele/fallback.png")
|
||||||
|
local avatarHash = love.data.hash("sha512", avatar:getString())
|
||||||
|
levelloop.init({ adress = host, port = port }, "Fallback player", avatarHash)
|
||||||
|
|
||||||
|
elseif args[1] == "--help" then
|
||||||
|
print(" --server [host] <port>")
|
||||||
|
print(" Start a local server")
|
||||||
|
print("")
|
||||||
|
print(" --help")
|
||||||
|
print(" print this help")
|
||||||
|
return
|
||||||
|
else
|
||||||
|
constants = require("server.constants")
|
||||||
|
local menu = require("lua.menuloop")
|
||||||
|
menu.init()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
#!sh
|
||||||
|
zip -r Poppy.love main.lua conf.lua lua/ shared/ ressources/Poppy.png textures/menu/AddIcon.png textures/menu/serverPlay.png server/ textures/blocks/ textures/menu/serverPlay.png textures/player/
|
6
run
|
@ -1,11 +1,13 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
#adjust the next line... not sure how to figure this out from a shellscript, sorry :)
|
# This mechanism is fairly unreliable, but it is the only "okay" way currently :/
|
||||||
cd ~/proj/poppy
|
# realpath is also not part of any POSIX standard
|
||||||
|
|
||||||
|
cd $(dirname $(realpath "$0"))
|
||||||
if test $? -eq 1; then
|
if test $? -eq 1; then
|
||||||
alert --stop "cant figure out where this script is" "Ill adjust it!"
|
alert --stop "cant figure out where this script is" "Ill adjust it!"
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
|
||||||
uname=`uname -o`
|
uname=`uname -o`
|
||||||
if test "$uname" = "Haiku"
|
if test "$uname" = "Haiku"
|
||||||
then
|
then
|
||||||
|
|
1
server
|
@ -1 +0,0 @@
|
||||||
Subproject commit 3447cbeba95455783a3fa28a26935e8960ff1a42
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
Copyright (C) 2022 by Pascal R. G. Abresch <nep@packageloss.eu>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
@ -0,0 +1,12 @@
|
||||||
|
local utils = require("shared.utils")
|
||||||
|
|
||||||
|
local constants = require("server.constants")
|
||||||
|
local rpc = require("server.rpc")
|
||||||
|
|
||||||
|
return function(commands)
|
||||||
|
function commands.chatMessage(clientID, message)
|
||||||
|
assert(participants[clientID])
|
||||||
|
print("Chat " .. participants[clientID].name .. ": " .. message)
|
||||||
|
broadcast(clientID, "chatMessage", message)
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,39 @@
|
||||||
|
local constants = require("server.constants")
|
||||||
|
local util = require("shared.utils")
|
||||||
|
local rpc = require("server.rpc")
|
||||||
|
require("server.compat")
|
||||||
|
|
||||||
|
return function(commands)
|
||||||
|
function commands.moveUpdate(clientID, x, y, speedx, speedy)
|
||||||
|
if not participants[clientID] then rpc.print("moveUpdate: invalid clientid [" .. clientID .. "]") return end
|
||||||
|
participants[clientID].x = x
|
||||||
|
participants[clientID].y = y
|
||||||
|
participants[clientID].speedx = speedx
|
||||||
|
participants[clientID].speedy = speedy
|
||||||
|
|
||||||
|
boradcast(clientID, "moveUpdate", x, y, speedx, speedy)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function commands.moveFlying(clientID, flying)
|
||||||
|
if not participants[clientID] then rpc.print("moveFlying: invalid clientid [" .. clientID .. "]") return end
|
||||||
|
broadcast(clientID, "moveFlying", flying)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function commands.deletePhysics(clientID, x, y, width, height)
|
||||||
|
if not WORLD_LOCK then
|
||||||
|
if not participants[clientID] then rpc.print("deletePhysics: invalid clientid [" .. clientID .. "]") return end
|
||||||
|
broadcast(clientID, "deletePhysics", x, y, width, height)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function commands.drawPhysics(clientID, x, y, width, height)
|
||||||
|
print("got physX")
|
||||||
|
if not WORLD_LOCK then
|
||||||
|
if not participants[clientID] then rpc.print("drawPhysics: invalid clientid [" .. clientID .. "]") return end
|
||||||
|
broadcast(clientID, "drawPhysics", x, y, width, height)
|
||||||
|
print("got physX broadcasting")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,9 @@
|
||||||
|
local util = require("shared.utils")
|
||||||
|
|
||||||
|
return function(commands)
|
||||||
|
function commands.playerLeave(clientID)
|
||||||
|
print("Old client disconnected. id: [" .. clientID .. "]")
|
||||||
|
participants[clientID] = nil
|
||||||
|
broadcast(clientID, "playerLeave")
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,47 @@
|
||||||
|
local util = require("shared.utils")
|
||||||
|
|
||||||
|
local constants = require("server.constants")
|
||||||
|
local rpc = require("server.rpc")
|
||||||
|
|
||||||
|
local os = require("os")
|
||||||
|
|
||||||
|
return function(commands)
|
||||||
|
function commands.listSaves(clientID, args)
|
||||||
|
-- local requestID = args
|
||||||
|
-- files = love.filesystem.getDirectoryItems( dir )
|
||||||
|
-- ChunkedReply("SaveData", files)
|
||||||
|
end
|
||||||
|
|
||||||
|
function commands.saveWorld(clientID, savename)
|
||||||
|
local handle = io.popen("date -u --rfc-3339=seconds")
|
||||||
|
local datetime = handle:read("*a")
|
||||||
|
handle:close()
|
||||||
|
datetime = datetime:sub(1, -8)
|
||||||
|
datetime = datetime:gsub("%s+", "_")
|
||||||
|
datetime = datetime:gsub(":+", "")
|
||||||
|
if savename then
|
||||||
|
savename = args .. "/" .. datetime
|
||||||
|
else
|
||||||
|
savename = datetime
|
||||||
|
end
|
||||||
|
local directory = "Saves/" .. savename
|
||||||
|
local result = love.filesystem.createDirectory(directory)
|
||||||
|
-- TODO check this result...
|
||||||
|
if result then
|
||||||
|
levelloop.Canvas.fg:newImageData():encode("png", directory .. "/fg.png")
|
||||||
|
levelloop.Canvas.bg:newImageData():encode("png", directory .. "/bg.png")
|
||||||
|
print("saved world. " .. savename)
|
||||||
|
broadcast(0, "saveWorld", true, savename)
|
||||||
|
return
|
||||||
|
else
|
||||||
|
print("Failed to save world!")
|
||||||
|
remoteCallTarget(clientID, 0, "saveWorld", false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
function commands.loadWorld(clientID, savename)
|
||||||
|
|
||||||
|
end
|
||||||
|
function commands.saveWorldAndLoad(clientID, args)
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,4 @@
|
||||||
|
table.unpack = table.unpack or unpack
|
||||||
|
string.pack = string.pack or love.data.pack
|
||||||
|
string.unpack = string.unpack or love.data.unpack
|
||||||
|
string.packsize = string.packsize or love.data.getPackedSize
|
|
@ -0,0 +1,21 @@
|
||||||
|
love.conf = function(conf)
|
||||||
|
conf.version = "11.0"
|
||||||
|
conf.modules.font = false
|
||||||
|
conf.modules.mouse = false
|
||||||
|
--conf.modules.image
|
||||||
|
--conf.modules.system
|
||||||
|
conf.modules.audio = false
|
||||||
|
conf.modules.touch = false
|
||||||
|
conf.modules.joystick = false
|
||||||
|
conf.modules.keyboard = false
|
||||||
|
--conf.modules.timer
|
||||||
|
--conf.modules.graphics
|
||||||
|
conf.modules.window = false
|
||||||
|
--conf.modules.math
|
||||||
|
--conf.modules.data
|
||||||
|
--conf.modules.event
|
||||||
|
conf.modules.sound = false
|
||||||
|
--conf.modules.thread
|
||||||
|
conf.modules.video = false
|
||||||
|
conf.modules.physics = false
|
||||||
|
end
|
|
@ -0,0 +1,8 @@
|
||||||
|
return {
|
||||||
|
protocolVersion = "PYY\x03",
|
||||||
|
world = {
|
||||||
|
x = 768,
|
||||||
|
y = 768,
|
||||||
|
id = "NYAAA"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,127 @@
|
||||||
|
local socket = require("socket")
|
||||||
|
|
||||||
|
local fonts = require("shared.fonts")
|
||||||
|
|
||||||
|
local constants = require("server.constants")
|
||||||
|
local rpc = require("server.rpc")
|
||||||
|
local util = require("shared.utils")
|
||||||
|
protocolVersion = constants.protocolVersion
|
||||||
|
|
||||||
|
local commands = {}
|
||||||
|
require("server.commands.players")(commands)
|
||||||
|
require("server.commands.physics")(commands)
|
||||||
|
require("server.commands.chat")(commands)
|
||||||
|
require("server.commands.saving")(commands)
|
||||||
|
require("shared.commands")(commands)
|
||||||
|
|
||||||
|
local format = require("shared.networkFormat")
|
||||||
|
|
||||||
|
local errorHandler = require("shared.error")
|
||||||
|
|
||||||
|
local unpriviligedCommands = {}
|
||||||
|
require("server.unpriviliged_commands.init")(unpriviligedCommands)
|
||||||
|
|
||||||
|
server = assert(socket.udp())
|
||||||
|
local ip, port = nil, nil
|
||||||
|
|
||||||
|
participants = {}
|
||||||
|
|
||||||
|
levelloop = {}
|
||||||
|
levelloop.textures = {}
|
||||||
|
|
||||||
|
server:settimeout(0)
|
||||||
|
if not server or not socket then error("failed to listen on socket") end
|
||||||
|
|
||||||
|
local function remoteCallTarget(target, clientID, name, ...)
|
||||||
|
local request = "PPY\x03" .. format.encodeFunction(name, clientID, ...)
|
||||||
|
server:sendto(request, target.ip, target.port)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function broadcast(clientID, name, ...)
|
||||||
|
for i, player in pairs(participants) do
|
||||||
|
if i ~= clientID then
|
||||||
|
remoteCallTarget(player, clientID, name, ...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function loadTextures(directory, container)
|
||||||
|
if not container then container = {} end
|
||||||
|
local entries = love.filesystem.getDirectoryItems(directory)
|
||||||
|
if #entries == 0 then return container end
|
||||||
|
for i, file in pairs(entries) do
|
||||||
|
local path = directory .."/".. file
|
||||||
|
local entry = love.filesystem.getInfo(path)
|
||||||
|
if entry.type == "directory" and file ~= ".git" then
|
||||||
|
container[file] = {}
|
||||||
|
container[file] = loadTextures(path, container[file])
|
||||||
|
elseif entry.type == "file" or entry.type == "symlink" then
|
||||||
|
local status, imageData = pcall(love.image.newImageData, path);
|
||||||
|
if status then
|
||||||
|
local hash = love.data.hash("sha512", imageData:getString())
|
||||||
|
levelloop.textures[hash] = {data = imageData}
|
||||||
|
container[file] = hash
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return container
|
||||||
|
end
|
||||||
|
|
||||||
|
love.window.minimize( )
|
||||||
|
love.window.setTitle( "Poppy server" )
|
||||||
|
function mainloop(dt)
|
||||||
|
|
||||||
|
local data, ip, port
|
||||||
|
data, ip, port = server:receivefrom()
|
||||||
|
if not data then
|
||||||
|
if ip == "timeout" then return else
|
||||||
|
print(ip)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local protocolVersion, index = string.unpack("<c4", data, index)
|
||||||
|
if protocolVersion ~= "PPY\x03" then
|
||||||
|
print("Dropping packet with invalid protocol " .. protocolVersion)
|
||||||
|
else
|
||||||
|
local DecoderType, index = string.unpack("<I1", data, index)
|
||||||
|
if (DecoderType == 0x81) then
|
||||||
|
name, clientID, args = format.decodeFunction(data, index)
|
||||||
|
if clientID == 0 then
|
||||||
|
unpriviligedCommands[name](ip, port, table.unpack(args))
|
||||||
|
else
|
||||||
|
commands[name](clientID, table.unpack(args))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function levelloop.init(host)
|
||||||
|
love.errorhandler = errorHandler.redscreen
|
||||||
|
status, value = server:setsockname(host[1], host[2])
|
||||||
|
if not status then
|
||||||
|
error("Server could not be started :( " .. value)
|
||||||
|
end
|
||||||
|
local ip, port =server:getsockname()
|
||||||
|
print("Server startup. listening on " .. ip .. " [".. port .."]")
|
||||||
|
loadTextures("textures/blocks", levelloop.textures)
|
||||||
|
levelloop.world = constants.world
|
||||||
|
levelloop.Canvas = {}
|
||||||
|
levelloop.Canvas.fg = love.graphics.newCanvas(levelloop.world.x, levelloop.world.y)
|
||||||
|
levelloop.Canvas.bg = love.graphics.newCanvas(levelloop.world.x, levelloop.world.y)
|
||||||
|
print("Startup!")
|
||||||
|
SERVER = true
|
||||||
|
CLIENT = false
|
||||||
|
love.update = mainloop
|
||||||
|
love.draw = function()
|
||||||
|
love.graphics.clear(.2, 0, .2)
|
||||||
|
love.graphics.print("Poppy server is running.", fonts.normalFont, 20, 20)
|
||||||
|
love.graphics.print("listening on " .. ip .. " [".. port .."]" , fonts.normalFont, 20, 50)
|
||||||
|
return end
|
||||||
|
love.keyreleased = function() return end
|
||||||
|
love.keypressed = function() return end
|
||||||
|
love.mousepressed = function() return end
|
||||||
|
end
|
||||||
|
|
||||||
|
return levelloop
|
|
@ -0,0 +1,13 @@
|
||||||
|
local nwBuffer = {}
|
||||||
|
nwBuffer.replyBuffers = {}
|
||||||
|
-- This implementation requires the client to supply a requestID, so that it later can
|
||||||
|
-- tell if the transfer suceeded or not, and re-request data out of a chunked reply.
|
||||||
|
|
||||||
|
nwbuffer.commands.requestChunk(clientID, args)
|
||||||
|
local chunkNumber = args
|
||||||
|
if replybuffers[clientID .. "_" .. args] then
|
||||||
|
reply(clientID, "chunk", args, chunk[clientID .. "_" .. args])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return nwBuffer
|
|
@ -0,0 +1,40 @@
|
||||||
|
local utils = require("shared.utils")
|
||||||
|
|
||||||
|
local constants = require("server.constants")
|
||||||
|
local format = require("shared.networkFormat")
|
||||||
|
local rpc = require("server.rpc")
|
||||||
|
|
||||||
|
local clientCount = 0
|
||||||
|
|
||||||
|
local function remoteCallTo(ip, port, name, ...)
|
||||||
|
local request = "PPY\x03" .. format.encodeFunction(name, 0, ...)
|
||||||
|
server:sendto(request, ip, port)
|
||||||
|
print("send request to " .. ip .. " " .. port .. "[" .. request .. "]")
|
||||||
|
end
|
||||||
|
|
||||||
|
return function(commands)
|
||||||
|
function commands.init(ip, port, nickname, avatar)
|
||||||
|
clientCount = clientCount +1
|
||||||
|
participants[clientCount] = { ip = ip, port = port, name = nickname, avatar = avatar }
|
||||||
|
|
||||||
|
print("New client connected. Granting id: [" .. clientCount .. "] IP: " .. ip .. " Port:" .. port)
|
||||||
|
for i, client in pairs(participants) do
|
||||||
|
if i ~= clientCount then
|
||||||
|
-- to Existing client
|
||||||
|
remoteCallTo(client.ip, client.port, "playerJoin", clientCount, nickname, avatar)
|
||||||
|
-- to New client
|
||||||
|
remoteCallTo(ip, port, "playerJoin", i, client.name, client.avatar)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
WORLD_LOCK = true
|
||||||
|
-- The world is now locked
|
||||||
|
-- Client response
|
||||||
|
remoteCallTo(ip, port, "init", clientCount, nickname, avatar, constants.world.id,
|
||||||
|
constants.world.x, constants.world.y)
|
||||||
|
-- Send World
|
||||||
|
local fg = love.data.compress("string", "lz4", levelloop.Canvas.fg:newImageData())
|
||||||
|
local bg = love.data.compress("string", "lz4", levelloop.Canvas.bg:newImageData())
|
||||||
|
--network.prepareChunk(fg
|
||||||
|
WORLD_LOCK = false
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,23 @@
|
||||||
|
local asserts = {}
|
||||||
|
|
||||||
|
function asserts.number(name, count, ...)
|
||||||
|
local args = { ... }
|
||||||
|
for i = 1, count do
|
||||||
|
assert(args[i] ~= nil, "Argument " .. i .. " to " .. name .. " may not be nil and must be an int!")
|
||||||
|
assert(type(args[i]) == "number", "Argument " .. i .. " to " .. name .. " must be an int!")
|
||||||
|
assert(args[i] == args[i], "Argument " .. i .. " to " .. name .. " must not be NaN!")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function asserts.numberOrNil(name, count, ...)
|
||||||
|
local args = { ... }
|
||||||
|
for i = 1, count do
|
||||||
|
if (args[i] ~= nil) then
|
||||||
|
assert(type(args[i]) == "number", "Argument " .. i .. " to " .. name .. " can only be of type int!")
|
||||||
|
assert(args[i] == args[i], "Argument " .. i .. " to " .. name .. " may not be NaN!")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return asserts
|
|
@ -0,0 +1,31 @@
|
||||||
|
local catalog = {}
|
||||||
|
catalog.firstNames = { "Herbert", "Thorben", "Martin", "Heinrich", "Dietrich",
|
||||||
|
"Markus", "Florian", "Helmut", "Willhelm", "Fritz", "Gustav", "Konrad",
|
||||||
|
"Berta", "Charlotte", "Hildegard", "Lieselotte", "Gudrun", "Giesela",
|
||||||
|
"Margarete", "Antonia", "Friederike", "Clotilde", "Marlies", "Hedwig",
|
||||||
|
"Agathe" }
|
||||||
|
|
||||||
|
catalog.surNames = { "Müller", "Schmidt", "Meier", "Bauer", "Werner",
|
||||||
|
"Schumacher", "Bergmann", "Eisenhauer", "Heisenberg" }
|
||||||
|
|
||||||
|
catalog.pastelColors = {
|
||||||
|
{234/255, 155/255, 254/255, .6}, --pinkish
|
||||||
|
{156/255, 154/255, 251/255, .6}, --blueish
|
||||||
|
{242/255, 228/255, 148/255, .6}, --orange yellowish
|
||||||
|
{255/255, 156/255, 182/255, .6}, --magenta/orange
|
||||||
|
{145/255, 237/255, 216/255, .6} --turqoise
|
||||||
|
}
|
||||||
|
|
||||||
|
catalog.buttonColors = {
|
||||||
|
blue = { {50/255, 65/255, 98/255}, {125/255, 151/255, 208/255}, {1, 1, 1}},
|
||||||
|
pink = { {208/255, 37/255, 134/255}, {241/255, 109/255, 184/255}, {1, 1, 1}},
|
||||||
|
green = {{30/255,183/255,127/255},{109/255,241/255,193/255}, {1,1,1}},
|
||||||
|
blueTranslucent = { {50/255, 65/255, 98/255, .7}, {125/255, 151/255, 208/255, .7}, {1, 1, 1, .9}},
|
||||||
|
pinkTranslucent = { {208/255, 37/255, 134/255, .7}, {241/255, 109/255, 184/255, .7}, {1, 1, 1, .9}},
|
||||||
|
greenTranslucent = {{30/255,183/255,127/255, .7},{109/255,241/255,193/255, .7}, {1,1,1, .9}},
|
||||||
|
grayTranslucent = {{.1,.1,.1, .7}, {1,1,1, .7}, {1,1,1, .9}}
|
||||||
|
}
|
||||||
|
catalog.default = {
|
||||||
|
avatar = "textures/player/noriko/liz.png",
|
||||||
|
}
|
||||||
|
return catalog
|
|
@ -0,0 +1,108 @@
|
||||||
|
local player = require("lua.player")
|
||||||
|
local utils = require("shared.utils")
|
||||||
|
|
||||||
|
return function(commands)
|
||||||
|
commands.moveUpdate = function(clientID, x, y, speedx, speedy)
|
||||||
|
if SERVER then
|
||||||
|
broadcast(clientID, "moveUpdate", x, y, speedx, speedy)
|
||||||
|
end
|
||||||
|
if not participants[clientID] then return end
|
||||||
|
|
||||||
|
participants[clientID].x = x
|
||||||
|
participants[clientID].y = y
|
||||||
|
if not participants[clientID].speed then participants[clientID].speed = {} end
|
||||||
|
participants[clientID].speed.x = speedx
|
||||||
|
participants[clientID].speed.y = speedy
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
commands.moveFlying = function(clientID, flying)
|
||||||
|
if SERVER then
|
||||||
|
broadcast(clientID, "moveFlying", flying)
|
||||||
|
end
|
||||||
|
if not participants[clientID] then return end
|
||||||
|
participants[clientID].flying = flying
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
commands.drawTexture = function(clientID, x, y, layer, hash, color)
|
||||||
|
if SERVER then
|
||||||
|
color.nwType = "color"
|
||||||
|
broadcast(clientID, "drawTexture", x, y, layer, hash, color)
|
||||||
|
end
|
||||||
|
if not participants[clientID] then return end
|
||||||
|
if not levelloop.textures[hash] then error("Failed to find hash!") return end
|
||||||
|
if not levelloop.textures[hash].image then
|
||||||
|
levelloop.textures[hash].image = love.graphics.newImage(levelloop.textures[hash].data)
|
||||||
|
end
|
||||||
|
love.graphics.setCanvas(levelloop.Canvas[layer])
|
||||||
|
love.graphics.setBlendMode("alpha")
|
||||||
|
utils.color_push()
|
||||||
|
love.graphics.setColor(color)
|
||||||
|
love.graphics.draw(levelloop.textures[hash].image, x, y)
|
||||||
|
utils.color_pop()
|
||||||
|
love.graphics.setCanvas()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
commands.deleteTexture = function(clientID, x, y, width, height, layer)
|
||||||
|
if SERVER then
|
||||||
|
broadcast(clientID, "deleteTexture", x, y, width, height, layer)
|
||||||
|
end
|
||||||
|
if not participants[clientID] then return end
|
||||||
|
love.graphics.setCanvas(levelloop.Canvas[layer])
|
||||||
|
love.graphics.setBlendMode("replace")
|
||||||
|
utils.color_push()
|
||||||
|
love.graphics.setColor(0,0,0,0)
|
||||||
|
love.graphics.rectangle("fill", x, y, width, height)
|
||||||
|
utils.color_pop()
|
||||||
|
love.graphics.setBlendMode("alpha")
|
||||||
|
love.graphics.setCanvas()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
commands.fillCanvas = function(clientID, layer, hash, color)
|
||||||
|
if SERVER then
|
||||||
|
color.nwType = "color"
|
||||||
|
broadcast(clientID, "fillCanvas", layer, hash, color)
|
||||||
|
end
|
||||||
|
if not participants[clientID] then return end
|
||||||
|
|
||||||
|
love.graphics.setCanvas(levelloop.Canvas[layer])
|
||||||
|
love.graphics.clear()
|
||||||
|
if not levelloop.textures[hash] then error("Failed to find hash!") return end
|
||||||
|
if not levelloop.textures[hash].image then
|
||||||
|
levelloop.textures[hash].image = love.graphics.newImage(levelloop.textures[hash].data)
|
||||||
|
end
|
||||||
|
utils.color_push()
|
||||||
|
love.graphics.setColor(color)
|
||||||
|
if not levelloop.textures[hash].image then
|
||||||
|
love.graphics.newImage(levelloop.textures[hash].data)
|
||||||
|
end
|
||||||
|
for x = 0, levelloop.world.x -levelloop.textures[hash].image:getPixelWidth(),
|
||||||
|
levelloop.textures[hash].image:getPixelWidth() do
|
||||||
|
for y = 0, levelloop.world.y -levelloop.textures[hash].image:getPixelHeight(),
|
||||||
|
levelloop.textures[hash].image:getPixelHeight() do
|
||||||
|
love.graphics.draw(levelloop.textures[hash].image, x, y)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
utils.color_pop()
|
||||||
|
love.graphics.setCanvas()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
commands.clearCanvas = function(clientID)
|
||||||
|
if SERVER then
|
||||||
|
broadcast(clientID, "clearCanvas")
|
||||||
|
end
|
||||||
|
love.graphics.setCanvas(levelloop.Canvas.fg)
|
||||||
|
love.graphics.clear()
|
||||||
|
love.graphics.setCanvas(levelloop.Canvas.bg)
|
||||||
|
love.graphics.clear()
|
||||||
|
love.graphics.setCanvas(levelloop.Canvas.dbg)
|
||||||
|
love.graphics.clear()
|
||||||
|
love.graphics.setCanvas()
|
||||||
|
levelloop.Canvas.physics = {}
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,43 @@
|
||||||
|
local errorHandler = {}
|
||||||
|
function errorHandler.redscreen(msg)
|
||||||
|
local scale = 0.5
|
||||||
|
local errorfont
|
||||||
|
if love.filesystem.getInfo("fonts/font.ttf") then
|
||||||
|
errorfont = love.graphics.newFont("fonts/font.ttf", 45 * scale)
|
||||||
|
else
|
||||||
|
errorfont = love.graphics.newFont(45 * scale)
|
||||||
|
end
|
||||||
|
love.graphics.reset()
|
||||||
|
love.graphics.origin()
|
||||||
|
love.graphics.clear(.4, 0, 0)
|
||||||
|
love.graphics.setColor(1, 1, 1)
|
||||||
|
local message = love.graphics.newText(errorfont, "Poppy has encountered a critical fault")
|
||||||
|
local message2 = love.graphics.newText(errorfont, "Press q to exit")
|
||||||
|
local message3 = love.graphics.newText(errorfont, msg)
|
||||||
|
love.graphics.draw(message, 40, 40 * scale, 0, 1, 1)
|
||||||
|
love.graphics.draw(message2, 40, 90 * scale, 0, 1, 1)
|
||||||
|
love.graphics.draw(message3, 40, 140 * scale, 0, 1, 1)
|
||||||
|
print(msg)
|
||||||
|
local indent = 0
|
||||||
|
local messageline
|
||||||
|
for line in debug.traceback():gmatch("([^\n]*)\n?") do
|
||||||
|
print(line)
|
||||||
|
messageline = love.graphics.newText(errorfont, line)
|
||||||
|
love.graphics.draw(messageline, 40, (250 * scale) + indent, 0, 1, 1)
|
||||||
|
indent = indent + 50 * scale
|
||||||
|
end
|
||||||
|
love.graphics.present()
|
||||||
|
return function()
|
||||||
|
love.event.pump()
|
||||||
|
for event, action in love.event.poll() do
|
||||||
|
if event == "quit" then return 1 end
|
||||||
|
if event == "keypressed" then
|
||||||
|
if action == "q" then
|
||||||
|
love.event.quit( 1 )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
love.timer.sleep(0.1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return errorHandler
|
|
@ -0,0 +1,15 @@
|
||||||
|
local fonts = {}
|
||||||
|
|
||||||
|
if love.filesystem.getInfo("fonts/font.ttf") then
|
||||||
|
fonts.smallFont = love.graphics.newFont("fonts/font.ttf", 14)
|
||||||
|
fonts.normalFont = love.graphics.newFont("fonts/font.ttf", 20)
|
||||||
|
fonts.headerFont = love.graphics.newFont("fonts/font.ttf", 25)
|
||||||
|
fonts.bigFont = love.graphics.newFont("fonts/font.ttf", 45)
|
||||||
|
else
|
||||||
|
fonts.smallFont = love.graphics.newFont(14)
|
||||||
|
fonts.normalFont = love.graphics.newFont(20)
|
||||||
|
fonts.headerFont = love.graphics.newFont(25)
|
||||||
|
fonts.bigFont = love.graphics.newFont(45)
|
||||||
|
end
|
||||||
|
|
||||||
|
return fonts
|
|
@ -0,0 +1,7 @@
|
||||||
|
local icon = {
|
||||||
|
logo = love.graphics.newImage("ressources/Poppy.png"),
|
||||||
|
joinServer = love.graphics.newImage("textures/menu/serverPlay.png"),
|
||||||
|
startServer = love.graphics.newImage("textures/menu/StartServer.png"),
|
||||||
|
addServer = love.graphics.newImage("textures/menu/AddIcon.png")
|
||||||
|
}
|
||||||
|
return icon
|
|
@ -0,0 +1,31 @@
|
||||||
|
local log = {}
|
||||||
|
|
||||||
|
log.color = {
|
||||||
|
default = 1,
|
||||||
|
blue = 2,
|
||||||
|
pink = 3,
|
||||||
|
orange = 4
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
log.kind = {
|
||||||
|
warning = 2,
|
||||||
|
failure = 3,
|
||||||
|
info = 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
log.log = function(message, level, kind, source, color)
|
||||||
|
if not color then
|
||||||
|
if kind = log.kind.failure then
|
||||||
|
color = log.color.red
|
||||||
|
elseif kind = log.kind.warning then
|
||||||
|
color = log.color.orange
|
||||||
|
elseif kind = log
|
||||||
|
color = log.color.default
|
||||||
|
end
|
||||||
|
table.insert(log.actuallog, {message = message, level = level, source = source
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
log.actuallog = { { level = TRACE, message = "Log initialized", color = BLUE, source = "log.actallog Init"}
|
||||||
|
log.level = { INFO = 1, DEBUG = 2, TRACE = 3
|
|
@ -0,0 +1,154 @@
|
||||||
|
table.unpack = table.unpack or unpack
|
||||||
|
string.pack = function(...) return love.data.pack("string", ...) end
|
||||||
|
string.unpack = love.data.unpack
|
||||||
|
string.packsize = string.packsize or love.data.getPackedSize
|
||||||
|
|
||||||
|
-- Lua data types: nil, boolean, number, string, function, userdata, thread, and table.
|
||||||
|
-- Not representable here: function, userdata, thread.
|
||||||
|
format = {
|
||||||
|
--[[ Poppy specific
|
||||||
|
0x81 = "remoteCall",
|
||||||
|
-- lua specific
|
||||||
|
0x02 = "shortString",
|
||||||
|
0x03 = "longString",
|
||||||
|
0x04 = "nil",
|
||||||
|
0x05 = "true",
|
||||||
|
0x06 = "false",
|
||||||
|
0x07 = "number",
|
||||||
|
-- love2d/poppy specific
|
||||||
|
0x48 = "colorAlpha",
|
||||||
|
0x49 = "color"]]--
|
||||||
|
}
|
||||||
|
format.decodeFunction = function(encoded, index)
|
||||||
|
local nameLength, index = string.unpack("<I1", encoded, index)
|
||||||
|
local name, index = string.unpack("<c".. nameLength, encoded, index)
|
||||||
|
if name == "init" then DEBUG = true end
|
||||||
|
local argNumber, index = string.unpack("<I1", encoded, index)
|
||||||
|
local clientID, index = string.unpack("<I2", encoded, index)
|
||||||
|
local arguments = {}
|
||||||
|
for i = 1, argNumber do
|
||||||
|
local argtype = ""
|
||||||
|
argType, index = string.unpack("<I1", encoded, index)
|
||||||
|
if argType == 0x02 then
|
||||||
|
local stringLength, nwString
|
||||||
|
stringLength, index = string.unpack("<I1", encoded, index)
|
||||||
|
nwString, index = string.unpack("<c"..stringLength, encoded, index)
|
||||||
|
arguments[i] = nwString
|
||||||
|
elseif argType == 0x03 then
|
||||||
|
local stringLength, nwString
|
||||||
|
stringLength, index = string.unpack("<I2", encoded, index)
|
||||||
|
nwString, index = string.unpack("<c"..stringLength, encoded, index)
|
||||||
|
arguments[i] = string
|
||||||
|
elseif argType == 0x04 then
|
||||||
|
arguments[i] = nil
|
||||||
|
elseif argType == 0x05 then
|
||||||
|
arguments[i] = true
|
||||||
|
elseif argType == 0x06 then
|
||||||
|
arguments[i] = false
|
||||||
|
elseif argType == 0x07 then
|
||||||
|
local number
|
||||||
|
number, index = string.unpack("<n", encoded, index)
|
||||||
|
arguments[i] = number
|
||||||
|
elseif argType == 0x48 then
|
||||||
|
local color = {}
|
||||||
|
color[1], color[2], color[3], color[4], index = string.unpack("<ffff", encoded, index)
|
||||||
|
arguments[i] = color
|
||||||
|
elseif argType == 0x49 then
|
||||||
|
local color = {nwType = "color"}
|
||||||
|
color[1], color[2], color[3], index = string.unpack("<fff", encoded, index)
|
||||||
|
arguments[i] = color
|
||||||
|
else
|
||||||
|
error("failed to decode argument " .. i .. " index is at " ..index.. " len is " .. encoded:len() .. "value is " .. argType)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return name, clientID, arguments
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
format.encodeFunction = function(name, clientID, ...)
|
||||||
|
assert(name and clientID)
|
||||||
|
-- Format
|
||||||
|
-- byte1 0x81 to indicate a function
|
||||||
|
-- byte2 length of the name of the function
|
||||||
|
-- byte3-byteN name of the function
|
||||||
|
-- byteN+1 number of arguments to the function
|
||||||
|
-- byteN+2 type of argument
|
||||||
|
-- byteN+3-byteM argument specific syntax
|
||||||
|
-- byteM+1 type of next argument
|
||||||
|
-- and so on
|
||||||
|
local encoded = "\x81"
|
||||||
|
encoded = encoded .. string.pack("<I1", name:len())
|
||||||
|
encoded = encoded .. name .. string.pack("<I1", select("#", ...))
|
||||||
|
-- can't use ipairs, it does stop on nil
|
||||||
|
-- can't use pairs, it ignores nil :D
|
||||||
|
encoded = encoded .. string.pack("<I2", clientID)
|
||||||
|
local args = { ... }
|
||||||
|
for i=1, select("#", ...) do
|
||||||
|
local value = args[i]
|
||||||
|
if type(value) == "function" or type(value) == "CFunction" or type(value) == "userdata"
|
||||||
|
or type(value) == "thread" then
|
||||||
|
error("unencodeable data passed! of type: " .. type(value))
|
||||||
|
elseif type(value) == "nil" then
|
||||||
|
encoded = encoded .. "\x04"
|
||||||
|
elseif type(value) == "boolean" then
|
||||||
|
if value then
|
||||||
|
encoded = encoded .. "\x05"
|
||||||
|
else
|
||||||
|
encoded = encoded .. "\x06"
|
||||||
|
end
|
||||||
|
elseif type(value) == "string" then
|
||||||
|
if value:len() > 255 then
|
||||||
|
encoded = encoded .. "\x03" .. string.pack("<I2", value:len()) .. value
|
||||||
|
else
|
||||||
|
encoded = encoded .. "\x02" .. string.pack("<I1", value:len()) .. value
|
||||||
|
end
|
||||||
|
elseif type(value) == "number" then
|
||||||
|
encoded = encoded .. "\x07" .. string.pack("<n", value)
|
||||||
|
elseif type(value) == "table" then
|
||||||
|
if value.nwType == "color" and #value == 3 then
|
||||||
|
encoded = encoded .. "\x49" .. string.pack("<fff", value[1], value[2], value[3])
|
||||||
|
elseif value.nwType == "color" and #value == 4 then
|
||||||
|
encoded = encoded .. "\x48" ..
|
||||||
|
string.pack("<ffff", value[1], value[2], value[3], value[4])
|
||||||
|
else
|
||||||
|
error("Got table but don't know how to encode it")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
error("unimplemented type for encoding!")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return encoded
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function format.testEncoder()
|
||||||
|
local testCases = {
|
||||||
|
Jeremeeeey = { true, true, nil, false, 69, "Jeremy stop that >:(" },
|
||||||
|
buildBlocks = { 32, 32, 64, 64, "longHashValueISwear" },
|
||||||
|
colorWorld = { {nwType = "color", 0.4, 0.4, 0.4 }, {nwType = "color", 0.4, 0.4, 0.4, 0.5}},
|
||||||
|
slangvariants = { "oy mate", "how ya doing", "sup brooo", "heya", "hey there ma dude" }
|
||||||
|
}
|
||||||
|
print("Running encoder tests...")
|
||||||
|
|
||||||
|
for name, value in pairs(testCases) do
|
||||||
|
local encoded = format.encodeFunction(name, 3, table.unpack(value))
|
||||||
|
local name, _, decodedArgs = format.decodeFunction(encoded)
|
||||||
|
|
||||||
|
local sucess = true
|
||||||
|
for iter= 1, #value do
|
||||||
|
local var = value[iter]
|
||||||
|
if var ~= decodedArgs[iter] and not type(var) == table then
|
||||||
|
sucess = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if sucess then
|
||||||
|
print(name .. " suceeded!")
|
||||||
|
else
|
||||||
|
print(name .. " failed.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return format
|
|
@ -0,0 +1,22 @@
|
||||||
|
local utils = {}
|
||||||
|
utils.colorstack = {}
|
||||||
|
|
||||||
|
-- Colors
|
||||||
|
|
||||||
|
function utils.color_push()
|
||||||
|
local colorstack = utils.colorstack
|
||||||
|
local depth = #utils.colorstack +1
|
||||||
|
colorstack[depth] = {}
|
||||||
|
colorstack[depth].r, colorstack[depth].g, colorstack[depth].b,
|
||||||
|
colorstack[depth].a = love.graphics.getColor()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function utils.color_pop()
|
||||||
|
local colorstack = utils.colorstack
|
||||||
|
love.graphics.setColor(colorstack[#colorstack].r, colorstack[#colorstack].g,
|
||||||
|
colorstack[#colorstack].b, colorstack[#colorstack].a)
|
||||||
|
table.remove(colorstack, #colorstack)
|
||||||
|
end
|
||||||
|
|
||||||
|
return utils
|
Before Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 213 B |
After Width: | Height: | Size: 216 B |
After Width: | Height: | Size: 308 B |
After Width: | Height: | Size: 416 B |
After Width: | Height: | Size: 458 B |
After Width: | Height: | Size: 308 B |
After Width: | Height: | Size: 471 B |
After Width: | Height: | Size: 308 B |
After Width: | Height: | Size: 308 B |
After Width: | Height: | Size: 418 B |
Before Width: | Height: | Size: 543 B |
Before Width: | Height: | Size: 543 B After Width: | Height: | Size: 476 B |
After Width: | Height: | Size: 474 B |
Before Width: | Height: | Size: 543 B |
Before Width: | Height: | Size: 543 B |
Before Width: | Height: | Size: 450 B |
Before Width: | Height: | Size: 543 B |
Before Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 272 B |
After Width: | Height: | Size: 299 B |
After Width: | Height: | Size: 681 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 675 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 676 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 952 B |
After Width: | Height: | Size: 1023 B |
Before Width: | Height: | Size: 956 B After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 952 B |
Before Width: | Height: | Size: 949 B |
Before Width: | Height: | Size: 491 B |
Before Width: | Height: | Size: 956 B |
Before Width: | Height: | Size: 956 B |
Before Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 552 B |
After Width: | Height: | Size: 262 B |
After Width: | Height: | Size: 616 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 608 B |
After Width: | Height: | Size: 644 B |
After Width: | Height: | Size: 619 B |
After Width: | Height: | Size: 634 B |
After Width: | Height: | Size: 638 B |