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
|
||||
#adjust the next line... not sure how to figure this out from a shellscript, sorry :)
|
||||
cd ~/proj/poppy-client-loeve
|
||||
# This mechanism is fairly unreliable, but it is the only "okay" way currently :/
|
||||
# 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
|
||||
alert --stop "cant figure out where this script is" "Ill adjust it!"
|
||||
exit
|
||||
fi
|
||||
|
||||
touch log
|
||||
for i in lua/*.lua; do
|
||||
echo "$i" >> log
|
||||
addattr -t mime BEOS:TYPE text/x-source-code $i;
|
||||
done
|
||||
|
||||
for i in server/*.lua; do
|
||||
echo "$i" >> log
|
||||
addattr -t mime BEOS:TYPE text/x-source-code $i;
|
||||
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\
|
||||
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.\
|
||||
(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,\
|
||||
|
@ -15,7 +14,7 @@ I have not investigated this much yet. Safe to say it is not intended to run so
|
|||
* Loading textures
|
||||
* drawing on canvases
|
||||
* physics calculation for collison and movement
|
||||
* drawing physics collisions (on the physics layer)
|
||||
* drawing physics collisions (on the physics layer) (debug)
|
||||
* Basic networking
|
||||
* Get clientID
|
||||
* 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
|
||||
* Chat support
|
||||
|
||||
* Retrieve initial game state (partial)
|
||||
* Get users name
|
||||
* Get users avatar
|
||||
* Get world size
|
||||
|
||||
* Drawing
|
||||
* Draw with custom color
|
||||
* 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)
|
||||
* UI to connect to specific server
|
||||
* Reconnect to server
|
||||
* connect to other server during a session
|
||||
* Open second window? Open second instance instead? (does love2d allow that?)
|
||||
* Saving the game (server stuff?)
|
||||
* Loading the game/map (server stuff?)
|
||||
* Remember visited servers (save the list)
|
||||
* Add new servers to the list by user input
|
||||
* Add new servers at runtime via DNS-SD mDNS discovery
|
||||
|
||||
* Saving the map
|
||||
* 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)
|
||||
* 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
|
||||
* 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)
|
||||
* Tell the user if the server doesn't support the networking version requested
|
||||
|
||||
|
|
7
conf.lua
|
@ -1,11 +1,12 @@
|
|||
love.conf = function(conf)
|
||||
conf.window.height = 480
|
||||
conf.identity = "Poppy"
|
||||
conf.window.height = 612
|
||||
conf.window.minheight = 480
|
||||
conf.window.width = 640
|
||||
conf.window.width = 1024
|
||||
conf.window.minwidth = 640
|
||||
conf.window.resizable = true
|
||||
conf.window.vsync = -1
|
||||
conf.window.title = "poppy"
|
||||
conf.window.title = "Poppy"
|
||||
conf.version = "11.0"
|
||||
|
||||
conf.modules.joystick = false
|
||||
|
|
|
@ -2,14 +2,16 @@ local CAM_SPEED = 10
|
|||
|
||||
camera = {}
|
||||
camera.target = 0
|
||||
camera.offset = { x = 0, y = 300 }
|
||||
camera.offset = { x = 0, y = 0 }
|
||||
local fullscreen = false
|
||||
|
||||
local altPressed, enterPressed = false, false
|
||||
function camera.keypressed(key, _)
|
||||
if key == "return" then enterPressed = 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
|
||||
love.window.setFullscreen(fullscreen, "desktop")
|
||||
end
|
||||
|
@ -93,7 +95,7 @@ end
|
|||
|
||||
function camera.setplayer()
|
||||
if camera.target == 0 then return end
|
||||
player = players[camera.target]
|
||||
player = participants[camera.target]
|
||||
camera.offsetclamp()
|
||||
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
|
||||
|
@ -105,7 +107,7 @@ end
|
|||
|
||||
function camera.update()
|
||||
if camera.target == 0 then return end
|
||||
player = players[camera.target]
|
||||
player = participants[camera.target]
|
||||
--[[ 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 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)
|
||||
end
|
||||
|
||||
local utils = require("shared.utils")
|
||||
|
||||
function drawing.clearAllCanvases()
|
||||
love.graphics.setCanvas(gameloop.buffer.pixel.fg)
|
||||
love.graphics.setCanvas(levelloop.Canvas.fg)
|
||||
love.graphics.clear()
|
||||
love.graphics.setCanvas(gameloop.buffer.pixel.bg)
|
||||
love.graphics.setCanvas(levelloop.Canvas.bg)
|
||||
love.graphics.clear()
|
||||
love.graphics.setCanvas(gameloop.buffer.pixel.dbg)
|
||||
love.graphics.setCanvas(levelloop.Canvas.dbg)
|
||||
love.graphics.clear()
|
||||
love.graphics.setCanvas()
|
||||
gameloop.buffer.physics = {}
|
||||
levelloop.Canvas.physics = {}
|
||||
end
|
||||
|
||||
|
||||
function drawing.clearAllCanvasesNetwork()
|
||||
gameloop.networkSend("clearCanvas")
|
||||
levelloop.remoteCall("clearCanvas")
|
||||
drawing.clearAllCanvases()
|
||||
end
|
||||
|
||||
|
||||
function drawing.fillCanvas(layer, hash)
|
||||
love.graphics.setCanvas(gameloop.buffer.pixel[layer])
|
||||
love.graphics.clear()
|
||||
for x = 0, gameloop.world.x -gameloop.textures[hash].image:getPixelWidth(),
|
||||
gameloop.textures[hash].image:getPixelWidth() do
|
||||
for y = 0, gameloop.world.y -gameloop.textures[hash].image:getPixelHeight(),
|
||||
gameloop.textures[hash].image:getPixelHeight() do
|
||||
love.graphics.draw(gameloop.textures[hash].image, x, y)
|
||||
end
|
||||
function drawing.fillCanvas(layer, hash, color)
|
||||
love.graphics.setCanvas(levelloop.Canvas[layer])
|
||||
love.graphics.clear()
|
||||
utils.color_push()
|
||||
love.graphics.setColor(color)
|
||||
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
|
||||
love.graphics.setCanvas()
|
||||
end
|
||||
utils.color_pop()
|
||||
love.graphics.setCanvas()
|
||||
end
|
||||
|
||||
|
||||
function drawing.fillCanvasNetwork(layer, hash)
|
||||
gameloop.networkSend(unit("fillCanvas", layer, love.data.encode("string", "base64", hash)))
|
||||
drawing.fillCanvas(layer, hash)
|
||||
function drawing.fillCanvasNetwork(layer, hash, color)
|
||||
color.nwType = "color"
|
||||
levelloop.remoteCall("fillCanvas", layer, hash, color)
|
||||
drawing.fillCanvas(layer, hash, color)
|
||||
end
|
||||
|
||||
|
||||
function drawing.keyreleased(key, _)
|
||||
assert(drawing.color)
|
||||
if key == "c" then
|
||||
drawing.clearAllCanvasesNetwork()
|
||||
end
|
||||
if key == "f" then
|
||||
drawing.fillCanvasNetwork("fg", drawing.cursorHash)
|
||||
drawing.fillCanvasNetwork("fg", drawing.cursorHash, drawing.color)
|
||||
end
|
||||
if key == "b" then
|
||||
drawing.fillCanvasNetwork("bg", drawing.cursorHash)
|
||||
drawing.fillCanvasNetwork("bg", drawing.cursorHash, drawing.color)
|
||||
end
|
||||
end
|
||||
|
||||
local drawmouse = {}
|
||||
function drawing.input(dt)
|
||||
local textures = gameloop.textures
|
||||
local textures = levelloop.textures
|
||||
local cursor = drawing.cursor
|
||||
if not cursor or not cursor.data then return end
|
||||
assert(cursor, "Failed to find cursor")
|
||||
|
@ -73,8 +79,8 @@ function drawing.input(dt)
|
|||
end
|
||||
mousex = mousex / scale - view.x
|
||||
mousey = mousey / scale - view.y
|
||||
if mousex > gameloop.world.x or
|
||||
mousey > gameloop.world.y or
|
||||
if mousex > levelloop.world.x or
|
||||
mousey > levelloop.world.y or
|
||||
mousex < 0 or
|
||||
mousey < 0 then
|
||||
return
|
||||
|
@ -99,18 +105,17 @@ function drawing.input(dt)
|
|||
if not (thisx == thisx) or not (thisy == thisy) then return end
|
||||
|
||||
local ctrlDown = love.keyboard.isDown("lctrl") or love.keyboard.isDown("rctrl")
|
||||
love.graphics.setBlendMode("replace")
|
||||
if ctrlDown then
|
||||
love.graphics.setCanvas(gameloop.buffer.pixel.bg)
|
||||
love.graphics.setCanvas(levelloop.Canvas.bg)
|
||||
else
|
||||
love.graphics.setCanvas(gameloop.buffer.pixel.fg)
|
||||
love.graphics.setCanvas(levelloop.Canvas.fg)
|
||||
end
|
||||
|
||||
if love.mouse.isDown(1) then -- only delete
|
||||
color_push()
|
||||
utils.color_push()
|
||||
love.graphics.setColor(drawing.color)
|
||||
love.graphics.draw(cursor.image, thisx, thisy)
|
||||
color_pop()
|
||||
utils.color_pop()
|
||||
if ctrlDown then
|
||||
drawing.draw_texture(drawing.cursorHash, thisx, thisy, "bg", drawing.color)
|
||||
else
|
||||
|
@ -120,7 +125,8 @@ function drawing.input(dt)
|
|||
cursor.image:getPixelHeight())
|
||||
end
|
||||
else
|
||||
color_push()
|
||||
love.graphics.setBlendMode("replace")
|
||||
utils.color_push()
|
||||
love.graphics.setColor(0,0,0,0)
|
||||
love.graphics.rectangle("fill", thisx, thisy,
|
||||
cursor.image:getPixelWidth(),
|
||||
|
@ -137,9 +143,9 @@ function drawing.input(dt)
|
|||
cursor.image:getPixelWidth(),
|
||||
cursor.image:getPixelHeight())
|
||||
end
|
||||
color_pop()
|
||||
utils.color_pop()
|
||||
love.graphics.setBlendMode("alpha")
|
||||
end
|
||||
love.graphics.setBlendMode("alpha")
|
||||
love.graphics.setCanvas()
|
||||
end
|
||||
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
|
||||
local last = drawing.lastTexture
|
||||
if hash ~= last.hash or x ~= last.x or y ~= last.y or layer ~= last.layer then
|
||||
gameloop.networkSend(unit("drawTexture", x, y,
|
||||
layer, love.data.encode("string", "base64", hash), color[1], color[2], color[3], color[4]))
|
||||
color.nwType = "color"
|
||||
levelloop.remoteCall("drawTexture", x, y, layer, hash, color)
|
||||
end
|
||||
last.hash, last.x, last.y, last.layer = hash, x, y, layer
|
||||
end
|
||||
|
@ -168,7 +174,7 @@ function drawing.del_texture(x, y, width, height, layer)
|
|||
local last = drawing.lastTextureDel
|
||||
if x ~= last.x or y ~= last.y or width ~= last.width
|
||||
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
|
||||
end
|
||||
end
|
||||
|
@ -177,7 +183,7 @@ end
|
|||
local lastDWP = { x = 0, y = 0, width = 0, height = 0}
|
||||
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
|
||||
gameloop.networkSend(unit("drawPhysics", x, y, width, height))
|
||||
levelloop.remoteCall("drawPhysics", x, y, width, height)
|
||||
physics.draw_solid(x, y, width, height)
|
||||
lastDWP.x, lastDWP.y, lastDWP.width, lastDWP.height = x, y, width, height
|
||||
end
|
||||
|
@ -187,14 +193,15 @@ end
|
|||
local lastRMP = { x = 0, y = 0, width = 0, height = 0}
|
||||
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
|
||||
gameloop.networkSend(unit("deletePhysics", x, y, width, height))
|
||||
levelloop.remoteCall("deletePhysics", x, y, width, height)
|
||||
physics.remove_solid(x, y, width, height)
|
||||
lastRMP.x, lastRMP.y, lastRMP.width, lastRMP.height = x, y, width, height
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
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),
|
||||
image = love.graphics.newImage(path)}
|
||||
drawing.color = {1, 1, 1, 1}
|
||||
|
@ -203,7 +210,6 @@ function drawing.init()
|
|||
drawing.cursor = textures[drawing.cursorHash]
|
||||
|
||||
texture, path = nil, nil
|
||||
local clientChannel = love.thread.getChannel ( "clientChannel" );
|
||||
|
||||
mouse.lastpos = {}
|
||||
mouse.lastpos.x = 0
|
||||
|
|
315
lua/gameloop.lua
|
@ -1,26 +1,44 @@
|
|||
gameloop = {}
|
||||
gameloop.world = nil
|
||||
gameloop.textures = {}
|
||||
local rpc = require("server.rpc")
|
||||
local v2_message = require("lua.network")
|
||||
local socket = require "socket"
|
||||
local util = require("server.utils")
|
||||
levelloop = {}
|
||||
levelloop.CLIENT = true
|
||||
levelloop.world = nil
|
||||
levelloop.textures = {}
|
||||
levelloop.avatars = {}
|
||||
commands = {}
|
||||
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()
|
||||
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)
|
||||
|
||||
-- Draw background
|
||||
if drawBackground then
|
||||
color_push()
|
||||
utils.color_push()
|
||||
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.draw(gameloop.buffer.pixel.bg, layerTransform)
|
||||
color_pop()
|
||||
love.graphics.draw(levelloop.Canvas.bg, layerTransform)
|
||||
utils.color_pop()
|
||||
end
|
||||
|
||||
-- draw player
|
||||
for i, player in pairs(players) do
|
||||
for i, player in pairs(participants) do
|
||||
local playerTransform = love.math.newTransform(
|
||||
math.floor(player.x) * scale + math.floor(view.x) * scale,
|
||||
math.floor(player.y) * scale + math.floor(view.y) * scale,
|
||||
|
@ -29,7 +47,7 @@ local function normalDraw()
|
|||
love.graphics.draw(player.avatar, playerTransform)
|
||||
if player.name 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
|
||||
local labelTransform = love.math.newTransform(
|
||||
math.floor(player.x - (player.label:getWidth() / 2) +
|
||||
|
@ -41,7 +59,7 @@ local function normalDraw()
|
|||
end
|
||||
|
||||
-- Draw foreground
|
||||
love.graphics.draw(gameloop.buffer.pixel.fg, layerTransform)
|
||||
love.graphics.draw(levelloop.Canvas.fg, layerTransform)
|
||||
|
||||
-- Draw UI
|
||||
love.graphics.draw(ui.buffer)
|
||||
|
@ -51,184 +69,156 @@ end
|
|||
local function debugDraw()
|
||||
normalDraw()
|
||||
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.draw(gameloop.buffer.pixel.dbg2, layerTransform)
|
||||
|
||||
love.graphics.setCanvas(gameloop.buffer.pixel.dbg2)
|
||||
love.graphics.setCanvas(levelloop.Canvas.dbg2)
|
||||
love.graphics.clear()
|
||||
love.graphics.setCanvas()
|
||||
end
|
||||
|
||||
|
||||
local function normalNetworkSend(args)
|
||||
if clientID == "0" then print("Invalid clientID?") end
|
||||
local request = "poppyV002" ..US.. clientID ..US.. args
|
||||
gameloop.client:send(request)
|
||||
local function remoteCall(name, ...)
|
||||
local request = protocolVersion .. format.encodeFunction(name, levelloop.clientID or 0, ...)
|
||||
levelloop.client:send(request)
|
||||
end
|
||||
|
||||
|
||||
local function debugNetworkSend(args)
|
||||
assert(clientID ~= 0)
|
||||
local request = "poppyV002" ..US.. clientID ..US.. args
|
||||
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()
|
||||
local function networkSync()
|
||||
while true do
|
||||
local message, fault = levelloop.client:receive()
|
||||
if not message then
|
||||
if errorString == "timeout" then return else
|
||||
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()
|
||||
if fault == "timeout" then
|
||||
return
|
||||
else
|
||||
error(fault)
|
||||
end
|
||||
end
|
||||
local response = rpc.validate(message)
|
||||
if not response.errorMsg then
|
||||
rpc.execute(v2_message, response.clientID, response.record)
|
||||
message = nil
|
||||
local protocolVersion, index = string.unpack("<c4", message, 1)
|
||||
if protocolVersion ~= "PPY\x03" then
|
||||
print("Dropping packet with invalid protocol " .. protocolVersion)
|
||||
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
|
||||
|
||||
|
||||
local function errorhandlerNetwork(msg)
|
||||
gameloop.networkSend(unit("playerLeave"))
|
||||
return errorhandler(msg)
|
||||
levelloop.remoteCall("playerLeave")
|
||||
return errorHandler.redscreen(msg)
|
||||
end
|
||||
|
||||
|
||||
function errorhandler(msg)
|
||||
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)
|
||||
function levelloop.mousepressed(mousex, mousey)
|
||||
ui.mousepressed(mousex, mousey)
|
||||
end
|
||||
|
||||
|
||||
function gameloop.init(server, nickname)
|
||||
love.errorhandler = errorhandler
|
||||
assert(server, "Gameloop called without server to connect to?")
|
||||
assert(nickname, "Gameloop called without nickname?")
|
||||
gameloop.server = server
|
||||
|
||||
function levelloop.loadAvatars()
|
||||
local container = {}
|
||||
local entries = love.filesystem.getDirectoryItems("textures/player")
|
||||
if #entries == 0 then return error("no avatars found") end
|
||||
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
|
||||
|
||||
|
||||
love.graphics.setDefaultFilter("nearest", "nearest", 0)
|
||||
|
||||
|
||||
layer = true
|
||||
|
||||
|
||||
grid = 16
|
||||
color = {}
|
||||
mouse = {}
|
||||
|
||||
|
||||
window = {}
|
||||
window.x, window.y = love.graphics.getDimensions()
|
||||
|
||||
players = {}
|
||||
participants = {}
|
||||
textures = {}
|
||||
|
||||
|
||||
view = {}
|
||||
view.x = 0
|
||||
view.y = 0
|
||||
clientID = "0"
|
||||
|
||||
|
||||
gameloop.client = socket.udp()
|
||||
gameloop.client:setpeername(server.adress, server.port)
|
||||
gameloop.client:settimeout(0)
|
||||
-- Must be done now because it has to be ready before "init" returns from the server
|
||||
-- which is /before/ the game world is loaded
|
||||
levelloop.loadAvatars()
|
||||
|
||||
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)
|
||||
|
||||
local request = "poppyV002" ..US.. "0" ..US.. unit("init", nickname)
|
||||
gameloop.client:send(request)
|
||||
if NETWORK_DEBUG then print("=> " .. util.pprint(request)) end
|
||||
remoteCall("init", nickname, avatarHash)
|
||||
|
||||
if not NETWORK_DEBUG then
|
||||
gameloop.networkSend = normalNetworkSend
|
||||
gameloop.networkSync = normalNetworkSync
|
||||
else
|
||||
gameloop.networkSend = debugNetworkSend
|
||||
gameloop.networkSync = debugNetworkSync
|
||||
end
|
||||
levelloop.remoteCall = remoteCall
|
||||
levelloop.networkSync = networkSync
|
||||
|
||||
love.update = function(dt)
|
||||
gameloop.networkSync(dt)
|
||||
networkSync(dt)
|
||||
love.timer.sleep((1/60) -dt)
|
||||
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.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
|
||||
|
||||
|
||||
function gameloop.normalVisible(dt)
|
||||
gameloop.networkSync(dt)
|
||||
function levelloop.normalVisible(dt)
|
||||
networkSync(dt)
|
||||
camera.input(dt)
|
||||
physics.input(dt)
|
||||
drawing.input(dt)
|
||||
|
@ -238,20 +228,20 @@ function gameloop.normalVisible(dt)
|
|||
end
|
||||
|
||||
|
||||
function gameloop.reducedVisible(dt)
|
||||
gameloop.networkSync(dt)
|
||||
function levelloop.reducedVisible(dt)
|
||||
networkSync(dt)
|
||||
physics.update(dt)
|
||||
love.timer.sleep((1/60) -dt)
|
||||
end
|
||||
|
||||
|
||||
function gameloop.keypressed(key, _)
|
||||
function levelloop.keypressed(key, _)
|
||||
physics.keypressed(key, _)
|
||||
camera.keypressed(key, _)
|
||||
end
|
||||
|
||||
|
||||
function gameloop.keyreleased(key, _)
|
||||
function levelloop.keyreleased(key, _)
|
||||
if key == "r" then
|
||||
drawBackground = not drawBackground
|
||||
end
|
||||
|
@ -261,53 +251,52 @@ function gameloop.keyreleased(key, _)
|
|||
end
|
||||
|
||||
|
||||
function gameloop.loadworld(world)
|
||||
function levelloop.loadworld(world)
|
||||
-- Game connected!
|
||||
love.errorhandler = errorhandlerNetwork
|
||||
local buffer = {}
|
||||
buffer.pixel = {}
|
||||
buffer.pixel.fg = love.graphics.newCanvas(world.x, world.y)
|
||||
buffer.pixel.bg = love.graphics.newCanvas(world.x, world.y)
|
||||
--love.errorhandler = errorhandlerNetwork
|
||||
print("make canvas!")
|
||||
levelloop.Canvas.fg = love.graphics.newCanvas(world.x, world.y)
|
||||
levelloop.Canvas.bg = love.graphics.newCanvas(world.x, world.y)
|
||||
if PHYSICS_DEBUG then
|
||||
buffer.pixel.dbg = love.graphics.newCanvas(world.x, world.y)
|
||||
buffer.pixel.dbg2 = love.graphics.newCanvas(world.x, world.y)
|
||||
levelloop.Canvas.dbg = love.graphics.newCanvas(world.x, world.y)
|
||||
levelloop.Canvas.dbg2 = love.graphics.newCanvas(world.x, world.y)
|
||||
end
|
||||
buffer.physics = {}
|
||||
gameloop.buffer = buffer
|
||||
levelloop.Canvas.physics = {}
|
||||
levelloop.world = world
|
||||
|
||||
|
||||
ui.init()
|
||||
drawing.init()
|
||||
love.update = gameloop.normalVisible
|
||||
love.update = levelloop.normalVisible
|
||||
love.visible = function(visible)
|
||||
if visible then
|
||||
love.update = gameloop.normalVisible
|
||||
love.update = levelloop.normalVisible
|
||||
else
|
||||
love.update = gameloop.reducedVisible
|
||||
love.update = levelloop.reducedVisible
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
drawBackground = true
|
||||
|
||||
love.keyreleased = gameloop.keyreleased
|
||||
love.keypressed = gameloop.keypressed
|
||||
love.mousepressed = gameloop.mousepressed
|
||||
love.keyreleased = levelloop.keyreleased
|
||||
love.keypressed = levelloop.keypressed
|
||||
love.mousepressed = levelloop.mousepressed
|
||||
if not PHYSICS_DEBUG then
|
||||
love.draw = normalDraw
|
||||
else
|
||||
love.draw = debugDraw
|
||||
end
|
||||
|
||||
|
||||
|
||||
love.resize = function()
|
||||
normalDraw()
|
||||
ui.resize()
|
||||
camera.resize()
|
||||
end
|
||||
|
||||
|
||||
love.quit = function()
|
||||
gameloop.networkSend(unit("playerLeave"))
|
||||
levelloop.remoteCall("playerLeave")
|
||||
end
|
||||
|
||||
gameloop.world = world
|
||||
levelloop.world = world
|
||||
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 = {}
|
||||
uiState = {}
|
||||
local layout = {}
|
||||
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)
|
||||
end
|
||||
|
||||
|
||||
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
|
||||
color_push()
|
||||
utils.color_push()
|
||||
love.graphics.setCanvas(container.canvas)
|
||||
x, y = layout.handle(container[1], x, y, simulate)
|
||||
love.graphics.setCanvas()
|
||||
color_pop()
|
||||
utils.color_pop()
|
||||
return x, y
|
||||
end
|
||||
|
||||
|
||||
function layout.rotate(container, x, y, simulate)
|
||||
assert_num("layout.rotate", 2, x, y)
|
||||
assert_num("layout.rotate (container)", container.rotation)
|
||||
asserts.number("layout.rotate", 2, x, y)
|
||||
asserts.number("layout.rotate (container)", container.rotation)
|
||||
|
||||
local transform = love.math.newTransform(0, 0, container.rotation)
|
||||
local noTransform = love.math.newTransform(0, 0, 0)
|
||||
love.graphics.replaceTransform(transform)
|
||||
|
@ -30,7 +41,8 @@ end
|
|||
|
||||
|
||||
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)
|
||||
if not simulate then
|
||||
love.graphics.rectangle("line", x, y, x2 -x, y2 -y)
|
||||
|
@ -41,31 +53,22 @@ end
|
|||
|
||||
function layout.cursorSelect(container, x, y, simulate)
|
||||
x2, y2 = layout.handle(container[1], x, y, simulate)
|
||||
assert(container.uiSelect)
|
||||
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 })
|
||||
end
|
||||
return x2, y2
|
||||
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)
|
||||
assert(container.width or container.height, "No arguments for layout.spacer!")
|
||||
assert(container.width == container.width,
|
||||
"Argument container.width to layout.spacer must not be NaN!")
|
||||
assert(container.height == container.height,
|
||||
"Argument container.height to layout.spacer must not be NaN!")
|
||||
|
||||
local width = container.width or 0
|
||||
local height = container.height or 0
|
||||
if container[1] then
|
||||
|
@ -82,12 +85,12 @@ function layout.drawTexture(container, x, y, simulate)
|
|||
if not simulate then
|
||||
love.graphics.draw(texture, x, y, 0, scale, scale)
|
||||
end
|
||||
return x + texture:getWidth()* scale, y + texture:getHeight() * scale
|
||||
return x + (texture:getWidth() * scale), y + texture:getHeight() * scale
|
||||
end
|
||||
|
||||
|
||||
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
|
||||
if not texture.image then
|
||||
texture.image = love.graphics.newImage(texture.data)
|
||||
|
@ -99,42 +102,80 @@ function layout.drawHash(container, x, y, simulate)
|
|||
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)
|
||||
assert(x and y)
|
||||
asserts.number("layout.colorPicker", 2, x, y)
|
||||
|
||||
local startx, starty = x, y
|
||||
granularity = container.granularity or 0.2
|
||||
local points = {}
|
||||
local myx, myy = x, y
|
||||
local endx = 0
|
||||
local pointsize = 16
|
||||
local pointSize = container.pointSize or 8
|
||||
|
||||
if not simulate then
|
||||
color_push()
|
||||
love.graphics.setPointSize(pointsize)
|
||||
utils.color_push()
|
||||
love.graphics.setPointSize(pointSize)
|
||||
|
||||
for r=0, 1.0, granularity do
|
||||
for g=0, 1.0, granularity do
|
||||
for b=0, 1.0, granularity do
|
||||
love.graphics.setColor(r, g, b, 1)
|
||||
myx=myx+16
|
||||
love.graphics.points(myx-4.5, myy+4.5)
|
||||
myx=myx+pointSize
|
||||
love.graphics.points(myx-(pointSize/2) -.5, myy +(pointSize/2))
|
||||
end
|
||||
end
|
||||
myy = myy + 16
|
||||
myy = myy + pointSize
|
||||
if myx > endx then endx = myx end
|
||||
myx = x
|
||||
end
|
||||
color_pop()
|
||||
utils.color_pop()
|
||||
|
||||
local iterationCount = (1.0 / granularity) + 1
|
||||
maxx = x + pointsize * iterationCount * iterationCount
|
||||
table.insert(uiState, { granularity = granularity, pointsize = pointsize,
|
||||
maxx = x + pointSize * iterationCount * iterationCount
|
||||
table.insert(layout.uiState.GameUI, { granularity = granularity, pointSize = pointSize,
|
||||
kind=container.kind,
|
||||
x=x, x2=maxx, y=y, y2=myy })
|
||||
else
|
||||
local iterationCount = (1.0 / granularity) + 1
|
||||
maxx = x + pointsize * iterationCount * iterationCount
|
||||
maxy = y + pointsize * iterationCount
|
||||
maxx = x + pointSize * iterationCount * iterationCount
|
||||
maxy = y + pointSize * iterationCount
|
||||
return maxx, maxy
|
||||
end
|
||||
return maxx, myy
|
||||
end
|
||||
|
@ -142,34 +183,64 @@ end
|
|||
|
||||
function layout.color(container, x, y, simulate)
|
||||
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)
|
||||
color_push()
|
||||
utils.color_push()
|
||||
love.graphics.setColor(container.color)
|
||||
x, y = layout.handle(container[1], x, y, simulate)
|
||||
color_pop()
|
||||
utils.color_pop()
|
||||
return x, y
|
||||
end
|
||||
|
||||
|
||||
function layout.rect(container, x, y, simulate)
|
||||
assert_num("layout.rect", 2, x, y)
|
||||
assert_num("layout.rect (container)", 2, container.width, container.height)
|
||||
if not simulate then
|
||||
if container.color then
|
||||
color_push()
|
||||
love.graphics.setColor(container.color.r, container.color.g, container.color.b, container.color.a or 1)
|
||||
asserts.number("layout.rect", 2, x, y)
|
||||
asserts.number("layout.rect (container)", 2, container.width, container.height)
|
||||
|
||||
if simulate then return x + container.width, y + container.height end
|
||||
|
||||
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)
|
||||
color_pop()
|
||||
else
|
||||
love.graphics.rectangle(container.fill or "fill", x, y, container.width, container.height)
|
||||
end
|
||||
end
|
||||
|
||||
return x + container.width, y + container.height
|
||||
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)
|
||||
assert_num("layout.overlayRect", 2, x, y)
|
||||
asserts.number("layout.overlayRect", 2, x, y)
|
||||
|
||||
winx, winy = layout.handle(container[1], x, y, simulate)
|
||||
width = winx -x
|
||||
height = winy -y
|
||||
|
@ -180,11 +251,33 @@ function layout.overlayRect(container, x, y, simulate)
|
|||
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)
|
||||
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 )
|
||||
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
|
||||
love.graphics.draw(text, x, y)
|
||||
end
|
||||
|
@ -214,7 +307,40 @@ function layout.horizontal(container, x, y, simulate)
|
|||
if ret.y > bigy then bigy = ret.y end
|
||||
x = ret.x
|
||||
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
|
||||
|
||||
|
||||
|
@ -247,13 +373,22 @@ function layout.vertical(container, x, y, simulate)
|
|||
end
|
||||
|
||||
|
||||
function layout.skip(container, x, y, simulate)
|
||||
return x, y
|
||||
end
|
||||
|
||||
|
||||
local depth = -1
|
||||
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")
|
||||
|
||||
local name = container["name"]
|
||||
if not name then return end
|
||||
assert(name, "layout.handle container without name")
|
||||
assert(layout[name], "Unknown layout function " .. name .. " called")
|
||||
|
||||
depth = depth +1
|
||||
local depthCursor = ""
|
||||
for i=0, depth do
|
||||
|
@ -261,22 +396,24 @@ function layout.handle(container, x, y, simulate)
|
|||
end
|
||||
if depth == 0 then
|
||||
if simulate then
|
||||
debugPrint("Starting simulation")
|
||||
layout.debugPrint("Starting simulation")
|
||||
else
|
||||
debugPrint("Starting rendering")
|
||||
layout.debugPrint("Starting rendering")
|
||||
end
|
||||
end
|
||||
debugPrint(depthCursor.. ">" .. container.name)
|
||||
layout.debugPrint(depthCursor.. ">" .. container.name)
|
||||
x, y = layout[name](container, x, y, simulate)
|
||||
assert_num("layout.handle" .. container.name .. " is not returning properly!", 2, x, y)
|
||||
debugPrint(depthCursor.."<" .. container.name)
|
||||
asserts.number("layout.handle" .. container.name .. " is not returning properly!", 2, x, y)
|
||||
layout.debugPrint(depthCursor.."<" .. container.name)
|
||||
depth = depth -1
|
||||
if depth == -1 then
|
||||
if simulate then
|
||||
debugPrint("simulation ended\n")
|
||||
layout.debugPrint("simulation ended\n")
|
||||
else
|
||||
debugPrint("rendering ended\n")
|
||||
layout.debugPrint("rendering ended\n")
|
||||
end
|
||||
end
|
||||
return x, y
|
||||
end
|
||||
|
||||
return layout
|
||||
|
|
311
lua/menuloop.lua
|
@ -1,9 +1,16 @@
|
|||
menu = {}
|
||||
menu.serverlist = {
|
||||
{description = "A localhost server [ipv4]", host = "127.0.0.1", port = "11150"},
|
||||
{description = "A localhost server [ipv6]", host = "::1", port = "11150"}
|
||||
{description = "A localhost server", 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
|
||||
function menu.keyreleased(key, _)
|
||||
if key == "lalt" then
|
||||
|
@ -25,11 +32,6 @@ function menu.keypressed(key, _)
|
|||
end
|
||||
|
||||
|
||||
function menu.update(dt)
|
||||
love.timer.sleep((1/60) -dt)
|
||||
end
|
||||
|
||||
|
||||
function menu.resize()
|
||||
menu.width, menu.height = love.window.getMode()
|
||||
menu.Canvas = love.graphics.newCanvas(width, height)
|
||||
|
@ -40,20 +42,50 @@ function menu.resize()
|
|||
end
|
||||
|
||||
|
||||
local firstClient = true
|
||||
function menu.mousepressed(mousex, mousey)
|
||||
local textures = gameloop.textures
|
||||
for i, v in ipairs(uiState) do
|
||||
if mousex >= v.x and mousex <= v.x2 then
|
||||
if mousey >= v.y and mousey <= v.y2 then
|
||||
if v.kind == "server" then
|
||||
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" }
|
||||
local lastNames = { "Müller", "Schmidt", "Meier", "Bauer", "Werner", "Schumacher", "Bergmann", "Eisenhauer", "Heisenberg" }
|
||||
local fullName = names[love.math.random(1, #names)] .. " " .. lastNames[love.math.random(1, #lastNames)]
|
||||
local serverID = v.identifier
|
||||
local serverEntry = menu.serverlist[serverID]
|
||||
gameloop.init({ adress = serverEntry.host, port = serverEntry.port }, fullName)
|
||||
uiState = {}
|
||||
for i, v in ipairs(layout.uiState.MainMenu) do
|
||||
if mousex >= v.x and mousex <= v.x2 and mousey >= v.y and mousey <= v.y2 then
|
||||
if v.kind == "localServer" then
|
||||
menuMusic[menuMusic.trac]:stop()
|
||||
love.graphics.reset()
|
||||
love.graphics.origin()
|
||||
love.graphics.clear(.6, 0, .6)
|
||||
love.graphics.setColor(1, 1, 1)
|
||||
love.graphics.present()
|
||||
local serverloop = require("server.main")
|
||||
serverloop.init(v.identifier)
|
||||
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
|
||||
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
|
||||
|
@ -61,68 +93,245 @@ end
|
|||
|
||||
|
||||
menu.serverentry = function(id, description, host, port)
|
||||
return {name = "horizontal",
|
||||
{name = "label", text = description },
|
||||
{name = "spacer", width = 20 },
|
||||
{name = "label", text = "[".. host .."]"},
|
||||
{name = "label", text = ":".. port },
|
||||
{name = "spacer", width = 25 },
|
||||
{name = "cursorSelect", kind = "server", identifier = id,
|
||||
{name = "drawTexture", texture = love.graphics.newImage("textures/menu/serverPlay.png")}}}
|
||||
return {name = "cursorSelect", kind = "server", identifier = id, uiSelect = "MainMenu",
|
||||
{name = "horizontal",
|
||||
{name = "drawTexture", texture = icon.joinServer},
|
||||
{name = "spacer", width = 10 },
|
||||
{name = "labelWShadow", text = description },
|
||||
{name = "spacer", width = 20 },
|
||||
{name = "labelWShadow", text = "host: ".. host, font = fonts.smallFont},
|
||||
{name = "spacer", width = 10 },
|
||||
{name = "labelWShadow", text = "port:".. port, font = fonts.smallFont},
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
menu.layoutServerList = function()
|
||||
local list = {name = "vertical"}
|
||||
local list = {}
|
||||
for i, v in ipairs(menu.serverlist) do
|
||||
table.insert(list, menu.serverentry(i, v.description, v.host, v.port))
|
||||
end
|
||||
return list
|
||||
return unpack(list)
|
||||
end
|
||||
|
||||
|
||||
menu.layout = {name ="vertical",
|
||||
{name = "spacer", height = 20},
|
||||
{name = "horizontal",
|
||||
{name = "spacer", width = 20},
|
||||
{name = "drawTexture", texture = love.graphics.newImage("ressources/Poppy.png")},
|
||||
{name = "label", text = "Welcome to poppy!"},
|
||||
},
|
||||
{name = "horizontal",
|
||||
{name = "spacer", width = 60},
|
||||
{name = "vertical",
|
||||
{name = "spacer", height = 60},
|
||||
{name = "label", text = "Pick a server to join!"},
|
||||
{name = "spacer", height = 20},
|
||||
menu.layoutServerList()
|
||||
menu.layoutLocalServer = function()
|
||||
return {name = "vertical",
|
||||
{name = "cursorSelect", kind = "localServer",
|
||||
identifier = {"::1", "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"},
|
||||
}
|
||||
},
|
||||
{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()
|
||||
color_push()
|
||||
function drawMenu(message)
|
||||
utils.color_push()
|
||||
love.graphics.setCanvas(menu.Canvas)
|
||||
uiState = {}
|
||||
layout.uiState.MainMenu = {}
|
||||
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()
|
||||
color_pop()
|
||||
utils.color_pop()
|
||||
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)
|
||||
layout.handle(debugAudio(), 600, 300)
|
||||
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()
|
||||
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)
|
||||
drawMenu()
|
||||
love.draw = menu.draw
|
||||
love.update = menu.update
|
||||
love.keyreleased = menu.keyreleased
|
||||
love.keypressed = menu.keypressed
|
||||
love.mousepressed = menu.mousepressed
|
||||
end
|
||||
|
||||
|
||||
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 = {}
|
||||
|
||||
local asserts = require("shared.asserts")
|
||||
local utils = require("shared.utils")
|
||||
|
||||
function physics.keyreleased_debug(key, _)
|
||||
if key == "kp4" then
|
||||
player.x = player.x -1
|
||||
|
@ -17,30 +20,30 @@ end
|
|||
|
||||
|
||||
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_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
|
||||
|
||||
|
||||
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_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
|
||||
|
||||
|
||||
function draw_colision(x, y, x2, y2, pixelx, pixely)
|
||||
color_push()
|
||||
utils.color_push()
|
||||
love.graphics.setLineWidth(1)
|
||||
love.graphics.setDefaultFilter("nearest", "nearest", 0)
|
||||
love.graphics.setCanvas(gameloop.buffer.pixel.dbg2)
|
||||
love.graphics.setCanvas(levelloop.Canvas.dbg2)
|
||||
if pixelx then
|
||||
love.graphics.setColor(.6, 0, .6, 1)
|
||||
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)
|
||||
end
|
||||
love.graphics.setCanvas()
|
||||
color_pop()
|
||||
utils.color_pop()
|
||||
end
|
||||
|
||||
|
||||
function physics.is_blocked_x(x, y, sizex)
|
||||
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
|
||||
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
|
||||
return true
|
||||
end
|
||||
|
@ -71,9 +74,9 @@ end
|
|||
|
||||
function physics.is_blocked_y(x, y, sizey)
|
||||
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
|
||||
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
|
||||
return true
|
||||
end
|
||||
|
@ -85,16 +88,14 @@ end
|
|||
|
||||
function physics.keypressed( key, code, isrepeat)
|
||||
if isrepeat then return end
|
||||
|
||||
|
||||
if camera.target == 0 then return end
|
||||
player = players[camera.target]
|
||||
local player = participants[levelloop.clientID]
|
||||
if key == "p" then
|
||||
player.flying = not player.flying
|
||||
levelloop.remoteCall("moveFlying", player.flying)
|
||||
if player.flying then
|
||||
player.speed.y = 0
|
||||
gameloop.networkSend(unit("moveFlying", "true"))
|
||||
else
|
||||
gameloop.networkSend(unit("moveFlying", "false"))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -102,7 +103,7 @@ end
|
|||
|
||||
function physics.input(dt)
|
||||
if camera.target == 0 then return end
|
||||
player = players[camera.target]
|
||||
local player = participants[levelloop.clientID]
|
||||
local speedModified = dt * 20
|
||||
if love.keyboard.isDown("rshift") or love.keyboard.isDown("lshift")
|
||||
then
|
||||
|
@ -124,7 +125,7 @@ function physics.input(dt)
|
|||
local posx = player.x + player.avatar:getWidth()
|
||||
local posy = player.y + player.avatar:getHeight()
|
||||
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
|
||||
end
|
||||
end
|
||||
|
@ -132,16 +133,16 @@ end
|
|||
|
||||
|
||||
function physics.cap_world(width, height, newx, newy, new_sx, new_sy)
|
||||
if newx > gameloop.world.x - width then
|
||||
newx = gameloop.world.x - width
|
||||
if newx > levelloop.world.x - width then
|
||||
newx = levelloop.world.x - width
|
||||
new_sx = 0
|
||||
end
|
||||
if newx < 0 then
|
||||
newx = 0
|
||||
new_sx = 0
|
||||
end
|
||||
if newy > gameloop.world.y - height then
|
||||
newy = gameloop.world.y - height
|
||||
if newy > levelloop.world.y - height then
|
||||
newy = levelloop.world.y - height
|
||||
new_sy = 0
|
||||
end
|
||||
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
|
||||
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
|
||||
local new_sx = player.speed.x - player.speed.x / 16
|
||||
local new_sy
|
||||
if player.flying then
|
||||
new_sy = player.speed.y - player.speed.y / 16
|
||||
else
|
||||
new_sy = player.speed.y - player.speed.y / 6
|
||||
function physics.updatePlayer(dt, player, localplayer)
|
||||
local new_sx, new_sy
|
||||
if player.flying then
|
||||
new_sy = player.speed.y - player.speed.y / 6
|
||||
new_sx = player.speed.x - player.speed.x / 6
|
||||
else
|
||||
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
|
||||
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 not new_sy then new_sy = 0 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 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)
|
||||
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
|
||||
player.y = intermediateY
|
||||
return
|
||||
end
|
||||
|
||||
if blocked_y and not blocked_x then
|
||||
player.x = intermediateX
|
||||
return
|
||||
end
|
||||
|
||||
do -- SCOPE
|
||||
local blocked_x, blocked_y = false, false
|
||||
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
|
||||
if blocked_x and blocked_y then
|
||||
return
|
||||
end
|
||||
-- cap low speed
|
||||
if new_sx > -.1 and new_sx < .1 then
|
||||
new_sx = 0
|
||||
end
|
||||
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)
|
||||
|
||||
if not blocked_x and not blocked_y then
|
||||
player.x = intermediateX
|
||||
player.y = intermediateY
|
||||
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
|
||||
|
||||
|
||||
|
@ -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)
|
||||
if last.packet > 0.03 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.x, last.y = player.x, player.y
|
||||
last.speedx, last.speedy = player.speedx, player.speedy
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
local playerfuncs = {}
|
||||
playerfuncs.newPlayer = function(arg)
|
||||
assert(arg.avatar)
|
||||
assert(arg.name)
|
||||
local playerTable = {}
|
||||
playerTable.avatar = love.graphics.newImage("textures/player/andysphinx/personb.png")
|
||||
playerTable.avatar = arg.avatar
|
||||
playerTable.name = arg.name
|
||||
playerTable.speed = {}
|
||||
playerTable.speed.x = 0
|
||||
playerTable.speed.y = 0
|
||||
playerTable.x = 16
|
||||
playerTable.x = 16
|
||||
playerTable.y = 16
|
||||
playerTable.flying = false
|
||||
return playerTable
|
||||
|
|
395
lua/ui.lua
|
@ -1,40 +1,83 @@
|
|||
ui = {}
|
||||
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)
|
||||
scale = p_scale
|
||||
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
|
||||
[A/D] Move
|
||||
[Hold Shift] Move faster
|
||||
{A/D} Move
|
||||
{Shift} Move faster
|
||||
[Space] Jump
|
||||
[A/S/DW] Move (flying)
|
||||
{A/S/DW} Move (flying)
|
||||
[C] Clear canvas
|
||||
[F] Fill foreground
|
||||
[G] Fill background
|
||||
[Mouse1] Draw
|
||||
[Mouse2] Delete
|
||||
[Hold ctrl] Draw on Background
|
||||
[+/-/PGUP/PGDWN] Zoom canvas
|
||||
[Cursor keys] Move camera
|
||||
{Mouse1} Draw
|
||||
{Mouse2} Delete
|
||||
{ctrl} Background draw
|
||||
[PGUP/PGDWN] Zoom canvas
|
||||
{Cursor keys} Move camera
|
||||
]]
|
||||
|
||||
|
||||
local menuVisible = false
|
||||
local textEnabled = false
|
||||
local textEntry = ""
|
||||
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
|
||||
textEnabled = true
|
||||
love.keyboard.setTextInput(true)
|
||||
|
@ -44,14 +87,14 @@ function ui.keyreleased(key, _)
|
|||
love.visible = function(visible)
|
||||
if visible then
|
||||
love.update = function(dt)
|
||||
gameloop.networkSync(dt)
|
||||
levelloop.networkSync(dt)
|
||||
drawing.input(dt)
|
||||
physics.update(dt)
|
||||
camera.update(dt)
|
||||
end
|
||||
else
|
||||
love.update = function(dt)
|
||||
gameloop.networkSync(dt)
|
||||
levelloop.networkSync(dt)
|
||||
physics.update(dt)
|
||||
end
|
||||
end
|
||||
|
@ -61,14 +104,14 @@ function ui.keyreleased(key, _)
|
|||
if key == "return" and not love.keyboard.isDown("lalt") then
|
||||
textEnabled = false
|
||||
if textEntry ~= "" then
|
||||
gameloop.networkSend(unit("chatMessage", textEntry))
|
||||
levelloop.remoteCall("chatMessage", textEntry)
|
||||
textEntry = ""
|
||||
end
|
||||
ui.draw(window.x, window.y)
|
||||
love.keyboard.setTextInput(false)
|
||||
love.keyreleased = gameloop.keyreleased
|
||||
love.keypressed = gameloop.keypressed
|
||||
love.update = gameloop.normalVisible
|
||||
love.keyreleased = levelloop.keyreleased
|
||||
love.keypressed = levelloop.keypressed
|
||||
love.update = levelloop.normalVisible
|
||||
elseif key == "backspace" then
|
||||
textEntry = string.sub(textEntry, 0, #textEntry -1)
|
||||
ui.draw(window.x, window.y)
|
||||
|
@ -76,8 +119,8 @@ function ui.keyreleased(key, _)
|
|||
end
|
||||
love.keypressed = function(key, _) return end
|
||||
else
|
||||
love.keyreleased = gameloop.keyreleased
|
||||
love.keypressed = gameloop.keypressed
|
||||
love.keyreleased = levelloop.keyreleased
|
||||
love.keypressed = levelloop.keypressed
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -97,11 +140,11 @@ function ui.chatEntry()
|
|||
fill = "fill",
|
||||
color = { r = .1, g = .1, b = .1, .7 }
|
||||
},
|
||||
{name = "frame",
|
||||
{name = "label",
|
||||
text = passedEntry
|
||||
{name = "frame",
|
||||
{name = "label",
|
||||
text = passedEntry
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return chatEntry
|
||||
end
|
||||
|
@ -125,22 +168,22 @@ function ui.addChatEntry(user, message)
|
|||
end
|
||||
local entry = {name = "rotate",
|
||||
rotation = 0, -- OKAY DIEGO >:(
|
||||
{name = "copySize",
|
||||
{name = "rect",
|
||||
fill = "fill",
|
||||
color = { r = .1, g = .1, b = .1, a = .6 }
|
||||
{name = "copySize",
|
||||
{name = "rect",
|
||||
fill = "fill",
|
||||
color = { r = .1, g = .1, b = .1, a = .6 }
|
||||
},
|
||||
{name = "vertical",
|
||||
{name = "label",
|
||||
text = chatText,
|
||||
font = fonts.smallFont
|
||||
},
|
||||
{name = "vertical",
|
||||
{name = "label",
|
||||
text = chatText,
|
||||
font = ui.smallFont
|
||||
},
|
||||
{name = "spacer",
|
||||
height = 2
|
||||
}
|
||||
{name = "spacer",
|
||||
height = 2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
table.insert(chatentries, entry)
|
||||
ui.draw(window.x, window.y)
|
||||
end
|
||||
|
@ -153,18 +196,16 @@ function ui.loadTextures(directory, container)
|
|||
for i, file in pairs(entries) do
|
||||
local path = directory .."/".. file
|
||||
local entry = love.filesystem.getInfo(path)
|
||||
if entry.type == "directory" then
|
||||
if entry.type == "directory" and file ~= ".git" then
|
||||
container[file] = {}
|
||||
container[file] = ui.loadTextures(path, container[file])
|
||||
elseif entry.type == "file" or entry.type == "symlink" then
|
||||
if file ~= "license.txt" then
|
||||
local imageData = love.image.newImageData(path)
|
||||
local status, imageData = pcall(love.image.newImageData, path);
|
||||
if status then
|
||||
local hash = love.data.hash("sha512", imageData:getString())
|
||||
gameloop.textures[hash] = {data = imageData}
|
||||
levelloop.textures[hash] = {data = imageData}
|
||||
container[file] = hash
|
||||
end
|
||||
elseif entry.type == "other" then
|
||||
error("Can't figure out what " .. path .. " is!")
|
||||
end
|
||||
end
|
||||
return container
|
||||
|
@ -172,31 +213,29 @@ end
|
|||
|
||||
|
||||
function textureEntry(hash)
|
||||
local texture = gameloop.textures[hash]
|
||||
local texture = levelloop.textures[hash]
|
||||
assert(texture, "No texture found for textureentry?")
|
||||
assert(texture.data, "No imagedata found for textureentry?")
|
||||
if not texture.image then
|
||||
texture.image = love.graphics.newImage(texture.data)
|
||||
end
|
||||
local width = texture.image:getWidth()
|
||||
local scale = 1
|
||||
if width <= 16 then scale = 2 end
|
||||
if width <= 8 then scale = 4 end
|
||||
if drawing and drawing.cursor and hash == drawing.cursor then
|
||||
return{name = "overlayRect",
|
||||
local height = texture.image:getHeight()
|
||||
if drawing and drawing.cursorHash and hash == drawing.cursorHash then
|
||||
return {name = "overlayRect",
|
||||
fill = "line",
|
||||
{name = "drawHash",
|
||||
hash = hash,
|
||||
scale = scale
|
||||
scale = ui.sidebarScale
|
||||
}
|
||||
}
|
||||
else
|
||||
return{name = "cursorSelect",
|
||||
return {name = "cursorSelect", uiSelect = "GameUI",
|
||||
identifier = hash,
|
||||
kind = "picker",
|
||||
{name = "drawHash",
|
||||
hash = hash,
|
||||
scale = scale
|
||||
scale = ui.sidebarScale
|
||||
}
|
||||
}
|
||||
end
|
||||
|
@ -204,52 +243,54 @@ end
|
|||
|
||||
|
||||
function textureRun(entryTable, width)
|
||||
local textures = gameloop.textures
|
||||
local superEntry = {name = "vertical"}
|
||||
local entries = {name = "horizontal"}
|
||||
local textures = levelloop.textures
|
||||
local entries = {name = "naiveGrid", width = width - 20}
|
||||
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
|
||||
table.insert(entryTableSorted, {name = iter, entry = entry})
|
||||
end
|
||||
table.sort(entryTableSorted, function(k1, k2) return k1.name < k2.name end)
|
||||
|
||||
for iter, entry in pairs(entryTableSorted) do
|
||||
if #entries > 2 then
|
||||
local NextX, nextY = layout.handle(entries, 0, 0, true)
|
||||
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))
|
||||
if not textures[entry.entry].image then
|
||||
textures[entry.entry].image = love.graphics.newImage(textures[entry.entry].data)
|
||||
end
|
||||
table.insert(entries, textureEntry(entry.entry))
|
||||
end
|
||||
if #entries > 1 then table.insert(superEntry, entries) end
|
||||
if #superEntry > 1 then
|
||||
return superEntry
|
||||
else
|
||||
return entries
|
||||
end
|
||||
return entries
|
||||
end
|
||||
|
||||
|
||||
function ui.blockDrawer(textureTree, w, h, container)
|
||||
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",
|
||||
{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
|
||||
local textureTreeSorted = {}
|
||||
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"
|
||||
table.insert(container, {name = "label", text = pack})
|
||||
table.insert(container,
|
||||
{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)
|
||||
})
|
||||
end
|
||||
|
@ -258,107 +299,111 @@ function ui.blockDrawer(textureTree, w, h, container)
|
|||
end
|
||||
|
||||
|
||||
function ui.helpDrawer(w, h)
|
||||
return {name = "label",
|
||||
text = ui.helptext
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
function ui.testDrawer(w, h)
|
||||
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] "
|
||||
}
|
||||
function ui.menuDrawer(w, h)
|
||||
return {name = "horizontal",
|
||||
{name = "vertical",
|
||||
{name = "button",
|
||||
identifier = "save",
|
||||
uiSelect = "Menu",
|
||||
buttonColor = "greenTranslucent",
|
||||
text = "Save world"
|
||||
},
|
||||
{name = "cursorSelect",
|
||||
{name = "spacer",
|
||||
height = 10,
|
||||
},
|
||||
{name = "button",
|
||||
identifier = "disconnect",
|
||||
kind = "button",
|
||||
{name = "label",
|
||||
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 }
|
||||
uiSelect = "Menu",
|
||||
buttonColor = "pinkTranslucent",
|
||||
text = "Disconnect"
|
||||
},
|
||||
{name = "horizontal",
|
||||
labels,
|
||||
{name = "spacer",
|
||||
height = 40,
|
||||
},
|
||||
{name = "spacer",
|
||||
height = 25,
|
||||
},
|
||||
{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 = "rect",
|
||||
fill = "fill",
|
||||
color = { r = .1, g = .1, b = .1 }
|
||||
},
|
||||
tab
|
||||
{name = "spacer",
|
||||
width = 80,
|
||||
},
|
||||
{name = "label",
|
||||
text = ui.helptext,
|
||||
font = fonts.normallFont
|
||||
}
|
||||
}
|
||||
return drawer, math.max(hWidth, bWidth)
|
||||
end
|
||||
|
||||
|
||||
function ui.draw(w, h)
|
||||
window.x, window.y = w, h
|
||||
color_push()
|
||||
love.graphics.setCanvas(ui.buffer)
|
||||
uiState = {}
|
||||
layout.uiState.GameUI = {}
|
||||
love.graphics.clear( )
|
||||
local sidebar, width = ui.tabDrawer(ui.textureTree, w, h)
|
||||
layout.handle(sidebar, window.x -width, 0)
|
||||
ui.space = width
|
||||
if textEnabled then
|
||||
local chatLayout = ui.chatEntry()
|
||||
local sizex, sizey = layout.handle(chatLayout, 0, 0, true)
|
||||
layout.handle(chatLayout, 0, window.y - sizey)
|
||||
end
|
||||
local blockDrawer = ui.blockDrawer(ui.textureTree, w, h)
|
||||
blockMaxX, blockMaxY = layout.handle(blockDrawer, 0, 0, true)
|
||||
local blockDrawerAdjusted = ui.blockDrawer(ui.textureTree, blockMaxX, blockMaxY)
|
||||
layout.handle(blockDrawerAdjusted, window.x - blockMaxX, 0)
|
||||
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()
|
||||
end
|
||||
|
||||
|
||||
function ui.mousepressed(mousex, mousey)
|
||||
local textures = gameloop.textures
|
||||
for i, v in ipairs(uiState) do
|
||||
if mousex >= v.x and mousex <= v.x2 then
|
||||
if mousey >= v.y and mousey <= v.y2 then
|
||||
local textures = levelloop.textures
|
||||
if menuVisible then
|
||||
for i, v in ipairs(layout.uiState.Menu) do
|
||||
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
|
||||
assert(v.identifier, "No identifier in picker!")
|
||||
drawing.cursorHash = v.identifier
|
||||
|
@ -371,19 +416,23 @@ function ui.mousepressed(mousex, mousey)
|
|||
ui.draw(window.x, window.y)
|
||||
return
|
||||
elseif v.kind == "colorpicker" then
|
||||
local scale = 1.0 / granularity
|
||||
local red = math.floor((mousey - v.y) / v.pointsize) / 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("Picker hit :D")
|
||||
local scale = ui.sidebarScale / granularity
|
||||
local red = math.floor((mousey - v.y) / v.pointSize) / 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}
|
||||
ui.draw(window.x, window.y)
|
||||
return
|
||||
elseif v.kind == "button" then
|
||||
if v.identifier == "disconnect" then
|
||||
gameloop.networkSend(unit("playerLeave"))
|
||||
menu.init()
|
||||
end
|
||||
elseif v.kind == "bwColorPicker" then
|
||||
local scale = 1.0 / granularity
|
||||
local saturation = math.floor((mousex - v.x) / v.pointSize) / scale
|
||||
drawing.color = {saturation, saturation, saturation, 1}
|
||||
ui.draw(window.x, window.y)
|
||||
return
|
||||
end
|
||||
return
|
||||
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)
|
||||
chunk()
|
||||
end
|
||||
dofile "lua/gameloop.lua"
|
||||
dofile "lua/utils.lua"
|
||||
dofile "lua/layout.lua"
|
||||
dofile "lua/init.lua"
|
||||
dofile "lua/ui.lua"
|
||||
dofile "lua/camera.lua"
|
||||
dofile "lua/physics.lua"
|
||||
dofile "lua/drawing.lua"
|
||||
|
||||
|
||||
function love.load(args)
|
||||
if args[1] == "--nwTest" then
|
||||
local format = require("shared.networkFormat")
|
||||
format.testEncoder()
|
||||
elseif args[1] == "--server" then
|
||||
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
|
||||
#adjust the next line... not sure how to figure this out from a shellscript, sorry :)
|
||||
cd ~/proj/poppy
|
||||
# This mechanism is fairly unreliable, but it is the only "okay" way currently :/
|
||||
# realpath is also not part of any POSIX standard
|
||||
|
||||
cd $(dirname $(realpath "$0"))
|
||||
if test $? -eq 1; then
|
||||
alert --stop "cant figure out where this script is" "Ill adjust it!"
|
||||
exit
|
||||
fi
|
||||
|
||||
uname=`uname -o`
|
||||
if test "$uname" = "Haiku"
|
||||
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 |