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
2019-05-06 17:46:48 +00:00
import com.unciv.logic.HexMath
2018-05-29 19:01:22 +00:00
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
2019-06-13 21:37:48 +00:00
import com.unciv.models.gamebasics.VictoryType
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
2019-05-11 20:32:12 +00:00
var humanNations = ArrayList < String > ( ) . apply { add ( " Babylon " ) } // Good default starting civ
2019-01-04 08:59:47 +00:00
var numberOfEnemies = 3
2019-04-30 16:33:32 +00:00
var numberOfCityStates = 0
2019-01-04 08:59:47 +00:00
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-06-13 21:37:48 +00:00
var victoryTypes : ArrayList < VictoryType > = VictoryType . values ( ) . toCollection ( ArrayList ( ) ) // By default, all victory types
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-05-06 17:46:48 +00:00
2018-07-18 15:45:37 +00:00
2019-01-11 07:58:32 +00:00
val availableCivNames = Stack < String > ( )
2019-04-30 16:33:32 +00:00
availableCivNames . addAll ( GameBasics . Nations . filter { ! it . value . isCityState ( ) } . 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 " )
2019-04-30 16:33:32 +00:00
val availableCityStatesNames = Stack < String > ( )
availableCityStatesNames . addAll ( GameBasics . Nations . filter { it . value . isCityState ( ) } . keys . shuffled ( ) )
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
}
2019-04-30 16:33:32 +00:00
for ( cityStateName in availableCityStatesNames . take ( newGameParameters . numberOfCityStates ) ) {
val civ = CivilizationInfo ( cityStateName )
gameInfo . civilizations . add ( civ )
}
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
2019-06-17 09:51:57 +00:00
val startingLocations = getStartingLocations (
gameInfo . civilizations . filter { ! it . isBarbarianCivilization ( ) } ,
gameInfo . tileMap )
2018-11-26 17:50:58 +00:00
for ( civ in gameInfo . civilizations . filter { ! it . isBarbarianCivilization ( ) } ) {
2019-06-17 09:51:57 +00:00
val startingLocation = startingLocations [ civ ] !!
2018-08-22 10:30:37 +00:00
2019-05-03 13:59:46 +00:00
civ . placeUnitNearTile ( startingLocation . position , Constants . settler )
2018-11-26 17:50:58 +00:00
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
2019-06-17 09:51:57 +00:00
fun getStartingLocations ( civs : List < CivilizationInfo > , tileMap : TileMap ) : HashMap < CivilizationInfo , TileInfo > {
2019-04-14 19:13:55 +00:00
var landTiles = tileMap . values
2019-05-02 20:15:22 +00:00
. filter { it . isLand && ! it . getBaseTerrain ( ) . impassable }
2019-04-14 19:13:55 +00:00
val landTilesInBigEnoughGroup = ArrayList < TileInfo > ( )
while ( landTiles . any ( ) ) {
2019-05-02 20:15:22 +00:00
val bfs = BFS ( landTiles . random ( ) ) { it . isLand && ! it . getBaseTerrain ( ) . impassable }
2019-04-14 19:13:55 +00:00
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-05-06 17:46:48 +00:00
for ( minimumDistanceBetweenStartingLocations in tileMap . tileMatrix . size / 3 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 ( )
2019-06-17 09:51:57 +00:00
val startingLocations = HashMap < CivilizationInfo , TileInfo > ( )
for ( civ in civs ) {
2018-11-26 17:50:58 +00:00
if ( freeTiles . isEmpty ( ) ) break // we failed to get all the starting locations with this minimum distance
2019-06-17 09:51:57 +00:00
var preferredTiles = freeTiles . toList ( )
for ( startBias in civ . getNation ( ) . startBias ) {
if ( startBias . startsWith ( " Avoid " ) ) {
val tileToAvoid = startBias . removePrefix ( " Avoid " )
preferredTiles = preferredTiles . filter { it . baseTerrain != tileToAvoid && it . terrainFeature != tileToAvoid }
}
2019-06-19 17:22:38 +00:00
else if ( startBias == Constants . coast ) preferredTiles = preferredTiles . filter { it . neighbors . any { n -> n . baseTerrain == startBias } }
2019-06-17 09:51:57 +00:00
else preferredTiles = preferredTiles . filter { it . baseTerrain == startBias || it . terrainFeature == startBias }
}
val randomLocation = if ( preferredTiles . isNotEmpty ( ) ) preferredTiles . random ( ) else freeTiles . random ( )
startingLocations . put ( civ , randomLocation )
2018-11-26 17:50:58 +00:00
freeTiles . removeAll ( tileMap . getTilesInDistance ( randomLocation . position , minimumDistanceBetweenStartingLocations ) )
}
2019-06-17 09:51:57 +00:00
if ( startingLocations . size < civs . size ) continue // let's try again with less minimum distance!
return startingLocations
2018-11-26 17:50:58 +00:00
}
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 {
2019-05-06 17:46:48 +00:00
// Since all maps are HEXAGONAL, the easiest way of checking if a tile is n steps away from the
// edge is checking the distance to the CENTER POINT
// Can't believe we used a dumb way of calculating this before!
val hexagonalRadius = - tileMap . leftX
val distanceFromCenter = HexMath ( ) . getDistance ( vector , Vector2 . Zero )
return hexagonalRadius - distanceFromCenter >= n
2018-11-26 17:50:58 +00:00
}
2018-05-29 19:01:22 +00:00
}