diff --git a/base.go b/base.go index cc39ae4..6017f78 100644 --- a/base.go +++ b/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 + 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 { - player.energy++ + 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 { + 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{ - rect: sdl.Rect{ - X: player.posX - 14, - Y: player.posY - 14, - W: 35, - H: 35, - }, - owner: (*players)[ownerID], + gameObject: gameObject, + owner: (*players)[ownerID], + openingWidth: openingWidth, }) (*bases)[ownerID].build(gameMap) } diff --git a/bullet.go b/bullet.go index 112472c..21ab0b6 100644 --- a/bullet.go +++ b/bullet.go @@ -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) diff --git a/gameobject.go b/gameobject.go new file mode 100644 index 0000000..964dd08 --- /dev/null +++ b/gameobject.go @@ -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) +} diff --git a/graphics.go b/graphics.go index ef5e309..216859c 100644 --- a/graphics.go +++ b/graphics.go @@ -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() } diff --git a/hud.go b/hud.go index 55fbf47..e340a65 100644 --- a/hud.go +++ b/hud.go @@ -2,77 +2,82 @@ package main import "github.com/veandco/go-sdl2/sdl" -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, - }, - } - - 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, - }, - } - - for _, rect := range eRects { - surface.FillRect(&rect, sdl.MapRGBA(surface.Format, 243, 235, 28, 255)) - } - 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)) +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) { + + for _, letterOffset := range letterOffsets { + surface.FillRect(&sdl.Rect{X: 16, Y: letterOffset - 1, W: 90, H: 7}, 0) + } + + HUDValues := []int32{ + mapRange(player.energy, 0, MaxEnergy, 0, 88), + mapRange(player.ammunition, 0, MaxAmmunition, 0, 88), + mapRange(player.shields, 0, MaxShields, 0, 88), + } + + for HUDValueIndex, HUDValue := range HUDValues { + surface.FillRect( + &sdl.Rect{ + X: 17, + Y: letterOffsets[HUDValueIndex], + W: HUDValue, + H: 5}, + letterColors[HUDValueIndex]) + } } diff --git a/main.go b/main.go index c12c292..163caca 100644 --- a/main.go +++ b/main.go @@ -5,15 +5,21 @@ import ( ) const ( - MapWidth = 1000 // Width of the map - MapHeight = 1000 // Height of the map - MaxEnergy = 1760 - MaxHealth = 88 - NormalShotCost = 3 - SuperShotCost = 20 - MovementCost = 1 - DiggingCost = 2 - ShootDiggingCostBonus = 1 + MapWidth = 1000 // Width of the map + MapHeight = 1000 // Height of the map + BlastRadius = 5 + + MaxEnergy = 3520 + MaxAmmunition = 6 + MaxShields = 100 + + NormalShotCost = 7 + SuperShotCost = 80 + ReloadCost = 4 + MovementCost = 1 + DiggingCost = 3 + ShootDiggingCostBonus = 1 + ShootCooldown = 8 RechargeCooldownOwn = 0 DiggingCooldown = 4 @@ -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) + } + + 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) + }) + + // Profile window adjustments + totalScalingTime += profileSection(func() { + adjustWindow(player.window, player.logicalSurface) + }) + + frameEnd := sdl.GetTicks64() + totalFrameTime += frameEnd - frameStart + frameCount++ + + // 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) } -} - -func handleInput(keyboard []uint8, bullets *[]*Bullet, player *Player, gameMap *GameMap, format *sdl.PixelFormat, players *[]*Player) bool { - shoot := false - super := false - - // Flags to track movement in each direction - moveUp := false - moveDown := false - moveLeft := false - moveRight := false - - // Process keyboard input - for key, value := range keyboard { - if value == 0 { - continue - } - - 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 - } - } - - // 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 } diff --git a/map.go b/map.go index 6cc7cf8..9202936 100644 --- a/map.go +++ b/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 { diff --git a/player.go b/player.go index e6b5483..8a100a9 100644 --- a/player.go +++ b/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, - energy: MaxEnergy, + playerColors: playerColors, + keyMap: keyMap, + joyMap: joyMap, + joyStick: joyStick, + shields: MaxShields, + energy: MaxEnergy, + gameObject: gameObject, + local: true, }) } diff --git a/playerData.go b/playerData.go new file mode 100644 index 0000000..e897bb4 --- /dev/null +++ b/playerData.go @@ -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 +} diff --git a/profiler.go b/profiler.go new file mode 100644 index 0000000..1e71fbb --- /dev/null +++ b/profiler.go @@ -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)) + } +}