Online multiplayer!

This commit is contained in:
Jaidyn Ann 2021-02-07 17:03:34 -06:00
parent ae75f23025
commit 693584ae98
4 changed files with 315 additions and 70 deletions

View File

@ -2,7 +2,7 @@ mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST)))
current_dir := $(notdir $(patsubst %/,%,$(dir $(mkfile_path)))) current_dir := $(notdir $(patsubst %/,%,$(dir $(mkfile_path))))
love: love:
zip -9r bin/horsehorse.love ./* zip -9r bin/horseseahorse.love ./*
win32: love win32: love
ifeq (,$(wildcard bin/love-win32.zip)) ifeq (,$(wildcard bin/love-win32.zip))
@ -10,18 +10,18 @@ ifeq (,$(wildcard bin/love-win32.zip))
https://github.com/love2d/love/releases/download/11.3/love-11.3-win32.zip https://github.com/love2d/love/releases/download/11.3/love-11.3-win32.zip
endif endif
unzip -d bin/ bin/love-win32.zip unzip -d bin/ bin/love-win32.zip
mv bin/love-*-win32 bin/horsehorse-win32 mv bin/love-*-win32 bin/horseseahorse-win32
rm bin/horsehorse-win32/changes.txt rm bin/horseseahorse-win32/changes.txt
rm bin/horsehorse-win32/readme.txt rm bin/horseseahorse-win32/readme.txt
rm bin/horsehorse-win32/lovec.exe rm bin/horseseahorse-win32/lovec.exe
cat bin/horsehorse.love >> bin/horsehorse-win32/love.exe cat bin/horseseahorse.love >> bin/horseseahorse-win32/love.exe
mv bin/horsehorse-win32/love.exe bin/horsehorse-win32/HorseHorse.exe mv bin/horseseahorse-win32/love.exe bin/horseseahorse-win32/HorseHorse.exe
cp lib/bin-license.txt bin/horsehorse-win32/license.txt cp lib/bin-license.txt bin/horseseahorse-win32/license.txt
zip -9jr bin/horsehorse-win32.zip bin/horsehorse-win32 zip -9jr bin/horseseahorse-win32.zip bin/horseseahorse-win32
rm -rf bin/horsehorse-win32 rm -rf bin/horseseahorse-win32
test: love test: love
love bin/horsehorse.love love bin/horseseahorse.love
clean: clean:
rm -rf ./bin/* rm -rf ./bin/*

View File

@ -1,20 +1,38 @@
HORSEHORSE HORSESEAHORSE
================================================================================ ================================================================================
HorseHorse is an tank-control duel-ish game, where valiant non-horses are pitted HorseSeaHorse is an tank-control duel-ish game, where valiant non-horses are
against one another. pitted against one another.
Made for Librejam 2020-02, with the theme "SUBNAUTICAL." Made for Librejam 2020-02, with the theme "SUBNAUTICAL."
RUNNING RUNNING
-------------------------------------------------- --------------------------------------------------
LINUX/UNIX/HAIKU UNIX/Linux/Haiku
-------------------- ----------------------------------------
Make sure love2d is installed with your package manager, and then: For UNIX/Linux/Haiku, if you have the "horseseahorse.love" file and love2d
$ make test installed through your package manager, then run:
$ love horseseahorse.love
WINDOWS
----------------------------------------
If you have "horseseahorse.exe", then just run it or whatever idk.
"BUILDING"
--------------------------------------------------
LOVE FILE
----------------------------------------
$ mkdir bin/
$ make
WINDOWS BINARY
----------------------------------------
$ mkdir bin/
$ make win32
BORING STUFF BORING STUFF
-------------------------------------------------- --------------------------------------------------
Jaidyn Ann <jadedctrl@teknik.io> Jaidyn Ann <jadedctrl@teknik.io>
Code under GPLv3 (see COPYING.txt) Code under GPLv3 (see COPYING.txt)
Art assets all under the CC-BY 3.0 Art assets all under the CC-BY 4.0

View File

@ -1,6 +1,6 @@
function love.conf(t) function love.conf(t)
t.window.resizable = true t.window.resizable = true
t.window.title = "HorseHorse" t.window.title = "HorseSeaHorse"
t.window.vsync = 0 t.window.vsync = 0
t.window.width = 800 t.window.width = 800
t.window.height = 800 t.window.height = 800

327
main.lua
View File

@ -108,10 +108,7 @@ end
-- IN-GAME -- IN-GAME
---------------------------------------- ----------------------------------------
Game = class("Game") function game_load(game)
function game_load(lobbiests)
game = Game:new(lobbiests)
game:install() game:install()
end end
@ -120,12 +117,12 @@ end
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
-- Fighter player class -- Fighter player class
---------------------------------------- ----------------------------------------
Fighter = class('Fighter') Player = class('Fighter')
function Fighter:initialize(game, x, y, character, swordType, swordSide) function Player:initialize(game, x, y, character, swordType, name)
self.game = game self.game = game
self.name = name
self.swordType = swordType or 'normal' self.swordType = swordType or 'normal'
self.swordSide = swordSide or 'top'
self.character = character or math.random(1, table.maxn(CHARACTERS)) self.character = character or math.random(1, table.maxn(CHARACTERS))
self.directionals = {} self.directionals = {}
@ -134,10 +131,12 @@ function Fighter:initialize(game, x, y, character, swordType, swordSide)
self:initBody(x, y) self:initBody(x, y)
self:initSword() self:initSword()
self:initShield() self:initShield()
self.id = math.random(1, 20000)
end end
function Fighter:update(dt) function Player:update(dt)
local dir = self.directionals local dir = self.directionals
self:movement() self:movement()
@ -148,7 +147,7 @@ function Fighter:update(dt)
end end
function Fighter:draw() function Player:draw()
local x,y = self.body:getWorldPoints(self.body.shape:getPoints()) local x,y = self.body:getWorldPoints(self.body.shape:getPoints())
love.graphics.draw(CHARACTERS[self.character], x, y, self.body:getAngle(), love.graphics.draw(CHARACTERS[self.character], x, y, self.body:getAngle(),
@ -156,13 +155,56 @@ function Fighter:draw()
end end
function Fighter:movement() function Player:movement()
end end
function Fighter:initBody(x, y) function Player:toTable()
local bx1,by1, bx2,by2, bx3,by3, bx4,by4 = self.body.shape:getPoints()
local bodyVertices = {bx1,by1, bx2,by2, bx3,by3, bx4,by4}
local sx1,sy1, sx2,sy2, sx3,sy3, sx4,sy4 = self.sword.shape:getPoints()
local swordVertices = {sx1,sy1, sy2,sy2, sx3,by3, sx4,by4}
local mx1,my1, mx2,my2, mx3,my3, mx4,my4 = self.shield.shape:getPoints()
local shieldVertices = {mx1,my1, my2,my2, mx3,by3, mx4,by4}
return {["bodyBox"] = bodyVertices, ["bodyAngle"] = self.body:getAngle(),
["swordBox"] = swordVertices, ["swordAngle"] = self.sword:getAngle(),
["shieldBox"] = shieldVertices, ["shieldAngle"] = self.shield:getAngle(),
["bodyX"] = self.body:getX(), ["bodyY"] = self.body:getY(),
["swordX"] = self.sword:getX(), ["swordY"] = self.sword:getY(),
["shieldX"] = self.shield:getX(), ["shieldY"] = self.shield:getY(),
["character"] = self.character, ["name"] = self.name,
["id"] = self.id}
end
function Player:applyTable(pTable)
self.body.shape = love.physics.newPolygonShape(pTable["bodyBox"])
self.body:setAngle(pTable["bodyAngle"])
self.body:setX(pTable["bodyX"])
self.body:setY(pTable["bodyY"])
self.sword.shape = love.physics.newPolygonShape(pTable["swordBox"])
self.sword:setAngle(pTable["swordAngle"])
self.sword:setX(pTable["swordX"])
self.sword:setY(pTable["swordY"])
self.shield.shape = love.physics.newPolygonShape(pTable["shieldBox"])
self.shield:setAngle(pTable["shieldAngle"])
self.shield:setX(pTable["shieldX"])
self.shield:setY(pTable["shieldY"])
self.character = pTable["character"]
self.id = pTable["id"]
self.name = pTable["name"]
end
function Player:initBody(x, y)
self.body = self.game.world:newRectangleCollider(x, y, 16, 16); self.body = self.game.world:newRectangleCollider(x, y, 16, 16);
self.body:setCollisionClass('Fighter') self.body:setCollisionClass('Player')
self.body:setObject(self) self.body:setObject(self)
self.body:setAngularDamping(2) self.body:setAngularDamping(2)
self.body:setLinearDamping(.5) self.body:setLinearDamping(.5)
@ -170,14 +212,14 @@ function Fighter:initBody(x, y)
end end
function Fighter:initShield() function Player:initShield()
self.shield = self.game.world:newRectangleCollider(0, 0, 20, 5); self.shield = self.game.world:newRectangleCollider(0, 0, 20, 5);
self.shield:setCollisionClass('Shield') self.shield:setCollisionClass('Shield')
self.shield:setObject(self) self.shield:setObject(self)
end end
function Fighter:initSword() function Player:initSword()
self.swordLength = SWORDLENGTH self.swordLength = SWORDLENGTH
if (self.swordType == 'normal') then if (self.swordType == 'normal') then
@ -191,7 +233,7 @@ function Fighter:initSword()
end end
function Fighter:glueSwordAndShield() function Player:glueSwordAndShield()
local x, y = self.body:getPosition() local x, y = self.body:getPosition()
local angle = self.body:getAngle() local angle = self.body:getAngle()
@ -214,9 +256,9 @@ function Fighter:glueSwordAndShield()
end end
function Fighter:makePostSolve() function Player:makePostSolve()
return function(col1, col2, contact) return function(col1, col2, contact)
if (col1.collision_class == "Fighter" if (col1.collision_class == "Player"
and col2.collision_class == "Sword") and col2.collision_class == "Sword")
then then
-- print(col2.shape) -- print(col2.shape)
@ -226,7 +268,7 @@ function Fighter:makePostSolve()
end end
function Fighter:makeSwordPostSolve() function Player:makeSwordPostSolve()
return function(col1, col2, contact) return function(col1, col2, contact)
if (col1.collision_class == "Sword" if (col1.collision_class == "Sword"
and col2.collision_class == "Shield") and col2.collision_class == "Shield")
@ -239,11 +281,11 @@ end
-- LocalPlayer for local players (ofc) -- LocalPlayer for local players (ofc)
---------------------------------------- ----------------------------------------
LocalPlayer = class("LocalPlayer", Fighter) LocalPlayer = class("LocalPlayer", Player)
function LocalPlayer:initialize(game, x, y, keymap, character, swordType, swordSide) function LocalPlayer:initialize(game, x, y, keymap, character, swordType, name)
self.keymap = keymap or KEYMAPS[1] self.keymap = keymap or KEYMAPS[1]
Fighter.initialize(self, game, x, y, character, swordType, swordSide) Player.initialize(self, game, x, y, character, swordType, name)
end end
@ -308,41 +350,49 @@ function LocalPlayer:keyreleased(key)
end end
-- LocalBot andddd for bots too -- NetPlayer for network players
---------------------------------------- ----------------------------------------
LocalBot = class("LocalBot", Fighter) NetPlayer = class("NetPlayer", Player)
function LocalBot:initialize(x, y, character, swordType, swordSide) function NetPlayer:initialize(ptable, game)
Fighter.initialize(self, x, y, character, swordType, swordSide) Player.initialize(self, game, 0, 0, 1, 'normal', "sldkfj")
self:applyTable(ptable)
end
-- HostPlayer for from-server players
----------------------------------------
HostPlayer = class("HostPlayer", NetPlayer)
function HostPlayer:intialize(ptable, game)
NetPlayer.initialize(self, ptable, game)
end
-- ClientPlayer for from-client players
----------------------------------------
ClientPlayer = class("ClientPlayer", NetPlayer)
function ClientPlayer:intialize(ptable, game)
NetPlayer.initialize(self, ptable, game)
end end
-- GAME superclass for matches -- GAME superclass for matches
---------------------------------------- ----------------------------------------
Game = class("Game")
function Game:initialize(lobbiests) function Game:initialize(lobbiests)
self.world = wind.newWorld(0, 0, true) self.world = wind.newWorld(0, 0, true)
self.world:addCollisionClass('Fighter') self.world:addCollisionClass('Player')
self.world:addCollisionClass('Shield') self.world:addCollisionClass('Shield')
self.world:addCollisionClass('Sword') self.world:addCollisionClass('Sword')
self.remotePlayers = {}
self.localPlayers = {} self.localPlayers = {}
self.remotePlayersN = 0
self.localPlayersN = 0 self.localPlayersN = 0
for k,lobbiest in pairs(lobbiests) do self:addLobbiests(lobbiests)
if (lobbiest.class.name == "LocalLobbiest") then
local i = self.localPlayersN + 1
self.localPlayers[i] = LocalPlayer:new(self, 0 + i * 50, 0 + i * 50,
KEYMAPS[i], lobbiest.character)
self.localPlayersN = i
end
end
self.localFighters = self.localPlayers
self.fighters = self.localFighters
end end
@ -358,24 +408,22 @@ end
function Game:update(dt) function Game:update(dt)
self.world:update(dt) self.world:update(dt)
for i, fighter in pairs(self.fighters) do for k,player in pairs(self:players()) do
fighter:update(dt) player:update(dt)
end end
-- local x, y = player.body:getPosition()
-- camera:follow(x, y)
end end
function Game:draw() function Game:draw()
self.world:draw() self.world:draw()
for i, fighter in pairs(self.fighters) do for k,player in pairs(self:players()) do
fighter:draw() player:draw()
end end
end end
function Game:keypressed(key) function Game:keypressed(key)
local dir = self.localFighters[1].directionals local dir = self.localPlayers[1].directionals
-- if a player presses the left key, then holds the right key, they should -- if a player presses the left key, then holds the right key, they should
-- go right until they let go, then they should go left. -- go right until they let go, then they should go left.
@ -384,8 +432,16 @@ function Game:keypressed(key)
elseif (key == "-" and camera.scale > .5) then elseif (key == "-" and camera.scale > .5) then
camera.scale = camera.scale - .5 camera.scale = camera.scale - .5
elseif (key == "t") then
local chatbox = TextBox:new(10,770, 2, 99, nil, nil,
function (text)
self:sendChat(text)
self:install()
end)
chatbox:install(false, drawFunction, nil, false)
elseif (key == "escape") then elseif (key == "escape") then
pause_load() menu_load(makeMainMenu())
else else
for i, player in pairs(self.localPlayers) do for i, player in pairs(self.localPlayers) do
player:keypressed(key) player:keypressed(key)
@ -401,6 +457,162 @@ function Game:keyreleased (key)
end end
function Game:players()
return self.localPlayers
end
function Game:sendChat(message)
local author = "AGhost"
if (self.localPlayersN > 0) then
author = self.localPlayers[1].name
end
logMsg(author, message)
return author,message
end
function Game:addLobbiests(localLobbiests)
for k,lobbiest in pairs(localLobbiests) do
local i = self.localPlayersN + 1
self.localPlayers[i] = LocalPlayer:new(self, 0 + i * 50, 0 + i * 50,
KEYMAPS[i], lobbiest.character, lobbiest.swordType, lobbiest.name)
self.localPlayersN = i
end
end
-- NETGAME superclass for online matches
----------------------------------------
NetGame = class("NetGame", Game)
function NetGame:initialize(localLobbiests, sock)
self.remotePlayers = {}
self.remotePlayersN = 0
self.sock = sock
Game.initialize(self, localLobbiests)
self:sockCallbacks()
end
function NetGame:sockCallbacks()
self.sock:on("newPlayers",
function(playerTables, client)
self:addNewPlayers(playerTables, client)
end)
self.sock:on("playerPing",
function(playerTables, client)
self:receivePlayers(playerTables, client)
end)
end
function NetGame:update(dt)
Game.update(self, dt)
self:sendPlayers()
self.sock:update()
end
function NetGame:players()
local players = {}
table.foreach(self.localPlayers,
function(k, v) table.insert(players, v) end)
table.foreach(self.remotePlayers,
function(k, v) table.insert(players, v) end)
return players
end
function NetGame:receivePlayers(playerTables, client)
for i,ptable in pairs(playerTables) do
local id = ptable["id"]
if (self.remotePlayers[id] == nil) then
self:addNewPlayer(ptable)
end
self.remotePlayers[id]:applyTable(ptable)
end
end
-- HOSTGAME for server matches
----------------------------------------
HostGame = class("HostGame", NetGame)
function HostGame:initialize(localLobbiests, server)
NetGame.initialize(self, localLobbiests, server)
self.sock:sendToAll("newGame", nil)
end
function HostGame:sockCallbacks()
NetGame.sockCallbacks(self)
end
function HostGame:sendChat(message)
local author,text = Game.sendChat(self, message)
self.sock:sendToAll("chat", {["author"] = author, ["text"] = text})
end
function HostGame:addNewPlayer(ptable)
self.remotePlayersN = self.remotePlayersN + 1
self.remotePlayers[ptable["id"]] = ClientPlayer:new(ptable, self)
end
function HostGame:sendPlayers()
local ptables = {}
for i,player in pairs(self.localPlayers) do
table.insert(ptables, player:toTable())
end
self.sock:sendToAll("playerPing", ptables)
end
function HostGame:receivePlayers(playerTables, client)
NetGame.receivePlayers(self, playerTables)
self.sock:sendToAllBut(client, "playerPing", playerTables)
end
-- CLIENTGAME for client-side matches
----------------------------------------
ClientGame = class("ClientGame", NetGame)
function ClientGame:initialize(localLobbiests, client)
NetGame.initialize(self, localLobbiests, client)
end
function ClientGame:sendChat(message)
local author,text = Game.sendChat(self, message)
self.sock:send("chat", {["author"] = author, ["text"] = text})
end
function ClientGame:sendPlayers()
local ptables = {}
for i,player in pairs(self.localPlayers) do
table.insert(ptables, player:toTable())
end
self.sock:send("playerPing", ptables)
end
function ClientGame:addNewPlayer(ptable)
self.remotePlayersN = self.remotePlayersN + 1
self.remotePlayers[ptable["id"]] = HostPlayer:new(ptable, self)
end
-- LOBBY superclass for pre-matches -- LOBBY superclass for pre-matches
---------------------------------------- ----------------------------------------
Lobby = class("Lobby") Lobby = class("Lobby")
@ -566,7 +778,7 @@ end
function LocalLobby:keypressed(key) function LocalLobby:keypressed(key)
if (key == "return" and self:lobbiestsN() > 1) then if (key == "return" and self:lobbiestsN() > 1) then
game_load(self:lobbiests()) game_load(Game:new(self:lobbiests()))
else else
Lobby.keypressed(self, key) Lobby.keypressed(self, key)
end end
@ -634,6 +846,7 @@ function HostLobby:sockCallbacks()
self.sock:on("connect", self.sock:on("connect",
function (data, client) function (data, client)
-- self:sendLobbiests() -- self:sendLobbiests()
-- st
end) end)
self.sock:on("disconnect", self.sock:on("disconnect",
@ -643,6 +856,15 @@ function HostLobby:sockCallbacks()
end end
function HostLobby:keypressed(key)
if (key == "return" and self:lobbiestsN() > 1) then
game_load(HostGame:new(self.localLobbiests, self.sock))
else
Lobby.keypressed(self, key)
end
end
function HostLobby:toMainMenu() function HostLobby:toMainMenu()
-- self.sock:destroy() -- self.sock:destroy()
-- Lobby.toMainMenu(self) -- Lobby.toMainMenu(self)
@ -753,6 +975,11 @@ function ClientLobby:sockCallbacks()
function (ignoredData) function (ignoredData)
self.status = "Disconnected" self.status = "Disconnected"
end) end)
self.sock:on("newGame",
function (data, client)
game_load(ClientGame:new(self.localLobbiests, self.sock))
end)
end end