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"
const (
openingWidth = 7
)
type Base struct {
rect sdl.Rect
owner *Player
openingWidth int32
gameObject *GameObject
}
func (base *Base) tick(players *[]*Player) {
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 MaxEnergy-player.energy < 4 {
player.energy++
} else {
player.energy += 4
}
if player == base.owner {
player.rechargeCooldown = RechargeCooldownOwn
} else {
player.rechargeCooldown = RechargeCooldownOpponent
}
}
if player == base.owner && player.repairCooldown == 0 && player.health < MaxHealth {
player.health++
if player == base.owner && player.repairCooldown == 0 && player.shields < MaxShields {
player.shields++
player.repairCooldown = RepairCooldown
}
}
@ -31,112 +32,79 @@ func (base *Base) tick(players *[]*Player) {
}
func (base *Base) render(camera *sdl.Rect, surface *sdl.Surface) {
borderWidth := base.rect.W - openingWidth
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)
}
}
base.gameObject.render(camera, surface)
}
func (base *Base) build(gameMap *GameMap) {
borderWidth := base.rect.W - openingWidth
borderWidth := base.gameObject.baseRect.W - base.openingWidth
borderWidthA := borderWidth / 2
if borderWidth < 0 {
panic("Bad border width")
}
if base.rect.H < 9 {
if base.gameObject.baseRect.H < 9 {
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")
}
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")
}
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")
}
for x := base.rect.X; x < base.rect.X+base.rect.W+1; x++ {
for y := base.rect.Y; y < base.rect.Y+base.rect.H+1; y++ {
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
}
}
for y := base.rect.Y; y < base.rect.Y+base.rect.H; y++ {
gameMap.tiles[base.rect.X][y] = 4
gameMap.tiles[base.rect.X+base.rect.W][y] = 4
for y := base.gameObject.baseRect.Y; y < base.gameObject.baseRect.Y+base.gameObject.baseRect.H; y++ {
gameMap.tiles[base.gameObject.baseRect.X][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++ {
gameMap.tiles[x][base.rect.Y] = 4
gameMap.tiles[x][base.rect.Y+base.rect.H] = 4
for x := base.gameObject.baseRect.X; x < base.gameObject.baseRect.X+borderWidthA; x++ {
gameMap.tiles[x][base.gameObject.baseRect.Y] = 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++ {
gameMap.tiles[x][base.rect.Y] = 4
gameMap.tiles[x][base.rect.Y+base.rect.H] = 4
for x := base.gameObject.baseRect.X + borderWidthA + base.openingWidth; x < base.gameObject.baseRect.X+base.gameObject.baseRect.W+1; x++ {
gameMap.tiles[x][base.gameObject.baseRect.Y] = 4
gameMap.tiles[x][base.gameObject.baseRect.Y+base.gameObject.baseRect.H] = 4
}
}
func createBases(players *[]*Player, gameMap *GameMap) *[]*Base {
bases := &[]*Base{}
for ownerID, player := range *players {
*bases = append(*bases, &Base{
rect: sdl.Rect{
X: player.posX - 14,
Y: player.posY - 14,
gameObject := &GameObject{}
gameObject.baseRect = sdl.Rect{
X: player.gameObject.baseRect.X - 14,
Y: player.gameObject.baseRect.Y - 14,
W: 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],
openingWidth: openingWidth,
})
(*bases)[ownerID].build(gameMap)
}

@ -149,14 +149,16 @@ func (bullet *Bullet) tick(gameMap *GameMap,
}
hitPlayer := false
for _, player := range *players {
if player.rect1.HasIntersection(&bulletRect) ||
player.rect2.HasIntersection(&bulletRect) ||
player.rect3.HasIntersection(&bulletRect) ||
player.rect4.HasIntersection(&bulletRect) {
hitPlayer = true
player.health -= 20
if hitPlayer {
break
}
for _, coloredRect := range player.gameObject.getCurrentRects() {
if coloredRect.rect.HasIntersection(&bulletRect) {
hitPlayer = true
player.shields -= 10
break
}
}
}
if collisionResult != 0 || hitPlayer {
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
import (
"fmt"
"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
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 {
panic(err)
}
@ -81,7 +97,6 @@ func handleWindowResize(window *sdl.Window, logicalSurface *sdl.Surface) {
letterboxX := (windowWidth - newWidth) / 2
letterboxY := (windowHeight - newHeight) / 2
windowSurface.FillRect(nil, 0)
srcRect := &sdl.Rect{X: 0, Y: 0, W: logicalSurface.W, H: logicalSurface.H}
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()
}
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) {
HUDColor := sdl.MapRGBA(HUDSurface.Format, 101, 101, 101, 255)
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) {
func adjustWindow(window *sdl.Window, logicalSurface *sdl.Surface) {
// Get window surface and handle any errors
windowSurface, err := window.GetSurface()
if err != nil {
panic(err)
}
// Retrieve window dimensions
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
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
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}
dstRect := &sdl.Rect{X: letterboxX, Y: letterboxY, W: newWidth, H: newHeight}
// Perform the scaled blit
logicalSurface.BlitScaled(srcRect, windowSurface, dstRect)
// Update the window surface
window.UpdateSurface()
}

139
hud.go

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

248
main.go

@ -7,13 +7,19 @@ import (
const (
MapWidth = 1000 // Width of the map
MapHeight = 1000 // Height of the map
MaxEnergy = 1760
MaxHealth = 88
NormalShotCost = 3
SuperShotCost = 20
BlastRadius = 5
MaxEnergy = 3520
MaxAmmunition = 6
MaxShields = 100
NormalShotCost = 7
SuperShotCost = 80
ReloadCost = 4
MovementCost = 1
DiggingCost = 2
DiggingCost = 3
ShootDiggingCostBonus = 1
ShootCooldown = 8
RechargeCooldownOwn = 0
DiggingCooldown = 4
@ -22,137 +28,159 @@ const (
MovementCooldown = 2
MovementCooldownNoEnergy = 4
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() {
initializeSDL()
defer sdl.Quit()
window, logicalSurface := setupWindowAndSurface()
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 := &GameMap{}
gameMap.createGameMap(MapWidth, MapHeight)
players := &[]*Player{}
initPlayerColors()
createPlayers(getNeededPlayers(), playerColors, keyMaps, joyMaps, gameMap, players)
createPlayer(
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)
defer closeThings(players)
createPlayer(
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)
bases := createBases(players, &gameMap)
bulletMap := &[]*Bullet{}
bulletParticleMap := &[]*BulletParticle{}
camera := sdl.Rect{X: 0, Y: 0, W: 76, H: 76}
bullets := &[]*Bullet{}
bulletParticles := &[]*BulletParticle{}
for playerIndex, player := range *players {
initPlayer(uint8(playerIndex), player)
}
if MapWindow {
mapWindow, mapSurface = setupMapWindowAndSurface()
}
running := true
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()
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)
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 _, bullet := range *bullets {
(*bullet).render(player.camera, player.playSurface)
}
for _, base := range *bases {
(*base).render(player.camera, player.playSurface)
}
for _, playerLoop := range *players {
(*playerLoop).render(player.camera, player.playSurface)
}
for _, bulletParticle := range *bulletParticles {
bulletParticle.render(player.camera, player.playSurface)
}
func handleInput(keyboard []uint8, bullets *[]*Bullet, player *Player, gameMap *GameMap, format *sdl.PixelFormat, players *[]*Player) bool {
shoot := false
super := false
player.tick(isShooting, bullets, player.playSurface.Format)
renderHud(player, player.HUDSurface)
player.playSurface.BlitScaled(player.playSurfaceRect, player.logicalSurface, player.playSurfaceTargetRect)
player.HUDSurface.BlitScaled(player.HUDSurfaceRect, player.logicalSurface, player.HUDSurfaceTargetRect)
})
// Flags to track movement in each direction
moveUp := false
moveDown := false
moveLeft := false
moveRight := false
// Profile window adjustments
totalScalingTime += profileSection(func() {
adjustWindow(player.window, player.logicalSurface)
})
// Process keyboard input
for key, value := range keyboard {
if value == 0 {
continue
}
frameEnd := sdl.GetTicks64()
totalFrameTime += frameEnd - frameStart
frameCount++
if key == sdl.SCANCODE_ESCAPE {
return false
} else if key == sdl.SCANCODE_SPACE {
shoot = true
} else if key == sdl.SCANCODE_LCTRL {
super = true
} else if key == sdl.SCANCODE_W {
moveUp = true
} else if key == sdl.SCANCODE_S {
moveDown = true
} else if key == sdl.SCANCODE_A {
moveLeft = true
} else if key == sdl.SCANCODE_D {
moveRight = true
// Log profiling information every 1000 frames
if frameCount%1000 == 0 && ProfilerOn && playerIndex == 0 {
logProfilingInfo(totalHandleEventsTime, totalRenderTime, totalScalingTime, totalFrameTime, frameCount)
resetProfilingCounters(&totalHandleEventsTime, &totalRenderTime, &totalScalingTime, &totalFrameTime, &frameCount)
}
}
// 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
return running
}

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) {
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())
for i := range gameMap.tiles {
gameMap.tiles[i] = make([]uint8, height)
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 {
gameMap.tiles[i][j] = 1
} else {

538
player.go

@ -7,107 +7,50 @@ import (
)
type Player struct {
posX int32
posY int32
orientation uint8
color1 uint32
color2 uint32
color3 uint32
local bool
playerColors PlayerColors
keyMap KeyMap
joyMap JoyMap
joyStick *sdl.Joystick
camera *sdl.Rect
energy uint16
health uint16
ammunition uint16
shields uint16
digCooldown uint8
shootCooldown uint8
repairCooldown uint8
rechargeCooldown uint8
movementCooldown uint8
rect1 sdl.Rect
rect2 sdl.Rect
rect3 sdl.Rect
rect4 sdl.Rect
cullingRect sdl.Rect
reloadCooldown uint8
gameObject *GameObject
window *sdl.Window
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) {
camera.X = player.posX - 37
camera.Y = player.posY - 38
camera.X = player.gameObject.baseRect.X - 37
camera.Y = player.gameObject.baseRect.Y - 38
}
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
player.cullingRect = sdl.Rect{X: player.posX, Y: player.posY, W: 7, H: 7}
cameraAdjustedCullingRect := sdl.Rect{
X: player.cullingRect.X - camera.X,
Y: player.cullingRect.Y - camera.Y,
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)
player.gameObject.baseRect = sdl.Rect{X: player.gameObject.baseRect.X, Y: player.gameObject.baseRect.Y, W: 7, H: 7}
if player.shields > 0 && player.shields <= MaxShields {
player.gameObject.render(camera, surface)
}
}
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
}
func (player *Player) tick() {
func (player *Player) tick(isShooting bool, bullets *[]*Bullet, format *sdl.PixelFormat) {
if player.digCooldown > 0 {
player.digCooldown--
}
@ -138,6 +81,17 @@ func (player *Player) tick() {
if player.movementCooldown > 0 {
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) {
@ -151,21 +105,21 @@ func (player *Player) tryMove(gameMap *GameMap, isShooting bool, players *[]*Pla
shouldPenalizeDigging := false
stopped := false
switch player.orientation {
switch player.gameObject.orientation {
case 0: // Up
collisionRect := sdl.Rect{
X: player.posX,
Y: player.posY - 1,
X: player.gameObject.baseRect.X,
Y: player.gameObject.baseRect.Y - 1,
W: 5,
H: 7,
}
for _, opponent := range *players {
if opponent != player && opponent.cullingRect.HasIntersection(&collisionRect) {
if opponent != player && opponent.gameObject.baseRect.HasIntersection(&collisionRect) {
return false
}
}
for x := player.posX; x < player.posX+5 && !stopped; x++ {
for y := player.posY - 1; y < player.posY+7; y++ {
for x := player.gameObject.baseRect.X; x < player.gameObject.baseRect.X+5 && !stopped; x++ {
for y := player.gameObject.baseRect.Y - 1; y < player.gameObject.baseRect.Y+7; y++ {
digResult := player.digBlock(x, y, gameMap, isShooting)
if digResult == 1 {
shouldPenalizeDigging = true
@ -181,23 +135,23 @@ func (player *Player) tryMove(gameMap *GameMap, isShooting bool, players *[]*Pla
}
if !stopped {
moved = true
player.posY--
player.gameObject.baseRect.Y--
}
case 1: // Right
collisionRect := sdl.Rect{
X: player.posX + 1,
Y: player.posY,
X: player.gameObject.baseRect.X + 1,
Y: player.gameObject.baseRect.Y,
W: 7,
H: 5,
}
for _, opponent := range *players {
if opponent != player && opponent.cullingRect.HasIntersection(&collisionRect) {
if opponent != player && opponent.gameObject.baseRect.HasIntersection(&collisionRect) {
return false
}
}
for y := player.posY; y < player.posY+7 && !stopped; y++ {
for x := player.posX + 1; x < player.posX+8; x++ {
for y := player.gameObject.baseRect.Y + 1; y < player.gameObject.baseRect.Y+6 && !stopped; y++ {
for x := player.gameObject.baseRect.X + 1; x < player.gameObject.baseRect.X+8; x++ {
digResult := player.digBlock(x, y, gameMap, isShooting)
if digResult == 1 {
shouldPenalizeDigging = true
@ -213,23 +167,23 @@ func (player *Player) tryMove(gameMap *GameMap, isShooting bool, players *[]*Pla
}
if !stopped {
moved = true
player.posX++
player.gameObject.baseRect.X++
}
case 2: // Down
collisionRect := sdl.Rect{
X: player.posX,
Y: player.posY + 1,
X: player.gameObject.baseRect.X,
Y: player.gameObject.baseRect.Y + 1,
W: 5,
H: 7,
}
for _, opponent := range *players {
if opponent != player && opponent.cullingRect.HasIntersection(&collisionRect) {
if opponent != player && opponent.gameObject.baseRect.HasIntersection(&collisionRect) {
return false
}
}
for x := player.posX; x < player.posX+5 && !stopped; x++ {
for y := player.posY + 1; y < player.posY+8; y++ {
for x := player.gameObject.baseRect.X; x < player.gameObject.baseRect.X+5 && !stopped; x++ {
for y := player.gameObject.baseRect.Y + 1; y < player.gameObject.baseRect.Y+8; y++ {
digResult := player.digBlock(x, y, gameMap, isShooting)
if digResult == 1 {
shouldPenalizeDigging = true
@ -245,23 +199,23 @@ func (player *Player) tryMove(gameMap *GameMap, isShooting bool, players *[]*Pla
}
if !stopped {
moved = true
player.posY++
player.gameObject.baseRect.Y++
}
case 3: // Left
collisionRect := sdl.Rect{
X: player.posX - 1,
Y: player.posY,
X: player.gameObject.baseRect.X - 1,
Y: player.gameObject.baseRect.Y,
W: 7,
H: 5,
}
for _, opponent := range *players {
if opponent != player && opponent.cullingRect.HasIntersection(&collisionRect) {
if opponent != player && opponent.gameObject.baseRect.HasIntersection(&collisionRect) {
return false
}
}
for y := player.posY; y < player.posY+6 && !stopped; y++ {
for x := player.posX - 1; x < player.posX+6; x++ {
for y := player.gameObject.baseRect.Y + 1; y < player.gameObject.baseRect.Y+6 && !stopped; y++ {
for x := player.gameObject.baseRect.X - 1; x < player.gameObject.baseRect.X+6; x++ {
digResult := player.digBlock(x, y, gameMap, isShooting)
if digResult == 1 {
shouldPenalizeDigging = true
@ -277,23 +231,23 @@ func (player *Player) tryMove(gameMap *GameMap, isShooting bool, players *[]*Pla
}
if !stopped {
moved = true
player.posX--
player.gameObject.baseRect.X--
}
case 4: // Up-Right
collisionRect := sdl.Rect{
X: player.posX + 1,
Y: player.posY - 1,
X: player.gameObject.baseRect.X + 1,
Y: player.gameObject.baseRect.Y - 1,
W: 7,
H: 7,
}
for _, opponent := range *players {
if opponent != player && opponent.cullingRect.HasIntersection(&collisionRect) {
if opponent != player && opponent.gameObject.baseRect.HasIntersection(&collisionRect) {
return false
}
}
for x := player.posX + 1; x < player.posX+8 && !stopped; x++ {
for y := player.posY - 1; y < player.posY+7; y++ {
for x := player.gameObject.baseRect.X + 1; x < player.gameObject.baseRect.X+8 && !stopped; x++ {
for y := player.gameObject.baseRect.Y - 1; y < player.gameObject.baseRect.Y+6; y++ {
digResult := player.digBlock(x, y, gameMap, isShooting)
if digResult == 1 {
shouldPenalizeDigging = true
@ -309,24 +263,24 @@ func (player *Player) tryMove(gameMap *GameMap, isShooting bool, players *[]*Pla
}
if !stopped {
moved = true
player.posX++
player.posY--
player.gameObject.baseRect.X++
player.gameObject.baseRect.Y--
}
case 5: // Up-Left
collisionRect := sdl.Rect{
X: player.posX - 1,
Y: player.posY - 1,
X: player.gameObject.baseRect.X - 1,
Y: player.gameObject.baseRect.Y - 1,
W: 7,
H: 7,
}
for _, opponent := range *players {
if opponent != player && opponent.cullingRect.HasIntersection(&collisionRect) {
if opponent != player && opponent.gameObject.baseRect.HasIntersection(&collisionRect) {
return false
}
}
for x := player.posX - 1; x < player.posX+6 && !stopped; x++ {
for y := player.posY - 1; y < player.posY+7; y++ {
for x := player.gameObject.baseRect.X - 1; x < player.gameObject.baseRect.X+6 && !stopped; x++ {
for y := player.gameObject.baseRect.Y - 1; y < player.gameObject.baseRect.Y+6; y++ {
digResult := player.digBlock(x, y, gameMap, isShooting)
if digResult == 1 {
shouldPenalizeDigging = true
@ -342,24 +296,24 @@ func (player *Player) tryMove(gameMap *GameMap, isShooting bool, players *[]*Pla
}
if !stopped {
moved = true
player.posX--
player.posY--
player.gameObject.baseRect.X--
player.gameObject.baseRect.Y--
}
case 6: // Down-Right
collisionRect := sdl.Rect{
X: player.posX + 1,
Y: player.posY + 1,
X: player.gameObject.baseRect.X + 1,
Y: player.gameObject.baseRect.Y + 1,
W: 7,
H: 7,
}
for _, opponent := range *players {
if opponent != player && opponent.cullingRect.HasIntersection(&collisionRect) {
if opponent != player && opponent.gameObject.baseRect.HasIntersection(&collisionRect) {
return false
}
}
for x := player.posX + 1; x < player.posX+8 && !stopped; x++ {
for y := player.posY + 1; y < player.posY+8; y++ {
for x := player.gameObject.baseRect.X + 1; x < player.gameObject.baseRect.X+8 && !stopped; x++ {
for y := player.gameObject.baseRect.Y + 1; y < player.gameObject.baseRect.Y+8; y++ {
digResult := player.digBlock(x, y, gameMap, isShooting)
if digResult == 1 {
shouldPenalizeDigging = true
@ -375,24 +329,24 @@ func (player *Player) tryMove(gameMap *GameMap, isShooting bool, players *[]*Pla
}
if !stopped {
moved = true
player.posX++
player.posY++
player.gameObject.baseRect.X++
player.gameObject.baseRect.Y++
}
case 7: // Down-Left
collisionRect := sdl.Rect{
X: player.posX - 1,
Y: player.posY + 1,
X: player.gameObject.baseRect.X - 1,
Y: player.gameObject.baseRect.Y + 1,
W: 7,
H: 7,
}
for _, opponent := range *players {
if opponent != player && opponent.cullingRect.HasIntersection(&collisionRect) {
if opponent != player && opponent.gameObject.baseRect.HasIntersection(&collisionRect) {
return false
}
}
for x := player.posX - 1; x < player.posX+6 && !stopped; x++ {
for y := player.posY + 1; y < player.posY+8; y++ {
for x := player.gameObject.baseRect.X - 1; x < player.gameObject.baseRect.X+6 && !stopped; x++ {
for y := player.gameObject.baseRect.Y + 1; y < player.gameObject.baseRect.Y+8; y++ {
digResult := player.digBlock(x, y, gameMap, isShooting)
if digResult == 1 {
shouldPenalizeDigging = true
@ -408,13 +362,16 @@ func (player *Player) tryMove(gameMap *GameMap, isShooting bool, players *[]*Pla
}
if !stopped {
moved = true
player.posX--
player.posY++
player.gameObject.baseRect.X--
player.gameObject.baseRect.Y++
}
}
// Penalties and cooldown handling
if shouldPenalizeDigging {
if isShooting && player.ammunition < MaxAmmunition && rand.Intn(2) == 0 {
player.ammunition++
}
if ranOutOfEnergy {
player.digCooldown = DiggingCooldownNoEnergy
}
@ -433,96 +390,327 @@ func (player *Player) tryMove(gameMap *GameMap, isShooting bool, players *[]*Pla
return
}
func (player *Player) shoot(super bool, bullets *[]*Bullet, format *sdl.PixelFormat) {
if (super && player.energy <= SuperShotCost) || (!super && player.energy <= NormalShotCost) {
func (player *Player) getRGBAColor(colorIndex uint8, format *sdl.PixelFormat) uint32 {
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
}
if player.shootCooldown == 0 {
var shootX, shootY int32
switch player.orientation {
switch player.gameObject.orientation {
case 0: // Up
shootY = player.posY - 1
shootX = player.posX + 2
shootY = player.gameObject.baseRect.Y - 1
shootX = player.gameObject.baseRect.X + 2
break
case 1: // Right
shootY = player.posY + 3
shootX = player.posX + 8
shootY = player.gameObject.baseRect.Y + 3
shootX = player.gameObject.baseRect.X + 8
break
case 2: // Down
shootX = player.posX + 2
shootY = player.posY + 8
shootX = player.gameObject.baseRect.X + 2
shootY = player.gameObject.baseRect.Y + 8
break
case 3: // Left
shootY = player.posY + 3
shootX = player.posX - 2
shootY = player.gameObject.baseRect.Y + 3
shootX = player.gameObject.baseRect.X - 2
break
case 4: // Up-Right
shootY = player.posY - 1
shootX = player.posX + 5
shootY = player.gameObject.baseRect.Y
shootX = player.gameObject.baseRect.X + 5
break
case 5: // Up-Left
shootY = player.posY - 1
shootX = player.posX - 1
shootY = player.gameObject.baseRect.Y
shootX = player.gameObject.baseRect.X - 1
break
case 6: // Down-Right
shootY = player.posY + 5
shootX = player.posX + 5
shootY = player.gameObject.baseRect.Y + 5
shootX = player.gameObject.baseRect.X + 5
break
case 7: // Down-Left
shootY = player.posY + 5
shootX = player.posX - 1
shootY = player.gameObject.baseRect.Y + 5
shootX = player.gameObject.baseRect.X - 1
break
}
// Set bullet color
r, g, b, a := sdl.GetRGBA(player.color2, format)
bulletColor := color.RGBA{R: r, G: g, B: b, A: a}
bulletColor := player.playerColors.body
// Create and add the bullet
*bullets = append(*bullets, &Bullet{
posX: shootX,
posY: shootY,
direction: player.orientation,
direction: player.gameObject.orientation,
color: bulletColor,
super: super,
})
// Set cooldown and decrease energy
player.shootCooldown = ShootCooldown
if super {
player.energy -= SuperShotCost
player.ammunition = 0
} else {
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
var posX, posY int32
for !coordsAreValid {
posX = int32(16 + rand.Intn(int(gameMap.width-36)))
posY = int32(16 + rand.Intn(int(gameMap.height-36)))
maxTries := 1000
for !coordsAreValid && maxTries >= 0 {
maxTries--
posX = int32(16 + rand.Intn(int(gameMap.width-43)))
posY = int32(16 + rand.Intn(int(gameMap.height-43)))
coordsAreValid = true
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
coordsAreValid = false
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{
posX: posX,
posY: posY,
orientation: 3,
color1: color1,
color2: color2,
color3: color3,
health: MaxHealth,
playerColors: playerColors,
keyMap: keyMap,
joyMap: joyMap,
joyStick: joyStick,
shields: MaxShields,
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))
}
}