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