GOingTunneling/player.go

529 lines
14 KiB
Go
Raw Normal View History

2024-08-29 00:05:28 +02:00
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,
})
}