package main import ( "github.com/veandco/go-sdl2/sdl" "image/color" "math/rand" ) type Player struct { posX int32 posY int32 orientation uint8 color1 uint32 color2 uint32 color3 uint32 energy uint16 health 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 } func (player *Player) track(camera *sdl.Rect) { camera.X = player.posX - 37 camera.Y = player.posY - 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) } } func (player *Player) digBlock(posX, posY int32, gameMap *GameMap, isShooting bool) uint8 { collisionAtDigSite := gameMap.checkCollision(posX, posY) if collisionAtDigSite == 0 { return 0 } else if collisionAtDigSite == 1 && (player.digCooldown == 0 || isShooting) { gameMap.tiles[posX][posY] = 0 player.digCooldown = DiggingCooldown return 1 } return 2 } func (player *Player) tick() { if player.digCooldown > 0 { player.digCooldown-- } if player.shootCooldown > 0 { player.shootCooldown-- } if player.repairCooldown > 0 { player.repairCooldown-- } if player.rechargeCooldown > 0 { player.rechargeCooldown-- } if player.movementCooldown > 0 { player.movementCooldown-- } } func (player *Player) tryMove(gameMap *GameMap, isShooting bool, players *[]*Player) (moved bool) { if player.movementCooldown > 0 { return false } ranOutOfEnergy := (isShooting && player.energy <= DiggingCost+ShootDiggingCostBonus) || player.energy <= DiggingCost if ranOutOfEnergy { isShooting = false } shouldPenalizeDigging := false stopped := false switch player.orientation { case 0: // Up collisionRect := sdl.Rect{ X: player.posX, Y: player.posY - 1, W: 5, H: 7, } for _, opponent := range *players { if opponent != player && opponent.cullingRect.HasIntersection(&collisionRect) { return false } } for x := player.posX; x < player.posX+5 && !stopped; x++ { for y := player.posY - 1; y < player.posY+7; y++ { digResult := player.digBlock(x, y, gameMap, isShooting) if digResult == 1 { shouldPenalizeDigging = true if !isShooting { stopped = true break } } else if digResult != 0 { stopped = true break } } } if !stopped { moved = true player.posY-- } case 1: // Right collisionRect := sdl.Rect{ X: player.posX + 1, Y: player.posY, W: 7, H: 5, } for _, opponent := range *players { if opponent != player && opponent.cullingRect.HasIntersection(&collisionRect) { return false } } for y := player.posY; y < player.posY+7 && !stopped; y++ { for x := player.posX + 1; x < player.posX+8; x++ { digResult := player.digBlock(x, y, gameMap, isShooting) if digResult == 1 { shouldPenalizeDigging = true if !isShooting { stopped = true break } } else if digResult != 0 { stopped = true break } } } if !stopped { moved = true player.posX++ } case 2: // Down collisionRect := sdl.Rect{ X: player.posX, Y: player.posY + 1, W: 5, H: 7, } for _, opponent := range *players { if opponent != player && opponent.cullingRect.HasIntersection(&collisionRect) { return false } } for x := player.posX; x < player.posX+5 && !stopped; x++ { for y := player.posY + 1; y < player.posY+8; y++ { digResult := player.digBlock(x, y, gameMap, isShooting) if digResult == 1 { shouldPenalizeDigging = true if !isShooting { stopped = true break } } else if digResult != 0 { stopped = true break } } } if !stopped { moved = true player.posY++ } case 3: // Left collisionRect := sdl.Rect{ X: player.posX - 1, Y: player.posY, W: 7, H: 5, } for _, opponent := range *players { if opponent != player && opponent.cullingRect.HasIntersection(&collisionRect) { return false } } for y := player.posY; y < player.posY+6 && !stopped; y++ { for x := player.posX - 1; x < player.posX+6; x++ { digResult := player.digBlock(x, y, gameMap, isShooting) if digResult == 1 { shouldPenalizeDigging = true if !isShooting { stopped = true break } } else if digResult != 0 { stopped = true break } } } if !stopped { moved = true player.posX-- } case 4: // Up-Right collisionRect := sdl.Rect{ X: player.posX + 1, Y: player.posY - 1, W: 7, H: 7, } for _, opponent := range *players { if opponent != player && opponent.cullingRect.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++ { digResult := player.digBlock(x, y, gameMap, isShooting) if digResult == 1 { shouldPenalizeDigging = true if !isShooting { stopped = true break } } else if digResult != 0 { stopped = true break } } } if !stopped { moved = true player.posX++ player.posY-- } case 5: // Up-Left collisionRect := sdl.Rect{ X: player.posX - 1, Y: player.posY - 1, W: 7, H: 7, } for _, opponent := range *players { if opponent != player && opponent.cullingRect.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++ { digResult := player.digBlock(x, y, gameMap, isShooting) if digResult == 1 { shouldPenalizeDigging = true if !isShooting { stopped = true break } } else if digResult != 0 { stopped = true break } } } if !stopped { moved = true player.posX-- player.posY-- } case 6: // Down-Right collisionRect := sdl.Rect{ X: player.posX + 1, Y: player.posY + 1, W: 7, H: 7, } for _, opponent := range *players { if opponent != player && opponent.cullingRect.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++ { digResult := player.digBlock(x, y, gameMap, isShooting) if digResult == 1 { shouldPenalizeDigging = true if !isShooting { stopped = true break } } else if digResult != 0 { stopped = true break } } } if !stopped { moved = true player.posX++ player.posY++ } case 7: // Down-Left collisionRect := sdl.Rect{ X: player.posX - 1, Y: player.posY + 1, W: 7, H: 7, } for _, opponent := range *players { if opponent != player && opponent.cullingRect.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++ { digResult := player.digBlock(x, y, gameMap, isShooting) if digResult == 1 { shouldPenalizeDigging = true if !isShooting { stopped = true break } } else if digResult != 0 { stopped = true break } } } if !stopped { moved = true player.posX-- player.posY++ } } // Penalties and cooldown handling if shouldPenalizeDigging { if ranOutOfEnergy { player.digCooldown = DiggingCooldownNoEnergy } player.energy -= DiggingCost if isShooting { player.energy -= ShootDiggingCostBonus } } if moved { if ranOutOfEnergy { player.movementCooldown = MovementCooldownNoEnergy } else { player.movementCooldown = MovementCooldown } } return } func (player *Player) shoot(super bool, bullets *[]*Bullet, format *sdl.PixelFormat) { if (super && player.energy <= SuperShotCost) || (!super && player.energy <= NormalShotCost) { return } if player.shootCooldown == 0 { var shootX, shootY int32 switch player.orientation { case 0: // Up shootY = player.posY - 1 shootX = player.posX + 2 break case 1: // Right shootY = player.posY + 3 shootX = player.posX + 8 break case 2: // Down shootX = player.posX + 2 shootY = player.posY + 8 break case 3: // Left shootY = player.posY + 3 shootX = player.posX - 2 break case 4: // Up-Right shootY = player.posY - 1 shootX = player.posX + 5 break case 5: // Up-Left shootY = player.posY - 1 shootX = player.posX - 1 break case 6: // Down-Right shootY = player.posY + 5 shootX = player.posX + 5 break case 7: // Down-Left shootY = player.posY + 5 shootX = player.posX - 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} // Create and add the bullet *bullets = append(*bullets, &Bullet{ posX: shootX, posY: shootY, direction: player.orientation, color: bulletColor, super: super, }) // Set cooldown and decrease energy player.shootCooldown = ShootCooldown if super { player.energy -= SuperShotCost } else { player.energy -= NormalShotCost } } } func createPlayer(color1 uint32, color2 uint32, color3 uint32, 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))) coordsAreValid = true for _, player := range *players { distance := (player.posX-posX)*(player.posX-posX) + (player.posY-posY)*(player.posY-posY) if distance < 300*300 { // Check if distance is less than 300 units coordsAreValid = false break } } } *players = append(*players, &Player{ posX: posX, posY: posY, orientation: 3, color1: color1, color2: color2, color3: color3, health: MaxHealth, energy: MaxEnergy, }) }