Unciv/core/src/com/unciv/GameStarter.kt

141 lines
6.6 KiB
Kotlin
Raw Normal View History

2018-05-29 19:01:22 +00:00
package com.unciv
import com.badlogic.gdx.math.Vector2
2018-05-29 19:01:22 +00:00
import com.unciv.logic.GameInfo
import com.unciv.logic.HexMath
2018-05-29 19:01:22 +00:00
import com.unciv.logic.civilization.CivilizationInfo
import com.unciv.logic.civilization.PlayerType
import com.unciv.logic.map.BFS
import com.unciv.logic.map.MapType
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
import com.unciv.models.gamebasics.VictoryType
import java.util.*
import kotlin.collections.ArrayList
2018-05-29 19:01:22 +00:00
class GameParameters{
var difficulty="Prince"
var mapRadius=20
var numberOfHumanPlayers=1
var humanNations=ArrayList<String>().apply { add("Babylon") } // Good default starting civ
var numberOfEnemies=3
var numberOfCityStates=0
var mapType= MapType.Perlin
var noBarbarians=false
var mapFileName :String?=null
var victoryTypes :ArrayList<VictoryType> = VictoryType.values().toCollection(ArrayList()) // By default, all victory types
}
class GameStarter{
fun startNewGame(newGameParameters: GameParameters): GameInfo {
2018-05-29 19:01:22 +00:00
val gameInfo = GameInfo()
gameInfo.gameParameters = newGameParameters
gameInfo.tileMap = TileMap(newGameParameters)
gameInfo.tileMap.gameInfo = gameInfo // need to set this transient before placing units in the map
val availableCivNames = Stack<String>()
availableCivNames.addAll(GameBasics.Nations.filter { !it.value.isCityState() }.keys.shuffled())
availableCivNames.removeAll(newGameParameters.humanNations)
availableCivNames.remove("Barbarians")
val availableCityStatesNames = Stack<String>()
availableCityStatesNames.addAll(GameBasics.Nations.filter { it.value.isCityState() }.keys.shuffled())
2018-05-29 19:01:22 +00:00
for(nation in newGameParameters.humanNations) {
val playerCiv = CivilizationInfo(nation)
gameInfo.difficulty = newGameParameters.difficulty
playerCiv.playerType = PlayerType.Human
gameInfo.civilizations.add(playerCiv)
}
val barbarianCivilization = CivilizationInfo("Barbarians")
2018-05-29 19:01:22 +00:00
gameInfo.civilizations.add(barbarianCivilization)// second is barbarian civ
for (nationName in availableCivNames.take(newGameParameters.numberOfEnemies)) {
val civ = CivilizationInfo(nationName)
2018-07-25 19:56:25 +00:00
gameInfo.civilizations.add(civ)
2018-05-29 19:01:22 +00:00
}
for (cityStateName in availableCityStatesNames.take(newGameParameters.numberOfCityStates)) {
val civ = CivilizationInfo(cityStateName)
gameInfo.civilizations.add(civ)
}
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!
val startingLocations = getStartingLocations(
gameInfo.civilizations.filter { !it.isBarbarianCivilization() },
gameInfo.tileMap)
for (civ in gameInfo.civilizations.filter { !it.isBarbarianCivilization() }) {
val startingLocation = startingLocations[civ]!!
2019-05-03 13:59:46 +00:00
civ.placeUnitNearTile(startingLocation.position, Constants.settler)
civ.placeUnitNearTile(startingLocation.position, "Warrior")
civ.placeUnitNearTile(startingLocation.position, "Scout")
}
2018-05-29 19:01:22 +00:00
return gameInfo
}
fun getStartingLocations(civs:List<CivilizationInfo>,tileMap: TileMap): HashMap<CivilizationInfo, TileInfo> {
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)
}
for(minimumDistanceBetweenStartingLocations in tileMap.tileMatrix.size/3 downTo 0){
val freeTiles = landTilesInBigEnoughGroup
.filter { vectorIsAtLeastNTilesAwayFromEdge(it.position,minimumDistanceBetweenStartingLocations,tileMap)}
.toMutableList()
val startingLocations = HashMap<CivilizationInfo,TileInfo>()
for(civ in civs){
if(freeTiles.isEmpty()) break // we failed to get all the starting locations with this minimum distance
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 } }
else preferredTiles = preferredTiles.filter { it.baseTerrain == startBias || it.terrainFeature==startBias }
}
val randomLocation = if(preferredTiles.isNotEmpty()) preferredTiles.random() else freeTiles.random()
startingLocations.put(civ, randomLocation)
freeTiles.removeAll(tileMap.getTilesInDistance(randomLocation.position,minimumDistanceBetweenStartingLocations))
}
if(startingLocations.size < civs.size) continue // let's try again with less minimum distance!
return startingLocations
}
throw Exception("Didn't manage to get starting locations even with distance of 1?")
}
fun vectorIsAtLeastNTilesAwayFromEdge(vector: Vector2, n:Int, tileMap: TileMap): Boolean {
// 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-05-29 19:01:22 +00:00
}