package main import ( "github.com/veandco/go-sdl2/sdl" "math/rand" "net" ) func getUnusedColor(colors []PlayerColors, players map[uint32]*Player) uint32 { for i, c := range colors { foundPlayerWithColor := false var colorCompare []uint8 clrR, clrG, clrB, clrA := c.tracks.RGBA() colorCompare = append(colorCompare, uint8(clrR), uint8(clrG), uint8(clrB), uint8(clrA)) clrR, clrG, clrB, clrA = c.body.RGBA() colorCompare = append(colorCompare, uint8(clrR), uint8(clrG), uint8(clrB), uint8(clrA)) clrR, clrG, clrB, clrA = c.cannon.RGBA() colorCompare = append(colorCompare, uint8(clrR), uint8(clrG), uint8(clrB), uint8(clrA)) playersMutex.RLock() for _, player := range players { var playerColorsCompare []uint8 plrR, plrG, plrB, plrA := player.playerColors.tracks.RGBA() playerColorsCompare = append(playerColorsCompare, uint8(plrR), uint8(plrG), uint8(plrB), uint8(plrA)) plrR, plrG, plrB, plrA = player.playerColors.body.RGBA() playerColorsCompare = append(playerColorsCompare, uint8(plrR), uint8(plrG), uint8(plrB), uint8(plrA)) plrR, plrG, plrB, plrA = player.playerColors.cannon.RGBA() playerColorsCompare = append(playerColorsCompare, uint8(plrR), uint8(plrG), uint8(plrB), uint8(plrA)) foundPlayerWithColor = false if len(playerColorsCompare) == len(colorCompare) { for i, c := range colorCompare { if playerColorsCompare[i] == c { foundPlayerWithColor = true } else { foundPlayerWithColor = false break } } if foundPlayerWithColor { break } } } playersMutex.RUnlock() if !foundPlayerWithColor { return uint32(i) } } return uint32(len(colors) - 1) } func createPlayers(amount uint8, playerColors []PlayerColors, keyMaps []KeyMap, joyMaps []JoyMap, gameMap *GameMap, players map[uint32]*Player, bases map[uint32]*Base) { joyStickCount := sdl.NumJoysticks() if amount > uint8(len(keyMaps)+len(joyMaps)) || amount > uint8(len(keyMaps)+joyStickCount) { panic("Too many players, not enough inputs") } if amount >= uint8(len(playerColors)) { panic("Too many players, not enough colors") } addedKeyboardPlayers := 0 for i := uint8(0); i < amount; i++ { var keyMap KeyMap var joyMap JoyMap var joyStick *sdl.Joystick if (config.DoAllKeymapsPlayers && i <= uint8(len(keyMaps))) || (config.DoKeymapPlayer && i == 0) || (uint8(joyStickCount) <= i) { keyMap = keyMaps[(addedKeyboardPlayers+int(config.KeyBindOffset))%(len(keyMaps)-1)] addedKeyboardPlayers++ } else { joyStickIndex := i - uint8(addedKeyboardPlayers) joyMap = joyMaps[joyStickIndex] joyStick = sdl.JoystickOpen(int(joyStickIndex)) } unusedColor := getUnusedColor(playerColors, players) createPlayer( true, playerColors[unusedColor], unusedColor, nil, keyMap, joyMap, joyStick, gameMap, players, bases, ) } } func closeThings(players map[uint32]*Player) { playersMutex.Lock() for _, player := range players { if player.joyStick != nil { player.joyStick.Close() } if player.window != nil { _ = player.window.Destroy() } if player.logicalSurface != nil { player.logicalSurface.Free() } if player.playSurface != nil { player.playSurface.Free() } if player.HUDSurface != nil { player.HUDSurface.Free() } } playersMutex.Unlock() } func createPlayer(local bool, thisPlayerColors PlayerColors, thisPlayerColorIndex uint32, conn *net.TCPConn, keyMap KeyMap, joyMap JoyMap, joyStick *sdl.Joystick, gameMap *GameMap, players map[uint32]*Player, bases map[uint32]*Base) uint32 { coordsAreValid := false var posX, posY uint32 maxTries := 1000 baseSize := uint32(36) // Since the base is 36x36 minDistance := uint32(200) // Minimum distance between bases maxDistance := uint32(500) // Maximum distance between bases for !coordsAreValid && maxTries >= 0 { maxTries-- posX = uint32(16 + rand.Intn(int(gameMap.width-baseSize-16))) posY = uint32(16 + rand.Intn(int(gameMap.height-baseSize-16))) coordsAreValid = true baseMutex.RLock() for _, base := range bases { basePosX := uint32(base.gameObject.baseRect.X) basePosY := uint32(base.gameObject.baseRect.Y) // Calculate the distance between the edges of the bases distanceX := max(0, max(basePosX-posX-baseSize, posX-basePosX-baseSize)) distanceY := max(0, max(basePosY-posY-baseSize, posY-basePosY-baseSize)) distanceSquared := distanceX*distanceX + distanceY*distanceY if distanceSquared < minDistance*minDistance && distanceSquared > maxDistance*maxDistance { coordsAreValid = false break } } baseMutex.RUnlock() // Edge clamping to ensure the base is within the map boundaries if posX < 16 || posX > gameMap.width-baseSize-16 || posY < 16 || posY > gameMap.height-baseSize-16 { coordsAreValid = false break } } if maxTries < 0 { panic("Could not place all players, increase map size") } gameObject := &GameObject{} gameObject.baseRect = &sdl.Rect{ X: int32(posX), Y: int32(posY), W: 7, H: 7, } gameObject.addColor(thisPlayerColors.tracks) gameObject.addColor(thisPlayerColors.body) gameObject.addColor(thisPlayerColors.cannon) gameObject.orientation = 0 // Up gameObject.addColoredRect(0, 1, 1, 6, 0) gameObject.addColoredRect(4, 1, 1, 6, 0) gameObject.addColoredRect(1, 2, 3, 4, 1) gameObject.addColoredRect(2, 0, 1, 4, 2) gameObject.orientation = 1 // Right gameObject.addColoredRect(0, 0, 6, 1, 0) gameObject.addColoredRect(0, 4, 6, 1, 0) gameObject.addColoredRect(1, 1, 4, 3, 1) gameObject.addColoredRect(3, 2, 4, 1, 2) gameObject.orientation = 2 // Down gameObject.addColoredRect(0, 0, 1, 6, 0) gameObject.addColoredRect(4, 0, 1, 6, 0) gameObject.addColoredRect(1, 1, 3, 4, 1) gameObject.addColoredRect(2, 3, 1, 4, 2) gameObject.orientation = 3 // Left gameObject.addColoredRect(1, 0, 6, 1, 0) gameObject.addColoredRect(1, 4, 6, 1, 0) gameObject.addColoredRect(2, 1, 4, 3, 1) gameObject.addColoredRect(0, 2, 4, 1, 2) gameObject.orientation = 4 // Up-Right gameObject.addColoredRect(3, 0, 1, 1, 0) gameObject.addColoredRect(2, 1, 1, 1, 0) gameObject.addColoredRect(1, 2, 1, 1, 0) gameObject.addColoredRect(0, 3, 1, 1, 0) gameObject.addColoredRect(6, 3, 1, 1, 0) gameObject.addColoredRect(5, 4, 1, 1, 0) gameObject.addColoredRect(4, 5, 1, 1, 0) gameObject.addColoredRect(3, 6, 1, 1, 0) gameObject.addColoredRect(3, 1, 1, 1, 1) gameObject.addColoredRect(2, 2, 2, 1, 1) gameObject.addColoredRect(1, 3, 2, 1, 1) gameObject.addColoredRect(4, 3, 2, 1, 1) gameObject.addColoredRect(2, 4, 3, 1, 1) gameObject.addColoredRect(3, 5, 1, 1, 1) gameObject.addColoredRect(5, 1, 1, 1, 2) gameObject.addColoredRect(4, 2, 1, 1, 2) gameObject.addColoredRect(3, 3, 1, 1, 2) // Up-Left orientation (Y-axis reflection) gameObject.orientation = 5 // Up-Left gameObject.addColoredRect(3, 0, 1, 1, 0) gameObject.addColoredRect(4, 1, 1, 1, 0) gameObject.addColoredRect(5, 2, 1, 1, 0) gameObject.addColoredRect(6, 3, 1, 1, 0) gameObject.addColoredRect(0, 3, 1, 1, 0) gameObject.addColoredRect(1, 4, 1, 1, 0) gameObject.addColoredRect(2, 5, 1, 1, 0) gameObject.addColoredRect(3, 6, 1, 1, 0) gameObject.addColoredRect(3, 1, 1, 1, 1) gameObject.addColoredRect(3, 2, 2, 1, 1) gameObject.addColoredRect(4, 3, 2, 1, 1) gameObject.addColoredRect(1, 3, 2, 1, 1) gameObject.addColoredRect(2, 4, 3, 1, 1) gameObject.addColoredRect(3, 5, 1, 1, 1) gameObject.addColoredRect(1, 1, 1, 1, 2) gameObject.addColoredRect(2, 2, 1, 1, 2) gameObject.addColoredRect(3, 3, 1, 1, 2) // Down-Right orientation (X-axis reflection) gameObject.orientation = 6 // Down-Right gameObject.addColoredRect(3, 6, 1, 1, 0) gameObject.addColoredRect(2, 5, 1, 1, 0) gameObject.addColoredRect(1, 4, 1, 1, 0) gameObject.addColoredRect(0, 3, 1, 1, 0) gameObject.addColoredRect(6, 3, 1, 1, 0) gameObject.addColoredRect(5, 2, 1, 1, 0) gameObject.addColoredRect(4, 1, 1, 1, 0) gameObject.addColoredRect(3, 0, 1, 1, 0) gameObject.addColoredRect(3, 5, 1, 1, 1) gameObject.addColoredRect(2, 4, 2, 1, 1) gameObject.addColoredRect(1, 3, 2, 1, 1) gameObject.addColoredRect(4, 3, 2, 1, 1) gameObject.addColoredRect(2, 2, 3, 1, 1) gameObject.addColoredRect(3, 1, 1, 1, 1) gameObject.addColoredRect(5, 5, 1, 1, 2) gameObject.addColoredRect(4, 4, 1, 1, 2) gameObject.addColoredRect(3, 3, 1, 1, 2) // Down-Left orientation (XY reflection) gameObject.orientation = 7 // Down-Left gameObject.addColoredRect(3, 6, 1, 1, 0) gameObject.addColoredRect(4, 5, 1, 1, 0) gameObject.addColoredRect(5, 4, 1, 1, 0) gameObject.addColoredRect(6, 3, 1, 1, 0) gameObject.addColoredRect(0, 3, 1, 1, 0) gameObject.addColoredRect(1, 2, 1, 1, 0) gameObject.addColoredRect(2, 1, 1, 1, 0) gameObject.addColoredRect(3, 0, 1, 1, 0) gameObject.addColoredRect(3, 1, 1, 1, 1) gameObject.addColoredRect(2, 2, 3, 1, 1) gameObject.addColoredRect(1, 3, 2, 1, 1) gameObject.addColoredRect(4, 3, 2, 1, 1) gameObject.addColoredRect(3, 4, 2, 1, 1) gameObject.addColoredRect(3, 5, 1, 1, 1) gameObject.addColoredRect(1, 5, 1, 1, 2) gameObject.addColoredRect(2, 4, 1, 1, 2) gameObject.addColoredRect(3, 3, 1, 1, 2) gameObject.orientation = 0 gameObject.adjustBaseRect() if !local && (keyMap.exit != keyMap.shoot || joyMap.exitButton != joyMap.shootButton) { panic("Input assigned to remote player") } knownGameMap := GameMap{width: gameMap.width, height: gameMap.height, tiles: make([][]uint8, serverConfig.MapWidth)} for i := uint32(0); i < serverConfig.MapWidth; i++ { knownGameMap.tiles[i] = make([]uint8, serverConfig.MapHeight) } playersMutex.Lock() players[lastPlayerID] = &Player{ playerColors: thisPlayerColors, playerColorID: thisPlayerColorIndex, keyMap: keyMap, joyMap: joyMap, joyStick: joyStick, shields: serverConfig.MaxShields, energy: serverConfig.MaxEnergy, gameObject: gameObject, local: local, connection: conn, playerID: lastPlayerID, knownBulletParticles: make(map[uint32]*BulletParticle), knownBases: make(map[uint32]*Base), knownPlayers: make(map[uint32]*Player), knownBullets: make(map[uint32]*Bullet), knownGameMap: &knownGameMap, } playersMutex.Unlock() lastPlayerID++ return lastPlayerID - 1 }