Init
This commit is contained in:
commit
352c7af7ce
8
.idea/.gitignore
vendored
Normal file
8
.idea/.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
7
.idea/discord.xml
Normal file
7
.idea/discord.xml
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DiscordProjectSettings">
|
||||
<option name="show" value="ASK" />
|
||||
<option name="description" value="" />
|
||||
</component>
|
||||
</project>
|
8
.idea/modules.xml
Normal file
8
.idea/modules.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/tunelar.iml" filepath="$PROJECT_DIR$/.idea/tunelar.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
9
.idea/tunelar.iml
Normal file
9
.idea/tunelar.iml
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="Go" enabled="true" />
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
6
.idea/vcs.xml
Normal file
6
.idea/vcs.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
144
base.go
Normal file
144
base.go
Normal file
@ -0,0 +1,144 @@
|
||||
package main
|
||||
|
||||
import "github.com/veandco/go-sdl2/sdl"
|
||||
|
||||
const (
|
||||
openingWidth = 7
|
||||
)
|
||||
|
||||
type Base struct {
|
||||
rect sdl.Rect
|
||||
owner *Player
|
||||
}
|
||||
|
||||
func (base *Base) tick(players *[]*Player) {
|
||||
for _, player := range *players {
|
||||
if player.cullingRect.HasIntersection(&base.rect) {
|
||||
if player.rechargeCooldown == 0 && player.energy < MaxEnergy {
|
||||
player.energy++
|
||||
if player == base.owner {
|
||||
player.rechargeCooldown = RechargeCooldownOwn
|
||||
} else {
|
||||
player.rechargeCooldown = RechargeCooldownOpponent
|
||||
}
|
||||
}
|
||||
if player == base.owner && player.repairCooldown == 0 && player.health < MaxHealth {
|
||||
player.health++
|
||||
player.repairCooldown = RepairCooldown
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (base *Base) build(gameMap *GameMap) {
|
||||
borderWidth := base.rect.W - openingWidth
|
||||
borderWidthA := borderWidth / 2
|
||||
if borderWidth < 0 {
|
||||
panic("Bad border width")
|
||||
}
|
||||
if base.rect.H < 9 {
|
||||
panic("Bad border height")
|
||||
}
|
||||
if gameMap.width-base.rect.X-base.rect.W <= 0 {
|
||||
panic("Bad base x location")
|
||||
}
|
||||
if gameMap.height-base.rect.Y-base.rect.H <= 0 {
|
||||
panic("Bad base y location")
|
||||
}
|
||||
if base.rect.X < 0 || base.rect.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++ {
|
||||
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 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.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
|
||||
}
|
||||
}
|
||||
|
||||
func createBases(players *[]*Player, gameMap *GameMap) *[]*Base {
|
||||
bases := &[]*Base{}
|
||||
for ownerID, player := range *players {
|
||||
*bases = append(*bases, &Base{
|
||||
rect: sdl.Rect{
|
||||
X: player.posX - 14,
|
||||
Y: player.posY - 14,
|
||||
W: 35,
|
||||
H: 35,
|
||||
},
|
||||
owner: (*players)[ownerID],
|
||||
})
|
||||
(*bases)[ownerID].build(gameMap)
|
||||
}
|
||||
return bases
|
||||
}
|
172
bullet.go
Normal file
172
bullet.go
Normal file
@ -0,0 +1,172 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/veandco/go-sdl2/sdl"
|
||||
"image/color"
|
||||
"math/rand"
|
||||
)
|
||||
|
||||
type Bullet struct {
|
||||
posX, posY int32
|
||||
direction uint8
|
||||
color color.Color
|
||||
super bool
|
||||
}
|
||||
|
||||
type BulletParticle struct {
|
||||
posX, posY int32
|
||||
expirationTimer uint8
|
||||
color color.Color
|
||||
}
|
||||
|
||||
func (bulletParticle *BulletParticle) render(camera *sdl.Rect, surface *sdl.Surface) {
|
||||
if camera.IntersectLine(&bulletParticle.posX, &bulletParticle.posY, &bulletParticle.posX, &bulletParticle.posY) {
|
||||
relativeBulletX := bulletParticle.posX - camera.X
|
||||
relativeBulletY := bulletParticle.posY - camera.Y
|
||||
surface.Set(int(relativeBulletX), int(relativeBulletY), bulletParticle.color)
|
||||
}
|
||||
}
|
||||
|
||||
func (bulletParticle *BulletParticle) tick(particles *[]*BulletParticle) {
|
||||
if bulletParticle.expirationTimer <= 0 {
|
||||
for i, particle := range *particles {
|
||||
if particle == bulletParticle {
|
||||
*particles = append((*particles)[:i], (*particles)[i+1:]...)
|
||||
}
|
||||
}
|
||||
}
|
||||
bulletParticle.expirationTimer--
|
||||
}
|
||||
|
||||
func (bullet *Bullet) render(camera *sdl.Rect, surface *sdl.Surface) {
|
||||
if camera.IntersectLine(&bullet.posX, &bullet.posY, &bullet.posX, &bullet.posY) {
|
||||
relativeBulletX := bullet.posX - camera.X
|
||||
relativeBulletY := bullet.posY - camera.Y
|
||||
surface.Set(int(relativeBulletX), int(relativeBulletY), bullet.color)
|
||||
}
|
||||
}
|
||||
|
||||
func (bullet *Bullet) explode(gameMap *GameMap, bulletParticleMap *[]*BulletParticle) {
|
||||
// Define the possible directions with their x and y velocity components.
|
||||
directions := [][2]int{
|
||||
{0, -1}, // Up
|
||||
{0, 1}, // Down
|
||||
{-1, 0}, // Left
|
||||
{1, 0}, // Right
|
||||
{-1, -1}, // Up-Left Diagonal
|
||||
{1, -1}, // Up-Right Diagonal
|
||||
{-1, 1}, // Down-Left Diagonal
|
||||
{1, 1}, // Down-Right Diagonal
|
||||
}
|
||||
|
||||
if !bullet.super {
|
||||
// Loop through each direction to generate particle streams.
|
||||
for _, dir := range directions {
|
||||
// Randomize the number of particles in each stream.
|
||||
particlesInStream := rand.Intn(5)
|
||||
for i := 0; i < particlesInStream; i++ {
|
||||
// Determine particle's position based on its direction and step count.
|
||||
step := rand.Intn(3) + 1 // Step can be 1, 2, or 3 units.
|
||||
xOffset := dir[0] * step
|
||||
yOffset := dir[1] * step
|
||||
|
||||
xPos, yPos := bullet.posX+int32(xOffset), bullet.posY+int32(yOffset)
|
||||
collision := gameMap.checkCollision(xPos, yPos)
|
||||
|
||||
// Check for collision and bullet behavior (normal or super).
|
||||
if collision == 1 || collision == 0 || (bullet.super && collision == 2) {
|
||||
*bulletParticleMap = append(*bulletParticleMap, &BulletParticle{
|
||||
posX: xPos,
|
||||
posY: yPos,
|
||||
expirationTimer: uint8(5 + rand.Intn(10)), // Randomize expiration time between 15 and 25.
|
||||
color: bullet.color,
|
||||
})
|
||||
if xPos > 0 && yPos > 0 && xPos < MapWidth && yPos < MapHeight {
|
||||
gameMap.tiles[xPos][yPos] = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for xOffset := -3; xOffset <= 3; xOffset++ {
|
||||
for yOffset := -3; yOffset <= 3; yOffset++ {
|
||||
xPos, yPos := bullet.posX+int32(xOffset), bullet.posY+int32(yOffset)
|
||||
collision := gameMap.checkCollision(xPos, yPos)
|
||||
if collision == 1 || collision == 0 || (bullet.super && collision == 2) {
|
||||
*bulletParticleMap = append(*bulletParticleMap, &BulletParticle{
|
||||
posX: xPos,
|
||||
posY: yPos,
|
||||
expirationTimer: uint8(15 + rand.Intn(15)),
|
||||
color: bullet.color,
|
||||
})
|
||||
gameMap.tiles[xPos][yPos] = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (bullet *Bullet) tick(gameMap *GameMap,
|
||||
bulletParticleMap *[]*BulletParticle, bulletMap *[]*Bullet, players *[]*Player) {
|
||||
var nextX, nextY int32
|
||||
nextX, nextY = bullet.posX, bullet.posY
|
||||
switch bullet.direction {
|
||||
case 0: //up
|
||||
nextY -= 1
|
||||
break
|
||||
case 1: //right
|
||||
nextX += 1
|
||||
break
|
||||
case 2: //down
|
||||
nextY += 1
|
||||
break
|
||||
case 3: //left
|
||||
nextX -= 1
|
||||
break
|
||||
case 4: //up right
|
||||
nextY -= 1
|
||||
nextX += 1
|
||||
break
|
||||
case 5: //up left
|
||||
nextY -= 1
|
||||
nextX -= 1
|
||||
break
|
||||
case 6: //down right
|
||||
nextY += 1
|
||||
nextX += 1
|
||||
break
|
||||
case 7: //down left
|
||||
nextY += 1
|
||||
nextX -= 1
|
||||
break
|
||||
}
|
||||
collisionResult := gameMap.checkCollision(nextX, nextY)
|
||||
bulletRect := sdl.Rect{
|
||||
X: bullet.posX,
|
||||
Y: bullet.posY,
|
||||
W: 1,
|
||||
H: 1,
|
||||
}
|
||||
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
|
||||
break
|
||||
}
|
||||
}
|
||||
if collisionResult != 0 || hitPlayer {
|
||||
bullet.explode(gameMap, bulletParticleMap)
|
||||
for i, bulletInMap := range *bulletMap {
|
||||
if bulletInMap == bullet {
|
||||
*bulletMap = append((*bulletMap)[:i], (*bulletMap)[i+1:]...)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
bullet.posX = nextX
|
||||
bullet.posY = nextY
|
||||
}
|
||||
}
|
8
go.mod
Normal file
8
go.mod
Normal file
@ -0,0 +1,8 @@
|
||||
module goingtunneling
|
||||
|
||||
go 1.21
|
||||
|
||||
require (
|
||||
github.com/aquilax/go-perlin v1.1.0
|
||||
github.com/veandco/go-sdl2 v0.4.40
|
||||
)
|
4
go.sum
Normal file
4
go.sum
Normal file
@ -0,0 +1,4 @@
|
||||
github.com/aquilax/go-perlin v1.1.0 h1:Gg+3jQ24wT4Y5GI7TCRLmYarzUG0k+n/JATFqOimb7s=
|
||||
github.com/aquilax/go-perlin v1.1.0/go.mod h1:z9Rl7EM4BZY0Ikp2fEN1I5mKSOJ26HQpk0O2TBdN2HE=
|
||||
github.com/veandco/go-sdl2 v0.4.40 h1:fZv6wC3zz1Xt167P09gazawnpa0KY5LM7JAvKpX9d/U=
|
||||
github.com/veandco/go-sdl2 v0.4.40/go.mod h1:OROqMhHD43nT4/i9crJukyVecjPNYYuCofep6SNiAjY=
|
156
graphics.go
Normal file
156
graphics.go
Normal file
@ -0,0 +1,156 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/veandco/go-sdl2/sdl"
|
||||
)
|
||||
|
||||
func initializeSDL() {
|
||||
if err := sdl.Init(sdl.INIT_EVERYTHING); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func setupWindowAndSurface() (*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)
|
||||
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 setupPlaySurface() (*sdl.Surface, *sdl.Rect, *sdl.Rect) {
|
||||
playSurface, err := sdl.CreateRGBSurface(0, 76, 76, 32, 0, 0, 0, 0)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
playSurfaceRect := &sdl.Rect{X: 0, Y: 0, W: 76, H: 76}
|
||||
playSurfaceTargetRect := &sdl.Rect{X: 42, Y: 0, W: 76, H: 76}
|
||||
|
||||
return playSurface, playSurfaceRect, playSurfaceTargetRect
|
||||
}
|
||||
|
||||
func setupHUDSurface() (*sdl.Surface, *sdl.Rect, *sdl.Rect) {
|
||||
HUDSurface, err := sdl.CreateRGBSurface(0, 112, 25, 32, 0, 0, 0, 0)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
HUDSurfaceRect := &sdl.Rect{X: 0, Y: 0, W: 112, H: 25}
|
||||
HUDSurfaceTargetRect := &sdl.Rect{X: 24, Y: 76, W: 112, H: 25}
|
||||
|
||||
return HUDSurface, HUDSurfaceRect, HUDSurfaceTargetRect
|
||||
}
|
||||
|
||||
func handleEvents(window *sdl.Window, logicalSurface *sdl.Surface) bool {
|
||||
for event := sdl.PollEvent(); event != nil; event = sdl.PollEvent() {
|
||||
switch e := event.(type) {
|
||||
case *sdl.QuitEvent:
|
||||
return false
|
||||
case *sdl.WindowEvent:
|
||||
if e.Event == sdl.WINDOWEVENT_RESIZED {
|
||||
handleWindowResize(window, logicalSurface)
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func handleWindowResize(window *sdl.Window, logicalSurface *sdl.Surface) {
|
||||
windowSurface, err := window.GetSurface()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
windowWidth, windowHeight := windowSurface.W, windowSurface.H
|
||||
|
||||
aspectRatio := float64(logicalSurface.W) / float64(logicalSurface.H)
|
||||
newWidth := windowWidth
|
||||
newHeight := int32(float64(windowWidth) / aspectRatio)
|
||||
|
||||
if newHeight > windowHeight {
|
||||
newHeight = windowHeight
|
||||
newWidth = int32(float64(windowHeight) * aspectRatio)
|
||||
}
|
||||
|
||||
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}
|
||||
|
||||
logicalSurface.BlitScaled(srcRect, windowSurface, dstRect)
|
||||
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) {
|
||||
windowSurface, err := window.GetSurface()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
windowWidth, windowHeight := windowSurface.W, windowSurface.H
|
||||
aspectRatio := float64(logicalSurface.W) / float64(logicalSurface.H)
|
||||
newWidth := windowWidth
|
||||
newHeight := int32(float64(windowWidth) / aspectRatio)
|
||||
|
||||
if newHeight > windowHeight {
|
||||
newHeight = windowHeight
|
||||
newWidth = int32(float64(windowHeight) * aspectRatio)
|
||||
}
|
||||
|
||||
letterboxX := (windowWidth - newWidth) / 2
|
||||
letterboxY := (windowHeight - newHeight) / 2
|
||||
|
||||
windowSurface.FillRect(nil, pixelBG)
|
||||
srcRect := &sdl.Rect{X: 0, Y: 0, W: logicalSurface.W, H: logicalSurface.H}
|
||||
dstRect := &sdl.Rect{X: letterboxX, Y: letterboxY, W: newWidth, H: newHeight}
|
||||
|
||||
logicalSurface.BlitScaled(srcRect, windowSurface, dstRect)
|
||||
window.UpdateSurface()
|
||||
}
|
78
hud.go
Normal file
78
hud.go
Normal file
@ -0,0 +1,78 @@
|
||||
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))
|
||||
}
|
158
main.go
Normal file
158
main.go
Normal file
@ -0,0 +1,158 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/veandco/go-sdl2/sdl"
|
||||
)
|
||||
|
||||
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
|
||||
ShootCooldown = 8
|
||||
RechargeCooldownOwn = 0
|
||||
DiggingCooldown = 4
|
||||
RechargeCooldownOpponent = 6
|
||||
RepairCooldown = 4
|
||||
MovementCooldown = 2
|
||||
MovementCooldownNoEnergy = 4
|
||||
DiggingCooldownNoEnergy = 8
|
||||
)
|
||||
|
||||
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.createGameMap(MapWidth, MapHeight)
|
||||
|
||||
players := &[]*Player{}
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
bulletMap := &[]*Bullet{}
|
||||
bulletParticleMap := &[]*BulletParticle{}
|
||||
|
||||
camera := sdl.Rect{X: 0, Y: 0, W: 76, H: 76}
|
||||
|
||||
running := true
|
||||
for running {
|
||||
currentTime := sdl.GetTicks64()
|
||||
|
||||
running = handleEvents(window, logicalSurface)
|
||||
|
||||
keyboard := sdl.GetKeyboardState()
|
||||
|
||||
running = running && handleInput(keyboard, bulletMap, (*players)[0], &gameMap, playSurface.Format, players)
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
116
map.go
Normal file
116
map.go
Normal file
@ -0,0 +1,116 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/aquilax/go-perlin"
|
||||
"github.com/veandco/go-sdl2/sdl"
|
||||
"image/color"
|
||||
"time"
|
||||
)
|
||||
|
||||
type GameMap struct {
|
||||
tiles [][]uint8
|
||||
//tile 0 - air
|
||||
//tile 1 - rock
|
||||
//tile 2 - soil 1
|
||||
//tile 3 - soil 2
|
||||
//tile 4 - base
|
||||
//tile 5 - world edge (should not be present in the map object)
|
||||
width int32
|
||||
height int32
|
||||
}
|
||||
|
||||
func GetTileColorValue(value uint8) color.Color {
|
||||
switch value {
|
||||
case 0:
|
||||
return color.RGBA{}
|
||||
case 1:
|
||||
return color.RGBA{R: 154, G: 154, B: 154, A: 255}
|
||||
case 2:
|
||||
return color.RGBA{R: 195, G: 121, B: 48, A: 255}
|
||||
case 3:
|
||||
return color.RGBA{R: 186, G: 89, B: 4, A: 255}
|
||||
case 4:
|
||||
return color.RGBA{R: 255, G: 44, B: 255, A: 255}
|
||||
case 5:
|
||||
return color.RGBA{R: 86, G: 86, B: 86, A: 255}
|
||||
default:
|
||||
return color.RGBA{R: 255, G: 255, B: 255, A: 255}
|
||||
}
|
||||
}
|
||||
|
||||
func (gameMap *GameMap) getTileColor(x int32, y int32) color.Color {
|
||||
if x < 0 || x >= gameMap.width || y < 0 || y >= gameMap.height {
|
||||
return GetTileColorValue(5)
|
||||
} else {
|
||||
tileValue := gameMap.tiles[x][y]
|
||||
return GetTileColorValue(tileValue)
|
||||
}
|
||||
}
|
||||
|
||||
func (gameMap *GameMap) createGameMap(width int32, height int32) {
|
||||
gameMap.tiles = make([][]uint8, width)
|
||||
perlinNoiseRock := perlin.NewPerlin(2, 2, 4, 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)
|
||||
if perlinRock > 0.2 {
|
||||
gameMap.tiles[i][j] = 1
|
||||
} else {
|
||||
perlinSoil := perlinNoiseSoil.Noise2D(float64(i)/float64(width)*10, float64(j)/float64(height)*10)
|
||||
if perlinSoil >= 0 {
|
||||
gameMap.tiles[i][j] = 2
|
||||
} else {
|
||||
gameMap.tiles[i][j] = 3
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
gameMap.width = width
|
||||
gameMap.height = height
|
||||
}
|
||||
|
||||
func (gameMap *GameMap) render(camera *sdl.Rect, surface *sdl.Surface) {
|
||||
fromX := camera.X
|
||||
fromY := camera.Y
|
||||
toX := camera.X + camera.W
|
||||
toY := camera.Y + camera.H
|
||||
for x := fromX; x < toX; x++ {
|
||||
for y := fromY; y < toY; y++ {
|
||||
cameraY := y - camera.Y
|
||||
cameraX := x - camera.X
|
||||
pixelColor := gameMap.getTileColor(x, y)
|
||||
surface.Set(int(cameraX), int(cameraY), pixelColor)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (gameMap *GameMap) checkCollision(posX, posY int32) uint8 {
|
||||
//0 no collision
|
||||
//1 destructible collision
|
||||
//2 rock collision
|
||||
//3 indestructible collision
|
||||
if posX >= gameMap.width || posX < 0 || posY >= gameMap.height || posY < 0 {
|
||||
return 3
|
||||
}
|
||||
switch gameMap.tiles[posX][posY] {
|
||||
case 0:
|
||||
return 0
|
||||
case 1:
|
||||
return 2
|
||||
case 2:
|
||||
return 1
|
||||
case 3:
|
||||
return 1
|
||||
case 4:
|
||||
return 3
|
||||
case 5:
|
||||
return 3
|
||||
default:
|
||||
return 3
|
||||
}
|
||||
|
||||
}
|
528
player.go
Normal file
528
player.go
Normal file
@ -0,0 +1,528 @@
|
||||
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,
|
||||
})
|
||||
}
|
Loading…
Reference in New Issue
Block a user