Add networking

This commit is contained in:
Bruno Rybársky 2024-08-30 19:07:56 +02:00
parent 842c41fdca
commit dc854d411f
13 changed files with 1529 additions and 296 deletions

3
.gitignore vendored Normal file

@ -0,0 +1,3 @@
proto/tuneller.pb.go
proto/*
config.json

84
base.go

@ -1,38 +1,44 @@
package main package main
import "github.com/veandco/go-sdl2/sdl" import (
"github.com/veandco/go-sdl2/sdl"
"image/color"
)
type Base struct { type Base struct {
owner *Player ownerID uint32
openingWidth int32 openingWidth int32
gameObject *GameObject gameObject *GameObject
} }
func (base *Base) tick(players *[]*Player) { func (base *Base) tick(players map[uint32]*Player) {
for _, player := range *players { for playerID, player := range players {
if player.gameObject.baseRect.HasIntersection(&base.gameObject.baseRect) { if player.gameObject.baseRect.HasIntersection(&base.gameObject.baseRect) {
if player.rechargeCooldown == 0 && player.energy < MaxEnergy { if player.rechargeCooldown == 0 && player.energy < serverConfig.MaxEnergy {
if MaxEnergy-player.energy < 4 { if serverConfig.MaxEnergy-player.energy < 4 {
player.energy++ player.energy++
} else { } else {
player.energy += 4 player.energy += 4
} }
if player == base.owner { if playerID == base.ownerID {
player.rechargeCooldown = RechargeCooldownOwn player.rechargeCooldown = serverConfig.RechargeCooldownOwn
} else { } else {
player.rechargeCooldown = RechargeCooldownOpponent player.rechargeCooldown = serverConfig.RechargeCooldownOpponent
} }
} }
if player == base.owner && player.repairCooldown == 0 && player.shields < MaxShields { if playerID == base.ownerID && player.repairCooldown == 0 && player.shields < serverConfig.MaxShields {
player.shields++ player.shields++
player.repairCooldown = RepairCooldown player.repairCooldown = serverConfig.RepairCooldown
} }
} }
} }
} }
func (base *Base) render(camera *sdl.Rect, surface *sdl.Surface) { func (base *Base) render(camera *sdl.Rect, surface *sdl.Surface, bases map[uint32]*Base) {
base.gameObject.render(camera, surface) base.gameObject.render(camera, surface)
if !base.gameObject.inView && !config.Server {
delete(bases, base.ownerID)
}
} }
func (base *Base) build(gameMap *GameMap) { func (base *Base) build(gameMap *GameMap) {
@ -44,10 +50,10 @@ func (base *Base) build(gameMap *GameMap) {
if base.gameObject.baseRect.H < 9 { if base.gameObject.baseRect.H < 9 {
panic("Bad border height") panic("Bad border height")
} }
if gameMap.width-base.gameObject.baseRect.X-base.gameObject.baseRect.W <= 0 { if gameMap.width-uint32(base.gameObject.baseRect.X)-uint32(base.gameObject.baseRect.W) <= 0 {
panic("Bad base x location") panic("Bad base x location")
} }
if gameMap.height-base.gameObject.baseRect.Y-base.gameObject.baseRect.H <= 0 { if gameMap.height-uint32(base.gameObject.baseRect.Y)-uint32(base.gameObject.baseRect.H) <= 0 {
panic("Bad base y location") panic("Bad base y location")
} }
if base.gameObject.baseRect.X < 0 || base.gameObject.baseRect.Y < 0 { if base.gameObject.baseRect.X < 0 || base.gameObject.baseRect.Y < 0 {
@ -72,41 +78,59 @@ func (base *Base) build(gameMap *GameMap) {
} }
} }
func createBases(players *[]*Player, gameMap *GameMap) *[]*Base { func createBase(gameMap *GameMap, baseColor color.Color, posX, posY, ownerID, openingWidth uint32) *Base {
bases := &[]*Base{}
for ownerID, player := range *players {
gameObject := &GameObject{} gameObject := &GameObject{}
gameObject.baseRect = sdl.Rect{ gameObject.baseRect = sdl.Rect{
X: player.gameObject.baseRect.X - 14, X: int32(posX),
Y: player.gameObject.baseRect.Y - 14, Y: int32(posY),
W: 35, W: 35,
H: 35, H: 35,
} }
openingWidth := int32(float64(player.gameObject.baseRect.W) * 1.5)
if openingWidth%2 == 0 { if openingWidth%2 == 0 {
openingWidth++ openingWidth++
} }
borderWidth := gameObject.baseRect.W - openingWidth borderWidth := gameObject.baseRect.W - int32(openingWidth)
borderWidthA := borderWidth / 2 borderWidthA := borderWidth / 2
borderWidthB := borderWidth - borderWidthA + 1 borderWidthB := borderWidth - borderWidthA + 1
if borderWidth < 0 { if borderWidth < 0 {
panic("Bad border width") panic("Bad border width")
} }
gameObject.addColor((*player).playerColors.body) gameObject.addColor(baseColor)
gameObject.addColoredRect(0, 0, 1, gameObject.baseRect.H, 0) gameObject.addColoredRect(0, 0, 1, gameObject.baseRect.H, 0)
gameObject.addColoredRect(gameObject.baseRect.W, 0, 1, gameObject.baseRect.H, 0) gameObject.addColoredRect(gameObject.baseRect.W, 0, 1, gameObject.baseRect.H, 0)
gameObject.addColoredRect(0, 0, borderWidthA, 1, 0) gameObject.addColoredRect(0, 0, borderWidthA, 1, 0)
gameObject.addColoredRect(borderWidthA+openingWidth, 0, borderWidthB, 1, 0) gameObject.addColoredRect(borderWidthA+int32(openingWidth), 0, borderWidthB, 1, 0)
gameObject.addColoredRect(0, gameObject.baseRect.H, borderWidthA, 1, 0) gameObject.addColoredRect(0, gameObject.baseRect.H, borderWidthA, 1, 0)
gameObject.addColoredRect(borderWidthA+openingWidth, gameObject.baseRect.H, borderWidthB, 1, 0) gameObject.addColoredRect(borderWidthA+int32(openingWidth), gameObject.baseRect.H, borderWidthB, 1, 0)
*bases = append(*bases, &Base{ base := &Base{
gameObject: gameObject, gameObject: gameObject,
owner: (*players)[ownerID], ownerID: ownerID,
openingWidth: openingWidth, openingWidth: int32(openingWidth),
}) }
(*bases)[ownerID].build(gameMap) base.build(gameMap)
return base
}
func (base *Base) delete(bases map[uint32]*Base) {
for x := base.gameObject.baseRect.X; x < base.gameObject.baseRect.X+base.gameObject.baseRect.W+1; x++ {
for y := base.gameObject.baseRect.Y; y < base.gameObject.baseRect.Y+base.gameObject.baseRect.H+1; y++ {
gameMap.tiles[x][y] = 0
}
}
delete(bases, base.ownerID)
}
func createBases(players map[uint32]*Player, gameMap *GameMap) map[uint32]*Base {
bases := map[uint32]*Base{}
for ownerID, player := range players {
bases[ownerID] = createBase(gameMap,
player.playerColors.body,
uint32(player.gameObject.baseRect.X-14),
uint32(player.gameObject.baseRect.Y-14),
ownerID,
uint32(float64(player.gameObject.baseRect.W)*1.5),
)
} }
return bases return bases
} }

@ -6,47 +6,53 @@ import (
"math/rand" "math/rand"
) )
var bulletLastID = uint32(0)
var bulletParticleLastID = uint32(0)
type Bullet struct { type Bullet struct {
posX, posY int32 posX, posY int32
direction uint8 direction uint8
color color.Color color color.Color
super bool super bool
id uint32
} }
type BulletParticle struct { type BulletParticle struct {
posX, posY int32 posX, posY int32
expirationTimer uint8 expirationTimer uint32
color color.Color color color.Color
id uint32
} }
func (bulletParticle *BulletParticle) render(camera *sdl.Rect, surface *sdl.Surface) { func (bulletParticle *BulletParticle) render(camera *sdl.Rect, surface *sdl.Surface, bulletParticles map[uint32]*BulletParticle) {
if camera.IntersectLine(&bulletParticle.posX, &bulletParticle.posY, &bulletParticle.posX, &bulletParticle.posY) { if camera.IntersectLine(&bulletParticle.posX, &bulletParticle.posY, &bulletParticle.posX, &bulletParticle.posY) {
relativeBulletX := bulletParticle.posX - camera.X relativeBulletX := bulletParticle.posX - camera.X
relativeBulletY := bulletParticle.posY - camera.Y relativeBulletY := bulletParticle.posY - camera.Y
surface.Set(int(relativeBulletX), int(relativeBulletY), bulletParticle.color) surface.Set(int(relativeBulletX), int(relativeBulletY), bulletParticle.color)
} else if !config.Server {
delete(bulletParticles, bulletParticle.id)
} }
} }
func (bulletParticle *BulletParticle) tick(particles *[]*BulletParticle) { func (bulletParticle *BulletParticle) tick(particles map[uint32]*BulletParticle) {
if bulletParticle.expirationTimer <= 0 { if bulletParticle.expirationTimer <= 0 {
for i, particle := range *particles { delete(particles, bulletParticle.id)
if particle == bulletParticle { } else {
*particles = append((*particles)[:i], (*particles)[i+1:]...)
}
}
}
bulletParticle.expirationTimer-- bulletParticle.expirationTimer--
} }
}
func (bullet *Bullet) render(camera *sdl.Rect, surface *sdl.Surface) { func (bullet *Bullet) render(camera *sdl.Rect, surface *sdl.Surface, bullets map[uint32]*Bullet) {
if camera.IntersectLine(&bullet.posX, &bullet.posY, &bullet.posX, &bullet.posY) { if camera.IntersectLine(&bullet.posX, &bullet.posY, &bullet.posX, &bullet.posY) {
relativeBulletX := bullet.posX - camera.X relativeBulletX := bullet.posX - camera.X
relativeBulletY := bullet.posY - camera.Y relativeBulletY := bullet.posY - camera.Y
surface.Set(int(relativeBulletX), int(relativeBulletY), bullet.color) surface.Set(int(relativeBulletX), int(relativeBulletY), bullet.color)
} else if !config.Server {
delete(bullets, bullet.id)
} }
} }
func (bullet *Bullet) explode(gameMap *GameMap, bulletParticleMap *[]*BulletParticle) { func (bullet *Bullet) explode(gameMap *GameMap, bulletParticleMap map[uint32]*BulletParticle) {
// Define the possible directions with their x and y velocity components. // Define the possible directions with their x and y velocity components.
directions := [][2]int{ directions := [][2]int{
{0, -1}, // Up {0, -1}, // Up
@ -75,13 +81,15 @@ func (bullet *Bullet) explode(gameMap *GameMap, bulletParticleMap *[]*BulletPart
// Check for collision and bullet behavior (normal or super). // Check for collision and bullet behavior (normal or super).
if collision == 1 || collision == 0 || (bullet.super && collision == 2) { if collision == 1 || collision == 0 || (bullet.super && collision == 2) {
*bulletParticleMap = append(*bulletParticleMap, &BulletParticle{ bulletParticleMap[bulletParticleLastID] = &BulletParticle{
posX: xPos, posX: xPos,
posY: yPos, posY: yPos,
expirationTimer: uint8(5 + rand.Intn(10)), // Randomize expiration time between 15 and 25. expirationTimer: uint32(5 + rand.Intn(10)), // Randomize expiration time between 15 and 25.
color: bullet.color, color: bullet.color,
}) id: bulletParticleLastID,
if xPos > 0 && yPos > 0 && xPos < MapWidth && yPos < MapHeight { }
bulletParticleLastID++
if xPos > 0 && yPos > 0 && uint32(xPos) < serverConfig.MapWidth && uint32(yPos) < serverConfig.MapHeight {
gameMap.tiles[xPos][yPos] = 0 gameMap.tiles[xPos][yPos] = 0
} }
} }
@ -93,12 +101,14 @@ func (bullet *Bullet) explode(gameMap *GameMap, bulletParticleMap *[]*BulletPart
xPos, yPos := bullet.posX+int32(xOffset), bullet.posY+int32(yOffset) xPos, yPos := bullet.posX+int32(xOffset), bullet.posY+int32(yOffset)
collision := gameMap.checkCollision(xPos, yPos) collision := gameMap.checkCollision(xPos, yPos)
if collision == 1 || collision == 0 || (bullet.super && collision == 2) { if collision == 1 || collision == 0 || (bullet.super && collision == 2) {
*bulletParticleMap = append(*bulletParticleMap, &BulletParticle{ bulletParticleMap[bulletParticleLastID] = &BulletParticle{
posX: xPos, posX: xPos,
posY: yPos, posY: yPos,
expirationTimer: uint8(15 + rand.Intn(15)), expirationTimer: uint32(15 + rand.Intn(15)),
color: bullet.color, color: bullet.color,
}) id: bulletParticleLastID,
}
bulletParticleLastID++
gameMap.tiles[xPos][yPos] = 0 gameMap.tiles[xPos][yPos] = 0
} }
} }
@ -107,7 +117,7 @@ func (bullet *Bullet) explode(gameMap *GameMap, bulletParticleMap *[]*BulletPart
} }
func (bullet *Bullet) tick(gameMap *GameMap, func (bullet *Bullet) tick(gameMap *GameMap,
bulletParticleMap *[]*BulletParticle, bulletMap *[]*Bullet, players *[]*Player) { bulletParticleMap map[uint32]*BulletParticle, bulletMap map[uint32]*Bullet, players map[uint32]*Player) {
var nextX, nextY int32 var nextX, nextY int32
nextX, nextY = bullet.posX, bullet.posY nextX, nextY = bullet.posX, bullet.posY
switch bullet.direction { switch bullet.direction {
@ -148,13 +158,13 @@ func (bullet *Bullet) tick(gameMap *GameMap,
H: 1, H: 1,
} }
hitPlayer := false hitPlayer := false
for _, player := range *players { for _, player := range players {
if hitPlayer { if hitPlayer {
break break
} }
for _, coloredRect := range player.gameObject.getCurrentRects() { for _, coloredRect := range player.gameObject.getCurrentRects() {
if coloredRect.rect.HasIntersection(&bulletRect) { if player.gameObject.adjustRectWorld(coloredRect.rect).HasIntersection(&bulletRect) {
hitPlayer = true hitPlayer = player.shields <= serverConfig.MaxShields
player.shields -= 10 player.shields -= 10
break break
} }
@ -162,11 +172,7 @@ func (bullet *Bullet) tick(gameMap *GameMap,
} }
if collisionResult != 0 || hitPlayer { if collisionResult != 0 || hitPlayer {
bullet.explode(gameMap, bulletParticleMap) bullet.explode(gameMap, bulletParticleMap)
for i, bulletInMap := range *bulletMap { delete(bulletMap, bullet.id)
if bulletInMap == bullet {
*bulletMap = append((*bulletMap)[:i], (*bulletMap)[i+1:]...)
}
}
} else { } else {
bullet.posX = nextX bullet.posX = nextX
bullet.posY = nextY bullet.posY = nextY

@ -7,10 +7,12 @@ import (
type GameObject struct { type GameObject struct {
baseRect sdl.Rect baseRect sdl.Rect
prevBaseRect sdl.Rect
borderRect sdl.Rect borderRect sdl.Rect
orientation uint8 orientation uint8
visualRects [][]*ColoredRect visualRects [][]*ColoredRect
colors []color.Color colors []color.Color
inView bool
} }
type ColoredRect struct { type ColoredRect struct {
@ -27,6 +29,15 @@ func adjustRectToCamera(object *sdl.Rect, camera *sdl.Rect) *sdl.Rect {
} }
} }
func (gameObject *GameObject) adjustRectWorld(offset *sdl.Rect) *sdl.Rect {
return &sdl.Rect{
X: gameObject.baseRect.X + offset.X,
Y: gameObject.baseRect.Y + offset.Y,
W: offset.W,
H: offset.H,
}
}
func (gameObject *GameObject) adjustRectToCamera(offset *sdl.Rect, camera *sdl.Rect) *sdl.Rect { func (gameObject *GameObject) adjustRectToCamera(offset *sdl.Rect, camera *sdl.Rect) *sdl.Rect {
return &sdl.Rect{ return &sdl.Rect{
X: gameObject.baseRect.X + offset.X - camera.X, X: gameObject.baseRect.X + offset.X - camera.X,
@ -38,7 +49,8 @@ func (gameObject *GameObject) adjustRectToCamera(offset *sdl.Rect, camera *sdl.R
func (gameObject *GameObject) render(camera *sdl.Rect, surface *sdl.Surface) { func (gameObject *GameObject) render(camera *sdl.Rect, surface *sdl.Surface) {
if camera.HasIntersection(&gameObject.baseRect) { if camera.HasIntersection(&gameObject.baseRect) {
if Debug { gameObject.inView = true
if config.Debug {
gameObject.borderRect = sdl.Rect{ gameObject.borderRect = sdl.Rect{
X: gameObject.baseRect.X - 1, X: gameObject.baseRect.X - 1,
Y: gameObject.baseRect.Y - 1, Y: gameObject.baseRect.Y - 1,
@ -50,13 +62,15 @@ func (gameObject *GameObject) render(camera *sdl.Rect, surface *sdl.Surface) {
surface.FillRect(borderRectFinal, sdl.MapRGBA(surface.Format, 20, 192, 128, 64)) surface.FillRect(borderRectFinal, sdl.MapRGBA(surface.Format, 20, 192, 128, 64))
surface.FillRect(baseRectFinal, sdl.MapRGBA(surface.Format, 255, 20, 10, 64)) surface.FillRect(baseRectFinal, sdl.MapRGBA(surface.Format, 255, 20, 10, 64))
} }
if RenderGameObjects { if config.RenderGameObjects {
for _, coloredRect := range gameObject.visualRects[gameObject.orientation] { for _, coloredRect := range gameObject.visualRects[gameObject.orientation] {
finalRect := gameObject.adjustRectToCamera(coloredRect.rect, camera) finalRect := gameObject.adjustRectToCamera(coloredRect.rect, camera)
r, g, b, a := coloredRect.color.RGBA() r, g, b, a := coloredRect.color.RGBA()
surface.FillRect(finalRect, sdl.MapRGBA(surface.Format, uint8(r), uint8(g), uint8(b), uint8(a))) surface.FillRect(finalRect, sdl.MapRGBA(surface.Format, uint8(r), uint8(g), uint8(b), uint8(a)))
} }
} }
} else {
gameObject.inView = false
} }
} }

1
go.mod

@ -5,4 +5,5 @@ go 1.21
require ( require (
github.com/aquilax/go-perlin v1.1.0 github.com/aquilax/go-perlin v1.1.0
github.com/veandco/go-sdl2 v0.4.40 github.com/veandco/go-sdl2 v0.4.40
google.golang.org/protobuf v1.34.2
) )

6
go.sum

@ -1,4 +1,10 @@
github.com/aquilax/go-perlin v1.1.0 h1:Gg+3jQ24wT4Y5GI7TCRLmYarzUG0k+n/JATFqOimb7s= github.com/aquilax/go-perlin v1.1.0 h1:Gg+3jQ24wT4Y5GI7TCRLmYarzUG0k+n/JATFqOimb7s=
github.com/aquilax/go-perlin v1.1.0/go.mod h1:z9Rl7EM4BZY0Ikp2fEN1I5mKSOJ26HQpk0O2TBdN2HE= github.com/aquilax/go-perlin v1.1.0/go.mod h1:z9Rl7EM4BZY0Ikp2fEN1I5mKSOJ26HQpk0O2TBdN2HE=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/veandco/go-sdl2 v0.4.40 h1:fZv6wC3zz1Xt167P09gazawnpa0KY5LM7JAvKpX9d/U= github.com/veandco/go-sdl2 v0.4.40 h1:fZv6wC3zz1Xt167P09gazawnpa0KY5LM7JAvKpX9d/U=
github.com/veandco/go-sdl2 v0.4.40/go.mod h1:OROqMhHD43nT4/i9crJukyVecjPNYYuCofep6SNiAjY= github.com/veandco/go-sdl2 v0.4.40/go.mod h1:OROqMhHD43nT4/i9crJukyVecjPNYYuCofep6SNiAjY=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=

@ -27,13 +27,12 @@ func setupWindowAndSurface(playerIndex uint8) (*sdl.Window, *sdl.Surface) {
} }
func setupMapWindowAndSurface() (*sdl.Window, *sdl.Surface) { func setupMapWindowAndSurface() (*sdl.Window, *sdl.Surface) {
const windowWidth, windowHeight = MapWidth, MapHeight window, err := sdl.CreateWindow("Tunneler map", sdl.WINDOWPOS_UNDEFINED, sdl.WINDOWPOS_UNDEFINED, int32(serverConfig.MapWidth), int32(serverConfig.MapWidth), sdl.WINDOW_SHOWN|sdl.WINDOW_RESIZABLE)
window, err := sdl.CreateWindow("Tunneler map", sdl.WINDOWPOS_UNDEFINED, sdl.WINDOWPOS_UNDEFINED, windowWidth, windowHeight, sdl.WINDOW_SHOWN|sdl.WINDOW_RESIZABLE)
if err != nil { if err != nil {
panic(err) panic(err)
} }
logicalSurface, err := sdl.CreateRGBSurface(0, windowWidth, windowHeight, 32, 0, 0, 0, 0) logicalSurface, err := sdl.CreateRGBSurface(0, int32(serverConfig.MapWidth), int32(serverConfig.MapWidth), 32, 0, 0, 0, 0)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -42,12 +41,12 @@ func setupMapWindowAndSurface() (*sdl.Window, *sdl.Surface) {
} }
func setupPlaySurface() (*sdl.Surface, *sdl.Rect, *sdl.Rect) { func setupPlaySurface() (*sdl.Surface, *sdl.Rect, *sdl.Rect) {
playSurface, err := sdl.CreateRGBSurface(0, 76, 76, 32, 0, 0, 0, 0) playSurface, err := sdl.CreateRGBSurface(0, config.CameraW, config.CameraH, 32, 0, 0, 0, 0)
if err != nil { if err != nil {
panic(err) panic(err)
} }
playSurfaceRect := &sdl.Rect{X: 0, Y: 0, W: 76, H: 76} playSurfaceRect := &sdl.Rect{X: 0, Y: 0, W: config.CameraW, H: config.CameraH}
playSurfaceTargetRect := &sdl.Rect{X: 42, Y: 0, W: 76, H: 76} playSurfaceTargetRect := &sdl.Rect{X: 42, Y: 0, W: config.CameraW, H: config.CameraH}
return playSurface, playSurfaceRect, playSurfaceTargetRect return playSurface, playSurfaceRect, playSurfaceTargetRect
} }
@ -58,7 +57,7 @@ func setupHUDSurface() (*sdl.Surface, *sdl.Rect, *sdl.Rect) {
panic(err) panic(err)
} }
HUDSurfaceRect := &sdl.Rect{X: 0, Y: 0, W: 112, H: 25} HUDSurfaceRect := &sdl.Rect{X: 0, Y: 0, W: 112, H: 25}
HUDSurfaceTargetRect := &sdl.Rect{X: 24, Y: 76, W: 112, H: 25} HUDSurfaceTargetRect := &sdl.Rect{X: 24, Y: config.CameraH, W: 112, H: 25}
return HUDSurface, HUDSurfaceRect, HUDSurfaceTargetRect return HUDSurface, HUDSurfaceRect, HUDSurfaceTargetRect
} }

8
hud.go

@ -11,7 +11,7 @@ var letterOffsets = []int32{
var letterColors []uint32 var letterColors []uint32
// Map function to convert value from range [0, 6] to range [0, 88] // Map function to convert value from range [0, 6] to range [0, 88]
func mapRange(x, minX, maxX, minY, maxY uint16) int32 { func mapRange(x, minX, maxX, minY, maxY uint32) int32 {
// Perform conversion to float64 to ensure proper division // Perform conversion to float64 to ensure proper division
return int32((float64(x-minX)/float64(maxX-minX))*float64(maxY-minY) + float64(minY)) return int32((float64(x-minX)/float64(maxX-minX))*float64(maxY-minY) + float64(minY))
} }
@ -66,9 +66,9 @@ func renderHud(player *Player, surface *sdl.Surface) {
} }
HUDValues := []int32{ HUDValues := []int32{
mapRange(player.energy, 0, MaxEnergy, 0, 88), mapRange(player.energy, 0, serverConfig.MaxEnergy, 0, 88),
mapRange(player.ammunition, 0, MaxAmmunition, 0, 88), mapRange(player.ammunition, 0, serverConfig.MaxAmmunition, 0, 88),
mapRange(player.shields, 0, MaxShields, 0, 88), mapRange(player.shields, 0, serverConfig.MaxShields, 0, 88),
} }
for HUDValueIndex, HUDValue := range HUDValues { for HUDValueIndex, HUDValue := range HUDValues {

339
main.go

@ -1,74 +1,213 @@
package main package main
import ( import (
"encoding/json"
"fmt"
"github.com/veandco/go-sdl2/sdl" "github.com/veandco/go-sdl2/sdl"
"io/ioutil"
"log"
"net"
"os"
"sync"
"time"
) )
const ( type ServerConfig struct {
MapWidth = 1000 // Width of the map MapWidth uint32
MapHeight = 1000 // Height of the map MapHeight uint32
BlastRadius = 5 BlastRadius uint32
MaxEnergy = 3520 MaxEnergy uint32
MaxAmmunition = 6 MaxAmmunition uint32
MaxShields = 100 MaxShields uint32
NormalShotCost = 7 NormalShotCost uint32
SuperShotCost = 80 SuperShotCost uint32
ReloadCost = 4 ReloadCost uint32
MovementCost = 1 MovementCost uint32
DiggingCost = 3 DiggingCost uint32
ShootDiggingCostBonus = 1 ShootDiggingCostBonus uint32
ShootCooldown = 8 ShootCooldown uint32
RechargeCooldownOwn = 0 RechargeCooldownOwn uint32
DiggingCooldown = 4 DiggingCooldown uint32
RechargeCooldownOpponent = 6 RechargeCooldownOpponent uint32
RepairCooldown = 4 RepairCooldown uint32
MovementCooldown = 2 MovementCooldown uint32
MovementCooldownNoEnergy = 4 MovementCooldownNoEnergy uint32
DiggingCooldownNoEnergy = 8 DiggingCooldownNoEnergy uint32
ReloadCooldown = 16 ReloadCooldown uint32
}
Debug = false type Config struct {
MapWindow = false Debug bool `json:"debug"`
RenderGameObjects = true MapWindow bool `json:"map_window"`
ProfilerOn = true RenderGameObjects bool `json:"render_game_objects"`
ProfilerInterval = 100 ProfilerOn bool `json:"profiler_on"`
ProfilerInterval uint64 `json:"profiler_interval"`
Server bool `json:"server"`
CameraW int32 `json:"camera_w"`
CameraH int32 `json:"camera_h"`
Version string `json:"version"`
Client bool `json:"client"`
Address string `json:"address"`
JoyStickDeadZone int16 `json:"joystick_dead_zone"`
DoAllKeymapsPlayers bool `json:"do_all_keymaps_players"`
DoJoyStickPlayers bool `json:"do_joystick_players"`
DoKeymapPlayer bool `json:"do_keymap_player"`
}
JoyStickDeadZone = 8000 func loadOrCreateConfig(filename string) (*Config, error) {
config := &Config{
Debug: false,
MapWindow: false,
RenderGameObjects: true,
ProfilerOn: false,
ProfilerInterval: 100,
Server: false,
CameraW: 76,
CameraH: 76,
Version: "69420",
Client: true,
Address: "192.168.1.8:5074",
JoyStickDeadZone: 8000,
DoAllKeymapsPlayers: false,
DoJoyStickPlayers: true,
DoKeymapPlayer: true,
}
DoAllKeymapsPlayers = false // Check if the file exists
DoJoyStickPlayers = true if _, err := os.Stat(filename); os.IsNotExist(err) {
DoKeymapPlayer = true // File does not exist, create it with the default config
) data, err := json.MarshalIndent(config, "", " ")
if err != nil {
return nil, fmt.Errorf("failed to marshal config: %v", err)
}
err = ioutil.WriteFile(filename, data, 0644)
if err != nil {
return nil, fmt.Errorf("failed to write config file: %v", err)
}
fmt.Println("Config file created with default values.")
} else {
// File exists, load the config
data, err := ioutil.ReadFile(filename)
if err != nil {
return nil, fmt.Errorf("failed to read config file: %v", err)
}
err = json.Unmarshal(data, config)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal config: %v", err)
}
fmt.Println("Config file loaded.")
}
return config, nil
}
var config *Config
var gameMap = &GameMap{}
var serverConfig = ServerConfig{
MapWidth: 1000,
MapHeight: 1000,
BlastRadius: 5,
MaxEnergy: 3520,
MaxAmmunition: 6,
MaxShields: 100,
NormalShotCost: 7,
SuperShotCost: 80,
ReloadCost: 4,
MovementCost: 1,
DiggingCost: 3,
ShootDiggingCostBonus: 1,
ShootCooldown: 8,
RechargeCooldownOwn: 0,
DiggingCooldown: 4,
RechargeCooldownOpponent: 6,
RepairCooldown: 4,
MovementCooldown: 2,
MovementCooldownNoEnergy: 4,
DiggingCooldownNoEnergy: 8,
ReloadCooldown: 16,
}
var mapWindow *sdl.Window var mapWindow *sdl.Window
var mapSurface *sdl.Surface var mapSurface *sdl.Surface
var mapRendererRect = &sdl.Rect{X: 0, Y: 0, W: MapWidth, H: MapHeight} var mapRendererRect = &sdl.Rect{X: 0, Y: 0, W: int32(serverConfig.MapWidth), H: int32(serverConfig.MapHeight)}
var netPlayerMapper = map[*net.Conn]*Player{}
var clientInitialized = false
var worldLock sync.Mutex
func main() { func main() {
initializeSDL() initializeSDL()
defer sdl.Quit() defer sdl.Quit()
configX, err := loadOrCreateConfig("config.json")
gameMap := &GameMap{} if err != nil {
gameMap.createGameMap(MapWidth, MapHeight) log.Fatal(err)
}
players := &[]*Player{} config = configX
players := make(map[uint32]*Player)
initPlayerColors() initPlayerColors()
bullets := make(map[uint32]*Bullet)
bulletParticles := make(map[uint32]*BulletParticle)
bases := make(map[uint32]*Base)
if config.Client && config.Server {
panic("You can' t run client and server in the same instance")
}
if !config.Client {
gameMap.createGameMap(true)
createPlayers(getNeededPlayers(), playerColors, keyMaps, joyMaps, gameMap, players) createPlayers(getNeededPlayers(), playerColors, keyMaps, joyMaps, gameMap, players)
bases = createBases(players, gameMap)
} else {
// Create a connection to the server
conn, err := net.Dial("tcp", config.Address)
if err != nil {
log.Fatalf("Failed to connect to server: %v", err)
}
defer conn.Close()
go handleConnectionClient(&conn, players, bases, bullets, bulletParticles)
for !clientInitialized {
time.Sleep(100 * time.Millisecond)
}
}
defer closeThings(players) defer closeThings(players)
bases := createBases(players, gameMap) for playerIndex, player := range players {
bullets := &[]*Bullet{}
bulletParticles := &[]*BulletParticle{}
for playerIndex, player := range *players {
initPlayer(uint8(playerIndex), player) initPlayer(uint8(playerIndex), player)
} }
if MapWindow {
if config.Server {
listen, err := net.Listen("tcp", "0.0.0.0:5074")
if err != nil {
log.Fatal(err)
}
// close listener
defer listen.Close()
go func() {
for {
conn, err := listen.Accept()
if err != nil {
log.Fatal(err)
}
go handleRequest(conn, players, bullets, bases)
}
}()
}
if config.MapWindow {
mapWindow, mapSurface = setupMapWindowAndSurface() mapWindow, mapSurface = setupMapWindowAndSurface()
} }
running := true running := true
@ -82,6 +221,7 @@ func main() {
currentTime := sdl.GetTicks64() currentTime := sdl.GetTicks64()
deltaTime := currentTime - prevTime deltaTime := currentTime - prevTime
prevTime = currentTime prevTime = currentTime
worldLock.Lock()
// Catch up in case of a large delta time // Catch up in case of a large delta time
for deltaTime > maxDeltaTime { for deltaTime > maxDeltaTime {
@ -91,34 +231,34 @@ func main() {
runGameLogic(players, gameMap, bases, bullets, bulletParticles) runGameLogic(players, gameMap, bases, bullets, bulletParticles)
} }
for playerIndex, player := range *players { // Run logic for the remaining delta time
runGameLogic(players, gameMap, bases, bullets, bulletParticles)
for playerIndex, player := range players {
if player.local { if player.local {
running := doPlayerFrame(uint8(playerIndex), player, players, gameMap, bases, bullets, bulletParticles) running = doPlayerFrame(uint8(playerIndex), player, players, gameMap, bases, bullets, bulletParticles)
if !running { if !running {
break break
} }
} }
} }
// Run logic for the remaining delta time
runGameLogic(players, gameMap, bases, bullets, bulletParticles)
// Render logic // Render logic
if MapWindow { if config.MapWindow {
// Profile rendering (aggregate all renderings) // Profile rendering (aggregate all renderings)
totalRenderTime += profileSection(func() { totalRenderTime += profileSection(func() {
gameMap.render(mapRendererRect, mapSurface) gameMap.render(mapRendererRect, mapSurface)
for _, bullet := range *bullets { for _, bullet := range bullets {
(*bullet).render(mapRendererRect, mapSurface) (*bullet).render(mapRendererRect, mapSurface, bullets)
} }
for _, base := range *bases { for _, base := range bases {
(*base).render(mapRendererRect, mapSurface) (*base).render(mapRendererRect, mapSurface, bases)
} }
for _, playerLoop := range *players { for _, playerLoop := range players {
(*playerLoop).render(mapRendererRect, mapSurface) (*playerLoop).render(mapRendererRect, mapSurface, players)
} }
for _, bulletParticle := range *bulletParticles { for _, bulletParticle := range bulletParticles {
bulletParticle.render(mapRendererRect, mapSurface) bulletParticle.render(mapRendererRect, mapSurface, bulletParticles)
} }
}) })
totalScalingTime += profileSection(func() { totalScalingTime += profileSection(func() {
@ -131,30 +271,70 @@ func main() {
frameCount++ frameCount++
// Log profiling information every 1000 frames // Log profiling information every 1000 frames
if frameCount%ProfilerInterval == 0 && ProfilerOn { if frameCount%config.ProfilerInterval == 0 && config.ProfilerOn {
logMapProfilingInfo(totalRenderTime, totalScalingTime, totalFrameTime, frameCount) logMapProfilingInfo(totalRenderTime, totalScalingTime, totalFrameTime, frameCount)
resetMapProfilingCounters(&totalRenderTime, &totalScalingTime, &totalFrameTime, &frameCount) resetMapProfilingCounters(&totalRenderTime, &totalScalingTime, &totalFrameTime, &frameCount)
} }
worldLock.Unlock()
enforceFrameRate(currentTime, 60) enforceFrameRate(currentTime, 60)
} }
} }
// Separate function to handle game logic // Separate function to handle game logic
func runGameLogic(players *[]*Player, gameMap *GameMap, bases *[]*Base, bullets *[]*Bullet, bulletParticles *[]*BulletParticle) { func runGameLogic(players map[uint32]*Player, gameMap *GameMap, bases map[uint32]*Base, bullets map[uint32]*Bullet, bulletParticles map[uint32]*BulletParticle) {
// Tick world // Tick world
for _, bullet := range *bullets {
if config.Server {
//update remote player maps
for _, player := range players {
if player.local {
continue
}
go player.updateRemotePlayerMap()
go player.updateRemotePlayerBases(bases)
go player.updateRemotePlayerBullets(bullets)
go player.updateRemotePlayerBulletParticles(bulletParticles)
go player.updateRemotePlayerPlayers(players)
fail := player.sendInfoToPlayer()
if fail {
if player.connection != nil {
(*player.connection).Close()
}
delete(players, player.playerID)
continue
}
fail = player.sendUpdatesToPlayer(players)
if fail {
if player.connection != nil {
(*player.connection).Close()
}
delete(players, player.playerID)
continue
}
}
}
for _, bullet := range bullets {
(*bullet).tick(gameMap, bulletParticles, bullets, players) (*bullet).tick(gameMap, bulletParticles, bullets, players)
} }
for _, base := range *bases { for _, player := range players {
if player.local {
continue
}
(*player).tick(bullets)
}
for _, base := range bases {
(*base).tick(players) (*base).tick(players)
} }
for _, bulletParticle := range *bulletParticles { for _, bulletParticle := range bulletParticles {
bulletParticle.tick(bulletParticles) bulletParticle.tick(bulletParticles)
} }
} }
func initPlayer(playerIndex uint8, player *Player) { func initPlayer(playerIndex uint8, player *Player) {
if !player.local {
return
}
player.window, player.logicalSurface = setupWindowAndSurface(playerIndex) player.window, player.logicalSurface = setupWindowAndSurface(playerIndex)
logicalColor := sdl.MapRGBA(player.logicalSurface.Format, 101, 101, 0, 255) logicalColor := sdl.MapRGBA(player.logicalSurface.Format, 101, 101, 0, 255)
player.logicalSurface.FillRect(nil, logicalColor) player.logicalSurface.FillRect(nil, logicalColor)
@ -165,13 +345,13 @@ func initPlayer(playerIndex uint8, player *Player) {
player.HUDSurface, player.HUDSurfaceRect, player.HUDSurfaceTargetRect = setupHUDSurface() player.HUDSurface, player.HUDSurfaceRect, player.HUDSurfaceTargetRect = setupHUDSurface()
initHud(player.HUDSurface) initHud(player.HUDSurface)
player.camera = &sdl.Rect{X: 0, Y: 0, W: 76, H: 76} player.camera = &sdl.Rect{X: 0, Y: 0, W: config.CameraW, H: config.CameraH}
} }
func doPlayerFrame(playerIndex uint8, player *Player, players *[]*Player, gameMap *GameMap, bases *[]*Base, bullets *[]*Bullet, bulletParticles *[]*BulletParticle) bool { func doPlayerFrame(playerIndex uint8, player *Player, players map[uint32]*Player, gameMap *GameMap, bases map[uint32]*Base, bullets map[uint32]*Bullet, bulletParticles map[uint32]*BulletParticle) bool {
running := true running := true
var ( var (
isShooting, shouldContinue bool shouldContinue bool
) )
frameStart := sdl.GetTicks64() frameStart := sdl.GetTicks64()
@ -179,7 +359,7 @@ func doPlayerFrame(playerIndex uint8, player *Player, players *[]*Player, gameMa
player.totalHandleEventsTime += profileSection(func() { player.totalHandleEventsTime += profileSection(func() {
running = handleEvents(player.window, player.logicalSurface) running = handleEvents(player.window, player.logicalSurface)
keyboard := sdl.GetKeyboardState() keyboard := sdl.GetKeyboardState()
shouldContinue, isShooting = handleInput(keyboard, bullets, player, gameMap, players) shouldContinue = handleInput(keyboard, bullets, player, gameMap, players)
running = running && shouldContinue running = running && shouldContinue
}) })
@ -188,20 +368,21 @@ func doPlayerFrame(playerIndex uint8, player *Player, players *[]*Player, gameMa
player.track(player.camera) player.track(player.camera)
gameMap.render(player.camera, player.playSurface) gameMap.render(player.camera, player.playSurface)
for _, bullet := range *bullets { for _, bullet := range bullets {
(*bullet).render(player.camera, player.playSurface) (*bullet).render(player.camera, player.playSurface, bullets)
} }
for _, base := range *bases { for _, base := range bases {
(*base).render(player.camera, player.playSurface) (*base).render(player.camera, player.playSurface, bases)
} }
for _, playerLoop := range *players { for _, playerLoop := range players {
(*playerLoop).render(player.camera, player.playSurface) (*playerLoop).render(player.camera, player.playSurface, players)
} }
for _, bulletParticle := range *bulletParticles { for _, bulletParticle := range bulletParticles {
bulletParticle.render(player.camera, player.playSurface) bulletParticle.render(player.camera, player.playSurface, bulletParticles)
} }
player.tick(isShooting, bullets, player.playSurface.Format) player.tick(bullets)
player.gameObject.prevBaseRect = player.gameObject.baseRect
renderHud(player, player.HUDSurface) renderHud(player, player.HUDSurface)
player.playSurface.BlitScaled(player.playSurfaceRect, player.logicalSurface, player.playSurfaceTargetRect) player.playSurface.BlitScaled(player.playSurfaceRect, player.logicalSurface, player.playSurfaceTargetRect)
player.HUDSurface.BlitScaled(player.HUDSurfaceRect, player.logicalSurface, player.HUDSurfaceTargetRect) player.HUDSurface.BlitScaled(player.HUDSurfaceRect, player.logicalSurface, player.HUDSurfaceTargetRect)
@ -217,7 +398,7 @@ func doPlayerFrame(playerIndex uint8, player *Player, players *[]*Player, gameMa
player.frameCount++ player.frameCount++
// Log profiling information every 1000 frames // Log profiling information every 1000 frames
if player.frameCount%ProfilerInterval == 0 && ProfilerOn && playerIndex == 0 { if player.frameCount%config.ProfilerInterval == 0 && config.ProfilerOn && playerIndex == 0 {
logProfilingInfo(player.totalHandleEventsTime, player.totalRenderTime, player.totalScalingTime, player.totalFrameTime, player.frameCount) logProfilingInfo(player.totalHandleEventsTime, player.totalRenderTime, player.totalScalingTime, player.totalFrameTime, player.frameCount)
resetProfilingCounters(&player.totalHandleEventsTime, &player.totalRenderTime, &player.totalScalingTime, &player.totalFrameTime, &player.frameCount) resetProfilingCounters(&player.totalHandleEventsTime, &player.totalRenderTime, &player.totalScalingTime, &player.totalFrameTime, &player.frameCount)
} }

33
map.go

@ -16,8 +16,8 @@ type GameMap struct {
//tile 3 - soil 2 //tile 3 - soil 2
//tile 4 - base //tile 4 - base
//tile 5 - world edge (should not be present in the map object) //tile 5 - world edge (should not be present in the map object)
width int32 width uint32
height int32 height uint32
} }
func GetTileColorValue(value uint8) color.Color { func GetTileColorValue(value uint8) color.Color {
@ -40,7 +40,7 @@ func GetTileColorValue(value uint8) color.Color {
} }
func (gameMap *GameMap) getTileColor(x int32, y int32) color.Color { func (gameMap *GameMap) getTileColor(x int32, y int32) color.Color {
if x < 0 || x >= gameMap.width || y < 0 || y >= gameMap.height { if x < 0 || x >= int32(gameMap.width) || y < 0 || y >= int32(gameMap.height) {
return GetTileColorValue(5) return GetTileColorValue(5)
} else { } else {
tileValue := gameMap.tiles[x][y] tileValue := gameMap.tiles[x][y]
@ -48,10 +48,9 @@ func (gameMap *GameMap) getTileColor(x int32, y int32) color.Color {
} }
} }
func (gameMap *GameMap) createGameMap(width int32, height int32) { func (gameMap *GameMap) createGameMap(withData bool) {
// Initialize the 2D slice for game tiles // Initialize the 2D slice for game tiles
gameMap.tiles = make([][]uint8, width) gameMap.tiles = make([][]uint8, serverConfig.MapWidth)
// Create Perlin noise generators // Create Perlin noise generators
perlinNoiseRock := perlin.NewPerlin(2, 4, 8, time.Now().Unix()) perlinNoiseRock := perlin.NewPerlin(2, 4, 8, time.Now().Unix())
perlinNoiseSoil := perlin.NewPerlin(2, 8, 4, time.Now().Unix()) perlinNoiseSoil := perlin.NewPerlin(2, 8, 4, time.Now().Unix())
@ -61,23 +60,24 @@ func (gameMap *GameMap) createGameMap(width int32, height int32) {
// Loop over the rows // Loop over the rows
for i := range gameMap.tiles { for i := range gameMap.tiles {
if withData {
// Launch a goroutine for each row
// Increment WaitGroup counter for each goroutine // Increment WaitGroup counter for each goroutine
wg.Add(1) wg.Add(1)
// Launch a goroutine for each row
go func(i int) { go func(i int) {
defer wg.Done() // Signal that this goroutine is done defer wg.Done() // Signal that this goroutine is done
// Initialize the row // Initialize the row
gameMap.tiles[i] = make([]uint8, height) gameMap.tiles[i] = make([]uint8, serverConfig.MapHeight)
// Process each tile in the row // Process each tile in the row
for j := range gameMap.tiles[i] { for j := range gameMap.tiles[i] {
perlinRock := perlinNoiseRock.Noise2D(float64(i)/float64(width)*6, float64(j)/float64(height)*6) perlinRock := perlinNoiseRock.Noise2D(float64(i)/float64(serverConfig.MapWidth)*6, float64(j)/float64(serverConfig.MapHeight)*6)
if perlinRock > 0.2 { if perlinRock > 0.2 {
gameMap.tiles[i][j] = 1 gameMap.tiles[i][j] = 1
} else { } else {
perlinSoil := perlinNoiseSoil.Noise2D(float64(i)/float64(width)*10, float64(j)/float64(height)*10) perlinSoil := perlinNoiseSoil.Noise2D(float64(i)/float64(serverConfig.MapWidth)*10, float64(j)/float64(serverConfig.MapHeight)*10)
if perlinSoil >= 0 { if perlinSoil >= 0 {
gameMap.tiles[i][j] = 2 gameMap.tiles[i][j] = 2
} else { } else {
@ -86,14 +86,19 @@ func (gameMap *GameMap) createGameMap(width int32, height int32) {
} }
} }
}(i) }(i)
} else {
for x := uint32(0); x < serverConfig.MapWidth; x++ {
gameMap.tiles[x] = make([]uint8, serverConfig.MapHeight)
}
}
} }
// Wait for all goroutines to finish // Wait for all goroutines to finish
wg.Wait() wg.Wait()
// Set the dimensions of the game map // Set the dimensions of the game map
gameMap.width = width gameMap.width = serverConfig.MapWidth
gameMap.height = height gameMap.height = serverConfig.MapHeight
} }
func (gameMap *GameMap) render(camera *sdl.Rect, surface *sdl.Surface) { func (gameMap *GameMap) render(camera *sdl.Rect, surface *sdl.Surface) {
@ -131,7 +136,7 @@ func (gameMap *GameMap) checkCollision(posX, posY int32) uint8 {
//1 destructible collision //1 destructible collision
//2 rock collision //2 rock collision
//3 indestructible collision //3 indestructible collision
if posX >= gameMap.width || posX < 0 || posY >= gameMap.height || posY < 0 { if posX >= int32(gameMap.width) || posX < 0 || posY >= int32(gameMap.height) || posY < 0 {
return 3 return 3
} }
switch gameMap.tiles[posX][posY] { switch gameMap.tiles[posX][posY] {

995
player.go

File diff suppressed because it is too large Load Diff

@ -74,15 +74,15 @@ type PlayerColors struct {
func getNeededPlayers() (neededPlayers uint8) { func getNeededPlayers() (neededPlayers uint8) {
neededPlayers = 0 neededPlayers = 0
if DoAllKeymapsPlayers { if config.DoAllKeymapsPlayers {
neededPlayers += uint8(len(keyMaps)) neededPlayers += uint8(len(keyMaps))
} else if DoKeymapPlayer && len(keyMaps) > 0 { } else if config.DoKeymapPlayer && len(keyMaps) > 0 {
neededPlayers++ neededPlayers++
} }
if DoJoyStickPlayers { if config.DoJoyStickPlayers {
neededPlayers += uint8(sdl.NumJoysticks()) neededPlayers += uint8(sdl.NumJoysticks())
} }
if neededPlayers < 2 { if neededPlayers < 2 && !config.Server {
neededPlayers = 2 neededPlayers = 2
} }
return neededPlayers return neededPlayers
@ -285,9 +285,9 @@ func initPlayerColors() {
} }
} }
func handleInput(keyboard []uint8, bullets *[]*Bullet, player *Player, gameMap *GameMap, players *[]*Player) (bool, bool) { func handleInput(keyboard []uint8, bullets map[uint32]*Bullet, player *Player, gameMap *GameMap, players map[uint32]*Player) bool {
if !player.local || player.shields <= 0 || player.shields > MaxShields { if !player.local || player.shields <= 0 || player.shields > serverConfig.MaxShields {
return true, false return true
} }
shoot := false shoot := false
super := false super := false
@ -306,7 +306,7 @@ func handleInput(keyboard []uint8, bullets *[]*Bullet, player *Player, gameMap *
} }
if key == player.keyMap.exit { if key == player.keyMap.exit {
return false, shoot return false
} else if key == player.keyMap.shoot { } else if key == player.keyMap.shoot {
shoot = true shoot = true
} else if key == player.keyMap.super { } else if key == player.keyMap.super {
@ -329,10 +329,10 @@ func handleInput(keyboard []uint8, bullets *[]*Bullet, player *Player, gameMap *
xAxis := player.joyStick.Axis(player.joyMap.xAxis) xAxis := player.joyStick.Axis(player.joyMap.xAxis)
yAxis := player.joyStick.Axis(player.joyMap.yAxis) yAxis := player.joyStick.Axis(player.joyMap.yAxis)
moveRight = xAxis > JoyStickDeadZone moveRight = xAxis > config.JoyStickDeadZone
moveLeft = xAxis < -JoyStickDeadZone moveLeft = xAxis < -config.JoyStickDeadZone
moveUp = yAxis > JoyStickDeadZone moveUp = yAxis > config.JoyStickDeadZone
moveDown = yAxis < -JoyStickDeadZone moveDown = yAxis < -config.JoyStickDeadZone
} else { } else {
moveUp = player.joyStick.Button(player.joyMap.upButton) == sdl.PRESSED moveUp = player.joyStick.Button(player.joyMap.upButton) == sdl.PRESSED
@ -341,7 +341,7 @@ func handleInput(keyboard []uint8, bullets *[]*Bullet, player *Player, gameMap *
moveRight = player.joyStick.Button(player.joyMap.rightButton) == sdl.PRESSED moveRight = player.joyStick.Button(player.joyMap.rightButton) == sdl.PRESSED
} }
if player.joyStick.Button(player.joyMap.exitButton) == sdl.PRESSED { if player.joyStick.Button(player.joyMap.exitButton) == sdl.PRESSED {
return false, shoot return false
} }
if player.joyStick.Button(player.joyMap.shootButton) == sdl.PRESSED { if player.joyStick.Button(player.joyMap.shootButton) == sdl.PRESSED {
shoot = true shoot = true
@ -379,9 +379,9 @@ func handleInput(keyboard []uint8, bullets *[]*Bullet, player *Player, gameMap *
// Handle movement after the loop // Handle movement after the loop
if moveUp || moveDown || moveLeft || moveRight { if moveUp || moveDown || moveLeft || moveRight {
if player.tryMove(gameMap, shoot, players) { if player.tryMove(gameMap, shoot, players) {
player.energy -= MovementCost player.energy -= serverConfig.MovementCost
} }
} }
return true, shoot return true
} }

133
tuneller.proto Normal file

@ -0,0 +1,133 @@
syntax = "proto3";
package goingtunneling;
option go_package = "./proto";
message Position {
int32 posX = 1;
int32 posY = 2;
}
message Player {
uint32 playerID = 1;
PlayerLocation location = 2;
}
message PlayerLocation {
Position position = 1;
uint32 orientation = 2;
}
message Bullet {
Position position = 1;
uint32 direction = 2;
Color color = 3;
uint32 id = 4;
bool super = 5;
}
message Color {
uint32 red = 1;
uint32 green = 2;
uint32 blue = 3;
uint32 alpha = 4;
}
message BulletParticle {
Position position = 1;
uint32 expirationTimer = 2;
Color color = 3;
uint32 id = 4;
}
message ServerInfo {
uint32 maxEnergy = 1;
uint32 maxAmmunition = 2;
uint32 maxShields = 3;
uint32 mapWidth = 4;
uint32 mapHeight = 5;
uint32 normalShotCost = 6;
uint32 superShotCost = 7;
uint32 reloadCost = 8;
uint32 movementCost = 9;
uint32 diggingCost = 10;
uint32 shootDiggingBonus = 11;
uint32 shootCooldown = 12;
uint32 rechargeCooldown = 13;
uint32 rechargeOpponentCooldown = 14;
uint32 repairCooldown = 15;
uint32 diggingCooldown = 16;
uint32 movementCooldown = 17;
uint32 movementCooldownNoEnergy = 18;
uint32 diggingCooldownNoEnergy = 19;
uint32 reloadCooldown = 20;
uint32 blastRadius = 21;
uint32 playerID = 22;
uint32 playerColorID = 23;
}
message PlayerUpdate {
uint32 energy = 1;
uint32 ammo = 2;
uint32 shields = 3;
}
message BaseLocation {
Position position = 1;
Player owner = 2;
Color color = 3;
}
message TileUpdate {
Position position = 1;
uint32 kind = 2;
}
message WorldUpdate {
repeated Player players = 1;
repeated BaseLocation base = 2;
repeated Bullet bullets = 3;
repeated BulletParticle bulletParticles = 4;
repeated TileUpdate tileUpdate = 5;
}
message ClientBound {
oneof clientBoundMessage {
ServerInfo serverInfo = 1;
PlayerUpdate playerUpdate = 2;
PlayerLocation playerLocationUpdate = 3;
WorldUpdate worldUpdate = 4;
PlayerStartResponse playerStartResponse = 5;
}
}
message DigBlock {
Position position = 1;
bool isShooting = 2;
}
message Shoot {
bool super = 1;
}
message PlayerAction {
oneof playerAction {
DigBlock digBlock = 1;
Shoot shoot = 2;
}
}
message PlayerStartRequest {
string version = 1;
}
message PlayerStartResponse {
string version = 1;
}
message ServerBound {
oneof serverBoundMessage {
PlayerLocation playerPosition = 1;
PlayerAction playerAction = 2;
PlayerStartRequest playerStartRequest = 3;
}
}