Add multi monitor multiplayer

This commit is contained in:
Bruno Rybársky 2024-08-29 18:48:27 +02:00
parent 352c7af7ce
commit 696b827a7d
10 changed files with 1210 additions and 515 deletions

138
base.go

@ -2,28 +2,29 @@ package main
import "github.com/veandco/go-sdl2/sdl" import "github.com/veandco/go-sdl2/sdl"
const (
openingWidth = 7
)
type Base struct { type Base struct {
rect sdl.Rect
owner *Player owner *Player
openingWidth int32
gameObject *GameObject
} }
func (base *Base) tick(players *[]*Player) { func (base *Base) tick(players *[]*Player) {
for _, player := range *players { for _, player := range *players {
if player.cullingRect.HasIntersection(&base.rect) { if player.gameObject.baseRect.HasIntersection(&base.gameObject.baseRect) {
if player.rechargeCooldown == 0 && player.energy < MaxEnergy { if player.rechargeCooldown == 0 && player.energy < MaxEnergy {
if MaxEnergy-player.energy < 4 {
player.energy++ player.energy++
} else {
player.energy += 4
}
if player == base.owner { if player == base.owner {
player.rechargeCooldown = RechargeCooldownOwn player.rechargeCooldown = RechargeCooldownOwn
} else { } else {
player.rechargeCooldown = RechargeCooldownOpponent player.rechargeCooldown = RechargeCooldownOpponent
} }
} }
if player == base.owner && player.repairCooldown == 0 && player.health < MaxHealth { if player == base.owner && player.repairCooldown == 0 && player.shields < MaxShields {
player.health++ player.shields++
player.repairCooldown = RepairCooldown player.repairCooldown = RepairCooldown
} }
} }
@ -31,112 +32,79 @@ func (base *Base) tick(players *[]*Player) {
} }
func (base *Base) render(camera *sdl.Rect, surface *sdl.Surface) { func (base *Base) render(camera *sdl.Rect, surface *sdl.Surface) {
borderWidth := base.rect.W - openingWidth base.gameObject.render(camera, surface)
borderWidthA := borderWidth / 2
borderWidthB := borderWidth - borderWidthA + 1
if borderWidth < 0 {
panic("Bad border width")
}
rects := []sdl.Rect{
{
X: base.rect.X,
Y: base.rect.Y,
W: 1,
H: base.rect.H,
},
{
X: base.rect.X + base.rect.W,
Y: base.rect.Y,
W: 1,
H: base.rect.H,
},
{
X: base.rect.X,
Y: base.rect.Y,
W: borderWidthA,
H: 1,
},
{
X: base.rect.X + borderWidthA + openingWidth,
Y: base.rect.Y,
W: borderWidthB,
H: 1,
},
{
X: base.rect.X,
Y: base.rect.Y + base.rect.H,
W: borderWidthA,
H: 1,
},
{
X: base.rect.X + borderWidthA + openingWidth,
Y: base.rect.Y + base.rect.H,
W: borderWidthB,
H: 1,
},
}
for _, rect := range rects {
if camera.HasIntersection(&rect) {
cameraCompensatedRect := sdl.Rect{
X: rect.X - camera.X,
Y: rect.Y - camera.Y,
W: rect.W,
H: rect.H,
}
surface.FillRect(&cameraCompensatedRect, (*base.owner).color2)
}
}
} }
func (base *Base) build(gameMap *GameMap) { func (base *Base) build(gameMap *GameMap) {
borderWidth := base.rect.W - openingWidth borderWidth := base.gameObject.baseRect.W - base.openingWidth
borderWidthA := borderWidth / 2 borderWidthA := borderWidth / 2
if borderWidth < 0 { if borderWidth < 0 {
panic("Bad border width") panic("Bad border width")
} }
if base.rect.H < 9 { if base.gameObject.baseRect.H < 9 {
panic("Bad border height") panic("Bad border height")
} }
if gameMap.width-base.rect.X-base.rect.W <= 0 { if gameMap.width-base.gameObject.baseRect.X-base.gameObject.baseRect.W <= 0 {
panic("Bad base x location") panic("Bad base x location")
} }
if gameMap.height-base.rect.Y-base.rect.H <= 0 { if gameMap.height-base.gameObject.baseRect.Y-base.gameObject.baseRect.H <= 0 {
panic("Bad base y location") panic("Bad base y location")
} }
if base.rect.X < 0 || base.rect.Y < 0 { if base.gameObject.baseRect.X < 0 || base.gameObject.baseRect.Y < 0 {
panic("Bad base negative location") panic("Bad base negative location")
} }
for x := base.rect.X; x < base.rect.X+base.rect.W+1; x++ { for x := base.gameObject.baseRect.X; x < base.gameObject.baseRect.X+base.gameObject.baseRect.W+1; x++ {
for y := base.rect.Y; y < base.rect.Y+base.rect.H+1; y++ { for y := base.gameObject.baseRect.Y; y < base.gameObject.baseRect.Y+base.gameObject.baseRect.H+1; y++ {
gameMap.tiles[x][y] = 0 gameMap.tiles[x][y] = 0
} }
} }
for y := base.rect.Y; y < base.rect.Y+base.rect.H; y++ { for y := base.gameObject.baseRect.Y; y < base.gameObject.baseRect.Y+base.gameObject.baseRect.H; y++ {
gameMap.tiles[base.rect.X][y] = 4 gameMap.tiles[base.gameObject.baseRect.X][y] = 4
gameMap.tiles[base.rect.X+base.rect.W][y] = 4 gameMap.tiles[base.gameObject.baseRect.X+base.gameObject.baseRect.W][y] = 4
} }
for x := base.rect.X; x < base.rect.X+borderWidthA; x++ { for x := base.gameObject.baseRect.X; x < base.gameObject.baseRect.X+borderWidthA; x++ {
gameMap.tiles[x][base.rect.Y] = 4 gameMap.tiles[x][base.gameObject.baseRect.Y] = 4
gameMap.tiles[x][base.rect.Y+base.rect.H] = 4 gameMap.tiles[x][base.gameObject.baseRect.Y+base.gameObject.baseRect.H] = 4
} }
for x := base.rect.X + borderWidthA + openingWidth; x < base.rect.X+base.rect.W+1; x++ { for x := base.gameObject.baseRect.X + borderWidthA + base.openingWidth; x < base.gameObject.baseRect.X+base.gameObject.baseRect.W+1; x++ {
gameMap.tiles[x][base.rect.Y] = 4 gameMap.tiles[x][base.gameObject.baseRect.Y] = 4
gameMap.tiles[x][base.rect.Y+base.rect.H] = 4 gameMap.tiles[x][base.gameObject.baseRect.Y+base.gameObject.baseRect.H] = 4
} }
} }
func createBases(players *[]*Player, gameMap *GameMap) *[]*Base { func createBases(players *[]*Player, gameMap *GameMap) *[]*Base {
bases := &[]*Base{} bases := &[]*Base{}
for ownerID, player := range *players { for ownerID, player := range *players {
*bases = append(*bases, &Base{ gameObject := &GameObject{}
rect: sdl.Rect{ gameObject.baseRect = sdl.Rect{
X: player.posX - 14, X: player.gameObject.baseRect.X - 14,
Y: player.posY - 14, Y: player.gameObject.baseRect.Y - 14,
W: 35, W: 35,
H: 35, H: 35,
}, }
openingWidth := int32(float64(player.gameObject.baseRect.W) * 1.5)
if openingWidth%2 == 0 {
openingWidth++
}
borderWidth := gameObject.baseRect.W - openingWidth
borderWidthA := borderWidth / 2
borderWidthB := borderWidth - borderWidthA + 1
if borderWidth < 0 {
panic("Bad border width")
}
gameObject.addColor((*player).playerColors.body)
gameObject.addColoredRect(0, 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(borderWidthA+openingWidth, 0, borderWidthB, 1, 0)
gameObject.addColoredRect(0, gameObject.baseRect.H, borderWidthA, 1, 0)
gameObject.addColoredRect(borderWidthA+openingWidth, gameObject.baseRect.H, borderWidthB, 1, 0)
*bases = append(*bases, &Base{
gameObject: gameObject,
owner: (*players)[ownerID], owner: (*players)[ownerID],
openingWidth: openingWidth,
}) })
(*bases)[ownerID].build(gameMap) (*bases)[ownerID].build(gameMap)
} }

@ -149,14 +149,16 @@ func (bullet *Bullet) tick(gameMap *GameMap,
} }
hitPlayer := false hitPlayer := false
for _, player := range *players { for _, player := range *players {
if player.rect1.HasIntersection(&bulletRect) || if hitPlayer {
player.rect2.HasIntersection(&bulletRect) ||
player.rect3.HasIntersection(&bulletRect) ||
player.rect4.HasIntersection(&bulletRect) {
hitPlayer = true
player.health -= 20
break break
} }
for _, coloredRect := range player.gameObject.getCurrentRects() {
if coloredRect.rect.HasIntersection(&bulletRect) {
hitPlayer = true
player.shields -= 10
break
}
}
} }
if collisionResult != 0 || hitPlayer { if collisionResult != 0 || hitPlayer {
bullet.explode(gameMap, bulletParticleMap) bullet.explode(gameMap, bulletParticleMap)

87
gameobject.go Normal file

@ -0,0 +1,87 @@
package main
import (
"github.com/veandco/go-sdl2/sdl"
"image/color"
)
type GameObject struct {
baseRect sdl.Rect
borderRect sdl.Rect
orientation uint8
visualRects [][]*ColoredRect
colors []color.Color
}
type ColoredRect struct {
color color.Color
rect *sdl.Rect
}
func adjustRectToCamera(object *sdl.Rect, camera *sdl.Rect) *sdl.Rect {
return &sdl.Rect{
X: object.X - camera.X,
Y: object.Y - camera.Y,
W: object.W,
H: object.H,
}
}
func (gameObject *GameObject) adjustRectToCamera(offset *sdl.Rect, camera *sdl.Rect) *sdl.Rect {
return &sdl.Rect{
X: gameObject.baseRect.X + offset.X - camera.X,
Y: gameObject.baseRect.Y + offset.Y - camera.Y,
W: offset.W,
H: offset.H,
}
}
func (gameObject *GameObject) render(camera *sdl.Rect, surface *sdl.Surface) {
if camera.HasIntersection(&gameObject.baseRect) {
if Debug {
gameObject.borderRect = sdl.Rect{
X: gameObject.baseRect.X - 1,
Y: gameObject.baseRect.Y - 1,
W: gameObject.baseRect.W + 2,
H: gameObject.baseRect.H + 2,
}
borderRectFinal := adjustRectToCamera(&gameObject.borderRect, camera)
baseRectFinal := adjustRectToCamera(&gameObject.baseRect, camera)
surface.FillRect(borderRectFinal, sdl.MapRGBA(surface.Format, 20, 192, 128, 64))
surface.FillRect(baseRectFinal, sdl.MapRGBA(surface.Format, 255, 20, 10, 64))
}
if RenderGameObjects {
for _, coloredRect := range gameObject.visualRects[gameObject.orientation] {
finalRect := gameObject.adjustRectToCamera(coloredRect.rect, camera)
r, g, b, a := coloredRect.color.RGBA()
surface.FillRect(finalRect, sdl.MapRGBA(surface.Format, uint8(r), uint8(g), uint8(b), uint8(a)))
}
}
}
}
func (gameObject *GameObject) addColoredRect(x, y, w, h int32, color uint8) {
if gameObject.visualRects == nil {
gameObject.visualRects = make([][]*ColoredRect, 0)
}
if uint8(len(gameObject.visualRects)) <= gameObject.orientation {
gameObject.visualRects = append(gameObject.visualRects, make([]*ColoredRect, 0))
}
if uint8(len(gameObject.colors)) > color {
gameObject.visualRects[gameObject.orientation] = append(gameObject.visualRects[gameObject.orientation],
&ColoredRect{
color: gameObject.colors[color],
rect: &sdl.Rect{
X: x, Y: y, W: w, H: h,
},
})
}
}
func (gameObject *GameObject) getCurrentRects() []*ColoredRect {
return gameObject.visualRects[gameObject.orientation]
}
func (gameObject *GameObject) addColor(color color.Color) {
gameObject.colors = append(gameObject.colors, color)
}

@ -1,6 +1,7 @@
package main package main
import ( import (
"fmt"
"github.com/veandco/go-sdl2/sdl" "github.com/veandco/go-sdl2/sdl"
) )
@ -10,9 +11,24 @@ func initializeSDL() {
} }
} }
func setupWindowAndSurface() (*sdl.Window, *sdl.Surface) { func setupWindowAndSurface(playerIndex uint8) (*sdl.Window, *sdl.Surface) {
const windowWidth, windowHeight = 160, 100 const windowWidth, windowHeight = 160, 100
window, err := sdl.CreateWindow("test", sdl.WINDOWPOS_UNDEFINED, sdl.WINDOWPOS_UNDEFINED, windowWidth, windowHeight, sdl.WINDOW_SHOWN|sdl.WINDOW_RESIZABLE) window, err := sdl.CreateWindow(fmt.Sprintf("Tunneler - player %d", playerIndex), sdl.WINDOWPOS_UNDEFINED, sdl.WINDOWPOS_UNDEFINED, windowWidth, windowHeight, sdl.WINDOW_SHOWN|sdl.WINDOW_RESIZABLE)
if err != nil {
panic(err)
}
logicalSurface, err := sdl.CreateRGBSurface(0, windowWidth, windowHeight, 32, 0, 0, 0, 0)
if err != nil {
panic(err)
}
return window, logicalSurface
}
func setupMapWindowAndSurface() (*sdl.Window, *sdl.Surface) {
const windowWidth, windowHeight = MapWidth, MapHeight
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)
} }
@ -81,7 +97,6 @@ func handleWindowResize(window *sdl.Window, logicalSurface *sdl.Surface) {
letterboxX := (windowWidth - newWidth) / 2 letterboxX := (windowWidth - newWidth) / 2
letterboxY := (windowHeight - newHeight) / 2 letterboxY := (windowHeight - newHeight) / 2
windowSurface.FillRect(nil, 0)
srcRect := &sdl.Rect{X: 0, Y: 0, W: logicalSurface.W, H: logicalSurface.H} srcRect := &sdl.Rect{X: 0, Y: 0, W: logicalSurface.W, H: logicalSurface.H}
dstRect := &sdl.Rect{X: letterboxX, Y: letterboxY, W: newWidth, H: newHeight} dstRect := &sdl.Rect{X: letterboxX, Y: letterboxY, W: newWidth, H: newHeight}
@ -89,68 +104,45 @@ func handleWindowResize(window *sdl.Window, logicalSurface *sdl.Surface) {
window.UpdateSurface() window.UpdateSurface()
} }
func renderScene(logicalSurface, playSurface, HUDSurface *sdl.Surface, gameMap *GameMap, players *[]*Player, bases *[]*Base, bullets *[]*Bullet, bulletParticles *[]*BulletParticle, camera *sdl.Rect, playSurfaceRect, playSurfaceTargetRect, HUDSurfaceRect, HUDSurfaceTargetRect *sdl.Rect) { func adjustWindow(window *sdl.Window, logicalSurface *sdl.Surface) {
HUDColor := sdl.MapRGBA(HUDSurface.Format, 101, 101, 101, 255) // Get window surface and handle any errors
logicalColor := sdl.MapRGBA(HUDSurface.Format, 101, 101, 0, 255)
playColor := sdl.MapRGBA(HUDSurface.Format, 101, 0, 101, 255)
logicalSurface.FillRect(nil, logicalColor)
playSurface.FillRect(nil, playColor)
HUDSurface.FillRect(nil, HUDColor)
(*players)[0].track(camera)
gameMap.render(camera, playSurface)
for _, bullet := range *bullets {
(*bullet).render(camera, playSurface)
(*bullet).tick(gameMap, bulletParticles, bullets, players)
}
for _, base := range *bases {
(*base).render(camera, playSurface)
(*base).tick(players)
}
for _, player := range *players {
(*player).render(camera, playSurface)
(*player).tick()
}
for _, bulletParticle := range *bulletParticles {
bulletParticle.render(camera, playSurface)
bulletParticle.tick(bulletParticles)
}
renderHud((*players)[0], HUDSurface)
playSurface.BlitScaled(playSurfaceRect, logicalSurface, playSurfaceTargetRect)
HUDSurface.BlitScaled(HUDSurfaceRect, logicalSurface, HUDSurfaceTargetRect)
}
func adjustWindow(window *sdl.Window, logicalSurface *sdl.Surface, pixelBG uint32) {
windowSurface, err := window.GetSurface() windowSurface, err := window.GetSurface()
if err != nil { if err != nil {
panic(err) panic(err)
} }
// Retrieve window dimensions
windowWidth, windowHeight := windowSurface.W, windowSurface.H windowWidth, windowHeight := windowSurface.W, windowSurface.H
aspectRatio := float64(logicalSurface.W) / float64(logicalSurface.H)
newWidth := windowWidth
newHeight := int32(float64(windowWidth) / aspectRatio)
if newHeight > windowHeight { // Calculate aspect ratio
logicalAspectRatio := float64(logicalSurface.W) / float64(logicalSurface.H)
windowAspectRatio := float64(windowWidth) / float64(windowHeight)
var newWidth, newHeight int32
if windowAspectRatio > logicalAspectRatio {
// Window is wider than logical surface
newHeight = windowHeight newHeight = windowHeight
newWidth = int32(float64(windowHeight) * aspectRatio) newWidth = int32(float64(windowHeight) * logicalAspectRatio)
} else {
// Window is taller or equal in aspect ratio
newWidth = windowWidth
newHeight = int32(float64(windowWidth) / logicalAspectRatio)
} }
// Calculate letterbox positions
letterboxX := (windowWidth - newWidth) / 2 letterboxX := (windowWidth - newWidth) / 2
letterboxY := (windowHeight - newHeight) / 2 letterboxY := (windowHeight - newHeight) / 2
windowSurface.FillRect(nil, pixelBG) // Fill background
windowSurface.FillRect(nil, sdl.MapRGBA(logicalSurface.Format, 80, 20, 10, 255))
// Set source and destination rectangles
srcRect := &sdl.Rect{X: 0, Y: 0, W: logicalSurface.W, H: logicalSurface.H} srcRect := &sdl.Rect{X: 0, Y: 0, W: logicalSurface.W, H: logicalSurface.H}
dstRect := &sdl.Rect{X: letterboxX, Y: letterboxY, W: newWidth, H: newHeight} dstRect := &sdl.Rect{X: letterboxX, Y: letterboxY, W: newWidth, H: newHeight}
// Perform the scaled blit
logicalSurface.BlitScaled(srcRect, windowSurface, dstRect) logicalSurface.BlitScaled(srcRect, windowSurface, dstRect)
// Update the window surface
window.UpdateSurface() window.UpdateSurface()
} }

151
hud.go

@ -2,77 +2,82 @@ package main
import "github.com/veandco/go-sdl2/sdl" import "github.com/veandco/go-sdl2/sdl"
func renderHud(player *Player, surface *sdl.Surface) { var letterOffsets = []int32{
eRects := []sdl.Rect{ 2,
{ 10,
X: 5, 18,
Y: 4, }
W: 6,
H: 1, var letterColors []uint32
},
{ // Map function to convert value from range [0, 6] to range [0, 88]
X: 5, func mapRange(x, minX, maxX, minY, maxY uint16) int32 {
Y: 4, // Perform conversion to float64 to ensure proper division
W: 1, return int32((float64(x-minX)/float64(maxX-minX))*float64(maxY-minY) + float64(minY))
H: 5, }
},
{ var rects = [][]sdl.Rect{
X: 5, // Define the rects with modified Y-values
Y: 6, {
W: 6, {X: 5, Y: 0, W: 6, H: 1},
H: 1, {X: 5, Y: 0, W: 1, H: 5},
}, {X: 5, Y: 2, W: 6, H: 1},
{ {X: 5, Y: 4, W: 6, H: 1},
X: 5, },
Y: 8,
W: 6, {
H: 1, {X: 6, Y: 0, W: 4, H: 1}, // Top bar of "A"
}, {X: 5, Y: 1, W: 1, H: 4}, // Left vertical line of "A"
} {X: 10, Y: 1, W: 1, H: 4}, // Right vertical line of "A"
{X: 6, Y: 3, W: 4, H: 1}, // Middle bar of "A"
sRects := []sdl.Rect{ },
{
X: 5, {
Y: 18, {X: 5, Y: 0, W: 6, H: 1},
W: 6, {X: 5, Y: 0, W: 1, H: 3},
H: 1, {X: 5, Y: 2, W: 6, H: 1},
}, {X: 10, Y: 2, W: 1, H: 3},
{ {X: 5, Y: 4, W: 6, H: 1},
X: 5, },
Y: 18, }
W: 1,
H: 3, func initHud(surface *sdl.Surface) {
}, letterColors = []uint32{
{ sdl.MapRGBA(surface.Format, 243, 235, 28, 255),
X: 5, sdl.MapRGBA(surface.Format, 255, 80, 40, 255),
Y: 20, sdl.MapRGBA(surface.Format, 40, 243, 243, 255),
W: 6, }
H: 1, HUDColor := sdl.MapRGBA(surface.Format, 101, 101, 101, 255)
}, surface.FillRect(nil, HUDColor)
{
X: 10, for letterIndex, letter := range rects {
Y: 20, for _, letterRect := range letter {
W: 1, offsetRect := letterRect
H: 3, offsetRect.Y += letterOffsets[letterIndex]
}, surface.FillRect(&offsetRect, letterColors[letterIndex])
{ }
X: 5, }
Y: 22, }
W: 6,
H: 1, func renderHud(player *Player, surface *sdl.Surface) {
},
} for _, letterOffset := range letterOffsets {
surface.FillRect(&sdl.Rect{X: 16, Y: letterOffset - 1, W: 90, H: 7}, 0)
for _, rect := range eRects { }
surface.FillRect(&rect, sdl.MapRGBA(surface.Format, 243, 235, 28, 255))
} HUDValues := []int32{
for _, rect := range sRects { mapRange(player.energy, 0, MaxEnergy, 0, 88),
surface.FillRect(&rect, sdl.MapRGBA(surface.Format, 40, 243, 243, 255)) mapRange(player.ammunition, 0, MaxAmmunition, 0, 88),
} mapRange(player.shields, 0, MaxShields, 0, 88),
}
surface.FillRect(&sdl.Rect{X: 16, Y: 3, W: 90, H: 7}, sdl.MapRGBA(surface.Format, 0, 0, 0, 255))
surface.FillRect(&sdl.Rect{X: 16, Y: 17, W: 90, H: 7}, sdl.MapRGBA(surface.Format, 0, 0, 0, 255)) for HUDValueIndex, HUDValue := range HUDValues {
surface.FillRect(
surface.FillRect(&sdl.Rect{X: 17, Y: 4, W: int32(player.energy / 20), H: 5}, sdl.MapRGBA(surface.Format, 243, 235, 28, 255)) &sdl.Rect{
surface.FillRect(&sdl.Rect{X: 17, Y: 18, W: int32(player.health), H: 5}, sdl.MapRGBA(surface.Format, 40, 243, 243, 255)) X: 17,
Y: letterOffsets[HUDValueIndex],
W: HUDValue,
H: 5},
letterColors[HUDValueIndex])
}
} }

260
main.go

@ -7,13 +7,19 @@ import (
const ( const (
MapWidth = 1000 // Width of the map MapWidth = 1000 // Width of the map
MapHeight = 1000 // Height of the map MapHeight = 1000 // Height of the map
MaxEnergy = 1760 BlastRadius = 5
MaxHealth = 88
NormalShotCost = 3 MaxEnergy = 3520
SuperShotCost = 20 MaxAmmunition = 6
MaxShields = 100
NormalShotCost = 7
SuperShotCost = 80
ReloadCost = 4
MovementCost = 1 MovementCost = 1
DiggingCost = 2 DiggingCost = 3
ShootDiggingCostBonus = 1 ShootDiggingCostBonus = 1
ShootCooldown = 8 ShootCooldown = 8
RechargeCooldownOwn = 0 RechargeCooldownOwn = 0
DiggingCooldown = 4 DiggingCooldown = 4
@ -22,137 +28,159 @@ const (
MovementCooldown = 2 MovementCooldown = 2
MovementCooldownNoEnergy = 4 MovementCooldownNoEnergy = 4
DiggingCooldownNoEnergy = 8 DiggingCooldownNoEnergy = 8
ReloadCooldown = 16
Debug = false
MapWindow = true
RenderGameObjects = true
ProfilerOn = true
JoyStickDeadZone = 8000
DoAllKeymapsPlayers = true
DoJoyStickPlayers = true
DoKeymapPlayer = true
) )
var mapWindow *sdl.Window
var mapSurface *sdl.Surface
var mapRendererRect = &sdl.Rect{X: 0, Y: 0, W: MapWidth, H: MapHeight}
func main() { func main() {
initializeSDL() initializeSDL()
defer sdl.Quit() defer sdl.Quit()
window, logicalSurface := setupWindowAndSurface() gameMap := &GameMap{}
window.SetTitle("GOing to tunnel")
defer window.Destroy()
defer logicalSurface.Free()
playSurface, playSurfaceRect, playSurfaceTargetRect := setupPlaySurface()
defer playSurface.Free()
HUDSurface, HUDSurfaceRect, HUDSurfaceTargetRect := setupHUDSurface()
defer HUDSurface.Free()
pixelBG := sdl.MapRGBA(logicalSurface.Format, 80, 20, 10, 255)
gameMap := GameMap{}
gameMap.createGameMap(MapWidth, MapHeight) gameMap.createGameMap(MapWidth, MapHeight)
players := &[]*Player{} players := &[]*Player{}
initPlayerColors()
createPlayers(getNeededPlayers(), playerColors, keyMaps, joyMaps, gameMap, players)
createPlayer( defer closeThings(players)
sdl.MapRGBA(playSurface.Format, 0, 0, 182, 255),
sdl.MapRGBA(playSurface.Format, 44, 44, 255, 255),
sdl.MapRGBA(playSurface.Format, 243, 235, 28, 255),
&gameMap,
players)
createPlayer( bases := createBases(players, gameMap)
sdl.MapRGBA(playSurface.Format, 44, 184, 44, 255),
sdl.MapRGBA(playSurface.Format, 0, 249, 0, 255),
sdl.MapRGBA(playSurface.Format, 243, 235, 28, 255),
&gameMap,
players)
bases := createBases(players, &gameMap) bullets := &[]*Bullet{}
bulletParticles := &[]*BulletParticle{}
bulletMap := &[]*Bullet{}
bulletParticleMap := &[]*BulletParticle{}
camera := sdl.Rect{X: 0, Y: 0, W: 76, H: 76}
for playerIndex, player := range *players {
initPlayer(uint8(playerIndex), player)
}
if MapWindow {
mapWindow, mapSurface = setupMapWindowAndSurface()
}
running := true running := true
for running { for running {
currentTime := sdl.GetTicks64() frameStart := sdl.GetTicks64()
for playerIndex, player := range *players {
// Delay to achieve roughly 60 FPS
if player.local {
running = doPlayerFrame(uint8(playerIndex), player, players, gameMap, bases, bullets, bulletParticles)
if !running {
break
}
}
}
if MapWindow {
gameMap.render(mapRendererRect, mapSurface)
for _, bullet := range *bullets {
(*bullet).render(mapRendererRect, mapSurface)
}
for _, base := range *bases {
(*base).render(mapRendererRect, mapSurface)
}
for _, playerLoop := range *players {
(*playerLoop).render(mapRendererRect, mapSurface)
}
for _, bulletParticle := range *bulletParticles {
bulletParticle.render(mapRendererRect, mapSurface)
}
adjustWindow(mapWindow, mapSurface)
}
//now tick world
for _, bullet := range *bullets {
(*bullet).tick(gameMap, bulletParticles, bullets, players)
}
for _, base := range *bases {
(*base).tick(players)
}
for _, bulletParticle := range *bulletParticles {
bulletParticle.tick(bulletParticles)
}
running = handleEvents(window, logicalSurface) enforceFrameRate(frameStart, 60)
}
}
func initPlayer(playerIndex uint8, player *Player) {
player.window, player.logicalSurface = setupWindowAndSurface(playerIndex)
logicalColor := sdl.MapRGBA(player.logicalSurface.Format, 101, 101, 0, 255)
player.logicalSurface.FillRect(nil, logicalColor)
player.playSurface, player.playSurfaceRect, player.playSurfaceTargetRect = setupPlaySurface()
playColor := sdl.MapRGBA(player.playSurface.Format, 101, 0, 101, 255)
player.playSurface.FillRect(nil, playColor)
player.HUDSurface, player.HUDSurfaceRect, player.HUDSurfaceTargetRect = setupHUDSurface()
initHud(player.HUDSurface)
player.camera = &sdl.Rect{X: 0, Y: 0, W: 76, H: 76}
}
func doPlayerFrame(playerIndex uint8, player *Player, players *[]*Player, gameMap *GameMap, bases *[]*Base, bullets *[]*Bullet, bulletParticles *[]*BulletParticle) bool {
running := true
var (
totalHandleEventsTime, totalRenderTime, totalFrameTime uint64
totalScalingTime, frameCount uint64
isShooting, shouldContinue bool
)
frameStart := sdl.GetTicks64()
// Profile handleEvents
totalHandleEventsTime += profileSection(func() {
running = handleEvents(player.window, player.logicalSurface)
keyboard := sdl.GetKeyboardState() keyboard := sdl.GetKeyboardState()
shouldContinue, isShooting = handleInput(keyboard, bullets, player, gameMap, player.playSurface.Format, players)
running = running && shouldContinue
})
running = running && handleInput(keyboard, bulletMap, (*players)[0], &gameMap, playSurface.Format, players) // Profile rendering (aggregate all renderings)
totalRenderTime += profileSection(func() {
player.track(player.camera)
gameMap.render(player.camera, player.playSurface)
renderScene(logicalSurface, playSurface, HUDSurface, &gameMap, players, bases, bulletMap, bulletParticleMap, &camera, playSurfaceRect, playSurfaceTargetRect, HUDSurfaceRect, HUDSurfaceTargetRect) for _, bullet := range *bullets {
(*bullet).render(player.camera, player.playSurface)
adjustWindow(window, logicalSurface, pixelBG)
// Calculate delay to achieve roughly 60 FPS
frameDuration := 1000 / 60
elapsed := sdl.GetTicks64() - currentTime
if delay := frameDuration - int(elapsed); delay > 0 {
sdl.Delay(uint32(delay))
} }
for _, base := range *bases {
(*base).render(player.camera, player.playSurface)
} }
} for _, playerLoop := range *players {
(*playerLoop).render(player.camera, player.playSurface)
func handleInput(keyboard []uint8, bullets *[]*Bullet, player *Player, gameMap *GameMap, format *sdl.PixelFormat, players *[]*Player) bool { }
shoot := false for _, bulletParticle := range *bulletParticles {
super := false bulletParticle.render(player.camera, player.playSurface)
}
// Flags to track movement in each direction
moveUp := false player.tick(isShooting, bullets, player.playSurface.Format)
moveDown := false renderHud(player, player.HUDSurface)
moveLeft := false player.playSurface.BlitScaled(player.playSurfaceRect, player.logicalSurface, player.playSurfaceTargetRect)
moveRight := false player.HUDSurface.BlitScaled(player.HUDSurfaceRect, player.logicalSurface, player.HUDSurfaceTargetRect)
})
// Process keyboard input
for key, value := range keyboard { // Profile window adjustments
if value == 0 { totalScalingTime += profileSection(func() {
continue adjustWindow(player.window, player.logicalSurface)
} })
if key == sdl.SCANCODE_ESCAPE { frameEnd := sdl.GetTicks64()
return false totalFrameTime += frameEnd - frameStart
} else if key == sdl.SCANCODE_SPACE { frameCount++
shoot = true
} else if key == sdl.SCANCODE_LCTRL { // Log profiling information every 1000 frames
super = true if frameCount%1000 == 0 && ProfilerOn && playerIndex == 0 {
} else if key == sdl.SCANCODE_W { logProfilingInfo(totalHandleEventsTime, totalRenderTime, totalScalingTime, totalFrameTime, frameCount)
moveUp = true resetProfilingCounters(&totalHandleEventsTime, &totalRenderTime, &totalScalingTime, &totalFrameTime, &frameCount)
} else if key == sdl.SCANCODE_S { }
moveDown = true return running
} else if key == sdl.SCANCODE_A {
moveLeft = true
} else if key == sdl.SCANCODE_D {
moveRight = true
}
}
// Handle shooting after the loop
if shoot {
player.shoot(super, bullets, format)
}
// Determine player orientation for diagonal movement
if moveUp && moveRight {
player.orientation = 4 // Up-Right
} else if moveUp && moveLeft {
player.orientation = 5 // Up-Left
} else if moveDown && moveRight {
player.orientation = 6 // Down-Right
} else if moveDown && moveLeft {
player.orientation = 7 // Down-Left
} else if moveUp {
player.orientation = 0 // Up
} else if moveRight {
player.orientation = 1 // Right
} else if moveDown {
player.orientation = 2 // Down
} else if moveLeft {
player.orientation = 3 // Left
}
// Handle movement after the loop
if moveUp || moveDown || moveLeft || moveRight {
if player.tryMove(gameMap, shoot, players) {
player.energy -= MovementCost
}
}
return true
} }

4
map.go

@ -49,12 +49,12 @@ func (gameMap *GameMap) getTileColor(x int32, y int32) color.Color {
func (gameMap *GameMap) createGameMap(width int32, height int32) { func (gameMap *GameMap) createGameMap(width int32, height int32) {
gameMap.tiles = make([][]uint8, width) gameMap.tiles = make([][]uint8, width)
perlinNoiseRock := perlin.NewPerlin(2, 2, 4, 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())
for i := range gameMap.tiles { for i := range gameMap.tiles {
gameMap.tiles[i] = make([]uint8, height) gameMap.tiles[i] = make([]uint8, height)
for j := range gameMap.tiles[i] { for j := range gameMap.tiles[i] {
perlinRock := perlinNoiseRock.Noise2D(float64(i)/float64(width)*3, float64(j)/float64(height)*3) perlinRock := perlinNoiseRock.Noise2D(float64(i)/float64(width)*6, float64(j)/float64(height)*6)
if perlinRock > 0.2 { if perlinRock > 0.2 {
gameMap.tiles[i][j] = 1 gameMap.tiles[i][j] = 1
} else { } else {

538
player.go

@ -7,107 +7,50 @@ import (
) )
type Player struct { type Player struct {
posX int32 local bool
posY int32 playerColors PlayerColors
orientation uint8 keyMap KeyMap
color1 uint32 joyMap JoyMap
color2 uint32 joyStick *sdl.Joystick
color3 uint32 camera *sdl.Rect
energy uint16 energy uint16
health uint16 ammunition uint16
shields uint16
digCooldown uint8 digCooldown uint8
shootCooldown uint8 shootCooldown uint8
repairCooldown uint8 repairCooldown uint8
rechargeCooldown uint8 rechargeCooldown uint8
movementCooldown uint8 movementCooldown uint8
rect1 sdl.Rect reloadCooldown uint8
rect2 sdl.Rect gameObject *GameObject
rect3 sdl.Rect
rect4 sdl.Rect window *sdl.Window
cullingRect sdl.Rect
logicalSurface *sdl.Surface
playSurface *sdl.Surface
HUDSurface *sdl.Surface
playSurfaceRect *sdl.Rect
HUDSurfaceRect *sdl.Rect
playSurfaceTargetRect *sdl.Rect
HUDSurfaceTargetRect *sdl.Rect
} }
func (player *Player) track(camera *sdl.Rect) { func (player *Player) track(camera *sdl.Rect) {
camera.X = player.posX - 37 camera.X = player.gameObject.baseRect.X - 37
camera.Y = player.posY - 38 camera.Y = player.gameObject.baseRect.Y - 38
} }
func (player *Player) render(camera *sdl.Rect, surface *sdl.Surface) { func (player *Player) render(camera *sdl.Rect, surface *sdl.Surface) {
relativePlayerX := player.posX - camera.X
relativePlayerY := player.posY - camera.Y
switch player.orientation {
case 0: // Up
player.rect1 = sdl.Rect{X: relativePlayerX, Y: relativePlayerY + 1, W: 1, H: 6}
player.rect2 = sdl.Rect{X: relativePlayerX + 4, Y: relativePlayerY + 1, W: 1, H: 6}
player.rect3 = sdl.Rect{X: relativePlayerX + 1, Y: relativePlayerY + 2, W: 3, H: 4}
player.rect4 = sdl.Rect{X: relativePlayerX + 2, Y: relativePlayerY, W: 1, H: 4}
case 1: // Right
player.rect1 = sdl.Rect{X: relativePlayerX + 1, Y: relativePlayerY + 1, W: 6, H: 1}
player.rect2 = sdl.Rect{X: relativePlayerX + 1, Y: relativePlayerY + 5, W: 6, H: 1}
player.rect3 = sdl.Rect{X: relativePlayerX + 2, Y: relativePlayerY + 2, W: 4, H: 3}
player.rect4 = sdl.Rect{X: relativePlayerX + 4, Y: relativePlayerY + 3, W: 4, H: 1}
case 2: // Down
player.rect1 = sdl.Rect{X: relativePlayerX, Y: relativePlayerY + 1, W: 1, H: 6}
player.rect2 = sdl.Rect{X: relativePlayerX + 4, Y: relativePlayerY + 1, W: 1, H: 6}
player.rect3 = sdl.Rect{X: relativePlayerX + 1, Y: relativePlayerY + 2, W: 3, H: 4}
player.rect4 = sdl.Rect{X: relativePlayerX + 2, Y: relativePlayerY + 4, W: 1, H: 4}
case 3: // Left
player.rect1 = sdl.Rect{X: relativePlayerX + 1, Y: relativePlayerY + 1, W: 6, H: 1}
player.rect2 = sdl.Rect{X: relativePlayerX + 1, Y: relativePlayerY + 5, W: 6, H: 1}
player.rect3 = sdl.Rect{X: relativePlayerX + 2, Y: relativePlayerY + 2, W: 4, H: 3}
player.rect4 = sdl.Rect{X: relativePlayerX, Y: relativePlayerY + 3, W: 4, H: 1}
case 4: // Up-Right
player.rect1 = sdl.Rect{X: relativePlayerX + 1, Y: relativePlayerY + 6, W: 5, H: 1}
player.rect2 = sdl.Rect{X: relativePlayerX + 1, Y: relativePlayerY + 2, W: 1, H: 4}
player.rect3 = sdl.Rect{X: relativePlayerX + 2, Y: relativePlayerY + 2, W: 4, H: 4}
player.rect4 = sdl.Rect{X: relativePlayerX + 4, Y: relativePlayerY + 2, W: 2, H: 2}
case 5: // Up-Left
player.rect1 = sdl.Rect{X: relativePlayerX + 1, Y: relativePlayerY + 6, W: 5, H: 1}
player.rect2 = sdl.Rect{X: relativePlayerX + 5, Y: relativePlayerY + 2, W: 1, H: 5}
player.rect3 = sdl.Rect{X: relativePlayerX + 1, Y: relativePlayerY + 2, W: 4, H: 4}
player.rect4 = sdl.Rect{X: relativePlayerX + 1, Y: relativePlayerY + 2, W: 2, H: 2}
case 6: // Down-Right
player.rect1 = sdl.Rect{X: relativePlayerX + 1, Y: relativePlayerY + 1, W: 5, H: 1}
player.rect2 = sdl.Rect{X: relativePlayerX + 1, Y: relativePlayerY + 1, W: 1, H: 5}
player.rect3 = sdl.Rect{X: relativePlayerX + 2, Y: relativePlayerY + 2, W: 4, H: 4}
player.rect4 = sdl.Rect{X: relativePlayerX + 4, Y: relativePlayerY + 4, W: 2, H: 2}
case 7: // Down-Left
player.rect1 = sdl.Rect{X: relativePlayerX + 1, Y: relativePlayerY + 1, W: 4, H: 1}
player.rect2 = sdl.Rect{X: relativePlayerX + 5, Y: relativePlayerY + 1, W: 1, H: 5}
player.rect3 = sdl.Rect{X: relativePlayerX + 1, Y: relativePlayerY + 2, W: 4, H: 4}
player.rect4 = sdl.Rect{X: relativePlayerX + 1, Y: relativePlayerY + 4, W: 2, H: 2}
}
// Common part: Rendering player // Common part: Rendering player
player.cullingRect = sdl.Rect{X: player.posX, Y: player.posY, W: 7, H: 7} player.gameObject.baseRect = sdl.Rect{X: player.gameObject.baseRect.X, Y: player.gameObject.baseRect.Y, W: 7, H: 7}
cameraAdjustedCullingRect := sdl.Rect{
X: player.cullingRect.X - camera.X, if player.shields > 0 && player.shields <= MaxShields {
Y: player.cullingRect.Y - camera.Y, player.gameObject.render(camera, surface)
W: player.cullingRect.W,
H: player.cullingRect.H,
}
cameraAdjustedCullingRectBorder := sdl.Rect{
X: player.cullingRect.X - camera.X - 1,
Y: player.cullingRect.Y - camera.Y - 1,
W: player.cullingRect.W + 2,
H: player.cullingRect.H + 2,
}
if camera.HasIntersection(&player.cullingRect) {
surface.FillRect(&cameraAdjustedCullingRectBorder, sdl.MapRGBA(surface.Format, 10, 255, 255, 64))
surface.FillRect(&cameraAdjustedCullingRect, sdl.MapRGBA(surface.Format, 255, 20, 10, 64))
surface.FillRect(&player.rect1, player.color1)
surface.FillRect(&player.rect2, player.color1)
surface.FillRect(&player.rect3, player.color2)
surface.FillRect(&player.rect4, player.color3)
} }
} }
func (player *Player) digBlock(posX, posY int32, gameMap *GameMap, isShooting bool) uint8 { func (player *Player) digBlock(posX, posY int32, gameMap *GameMap, isShooting bool) uint8 {
@ -122,7 +65,7 @@ func (player *Player) digBlock(posX, posY int32, gameMap *GameMap, isShooting bo
return 2 return 2
} }
func (player *Player) tick() { func (player *Player) tick(isShooting bool, bullets *[]*Bullet, format *sdl.PixelFormat) {
if player.digCooldown > 0 { if player.digCooldown > 0 {
player.digCooldown-- player.digCooldown--
} }
@ -138,6 +81,17 @@ func (player *Player) tick() {
if player.movementCooldown > 0 { if player.movementCooldown > 0 {
player.movementCooldown-- player.movementCooldown--
} }
if player.reloadCooldown > 0 {
player.reloadCooldown--
} else if player.ammunition < MaxAmmunition && !isShooting && player.energy > ReloadCost {
player.ammunition++
player.reloadCooldown = ReloadCooldown
player.energy -= ReloadCost
}
if player.shields <= 0 {
player.shields = MaxShields + 1
player.explode(bullets)
}
} }
func (player *Player) tryMove(gameMap *GameMap, isShooting bool, players *[]*Player) (moved bool) { func (player *Player) tryMove(gameMap *GameMap, isShooting bool, players *[]*Player) (moved bool) {
@ -151,21 +105,21 @@ func (player *Player) tryMove(gameMap *GameMap, isShooting bool, players *[]*Pla
shouldPenalizeDigging := false shouldPenalizeDigging := false
stopped := false stopped := false
switch player.orientation { switch player.gameObject.orientation {
case 0: // Up case 0: // Up
collisionRect := sdl.Rect{ collisionRect := sdl.Rect{
X: player.posX, X: player.gameObject.baseRect.X,
Y: player.posY - 1, Y: player.gameObject.baseRect.Y - 1,
W: 5, W: 5,
H: 7, H: 7,
} }
for _, opponent := range *players { for _, opponent := range *players {
if opponent != player && opponent.cullingRect.HasIntersection(&collisionRect) { if opponent != player && opponent.gameObject.baseRect.HasIntersection(&collisionRect) {
return false return false
} }
} }
for x := player.posX; x < player.posX+5 && !stopped; x++ { for x := player.gameObject.baseRect.X; x < player.gameObject.baseRect.X+5 && !stopped; x++ {
for y := player.posY - 1; y < player.posY+7; y++ { for y := player.gameObject.baseRect.Y - 1; y < player.gameObject.baseRect.Y+7; y++ {
digResult := player.digBlock(x, y, gameMap, isShooting) digResult := player.digBlock(x, y, gameMap, isShooting)
if digResult == 1 { if digResult == 1 {
shouldPenalizeDigging = true shouldPenalizeDigging = true
@ -181,23 +135,23 @@ func (player *Player) tryMove(gameMap *GameMap, isShooting bool, players *[]*Pla
} }
if !stopped { if !stopped {
moved = true moved = true
player.posY-- player.gameObject.baseRect.Y--
} }
case 1: // Right case 1: // Right
collisionRect := sdl.Rect{ collisionRect := sdl.Rect{
X: player.posX + 1, X: player.gameObject.baseRect.X + 1,
Y: player.posY, Y: player.gameObject.baseRect.Y,
W: 7, W: 7,
H: 5, H: 5,
} }
for _, opponent := range *players { for _, opponent := range *players {
if opponent != player && opponent.cullingRect.HasIntersection(&collisionRect) { if opponent != player && opponent.gameObject.baseRect.HasIntersection(&collisionRect) {
return false return false
} }
} }
for y := player.posY; y < player.posY+7 && !stopped; y++ { for y := player.gameObject.baseRect.Y + 1; y < player.gameObject.baseRect.Y+6 && !stopped; y++ {
for x := player.posX + 1; x < player.posX+8; x++ { for x := player.gameObject.baseRect.X + 1; x < player.gameObject.baseRect.X+8; x++ {
digResult := player.digBlock(x, y, gameMap, isShooting) digResult := player.digBlock(x, y, gameMap, isShooting)
if digResult == 1 { if digResult == 1 {
shouldPenalizeDigging = true shouldPenalizeDigging = true
@ -213,23 +167,23 @@ func (player *Player) tryMove(gameMap *GameMap, isShooting bool, players *[]*Pla
} }
if !stopped { if !stopped {
moved = true moved = true
player.posX++ player.gameObject.baseRect.X++
} }
case 2: // Down case 2: // Down
collisionRect := sdl.Rect{ collisionRect := sdl.Rect{
X: player.posX, X: player.gameObject.baseRect.X,
Y: player.posY + 1, Y: player.gameObject.baseRect.Y + 1,
W: 5, W: 5,
H: 7, H: 7,
} }
for _, opponent := range *players { for _, opponent := range *players {
if opponent != player && opponent.cullingRect.HasIntersection(&collisionRect) { if opponent != player && opponent.gameObject.baseRect.HasIntersection(&collisionRect) {
return false return false
} }
} }
for x := player.posX; x < player.posX+5 && !stopped; x++ { for x := player.gameObject.baseRect.X; x < player.gameObject.baseRect.X+5 && !stopped; x++ {
for y := player.posY + 1; y < player.posY+8; y++ { for y := player.gameObject.baseRect.Y + 1; y < player.gameObject.baseRect.Y+8; y++ {
digResult := player.digBlock(x, y, gameMap, isShooting) digResult := player.digBlock(x, y, gameMap, isShooting)
if digResult == 1 { if digResult == 1 {
shouldPenalizeDigging = true shouldPenalizeDigging = true
@ -245,23 +199,23 @@ func (player *Player) tryMove(gameMap *GameMap, isShooting bool, players *[]*Pla
} }
if !stopped { if !stopped {
moved = true moved = true
player.posY++ player.gameObject.baseRect.Y++
} }
case 3: // Left case 3: // Left
collisionRect := sdl.Rect{ collisionRect := sdl.Rect{
X: player.posX - 1, X: player.gameObject.baseRect.X - 1,
Y: player.posY, Y: player.gameObject.baseRect.Y,
W: 7, W: 7,
H: 5, H: 5,
} }
for _, opponent := range *players { for _, opponent := range *players {
if opponent != player && opponent.cullingRect.HasIntersection(&collisionRect) { if opponent != player && opponent.gameObject.baseRect.HasIntersection(&collisionRect) {
return false return false
} }
} }
for y := player.posY; y < player.posY+6 && !stopped; y++ { for y := player.gameObject.baseRect.Y + 1; y < player.gameObject.baseRect.Y+6 && !stopped; y++ {
for x := player.posX - 1; x < player.posX+6; x++ { for x := player.gameObject.baseRect.X - 1; x < player.gameObject.baseRect.X+6; x++ {
digResult := player.digBlock(x, y, gameMap, isShooting) digResult := player.digBlock(x, y, gameMap, isShooting)
if digResult == 1 { if digResult == 1 {
shouldPenalizeDigging = true shouldPenalizeDigging = true
@ -277,23 +231,23 @@ func (player *Player) tryMove(gameMap *GameMap, isShooting bool, players *[]*Pla
} }
if !stopped { if !stopped {
moved = true moved = true
player.posX-- player.gameObject.baseRect.X--
} }
case 4: // Up-Right case 4: // Up-Right
collisionRect := sdl.Rect{ collisionRect := sdl.Rect{
X: player.posX + 1, X: player.gameObject.baseRect.X + 1,
Y: player.posY - 1, Y: player.gameObject.baseRect.Y - 1,
W: 7, W: 7,
H: 7, H: 7,
} }
for _, opponent := range *players { for _, opponent := range *players {
if opponent != player && opponent.cullingRect.HasIntersection(&collisionRect) { if opponent != player && opponent.gameObject.baseRect.HasIntersection(&collisionRect) {
return false return false
} }
} }
for x := player.posX + 1; x < player.posX+8 && !stopped; x++ { for x := player.gameObject.baseRect.X + 1; x < player.gameObject.baseRect.X+8 && !stopped; x++ {
for y := player.posY - 1; y < player.posY+7; y++ { for y := player.gameObject.baseRect.Y - 1; y < player.gameObject.baseRect.Y+6; y++ {
digResult := player.digBlock(x, y, gameMap, isShooting) digResult := player.digBlock(x, y, gameMap, isShooting)
if digResult == 1 { if digResult == 1 {
shouldPenalizeDigging = true shouldPenalizeDigging = true
@ -309,24 +263,24 @@ func (player *Player) tryMove(gameMap *GameMap, isShooting bool, players *[]*Pla
} }
if !stopped { if !stopped {
moved = true moved = true
player.posX++ player.gameObject.baseRect.X++
player.posY-- player.gameObject.baseRect.Y--
} }
case 5: // Up-Left case 5: // Up-Left
collisionRect := sdl.Rect{ collisionRect := sdl.Rect{
X: player.posX - 1, X: player.gameObject.baseRect.X - 1,
Y: player.posY - 1, Y: player.gameObject.baseRect.Y - 1,
W: 7, W: 7,
H: 7, H: 7,
} }
for _, opponent := range *players { for _, opponent := range *players {
if opponent != player && opponent.cullingRect.HasIntersection(&collisionRect) { if opponent != player && opponent.gameObject.baseRect.HasIntersection(&collisionRect) {
return false return false
} }
} }
for x := player.posX - 1; x < player.posX+6 && !stopped; x++ { for x := player.gameObject.baseRect.X - 1; x < player.gameObject.baseRect.X+6 && !stopped; x++ {
for y := player.posY - 1; y < player.posY+7; y++ { for y := player.gameObject.baseRect.Y - 1; y < player.gameObject.baseRect.Y+6; y++ {
digResult := player.digBlock(x, y, gameMap, isShooting) digResult := player.digBlock(x, y, gameMap, isShooting)
if digResult == 1 { if digResult == 1 {
shouldPenalizeDigging = true shouldPenalizeDigging = true
@ -342,24 +296,24 @@ func (player *Player) tryMove(gameMap *GameMap, isShooting bool, players *[]*Pla
} }
if !stopped { if !stopped {
moved = true moved = true
player.posX-- player.gameObject.baseRect.X--
player.posY-- player.gameObject.baseRect.Y--
} }
case 6: // Down-Right case 6: // Down-Right
collisionRect := sdl.Rect{ collisionRect := sdl.Rect{
X: player.posX + 1, X: player.gameObject.baseRect.X + 1,
Y: player.posY + 1, Y: player.gameObject.baseRect.Y + 1,
W: 7, W: 7,
H: 7, H: 7,
} }
for _, opponent := range *players { for _, opponent := range *players {
if opponent != player && opponent.cullingRect.HasIntersection(&collisionRect) { if opponent != player && opponent.gameObject.baseRect.HasIntersection(&collisionRect) {
return false return false
} }
} }
for x := player.posX + 1; x < player.posX+8 && !stopped; x++ { for x := player.gameObject.baseRect.X + 1; x < player.gameObject.baseRect.X+8 && !stopped; x++ {
for y := player.posY + 1; y < player.posY+8; y++ { for y := player.gameObject.baseRect.Y + 1; y < player.gameObject.baseRect.Y+8; y++ {
digResult := player.digBlock(x, y, gameMap, isShooting) digResult := player.digBlock(x, y, gameMap, isShooting)
if digResult == 1 { if digResult == 1 {
shouldPenalizeDigging = true shouldPenalizeDigging = true
@ -375,24 +329,24 @@ func (player *Player) tryMove(gameMap *GameMap, isShooting bool, players *[]*Pla
} }
if !stopped { if !stopped {
moved = true moved = true
player.posX++ player.gameObject.baseRect.X++
player.posY++ player.gameObject.baseRect.Y++
} }
case 7: // Down-Left case 7: // Down-Left
collisionRect := sdl.Rect{ collisionRect := sdl.Rect{
X: player.posX - 1, X: player.gameObject.baseRect.X - 1,
Y: player.posY + 1, Y: player.gameObject.baseRect.Y + 1,
W: 7, W: 7,
H: 7, H: 7,
} }
for _, opponent := range *players { for _, opponent := range *players {
if opponent != player && opponent.cullingRect.HasIntersection(&collisionRect) { if opponent != player && opponent.gameObject.baseRect.HasIntersection(&collisionRect) {
return false return false
} }
} }
for x := player.posX - 1; x < player.posX+6 && !stopped; x++ { for x := player.gameObject.baseRect.X - 1; x < player.gameObject.baseRect.X+6 && !stopped; x++ {
for y := player.posY + 1; y < player.posY+8; y++ { for y := player.gameObject.baseRect.Y + 1; y < player.gameObject.baseRect.Y+8; y++ {
digResult := player.digBlock(x, y, gameMap, isShooting) digResult := player.digBlock(x, y, gameMap, isShooting)
if digResult == 1 { if digResult == 1 {
shouldPenalizeDigging = true shouldPenalizeDigging = true
@ -408,13 +362,16 @@ func (player *Player) tryMove(gameMap *GameMap, isShooting bool, players *[]*Pla
} }
if !stopped { if !stopped {
moved = true moved = true
player.posX-- player.gameObject.baseRect.X--
player.posY++ player.gameObject.baseRect.Y++
} }
} }
// Penalties and cooldown handling // Penalties and cooldown handling
if shouldPenalizeDigging { if shouldPenalizeDigging {
if isShooting && player.ammunition < MaxAmmunition && rand.Intn(2) == 0 {
player.ammunition++
}
if ranOutOfEnergy { if ranOutOfEnergy {
player.digCooldown = DiggingCooldownNoEnergy player.digCooldown = DiggingCooldownNoEnergy
} }
@ -433,96 +390,327 @@ func (player *Player) tryMove(gameMap *GameMap, isShooting bool, players *[]*Pla
return return
} }
func (player *Player) shoot(super bool, bullets *[]*Bullet, format *sdl.PixelFormat) { func (player *Player) getRGBAColor(colorIndex uint8, format *sdl.PixelFormat) uint32 {
if (super && player.energy <= SuperShotCost) || (!super && player.energy <= NormalShotCost) { var selectedColor color.Color
switch colorIndex {
case 0:
selectedColor = player.playerColors.tracks
case 1:
selectedColor = player.playerColors.body
case 2:
selectedColor = player.playerColors.cannon
}
var r, g, b, a uint8
if selectedColor != nil {
rt, gt, bt, at := selectedColor.RGBA()
r, g, b, a = uint8(rt), uint8(gt), uint8(bt), uint8(at)
}
return sdl.MapRGBA(format, r, g, b, a)
}
func (player *Player) shoot(super bool, bullets *[]*Bullet) {
if (super && (player.energy <= SuperShotCost || player.ammunition < MaxAmmunition)) || (!super && (player.energy <= NormalShotCost || player.ammunition < 1)) {
return return
} }
if player.shootCooldown == 0 { if player.shootCooldown == 0 {
var shootX, shootY int32 var shootX, shootY int32
switch player.orientation { switch player.gameObject.orientation {
case 0: // Up case 0: // Up
shootY = player.posY - 1 shootY = player.gameObject.baseRect.Y - 1
shootX = player.posX + 2 shootX = player.gameObject.baseRect.X + 2
break break
case 1: // Right case 1: // Right
shootY = player.posY + 3 shootY = player.gameObject.baseRect.Y + 3
shootX = player.posX + 8 shootX = player.gameObject.baseRect.X + 8
break break
case 2: // Down case 2: // Down
shootX = player.posX + 2 shootX = player.gameObject.baseRect.X + 2
shootY = player.posY + 8 shootY = player.gameObject.baseRect.Y + 8
break break
case 3: // Left case 3: // Left
shootY = player.posY + 3 shootY = player.gameObject.baseRect.Y + 3
shootX = player.posX - 2 shootX = player.gameObject.baseRect.X - 2
break break
case 4: // Up-Right case 4: // Up-Right
shootY = player.posY - 1 shootY = player.gameObject.baseRect.Y
shootX = player.posX + 5 shootX = player.gameObject.baseRect.X + 5
break break
case 5: // Up-Left case 5: // Up-Left
shootY = player.posY - 1 shootY = player.gameObject.baseRect.Y
shootX = player.posX - 1 shootX = player.gameObject.baseRect.X - 1
break break
case 6: // Down-Right case 6: // Down-Right
shootY = player.posY + 5 shootY = player.gameObject.baseRect.Y + 5
shootX = player.posX + 5 shootX = player.gameObject.baseRect.X + 5
break break
case 7: // Down-Left case 7: // Down-Left
shootY = player.posY + 5 shootY = player.gameObject.baseRect.Y + 5
shootX = player.posX - 1 shootX = player.gameObject.baseRect.X - 1
break break
} }
// Set bullet color // Set bullet color
r, g, b, a := sdl.GetRGBA(player.color2, format) bulletColor := player.playerColors.body
bulletColor := color.RGBA{R: r, G: g, B: b, A: a}
// Create and add the bullet // Create and add the bullet
*bullets = append(*bullets, &Bullet{ *bullets = append(*bullets, &Bullet{
posX: shootX, posX: shootX,
posY: shootY, posY: shootY,
direction: player.orientation, direction: player.gameObject.orientation,
color: bulletColor, color: bulletColor,
super: super, super: super,
}) })
// Set cooldown and decrease energy // Set cooldown and decrease energy
player.shootCooldown = ShootCooldown player.shootCooldown = ShootCooldown
if super { if super {
player.energy -= SuperShotCost player.energy -= SuperShotCost
player.ammunition = 0
} else { } else {
player.energy -= NormalShotCost player.energy -= NormalShotCost
player.ammunition--
} }
} }
} }
func createPlayer(color1 uint32, color2 uint32, color3 uint32, gameMap *GameMap, players *[]*Player) { func (player *Player) explode(bullets *[]*Bullet) {
// Set bullet color
bulletColor := player.playerColors.body
for x := player.gameObject.baseRect.X - BlastRadius; x < BlastRadius*2+1; x++ {
for y := player.gameObject.baseRect.Y - BlastRadius; y < BlastRadius*2+1; y++ {
// Create and add the bullet
*bullets = append(*bullets, &Bullet{
posX: x,
posY: y,
direction: uint8(rand.Intn(8)),
color: bulletColor,
super: true,
})
}
}
}
func createPlayers(amount uint8, playerColors []PlayerColors, keyMaps []KeyMap, joyMaps []JoyMap, gameMap *GameMap, players *[]*Player) {
joyStickCount := sdl.NumJoysticks()
if amount > uint8(len(keyMaps)+len(joyMaps)) || amount > uint8(len(keyMaps)+joyStickCount) {
panic("Too many players, not enough inputs")
}
if amount >= uint8(len(playerColors)) {
panic("Too many players, not enough colors")
}
addedKeyboardPlayers := 0
for i := uint8(0); i < amount; i++ {
var keyMap KeyMap
var joyMap JoyMap
var joyStick *sdl.Joystick
if (DoAllKeymapsPlayers && i <= uint8(len(keyMaps))) || (DoKeymapPlayer && i == 0) || (uint8(joyStickCount) <= i) {
keyMap = keyMaps[addedKeyboardPlayers]
addedKeyboardPlayers++
} else {
joyStickIndex := i - uint8(addedKeyboardPlayers)
joyMap = joyMaps[joyStickIndex]
joyStick = sdl.JoystickOpen(int(joyStickIndex))
}
createPlayer(
playerColors[i],
keyMap,
joyMap,
joyStick,
gameMap,
players,
)
}
}
func closeThings(players *[]*Player) {
for _, player := range *players {
if player.joyStick != nil {
player.joyStick.Close()
}
if player.window != nil {
player.window.Destroy()
}
if player.logicalSurface != nil {
player.logicalSurface.Free()
}
if player.playSurface != nil {
player.playSurface.Free()
}
if player.HUDSurface != nil {
player.HUDSurface.Free()
}
}
}
func createPlayer(playerColors PlayerColors, keyMap KeyMap, joyMap JoyMap, joyStick *sdl.Joystick, gameMap *GameMap, players *[]*Player) {
coordsAreValid := false coordsAreValid := false
var posX, posY int32 var posX, posY int32
for !coordsAreValid { maxTries := 1000
posX = int32(16 + rand.Intn(int(gameMap.width-36))) for !coordsAreValid && maxTries >= 0 {
posY = int32(16 + rand.Intn(int(gameMap.height-36))) maxTries--
posX = int32(16 + rand.Intn(int(gameMap.width-43)))
posY = int32(16 + rand.Intn(int(gameMap.height-43)))
coordsAreValid = true coordsAreValid = true
for _, player := range *players { for _, player := range *players {
distance := (player.posX-posX)*(player.posX-posX) + (player.posY-posY)*(player.posY-posY) distance := (player.gameObject.baseRect.X-posX)*(player.gameObject.baseRect.X-posX) +
(player.gameObject.baseRect.Y-posY)*(player.gameObject.baseRect.Y-posY)
if distance < 300*300 { // Check if distance is less than 300 units if distance < 300*300 { // Check if distance is less than 300 units
coordsAreValid = false coordsAreValid = false
break break
} }
} }
if posX < 16 || posX > gameMap.width-36-7 || posY < 16 || posY > gameMap.height-36-7 {
coordsAreValid = false
break
}
}
if maxTries < 0 {
panic("Could not place all players, increase map size")
} }
gameObject := &GameObject{}
gameObject.baseRect = sdl.Rect{
X: posX,
Y: posY,
W: 7,
H: 7,
}
gameObject.addColor(playerColors.tracks)
gameObject.addColor(playerColors.body)
gameObject.addColor(playerColors.cannon)
gameObject.orientation = 0 // Up
gameObject.addColoredRect(0, 1, 1, 6, 0)
gameObject.addColoredRect(4, 1, 1, 6, 0)
gameObject.addColoredRect(1, 2, 3, 4, 1)
gameObject.addColoredRect(2, 0, 1, 4, 2)
gameObject.orientation = 1 // Right
gameObject.addColoredRect(1, 1, 6, 1, 0)
gameObject.addColoredRect(1, 5, 6, 1, 0)
gameObject.addColoredRect(2, 2, 4, 3, 1)
gameObject.addColoredRect(4, 3, 4, 1, 2)
gameObject.orientation = 2 // Down
gameObject.addColoredRect(0, 1, 1, 6, 0)
gameObject.addColoredRect(4, 1, 1, 6, 0)
gameObject.addColoredRect(1, 2, 3, 4, 1)
gameObject.addColoredRect(2, 4, 1, 4, 2)
gameObject.orientation = 3 // Left
gameObject.addColoredRect(1, 1, 6, 1, 0)
gameObject.addColoredRect(1, 5, 6, 1, 0)
gameObject.addColoredRect(2, 2, 4, 3, 1)
gameObject.addColoredRect(0, 3, 4, 1, 2)
gameObject.orientation = 4 // Up-Right
gameObject.addColoredRect(3, 0, 1, 1, 0)
gameObject.addColoredRect(2, 1, 1, 1, 0)
gameObject.addColoredRect(1, 2, 1, 1, 0)
gameObject.addColoredRect(0, 3, 1, 1, 0)
gameObject.addColoredRect(6, 3, 1, 1, 0)
gameObject.addColoredRect(5, 4, 1, 1, 0)
gameObject.addColoredRect(4, 5, 1, 1, 0)
gameObject.addColoredRect(3, 6, 1, 1, 0)
gameObject.addColoredRect(3, 1, 1, 1, 1)
gameObject.addColoredRect(2, 2, 2, 1, 1)
gameObject.addColoredRect(1, 3, 2, 1, 1)
gameObject.addColoredRect(4, 3, 2, 1, 1)
gameObject.addColoredRect(2, 4, 3, 1, 1)
gameObject.addColoredRect(3, 5, 1, 1, 1)
gameObject.addColoredRect(5, 1, 1, 1, 2)
gameObject.addColoredRect(4, 2, 1, 1, 2)
gameObject.addColoredRect(3, 3, 1, 1, 2)
// Up-Left orientation (Y-axis reflection)
gameObject.orientation = 5 // Up-Left
gameObject.addColoredRect(3, 0, 1, 1, 0)
gameObject.addColoredRect(4, 1, 1, 1, 0)
gameObject.addColoredRect(5, 2, 1, 1, 0)
gameObject.addColoredRect(6, 3, 1, 1, 0)
gameObject.addColoredRect(0, 3, 1, 1, 0)
gameObject.addColoredRect(1, 4, 1, 1, 0)
gameObject.addColoredRect(2, 5, 1, 1, 0)
gameObject.addColoredRect(3, 6, 1, 1, 0)
gameObject.addColoredRect(3, 1, 1, 1, 1)
gameObject.addColoredRect(3, 2, 2, 1, 1)
gameObject.addColoredRect(4, 3, 2, 1, 1)
gameObject.addColoredRect(1, 3, 2, 1, 1)
gameObject.addColoredRect(2, 4, 3, 1, 1)
gameObject.addColoredRect(3, 5, 1, 1, 1)
gameObject.addColoredRect(1, 1, 1, 1, 2)
gameObject.addColoredRect(2, 2, 1, 1, 2)
gameObject.addColoredRect(3, 3, 1, 1, 2)
// Down-Right orientation (X-axis reflection)
gameObject.orientation = 6 // Down-Right
gameObject.addColoredRect(3, 6, 1, 1, 0)
gameObject.addColoredRect(2, 5, 1, 1, 0)
gameObject.addColoredRect(1, 4, 1, 1, 0)
gameObject.addColoredRect(0, 3, 1, 1, 0)
gameObject.addColoredRect(6, 3, 1, 1, 0)
gameObject.addColoredRect(5, 2, 1, 1, 0)
gameObject.addColoredRect(4, 1, 1, 1, 0)
gameObject.addColoredRect(3, 0, 1, 1, 0)
gameObject.addColoredRect(3, 5, 1, 1, 1)
gameObject.addColoredRect(2, 4, 2, 1, 1)
gameObject.addColoredRect(1, 3, 2, 1, 1)
gameObject.addColoredRect(4, 3, 2, 1, 1)
gameObject.addColoredRect(2, 2, 3, 1, 1)
gameObject.addColoredRect(3, 1, 1, 1, 1)
gameObject.addColoredRect(5, 5, 1, 1, 2)
gameObject.addColoredRect(4, 4, 1, 1, 2)
gameObject.addColoredRect(3, 3, 1, 1, 2)
// Down-Left orientation (XY reflection)
gameObject.orientation = 7 // Down-Left
gameObject.addColoredRect(3, 6, 1, 1, 0)
gameObject.addColoredRect(4, 5, 1, 1, 0)
gameObject.addColoredRect(5, 4, 1, 1, 0)
gameObject.addColoredRect(6, 3, 1, 1, 0)
gameObject.addColoredRect(0, 3, 1, 1, 0)
gameObject.addColoredRect(1, 2, 1, 1, 0)
gameObject.addColoredRect(2, 1, 1, 1, 0)
gameObject.addColoredRect(3, 0, 1, 1, 0)
gameObject.addColoredRect(3, 1, 1, 1, 1)
gameObject.addColoredRect(2, 2, 3, 1, 1)
gameObject.addColoredRect(1, 3, 2, 1, 1)
gameObject.addColoredRect(4, 3, 2, 1, 1)
gameObject.addColoredRect(3, 4, 2, 1, 1)
gameObject.addColoredRect(3, 5, 1, 1, 1)
gameObject.addColoredRect(1, 5, 1, 1, 2)
gameObject.addColoredRect(2, 4, 1, 1, 2)
gameObject.addColoredRect(3, 3, 1, 1, 2)
gameObject.orientation = 0
*players = append(*players, &Player{ *players = append(*players, &Player{
posX: posX, playerColors: playerColors,
posY: posY, keyMap: keyMap,
orientation: 3, joyMap: joyMap,
color1: color1, joyStick: joyStick,
color2: color2, shields: MaxShields,
color3: color3,
health: MaxHealth,
energy: MaxEnergy, energy: MaxEnergy,
gameObject: gameObject,
local: true,
}) })
} }

387
playerData.go Normal file

@ -0,0 +1,387 @@
package main
import (
"github.com/veandco/go-sdl2/sdl"
"image/color"
)
type KeyMap struct {
exit int
shoot int
super int
up int
down int
left int
right int
}
var keyMaps = []KeyMap{
{
exit: sdl.SCANCODE_ESCAPE,
shoot: sdl.SCANCODE_SPACE,
super: sdl.SCANCODE_LCTRL,
up: sdl.SCANCODE_W,
down: sdl.SCANCODE_S,
left: sdl.SCANCODE_A,
right: sdl.SCANCODE_D,
},
{
exit: sdl.SCANCODE_END,
shoot: sdl.SCANCODE_RETURN,
super: sdl.SCANCODE_RCTRL,
up: sdl.SCANCODE_UP,
down: sdl.SCANCODE_DOWN,
left: sdl.SCANCODE_LEFT,
right: sdl.SCANCODE_RIGHT,
},
}
type JoyMap struct {
xAxis int
yAxis int
exitButton int
shootButton int
superButton int
upButton int
downButton int
leftButton int
rightButton int
}
var joyMaps = []JoyMap{
{
xAxis: 0,
yAxis: 1,
exitButton: 0,
shootButton: 1,
superButton: 2,
upButton: 3,
downButton: 4,
leftButton: 5,
rightButton: 6,
},
}
type PlayerColors struct {
tracks color.Color
body color.Color
cannon color.Color
}
func getNeededPlayers() (neededPlayers uint8) {
neededPlayers = 0
if DoAllKeymapsPlayers {
neededPlayers += uint8(len(keyMaps))
} else if DoKeymapPlayer && len(keyMaps) > 0 {
neededPlayers++
}
if DoJoyStickPlayers {
neededPlayers += uint8(sdl.NumJoysticks())
}
if neededPlayers < 2 {
neededPlayers = 2
}
return neededPlayers
}
var playerColors []PlayerColors
func initPlayerColors() {
playerColors = []PlayerColors{
{
tracks: color.RGBA{B: 182, A: 255},
body: color.RGBA{R: 44, G: 44, B: 255, A: 255},
cannon: color.RGBA{R: 243, G: 235, B: 28, A: 255},
},
{
tracks: color.RGBA{R: 44, G: 184, B: 44, A: 255},
body: color.RGBA{G: 249, A: 255},
cannon: color.RGBA{R: 243, G: 235, B: 28, A: 255},
},
// Set 3
{
tracks: color.RGBA{R: 255, A: 255}, // Bright Red
body: color.RGBA{G: 255, B: 255, A: 255}, // Cyan
cannon: color.RGBA{R: 255, G: 165, A: 255}, // Orange
},
// Set 4
{
tracks: color.RGBA{R: 75, B: 130, A: 255}, // Indigo
body: color.RGBA{R: 255, G: 20, B: 147, A: 255}, // Deep Pink
cannon: color.RGBA{G: 128, B: 128, A: 255}, // Teal
},
// Set 5
{
tracks: color.RGBA{R: 255, G: 105, B: 180, A: 255}, // Hot Pink
body: color.RGBA{R: 34, G: 139, B: 34, A: 255}, // Forest Green
cannon: color.RGBA{A: 255}, // Black
},
// Set 6
{
tracks: color.RGBA{R: 128, A: 255}, // Maroon
body: color.RGBA{R: 255, G: 255, A: 255}, // Yellow
cannon: color.RGBA{B: 128, A: 255}, // Navy Blue
},
// Set 7
{
tracks: color.RGBA{R: 255, G: 69, A: 255}, // Red-Orange
body: color.RGBA{G: 206, B: 209, A: 255}, // Dark Turquoise
cannon: color.RGBA{R: 50, G: 205, B: 50, A: 255}, // Lime Green
},
// Set 8
{
tracks: color.RGBA{R: 128, G: 128, A: 255}, // Olive
body: color.RGBA{R: 255, G: 105, B: 180, A: 255}, // Hot Pink
cannon: color.RGBA{G: 191, B: 255, A: 255}, // Deep Sky Blue
},
// Set 9
{
tracks: color.RGBA{R: 255, G: 20, B: 20, A: 255}, // Bright Red
body: color.RGBA{G: 255, B: 127, A: 255}, // Spring Green
cannon: color.RGBA{R: 186, G: 85, B: 211, A: 255}, // Medium Orchid
},
// Set 10
{
tracks: color.RGBA{R: 255, G: 215, A: 255}, // Gold
body: color.RGBA{B: 139, A: 255}, // Dark Blue
cannon: color.RGBA{R: 238, G: 130, B: 238, A: 255}, // Violet
},
// Set 11
{
tracks: color.RGBA{R: 210, G: 105, B: 30, A: 255}, // Chocolate
body: color.RGBA{R: 72, G: 61, B: 139, A: 255}, // Dark Slate Blue
cannon: color.RGBA{R: 152, G: 251, B: 152, A: 255}, // Pale Green
},
// Set 12
{
tracks: color.RGBA{R: 220, G: 20, B: 60, A: 255}, // Crimson
body: color.RGBA{R: 255, G: 250, B: 205, A: 255}, // Lemon Chiffon
cannon: color.RGBA{G: 128, A: 255}, // Green
},
// Set 13
{
tracks: color.RGBA{R: 139, B: 139, A: 255}, // Dark Magenta
body: color.RGBA{R: 144, G: 238, B: 144, A: 255}, // Light Green
cannon: color.RGBA{R: 255, G: 69, A: 255}, // Orange Red
},
// Set 14
{
tracks: color.RGBA{B: 255, A: 255}, // Blue
body: color.RGBA{R: 240, G: 128, B: 128, A: 255}, // Light Coral
cannon: color.RGBA{R: 218, G: 165, B: 32, A: 255}, // Goldenrod
},
// Set 15
{
tracks: color.RGBA{R: 65, G: 105, B: 225, A: 255}, // Royal Blue
body: color.RGBA{R: 255, G: 222, B: 173, A: 255}, // Navajo White
cannon: color.RGBA{R: 255, B: 255, A: 255}, // Magenta
},
// Set 16
{
tracks: color.RGBA{R: 147, G: 112, B: 219, A: 255}, // Medium Purple
body: color.RGBA{G: 255, B: 127, A: 255}, // Spring Green
cannon: color.RGBA{R: 255, G: 255, A: 255}, // Yellow
},
// Set 17
{
tracks: color.RGBA{R: 255, G: 140, A: 255}, // Dark Orange
body: color.RGBA{R: 127, G: 255, A: 255}, // Chartreuse
cannon: color.RGBA{R: 25, G: 25, B: 112, A: 255}, // Midnight Blue
},
// Set 18
{
tracks: color.RGBA{R: 240, G: 230, B: 140, A: 255}, // Khaki
body: color.RGBA{R: 173, G: 216, B: 230, A: 255}, // Light Blue
cannon: color.RGBA{R: 128, B: 128, A: 255}, // Purple
},
// Set 19
{
tracks: color.RGBA{R: 70, G: 130, B: 180, A: 255}, // Steel Blue
body: color.RGBA{R: 255, G: 99, B: 71, A: 255}, // Tomato
cannon: color.RGBA{R: 154, G: 205, B: 50, A: 255}, // Yellow Green
},
// Set 20
{
tracks: color.RGBA{R: 106, G: 90, B: 205, A: 255}, // Slate Blue
body: color.RGBA{R: 255, G: 182, B: 193, A: 255}, // Light Pink
cannon: color.RGBA{R: 46, G: 139, B: 87, A: 255}, // Sea Green
},
// Set 21
{
tracks: color.RGBA{R: 199, G: 21, B: 133, A: 255}, // Medium Violet Red
body: color.RGBA{G: 255, B: 255, A: 255}, // Aqua
cannon: color.RGBA{R: 255, G: 160, B: 122, A: 255}, // Light Salmon
},
// Set 22
{
tracks: color.RGBA{B: 139, A: 255}, // Dark Blue
body: color.RGBA{R: 173, G: 255, B: 47, A: 255}, // Green Yellow
cannon: color.RGBA{R: 255, G: 228, B: 181, A: 255},
},
// Set 23
{
tracks: color.RGBA{R: 205, G: 92, B: 92, A: 255}, // Indian Red
body: color.RGBA{G: 191, B: 255, A: 255}, // Deep Sky Blue
cannon: color.RGBA{R: 255, G: 255, B: 224, A: 255}, // Light Yellow
},
// Set 24
{
tracks: color.RGBA{R: 139, G: 69, B: 19, A: 255}, // Saddle Brown
body: color.RGBA{R: 255, G: 99, B: 71, A: 255}, // Tomato
cannon: color.RGBA{R: 70, G: 130, B: 180, A: 255}, // Steel Blue
},
// Set 25
{
tracks: color.RGBA{R: 135, G: 206, B: 250, A: 255}, // Light Sky Blue
body: color.RGBA{R: 255, G: 105, B: 180, A: 255}, // Hot Pink
cannon: color.RGBA{R: 184, G: 134, B: 11, A: 255}, // Dark Goldenrod
},
// Set 26
{
tracks: color.RGBA{G: 255, A: 255}, // Lime
body: color.RGBA{R: 255, G: 228, B: 225, A: 255}, // Misty Rose
cannon: color.RGBA{R: 128, A: 255}, // Maroon
},
// Set 27
{
tracks: color.RGBA{R: 255, B: 255, A: 255}, // Magenta
body: color.RGBA{G: 100, A: 255}, // Dark Green
cannon: color.RGBA{R: 255, G: 222, B: 173, A: 255}, // Navajo White
},
// Set 28
{
tracks: color.RGBA{R: 138, G: 43, B: 226, A: 255}, // Blue Violet
body: color.RGBA{R: 255, G: 228, B: 181, A: 255}, // Moccasin
cannon: color.RGBA{R: 47, G: 79, B: 79, A: 255}, // Dark Slate Gray
},
// Set 29
{
tracks: color.RGBA{R: 255, G: 182, B: 193, A: 255}, // Light Pink
body: color.RGBA{G: 128, B: 128, A: 255}, // Teal
cannon: color.RGBA{R: 255, G: 255, B: 240, A: 255}, // Ivory
},
// Set 30
{
tracks: color.RGBA{R: 255, G: 127, B: 80, A: 255}, // Coral
body: color.RGBA{R: 25, G: 25, B: 112, A: 255}, // Midnight Blue
cannon: color.RGBA{R: 173, G: 255, B: 47, A: 255}, // Green Yellow
},
// Set 31
{
tracks: color.RGBA{R: 218, G: 112, B: 214, A: 255}, // Orchid
body: color.RGBA{R: 244, G: 164, B: 96, A: 255}, // Sandy Brown
cannon: color.RGBA{R: 255, G: 239, B: 213, A: 255}, // Papaya Whip
},
// Set 32
{
tracks: color.RGBA{G: 250, B: 154, A: 255}, // Medium Spring Green
body: color.RGBA{R: 138, G: 43, B: 226, A: 255}, // Blue Violet
cannon: color.RGBA{R: 255, G: 69, A: 255}, // Orange Red
},
}
}
func handleInput(keyboard []uint8, bullets *[]*Bullet, player *Player, gameMap *GameMap, format *sdl.PixelFormat, players *[]*Player) (bool, bool) {
if !player.local || player.shields <= 0 || player.shields > MaxShields {
return true, false
}
shoot := false
super := false
// Flags to track movement in each direction
moveUp := false
moveDown := false
moveLeft := false
moveRight := false
if player.keyMap.exit != player.keyMap.shoot {
// Process keyboard input
for key, value := range keyboard {
if value == 0 {
continue
}
if key == player.keyMap.exit {
return false, shoot
} else if key == player.keyMap.shoot {
shoot = true
} else if key == player.keyMap.super {
super = true
} else if key == player.keyMap.up {
moveUp = true
} else if key == player.keyMap.down {
moveDown = true
} else if key == player.keyMap.left {
moveLeft = true
} else if key == player.keyMap.right {
moveRight = true
}
}
} else if player.joyStick != nil {
if player.joyStick.NumAxes() > player.joyMap.xAxis+1 &&
player.joyStick.NumAxes() > player.joyMap.yAxis+1 &&
player.joyMap.exitButton != player.joyMap.shootButton {
xAxis := player.joyStick.Axis(player.joyMap.xAxis)
yAxis := player.joyStick.Axis(player.joyMap.yAxis)
moveRight = xAxis > JoyStickDeadZone
moveLeft = xAxis < -JoyStickDeadZone
moveUp = yAxis > JoyStickDeadZone
moveDown = yAxis < -JoyStickDeadZone
} else {
moveUp = player.joyStick.Button(player.joyMap.upButton) == sdl.PRESSED
moveDown = player.joyStick.Button(player.joyMap.downButton) == sdl.PRESSED
moveLeft = player.joyStick.Button(player.joyMap.leftButton) == sdl.PRESSED
moveRight = player.joyStick.Button(player.joyMap.rightButton) == sdl.PRESSED
}
if player.joyStick.Button(player.joyMap.exitButton) == sdl.PRESSED {
return false, shoot
}
if player.joyStick.Button(player.joyMap.shootButton) == sdl.PRESSED {
shoot = true
}
if player.joyStick.Button(player.joyMap.superButton) == sdl.PRESSED {
super = true
}
}
// Handle shooting after the loop
if shoot {
player.shoot(super, bullets)
}
// Determine player orientation for diagonal movement
if moveUp && moveRight {
player.gameObject.orientation = 4 // Up-Right
} else if moveUp && moveLeft {
player.gameObject.orientation = 5 // Up-Left
} else if moveDown && moveRight {
player.gameObject.orientation = 6 // Down-Right
} else if moveDown && moveLeft {
player.gameObject.orientation = 7 // Down-Left
} else if moveUp {
player.gameObject.orientation = 0 // Up
} else if moveRight {
player.gameObject.orientation = 1 // Right
} else if moveDown {
player.gameObject.orientation = 2 // Down
} else if moveLeft {
player.gameObject.orientation = 3 // Left
}
// Handle movement after the loop
if moveUp || moveDown || moveLeft || moveRight {
if player.tryMove(gameMap, shoot, players) {
player.energy -= MovementCost
}
}
return true, shoot
}

38
profiler.go Normal file

@ -0,0 +1,38 @@
package main
import (
"fmt"
"github.com/veandco/go-sdl2/sdl"
)
func profileSection(section func()) uint64 {
start := sdl.GetTicks64()
section()
end := sdl.GetTicks64()
return end - start
}
func logProfilingInfo(handleEventsTime, renderTime, scaleTime, frameTime, frameCount uint64) {
floatFrame := float64(frameCount)
fmt.Printf("Average handleEvents time: %f ms\n", float64(handleEventsTime)/floatFrame)
fmt.Printf("Average render time: %f ms\n", float64(renderTime)/floatFrame)
fmt.Printf("Average scaling time: %f ms\n", float64(scaleTime)/floatFrame)
fmt.Printf("Average frame time: %f ms\n", float64(frameTime)/floatFrame)
fmt.Print("\n")
}
func resetProfilingCounters(handleEventsTime, renderTime, scaleTime, frameTime *uint64, frameCount *uint64) {
*handleEventsTime = 0
*renderTime = 0
*scaleTime = 0
*frameTime = 0
*frameCount = 0
}
func enforceFrameRate(frameStart uint64, targetFPS int) {
frameDuration := 1000 / targetFPS
elapsed := sdl.GetTicks64() - frameStart
if delay := frameDuration - int(elapsed); delay > 0 {
sdl.Delay(uint32(delay))
}
}