2018-05-29 19:01:22 +00:00
package com.unciv
2018-07-20 12:58:03 +00:00
import com.badlogic.gdx.math.Vector2
2018-05-29 19:01:22 +00:00
import com.unciv.logic.GameInfo
import com.unciv.logic.civilization.CivilizationInfo
2018-12-18 16:57:13 +00:00
import com.unciv.logic.civilization.PlayerType
2019-04-14 19:13:55 +00:00
import com.unciv.logic.map.BFS
2019-01-04 08:59:47 +00:00
import com.unciv.logic.map.MapType
2018-11-26 17:50:58 +00:00
import com.unciv.logic.map.TileInfo
2018-05-29 19:01:22 +00:00
import com.unciv.logic.map.TileMap
import com.unciv.models.gamebasics.GameBasics
2018-11-26 17:50:58 +00:00
import java.util.*
2019-04-14 19:12:54 +00:00
import kotlin.collections.ArrayList
2018-05-29 19:01:22 +00:00
2019-01-04 08:59:47 +00:00
class GameParameters {
var difficulty = " Prince "
var mapRadius = 20
2019-01-14 18:48:30 +00:00
var numberOfHumanPlayers = 1
var humanNations = ArrayList < String > ( ) . apply { add ( " Babylon " ) }
2019-01-04 08:59:47 +00:00
var numberOfEnemies = 3
var mapType = MapType . Perlin
2019-04-13 19:49:17 +00:00
var noBarbarians = false
2019-02-02 21:26:56 +00:00
var mapFileName : String ? = null
2019-01-04 08:59:47 +00:00
}
2018-12-04 21:21:57 +00:00
class GameStarter {
2019-01-04 08:59:47 +00:00
fun startNewGame ( newGameParameters : GameParameters ) : GameInfo {
2018-05-29 19:01:22 +00:00
val gameInfo = GameInfo ( )
2019-01-04 08:59:47 +00:00
gameInfo . gameParameters = newGameParameters
2019-02-02 21:26:56 +00:00
gameInfo . tileMap = TileMap ( newGameParameters )
2018-05-31 14:23:46 +00:00
gameInfo . tileMap . gameInfo = gameInfo // need to set this transient before placing units in the map
2019-01-10 19:29:24 +00:00
val startingLocations = getStartingLocations (
2019-01-14 18:48:30 +00:00
newGameParameters . numberOfEnemies + newGameParameters . numberOfHumanPlayers , gameInfo . tileMap )
2018-07-18 15:45:37 +00:00
2019-01-11 07:58:32 +00:00
val availableCivNames = Stack < String > ( )
availableCivNames . addAll ( GameBasics . Nations . keys . shuffled ( ) )
2019-01-14 18:48:30 +00:00
availableCivNames . removeAll ( newGameParameters . humanNations )
2019-01-15 20:17:34 +00:00
availableCivNames . remove ( " Barbarians " )
2018-05-29 19:01:22 +00:00
2019-01-14 18:48:30 +00:00
for ( nation in newGameParameters . humanNations ) {
val playerCiv = CivilizationInfo ( nation )
2019-01-10 19:29:24 +00:00
gameInfo . difficulty = newGameParameters . difficulty
playerCiv . playerType = PlayerType . Human
gameInfo . civilizations . add ( playerCiv )
}
2018-07-18 15:45:37 +00:00
2019-01-11 07:58:32 +00:00
val barbarianCivilization = CivilizationInfo ( " Barbarians " )
2018-05-29 19:01:22 +00:00
gameInfo . civilizations . add ( barbarianCivilization ) // second is barbarian civ
2019-01-11 07:58:32 +00:00
for ( nationName in availableCivNames . take ( newGameParameters . numberOfEnemies ) ) {
2018-11-17 19:02:42 +00:00
val civ = CivilizationInfo ( nationName )
2018-07-25 19:56:25 +00:00
gameInfo . civilizations . add ( civ )
2018-05-29 19:01:22 +00:00
}
2018-08-22 10:30:37 +00:00
2018-05-29 19:01:22 +00:00
gameInfo . setTransients ( ) // needs to be before placeBarbarianUnit because it depends on the tilemap having its gameinfo set
2018-12-24 12:01:32 +00:00
for ( civInfo in gameInfo . civilizations . filter { ! it . isBarbarianCivilization ( ) && ! it . isPlayerCivilization ( ) } ) {
for ( tech in gameInfo . getDifficulty ( ) . aiFreeTechs )
civInfo . tech . addTechnology ( tech )
}
2018-08-23 05:43:14 +00:00
// and only now do we add units for everyone, because otherwise both the gameInfo.setTransients() and the placeUnit will both add the unit to the civ's unit list!
2018-08-22 10:30:37 +00:00
2018-11-26 17:50:58 +00:00
for ( civ in gameInfo . civilizations . filter { ! it . isBarbarianCivilization ( ) } ) {
val startingLocation = startingLocations . pop ( ) !!
2018-08-22 10:30:37 +00:00
2018-11-26 17:50:58 +00:00
civ . placeUnitNearTile ( startingLocation . position , " Settler " )
civ . placeUnitNearTile ( startingLocation . position , " Warrior " )
civ . placeUnitNearTile ( startingLocation . position , " Scout " )
2018-08-22 10:30:37 +00:00
}
2018-05-29 19:01:22 +00:00
return gameInfo
}
2018-11-26 17:50:58 +00:00
fun getStartingLocations ( numberOfPlayers : Int , tileMap : TileMap ) : Stack < TileInfo > {
2019-04-14 19:13:55 +00:00
var landTiles = tileMap . values
. filter { it . isLand ( ) && ! it . getBaseTerrain ( ) . impassable }
val landTilesInBigEnoughGroup = ArrayList < TileInfo > ( )
while ( landTiles . any ( ) ) {
val bfs = BFS ( landTiles . random ( ) ) { it . isLand ( ) && ! it . getBaseTerrain ( ) . impassable }
bfs . stepToEnd ( )
val tilesInGroup = bfs . tilesReached . keys
landTiles = landTiles . filter { it !in tilesInGroup }
if ( tilesInGroup . size > 20 ) // is this a good number? I dunno, but it's easy enough to change later on
landTilesInBigEnoughGroup . addAll ( tilesInGroup )
}
2019-04-14 19:12:54 +00:00
2019-04-01 20:00:05 +00:00
for ( minimumDistanceBetweenStartingLocations in tileMap . tileMatrix . size / 2 downTo 0 ) {
2019-04-14 19:13:55 +00:00
val freeTiles = landTilesInBigEnoughGroup
2019-04-24 07:19:47 +00:00
. filter { vectorIsAtLeastNTilesAwayFromEdge ( it . position , minimumDistanceBetweenStartingLocations , tileMap ) }
2018-11-26 17:50:58 +00:00
. toMutableList ( )
val startingLocations = ArrayList < TileInfo > ( )
for ( player in 0. . numberOfPlayers ) {
if ( freeTiles . isEmpty ( ) ) break // we failed to get all the starting locations with this minimum distance
2019-04-14 19:12:54 +00:00
val randomLocation = freeTiles . random ( )
2018-11-26 17:50:58 +00:00
startingLocations . add ( randomLocation )
freeTiles . removeAll ( tileMap . getTilesInDistance ( randomLocation . position , minimumDistanceBetweenStartingLocations ) )
}
if ( startingLocations . size < numberOfPlayers ) continue // let's try again with less minimum distance!
val stack = Stack < TileInfo > ( )
stack . addAll ( startingLocations )
return stack
}
throw Exception ( " Didn't manage to get starting locations even with distance of 1? " )
}
2019-04-24 07:19:47 +00:00
fun vectorIsAtLeastNTilesAwayFromEdge ( vector : Vector2 , n : Int , tileMap : TileMap ) : Boolean {
2018-11-26 17:50:58 +00:00
val arrayXIndex = vector . x . toInt ( ) - tileMap . leftX
val arrayYIndex = vector . y . toInt ( ) - tileMap . bottomY
return arrayXIndex < tileMap . tileMatrix . size - n
&& arrayXIndex > n
&& arrayYIndex < tileMap . tileMatrix [ arrayXIndex ] . size - n
&& arrayYIndex > n
}
2018-05-29 19:01:22 +00:00
}