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