2024-08-29 00:05:28 +02:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"github.com/veandco/go-sdl2/sdl"
|
|
|
|
"image/color"
|
|
|
|
"math/rand"
|
2024-08-31 15:35:19 +02:00
|
|
|
"sync"
|
2024-08-29 00:05:28 +02:00
|
|
|
)
|
|
|
|
|
2024-08-30 19:07:56 +02:00
|
|
|
var bulletLastID = uint32(0)
|
|
|
|
var bulletParticleLastID = uint32(0)
|
|
|
|
|
2024-08-31 15:35:19 +02:00
|
|
|
var bulletMutex sync.RWMutex
|
|
|
|
var bulletParticleMutex sync.RWMutex
|
|
|
|
|
2024-08-29 00:05:28 +02:00
|
|
|
type Bullet struct {
|
|
|
|
posX, posY int32
|
|
|
|
direction uint8
|
|
|
|
color color.Color
|
|
|
|
super bool
|
2024-08-30 21:24:32 +02:00
|
|
|
ownerID uint32
|
2024-08-30 19:07:56 +02:00
|
|
|
id uint32
|
2024-08-29 00:05:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
type BulletParticle struct {
|
|
|
|
posX, posY int32
|
2024-08-30 19:07:56 +02:00
|
|
|
expirationTimer uint32
|
2024-08-29 00:05:28 +02:00
|
|
|
color color.Color
|
2024-08-30 19:07:56 +02:00
|
|
|
id uint32
|
2024-08-29 00:05:28 +02:00
|
|
|
}
|
|
|
|
|
2024-08-30 19:07:56 +02:00
|
|
|
func (bulletParticle *BulletParticle) render(camera *sdl.Rect, surface *sdl.Surface, bulletParticles map[uint32]*BulletParticle) {
|
2024-08-29 00:05:28 +02:00
|
|
|
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)
|
2024-08-30 19:07:56 +02:00
|
|
|
} else if !config.Server {
|
|
|
|
delete(bulletParticles, bulletParticle.id)
|
2024-08-29 00:05:28 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-30 19:07:56 +02:00
|
|
|
func (bulletParticle *BulletParticle) tick(particles map[uint32]*BulletParticle) {
|
2024-08-29 00:05:28 +02:00
|
|
|
if bulletParticle.expirationTimer <= 0 {
|
2024-08-30 19:07:56 +02:00
|
|
|
delete(particles, bulletParticle.id)
|
|
|
|
} else {
|
|
|
|
bulletParticle.expirationTimer--
|
2024-08-29 00:05:28 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-30 19:07:56 +02:00
|
|
|
func (bullet *Bullet) render(camera *sdl.Rect, surface *sdl.Surface, bullets map[uint32]*Bullet) {
|
2024-08-29 00:05:28 +02:00
|
|
|
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)
|
2024-08-30 19:07:56 +02:00
|
|
|
} else if !config.Server {
|
|
|
|
delete(bullets, bullet.id)
|
2024-08-29 00:05:28 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-30 19:07:56 +02:00
|
|
|
func (bullet *Bullet) explode(gameMap *GameMap, bulletParticleMap map[uint32]*BulletParticle) {
|
2024-08-29 00:05:28 +02:00
|
|
|
// 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) {
|
2024-08-30 19:07:56 +02:00
|
|
|
bulletParticleMap[bulletParticleLastID] = &BulletParticle{
|
2024-08-29 00:05:28 +02:00
|
|
|
posX: xPos,
|
|
|
|
posY: yPos,
|
2024-08-30 19:07:56 +02:00
|
|
|
expirationTimer: uint32(5 + rand.Intn(10)), // Randomize expiration time between 15 and 25.
|
2024-08-29 00:05:28 +02:00
|
|
|
color: bullet.color,
|
2024-08-30 19:07:56 +02:00
|
|
|
id: bulletParticleLastID,
|
|
|
|
}
|
|
|
|
bulletParticleLastID++
|
|
|
|
if xPos > 0 && yPos > 0 && uint32(xPos) < serverConfig.MapWidth && uint32(yPos) < serverConfig.MapHeight {
|
2024-08-29 00:05:28 +02:00
|
|
|
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) {
|
2024-08-30 19:07:56 +02:00
|
|
|
bulletParticleMap[bulletParticleLastID] = &BulletParticle{
|
2024-08-29 00:05:28 +02:00
|
|
|
posX: xPos,
|
|
|
|
posY: yPos,
|
2024-08-30 19:07:56 +02:00
|
|
|
expirationTimer: uint32(15 + rand.Intn(15)),
|
2024-08-29 00:05:28 +02:00
|
|
|
color: bullet.color,
|
2024-08-30 19:07:56 +02:00
|
|
|
id: bulletParticleLastID,
|
|
|
|
}
|
|
|
|
bulletParticleLastID++
|
2024-08-29 00:05:28 +02:00
|
|
|
gameMap.tiles[xPos][yPos] = 0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (bullet *Bullet) tick(gameMap *GameMap,
|
2024-08-30 19:07:56 +02:00
|
|
|
bulletParticleMap map[uint32]*BulletParticle, bulletMap map[uint32]*Bullet, players map[uint32]*Player) {
|
2024-08-29 00:05:28 +02:00
|
|
|
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
|
2024-08-31 15:35:19 +02:00
|
|
|
playersMutex.RLock()
|
2024-08-30 19:07:56 +02:00
|
|
|
for _, player := range players {
|
2024-08-30 21:24:32 +02:00
|
|
|
if player.playerID == bullet.ownerID {
|
|
|
|
continue
|
|
|
|
}
|
2024-08-29 18:48:27 +02:00
|
|
|
if hitPlayer {
|
2024-08-29 00:05:28 +02:00
|
|
|
break
|
|
|
|
}
|
2024-08-29 18:48:27 +02:00
|
|
|
for _, coloredRect := range player.gameObject.getCurrentRects() {
|
2024-08-30 19:07:56 +02:00
|
|
|
if player.gameObject.adjustRectWorld(coloredRect.rect).HasIntersection(&bulletRect) {
|
|
|
|
hitPlayer = player.shields <= serverConfig.MaxShields
|
2024-08-29 18:48:27 +02:00
|
|
|
player.shields -= 10
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2024-08-29 00:05:28 +02:00
|
|
|
}
|
2024-08-31 15:35:19 +02:00
|
|
|
playersMutex.RUnlock()
|
2024-08-29 00:05:28 +02:00
|
|
|
if collisionResult != 0 || hitPlayer {
|
2024-09-01 20:13:53 +02:00
|
|
|
if !config.Client {
|
|
|
|
bullet.explode(gameMap, bulletParticleMap)
|
|
|
|
}
|
2024-08-30 19:07:56 +02:00
|
|
|
delete(bulletMap, bullet.id)
|
2024-08-29 00:05:28 +02:00
|
|
|
} else {
|
|
|
|
bullet.posX = nextX
|
|
|
|
bullet.posY = nextY
|
|
|
|
}
|
|
|
|
}
|