529 lines
14 KiB
Go
529 lines
14 KiB
Go
|
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,
|
||
|
})
|
||
|
}
|