AI now accompanies settlers with military units

AI now picks promotions for promotable units
This commit is contained in:
Yair Morgenstern 2018-06-20 18:30:40 +03:00
parent 3f87cfa3db
commit 05b37dc660
13 changed files with 70 additions and 34 deletions

View File

@ -12,6 +12,14 @@ class UnCivGame : Game() {
var gameInfo: GameInfo = GameInfo() var gameInfo: GameInfo = GameInfo()
lateinit var settings : GameSettings lateinit var settings : GameSettings
/**
* This exists so that when debugging we can see the entire map.
* Remember to turn this to false before commit and upload!
*/
val viewEntireMapForDebug = false
lateinit var worldScreen: WorldScreen lateinit var worldScreen: WorldScreen
override fun create() { override fun create() {

View File

@ -81,14 +81,6 @@ class GameInfo {
civInfo.setTransients() civInfo.setTransients()
} }
val civNameToCiv = civilizations.associateBy ({ it.civName},{it})
for (tile in tileMap.values) {
if (tile.militaryUnit != null) tile.militaryUnit!!.civInfo = civNameToCiv[tile.militaryUnit!!.owner]!!
if (tile.civilianUnit!= null) tile.civilianUnit!!.civInfo = civNameToCiv[tile.civilianUnit!!.owner]!!
}
for (civInfo in civilizations) for (civInfo in civilizations)
for (cityInfo in civInfo.cities) for (cityInfo in civInfo.cities)
cityInfo.cityStats.update() cityInfo.cityStats.update()

View File

@ -47,19 +47,25 @@ class Automation {
val civilianUnits = mutableListOf<MapUnit>() val civilianUnits = mutableListOf<MapUnit>()
for (unit in civInfo.getCivUnits()) { for (unit in civInfo.getCivUnits()) {
if(unit.promotions.canBePromoted()){
val availablePromotions = unit.promotions.getAvailablePromotions()
if(availablePromotions.isNotEmpty())
unit.promotions.addPromotion(availablePromotions.getRandom().name)
}
val unitType = unit.getBaseUnit().unitType val unitType = unit.getBaseUnit().unitType
if(unitType.isRanged()) rangedUnits.add(unit) if(unitType.isRanged()) rangedUnits.add(unit)
else if(unitType.isMelee()) meleeUnits.add(unit) else if(unitType.isMelee()) meleeUnits.add(unit)
else civilianUnits.add(unit) else civilianUnits.add(unit)
} }
for (unit in civilianUnits) UnitAutomation().automateUnitMoves(unit) // They move first so that combat units can accompany a settler
for (unit in rangedUnits) UnitAutomation().automateUnitMoves(unit) for (unit in rangedUnits) UnitAutomation().automateUnitMoves(unit)
for (unit in meleeUnits) UnitAutomation().automateUnitMoves(unit) for (unit in meleeUnits) UnitAutomation().automateUnitMoves(unit)
for (unit in civilianUnits) UnitAutomation().automateUnitMoves(unit)
// train settler? // train settler?
if (civInfo.cities.any() if (civInfo.cities.any()
&& civInfo.happiness > 2*civInfo.cities.size +5 && civInfo.happiness > civInfo.cities.size +5
&& civInfo.getCivUnits().none { it.name == "Settler" } && civInfo.getCivUnits().none { it.name == "Settler" }
&& civInfo.cities.none { it.cityConstructions.currentConstruction == "Settler" }) { && civInfo.cities.none { it.cityConstructions.currentConstruction == "Settler" }) {

View File

@ -76,7 +76,16 @@ class UnitAutomation{
return return
} }
if(unit.name.startsWith("Great")) return // DON'T MOVE A MUSCLE if(unit.name.startsWith("Great")) return // I don't know what to do with you yet.
// Accompany settlers
val closeTileWithSettler = unit.getDistanceToTiles().keys.firstOrNull{
it.civilianUnit!=null && it.civilianUnit!!.name=="Settler"}
if(closeTileWithSettler != null && unit.canMoveTo(closeTileWithSettler)){
unit.movementAlgs().headTowards(closeTileWithSettler)
return
}
if (unit.health < 50) { if (unit.health < 50) {
healUnit(unit) healUnit(unit)
@ -87,7 +96,7 @@ class UnitAutomation{
val enemyTileToAttack = getAttackableEnemies(unit).firstOrNull() val enemyTileToAttack = getAttackableEnemies(unit).firstOrNull()
if (enemyTileToAttack != null) { if (enemyTileToAttack != null) {
val setupAction = UnitActions().getUnitActions(unit, UnCivGame.Current.worldScreen!!).firstOrNull{ it.name == "Set up" } val setupAction = UnitActions().getUnitActions(unit, UnCivGame.Current.worldScreen).firstOrNull{ it.name == "Set up" }
if(setupAction!=null) setupAction.action() if(setupAction!=null) setupAction.action()
val enemy = Battle().getMapCombatantOfTile(enemyTileToAttack)!! val enemy = Battle().getMapCombatantOfTile(enemyTileToAttack)!!
@ -160,6 +169,8 @@ class UnitAutomation{
} }
private fun automateSettlerActions(unit: MapUnit) { private fun automateSettlerActions(unit: MapUnit) {
if(unit.getTile().militaryUnit==null) return // Don;t move until you're accompanied by a military unit
// find best city location within 5 tiles // find best city location within 5 tiles
val tilesNearCities = unit.civInfo.gameInfo.civilizations.flatMap { it.cities } val tilesNearCities = unit.civInfo.gameInfo.civilizations.flatMap { it.cities }
.flatMap { it.getCenterTile().getTilesInDistance(2) } .flatMap { it.getCenterTile().getTilesInDistance(2) }

View File

@ -127,6 +127,12 @@ class CivilizationInfo {
policies.civInfo = this policies.civInfo = this
tech.civInfo = this tech.civInfo = this
for (unit in getCivUnits()) {
unit.civInfo=this
unit.setTransients()
}
for (cityInfo in cities) { for (cityInfo in cities) {
cityInfo.setTransients() cityInfo.setTransients()
cityInfo.civInfo = this cityInfo.civInfo = this
@ -180,7 +186,7 @@ class CivilizationInfo {
} }
fun getCivUnits(): List<MapUnit> { fun getCivUnits(): List<MapUnit> {
return gameInfo.tileMap.values.flatMap { it.getUnits() }.filter { it.civInfo==this } return gameInfo.tileMap.values.flatMap { it.getUnits() }.filter { it.owner==civName }
} }
fun getViewableTiles(): List<TileInfo> { fun getViewableTiles(): List<TileInfo> {

View File

@ -21,6 +21,10 @@ class MapUnit {
var attacksThisTurn = 0 var attacksThisTurn = 0
var promotions = UnitPromotions() var promotions = UnitPromotions()
init{
promotions.unit=this
}
fun getBaseUnit(): Unit = GameBasics.Units[name]!! fun getBaseUnit(): Unit = GameBasics.Units[name]!!
fun getMovementString(): String = DecimalFormat("0.#").format(currentMovement.toDouble()) + "/" + maxMovement fun getMovementString(): String = DecimalFormat("0.#").format(currentMovement.toDouble()) + "/" + maxMovement
fun getTile(): TileInfo { fun getTile(): TileInfo {
@ -190,4 +194,8 @@ class MapUnit {
if(hasUnique("Must set up to ranged attack") && action != "Set Up") return false if(hasUnique("Must set up to ranged attack") && action != "Set Up") return false
return true return true
} }
fun setTransients(){
promotions.unit=this
}
} }

View File

@ -67,7 +67,7 @@ class TileMap {
fun setTransients() { fun setTransients() {
for (tileInfo in values){ for (tileInfo in values){
tileInfo.tileMap = this tileInfo.tileMap = this
if(tileInfo.unit!=null){ if(tileInfo.unit!=null){ // thi is for the old "unit" field which has been replaced.
tileInfo.unit!!.putInTile(tileInfo) tileInfo.unit!!.putInTile(tileInfo)
tileInfo.unit=null tileInfo.unit=null
} }

View File

@ -1,6 +1,10 @@
package com.unciv.logic.map package com.unciv.logic.map
import com.unciv.models.gamebasics.GameBasics
import com.unciv.models.gamebasics.unit.Promotion
class UnitPromotions{ class UnitPromotions{
@Transient lateinit var unit:MapUnit
var XP=0 var XP=0
var promotions = HashSet<String>() var promotions = HashSet<String>()
var numberOfPromotions = 0 // The number of times this unit has been promoted - some promotions don't come from being promoted but from other things! var numberOfPromotions = 0 // The number of times this unit has been promoted - some promotions don't come from being promoted but from other things!
@ -13,4 +17,10 @@ class UnitPromotions{
promotions.add(promotionName) promotions.add(promotionName)
numberOfPromotions++ numberOfPromotions++
} }
fun getAvailablePromotions(): List<Promotion> {
return GameBasics.UnitPromotions.values
.filter { unit.getBaseUnit().unitType.toString() in it.unitTypes && it.name !in promotions }
.filter { it.prerequisites.isEmpty() || it.prerequisites.all { p->p in promotions } }
}
} }

View File

@ -21,13 +21,13 @@ class PromotionPickerScreen(mapUnit: MapUnit) : PickerScreen() {
dispose() dispose()
} }
val availablePromotions = VerticalGroup() val availablePromotionsGroup = VerticalGroup()
availablePromotions.space(10f) availablePromotionsGroup.space(10f)
val unitType = mapUnit.getBaseUnit().unitType val unitType = mapUnit.getBaseUnit().unitType
val promotionsForUnitType = GameBasics.UnitPromotions.values.filter { it.unitTypes.contains(unitType.toString()) } val promotionsForUnitType = GameBasics.UnitPromotions.values.filter { it.unitTypes.contains(unitType.toString()) }
val unitAvailablePromotions = mapUnit.promotions.getAvailablePromotions()
for (promotion in promotionsForUnitType) { for (promotion in promotionsForUnitType) {
val isPromotionAvailable = promotion.prerequisites.isEmpty() val isPromotionAvailable = promotion in unitAvailablePromotions
|| promotion.prerequisites.any { mapUnit.promotions.promotions.contains(it) }
val unitHasPromotion = mapUnit.promotions.promotions.contains(promotion.name) val unitHasPromotion = mapUnit.promotions.promotions.contains(promotion.name)
val promotionButton = Button(skin) val promotionButton = Button(skin)
@ -48,8 +48,8 @@ class PromotionPickerScreen(mapUnit: MapUnit) : PickerScreen() {
.joinToString(" OR ") .joinToString(" OR ")
descriptionLabel.setText(descriptionText) descriptionLabel.setText(descriptionText)
} }
availablePromotions.addActor(promotionButton) availablePromotionsGroup.addActor(promotionButton)
} }
topTable.add(availablePromotions) topTable.add(availablePromotionsGroup)
} }
} }

View File

@ -15,13 +15,6 @@ import com.unciv.ui.utils.center
import com.unciv.ui.utils.colorFromRGB import com.unciv.ui.utils.colorFromRGB
open class TileGroup(var tileInfo: TileInfo) : Group() { open class TileGroup(var tileInfo: TileInfo) : Group() {
/**
* This exists so that when debugging we can see the entire map.
* Remember to turn this to false before commit and upload!
*/
protected val viewEntireMapForDebug = false
protected val hexagon = ImageGetter.getImage("TerrainIcons/Hexagon.png") protected val hexagon = ImageGetter.getImage("TerrainIcons/Hexagon.png")
protected var terrainFeatureImage:Image?=null protected var terrainFeatureImage:Image?=null
@ -90,7 +83,7 @@ open class TileGroup(var tileInfo: TileInfo) : Group() {
open fun update(isViewable: Boolean) { open fun update(isViewable: Boolean) {
hideCircle() hideCircle()
if (!tileInfo.tileMap.gameInfo.getPlayerCivilization().exploredTiles.contains(tileInfo.position) if (!tileInfo.tileMap.gameInfo.getPlayerCivilization().exploredTiles.contains(tileInfo.position)
&& !viewEntireMapForDebug) { && !UnCivGame.Current.viewEntireMapForDebug) {
hexagon.color = Color.BLACK hexagon.color = Color.BLACK
return return
} }
@ -109,7 +102,7 @@ open class TileGroup(var tileInfo: TileInfo) : Group() {
updateBorderImages() updateBorderImages()
fogImage.toFront() fogImage.toFront()
fogImage.isVisible=!isViewable fogImage.isVisible=!(isViewable || UnCivGame.Current.viewEntireMapForDebug)
} }
private fun updateBorderImages() { private fun updateBorderImages() {
@ -256,7 +249,7 @@ open class TileGroup(var tileInfo: TileInfo) : Group() {
currentImage.remove() currentImage.remove()
} }
if (unit != null && (isViewable || viewEntireMapForDebug)) { // Tile is visible if (unit != null && isViewable) { // Tile is visible
newImage = getUnitImage(unit, unit.civInfo.getCivilization().getColor(), 25f) newImage = getUnitImage(unit, unit.civInfo.getCivilization().getColor(), 25f)
addActor(newImage) addActor(newImage)
newImage.center(this) newImage.center(this)

View File

@ -44,9 +44,9 @@ class WorldTileGroup(tileInfo: TileInfo) : TileGroup(tileInfo) {
addPopulationIcon() addPopulationIcon()
if (tileInfo.tileMap.gameInfo.getPlayerCivilization().exploredTiles.contains(tileInfo.position) if (tileInfo.tileMap.gameInfo.getPlayerCivilization().exploredTiles.contains(tileInfo.position)
|| viewEntireMapForDebug) updateCityButton(city, isViewable) // needs to be before the update so the units will be above the city button || UnCivGame.Current.viewEntireMapForDebug) updateCityButton(city, isViewable) // needs to be before the update so the units will be above the city button
super.update(isViewable) super.update(isViewable || UnCivGame.Current.viewEntireMapForDebug)
yieldGroup.isVisible = !UnCivGame.Current.settings.showResourcesAndImprovements yieldGroup.isVisible = !UnCivGame.Current.settings.showResourcesAndImprovements
if(yieldGroup.isVisible) if(yieldGroup.isVisible)

View File

@ -6,6 +6,7 @@ import com.badlogic.gdx.scenes.scene2d.Group
import com.badlogic.gdx.scenes.scene2d.InputListener import com.badlogic.gdx.scenes.scene2d.InputListener
import com.badlogic.gdx.scenes.scene2d.ui.Image import com.badlogic.gdx.scenes.scene2d.ui.Image
import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane
import com.unciv.UnCivGame
import com.unciv.logic.HexMath import com.unciv.logic.HexMath
import com.unciv.logic.map.TileInfo import com.unciv.logic.map.TileInfo
import com.unciv.ui.utils.ImageGetter import com.unciv.ui.utils.ImageGetter
@ -74,7 +75,7 @@ class Minimap(val tileMapHolder: TileMapHolder) : ScrollPane(null){
for(tileInfo in tileMapHolder.tileMap.values) { for(tileInfo in tileMapHolder.tileMap.values) {
val RGB = tileInfo.getBaseTerrain().RGB!! val RGB = tileInfo.getBaseTerrain().RGB!!
val hex = tileImages[tileInfo]!! val hex = tileImages[tileInfo]!!
if (!exploredTiles.contains(tileInfo.position)) hex.color = Color.BLACK if (!(exploredTiles.contains(tileInfo.position) || UnCivGame.Current.viewEntireMapForDebug)) hex.color = Color.BLACK
else if (tileInfo.isCityCenter()) hex.color = Color.WHITE else if (tileInfo.isCityCenter()) hex.color = Color.WHITE
else if (tileInfo.getCity() != null) hex.color = tileInfo.getOwner()!!.getCivilization().getColor() else if (tileInfo.getCity() != null) hex.color = tileInfo.getOwner()!!.getCivilization().getColor()
else hex.color = colorFromRGB(RGB[0], RGB[1], RGB[2]).lerp(Color.GRAY, 0.5f) // Todo add to baseterrain as function else hex.color = colorFromRGB(RGB[0], RGB[1], RGB[2]).lerp(Color.GRAY, 0.5f) // Todo add to baseterrain as function

View File

@ -3,6 +3,7 @@ package com.unciv.ui.worldscreen.bottombar
import com.badlogic.gdx.scenes.scene2d.ui.Label import com.badlogic.gdx.scenes.scene2d.ui.Label
import com.badlogic.gdx.scenes.scene2d.ui.Table import com.badlogic.gdx.scenes.scene2d.ui.Table
import com.badlogic.gdx.utils.Align import com.badlogic.gdx.utils.Align
import com.unciv.UnCivGame
import com.unciv.logic.map.TileInfo import com.unciv.logic.map.TileInfo
import com.unciv.ui.utils.CameraStageBaseScreen import com.unciv.ui.utils.CameraStageBaseScreen
import com.unciv.ui.utils.ImageGetter import com.unciv.ui.utils.ImageGetter
@ -18,7 +19,7 @@ class TileInfoTable(private val worldScreen: WorldScreen) : Table() {
val civInfo = worldScreen.civInfo val civInfo = worldScreen.civInfo
columnDefaults(0).padRight(10f) columnDefaults(0).padRight(10f)
if (civInfo.exploredTiles.contains(tile.position)) { if (civInfo.exploredTiles.contains(tile.position) || UnCivGame.Current.viewEntireMapForDebug) {
add(getStatsTable(tile)).pad(10f) add(getStatsTable(tile)).pad(10f)
add(Label(tile.toString(), skin)).colspan(2) add(Label(tile.toString(), skin)).colspan(2)
} }