mirror of
https://github.com/yairm210/Unciv.git
synced 2025-03-12 19:10:12 +07:00
Major update - enemies can now take over cities (and do!)
This commit is contained in:
parent
64f8b3fef8
commit
e1018371b3
@ -67,7 +67,11 @@ class Automation {
|
||||
if(city.civInfo.cities.any { it.getCenterTile().unit==null}
|
||||
&& combatUnits.any { it.unitType==UnitType.Archery }) // this is for city defence so get an archery unit if we can
|
||||
chosenUnit = combatUnits.filter { it.unitType==UnitType.Archery }.maxBy { it.cost }!!
|
||||
else chosenUnit = combatUnits.maxBy { it.cost }!!
|
||||
|
||||
else{ // randomize type of unit and takee the most expensive of its kind
|
||||
val chosenUnitType = combatUnits.map { it.unitType }.distinct().getRandom()
|
||||
chosenUnit = combatUnits.filter { it.unitType==chosenUnitType }.maxBy { it.cost }!!
|
||||
}
|
||||
|
||||
city.cityConstructions.currentConstruction = chosenUnit.name
|
||||
}
|
||||
|
@ -2,7 +2,10 @@ package com.unciv.logic.automation
|
||||
|
||||
import com.unciv.UnCivGame
|
||||
import com.unciv.logic.battle.Battle
|
||||
import com.unciv.logic.battle.CityCombatant
|
||||
import com.unciv.logic.battle.ICombatant
|
||||
import com.unciv.logic.battle.MapUnitCombatant
|
||||
import com.unciv.logic.civilization.CivilizationInfo
|
||||
import com.unciv.logic.map.MapUnit
|
||||
import com.unciv.logic.map.TileInfo
|
||||
import com.unciv.logic.map.UnitType
|
||||
@ -32,6 +35,36 @@ class UnitAutomation{
|
||||
}
|
||||
}
|
||||
|
||||
fun containsAttackableEnemy(tile: TileInfo, civInfo: CivilizationInfo): Boolean {
|
||||
return (tile.unit != null && tile.unit!!.owner != civInfo.civName)
|
||||
|| (tile.isCityCenter() && tile.getCity()!!.civInfo!=civInfo)
|
||||
}
|
||||
|
||||
fun getAttackableEnemies(unit: MapUnit): List<TileInfo> {
|
||||
val attackableTiles = unit.civInfo.getViewableTiles()
|
||||
.filter { containsAttackableEnemy(it,unit.civInfo) }
|
||||
|
||||
if(MapUnitCombatant(unit).isMelee()) {
|
||||
val distanceToTiles = unit.getDistanceToTiles()
|
||||
// If we're conducting a melee attack,
|
||||
// then there needs to be a tile adjacent to the enemy that we can get to,
|
||||
// AND STILL HAVE MOVEMENT POINTS REMAINING,
|
||||
return attackableTiles.filter {
|
||||
it.neighbors.any {
|
||||
unit.getTile()==it || // We're already right nearby
|
||||
it.unit == null
|
||||
&& distanceToTiles.containsKey(it)
|
||||
&& distanceToTiles[it]!! < unit.currentMovement // We can get there
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else { // Range attack, so enemy needs to be in range
|
||||
return attackableTiles.filter { unit.getTile().getTilesInDistance(2).contains(it) }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun automateUnitMoves(unit: MapUnit) {
|
||||
|
||||
if (unit.name == "Settler") {
|
||||
@ -52,26 +85,32 @@ class UnitAutomation{
|
||||
} // do nothing but heal
|
||||
|
||||
// if there is an attackable unit in the vicinity, attack!
|
||||
val attackableTiles = unit.civInfo.getViewableTiles()
|
||||
.filter { it.unit != null && it.unit!!.owner != unit.civInfo.civName && !it.isCityCenter() }.toHashSet()
|
||||
val distanceToTiles = unit.getDistanceToTiles()
|
||||
val unitTileToAttack = distanceToTiles.keys.firstOrNull { attackableTiles.contains(it) }
|
||||
val enemyTileToAttack = getAttackableEnemies(unit).firstOrNull()
|
||||
|
||||
if (unitTileToAttack != null) {
|
||||
val unitToAttack = unitTileToAttack.unit!!
|
||||
if (unitToAttack.getBaseUnit().unitType == UnitType.Civilian) { // kill
|
||||
unitToAttack.civInfo.addNotification("Our " + unitToAttack.name + " was destroyed by an enemy " + unit.name + "!", unitTileToAttack.position)
|
||||
unitTileToAttack.unit = null
|
||||
unit.movementAlgs().headTowards(unitTileToAttack)
|
||||
return
|
||||
if (enemyTileToAttack != null) {
|
||||
|
||||
val enemy:ICombatant
|
||||
if(enemyTileToAttack.isCityCenter()){
|
||||
enemy = CityCombatant(enemyTileToAttack.getCity()!!)
|
||||
}
|
||||
|
||||
val damageToAttacker = Battle(unit.civInfo.gameInfo).calculateDamageToAttacker(MapUnitCombatant(unit), MapUnitCombatant(unitToAttack))
|
||||
else {
|
||||
val unitToAttack = enemyTileToAttack.unit!!
|
||||
if (unitToAttack.getBaseUnit().unitType == UnitType.Civilian) { // kill
|
||||
unitToAttack.civInfo.addNotification("Our " + unitToAttack.name + " was destroyed by an enemy " + unit.name + "!", enemyTileToAttack.position)
|
||||
enemyTileToAttack.unit = null
|
||||
unit.movementAlgs().headTowards(enemyTileToAttack)
|
||||
return
|
||||
}
|
||||
enemy=MapUnitCombatant(unitToAttack)
|
||||
}
|
||||
|
||||
val damageToAttacker = Battle(unit.civInfo.gameInfo).calculateDamageToAttacker(MapUnitCombatant(unit), enemy)
|
||||
|
||||
if (damageToAttacker < unit.health) { // don't attack if we'll die from the attack
|
||||
if(MapUnitCombatant(unit).isMelee())
|
||||
unit.movementAlgs().headTowards(unitTileToAttack)
|
||||
Battle(unit.civInfo.gameInfo).attack(MapUnitCombatant(unit), MapUnitCombatant(unitToAttack))
|
||||
unit.movementAlgs().headTowards(enemyTileToAttack)
|
||||
Battle(unit.civInfo.gameInfo).attack(MapUnitCombatant(unit), enemy)
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -98,11 +137,11 @@ class UnitAutomation{
|
||||
// todo
|
||||
|
||||
// else, find the closest enemy unit that we know of within 5 spaces and advance towards it
|
||||
val closestUnit = unit.getTile().getTilesInDistance(5)
|
||||
.firstOrNull { attackableTiles.contains(it) }
|
||||
val closestEnemy = unit.getTile().getTilesInDistance(5)
|
||||
.firstOrNull { containsAttackableEnemy(it,unit.civInfo) && unit.movementAlgs().canReach(it) }
|
||||
|
||||
if (closestUnit != null) {
|
||||
unit.movementAlgs().headTowards(closestUnit)
|
||||
if (closestEnemy != null) {
|
||||
unit.movementAlgs().headTowards(closestEnemy)
|
||||
return
|
||||
}
|
||||
|
||||
@ -112,7 +151,9 @@ class UnitAutomation{
|
||||
}
|
||||
|
||||
// else, go to a random space
|
||||
unit.moveToTile(distanceToTiles.keys.filter { it.unit == null }.toList().getRandom())
|
||||
unit.moveToTile(unit.getDistanceToTiles()
|
||||
.filter { it.key.unit == null && it.value==unit.currentMovement } // at edge of walking distance
|
||||
.toList().getRandom().first)
|
||||
}
|
||||
|
||||
fun rankTileAsCityCenter(tileInfo: TileInfo, nearbyTileRankings: Map<TileInfo, Float>): Float {
|
||||
@ -156,4 +197,5 @@ class UnitAutomation{
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,8 +13,6 @@ import kotlin.math.max
|
||||
*/
|
||||
class Battle(val gameInfo:GameInfo) {
|
||||
|
||||
|
||||
|
||||
private fun getGeneralModifiers(combatant: ICombatant, enemy: ICombatant): HashMap<String, Float> {
|
||||
val modifiers = HashMap<String, Float>()
|
||||
if (combatant is MapUnitCombatant) {
|
||||
@ -140,7 +138,7 @@ class Battle(val gameInfo:GameInfo) {
|
||||
if (attacker.isDefeated()) " was destroyed while attacking"
|
||||
else " has " + (if (defender.isDefeated()) "destroyed" else "attacked")
|
||||
val defenderString =
|
||||
if (defender.getUnitType() == UnitType.City) defender.getName()
|
||||
if (defender.getUnitType() == UnitType.City) " "+defender.getName()
|
||||
else " our " + defender.getName()
|
||||
val notificationString = "An enemy " + attacker.getName() + whatHappenedString + defenderString
|
||||
gameInfo.getPlayerCivilization().addNotification(notificationString, attackedTile.position)
|
||||
|
@ -83,12 +83,18 @@ class MapUnit {
|
||||
if(health>100) health=100
|
||||
}
|
||||
|
||||
fun canMove(tile: TileInfo): Boolean {
|
||||
if(tile.unit!=null) return false
|
||||
if(tile.isCityCenter() && tile.getOwner()!=civInfo) return false
|
||||
return true
|
||||
}
|
||||
|
||||
fun moveToTile(otherTile: TileInfo) {
|
||||
val distanceToTiles = getDistanceToTiles()
|
||||
if (!distanceToTiles.containsKey(otherTile))
|
||||
throw Exception("You can't get there from here!")
|
||||
if (otherTile.unit != null ) throw Exception("Tile already contains a unit!")
|
||||
if(otherTile.isCityCenter() && otherTile.getOwner()!=civInfo) throw Exception("This is an enemy city, you can't go here!")
|
||||
|
||||
currentMovement -= distanceToTiles[otherTile]!!
|
||||
if (currentMovement < 0.1) currentMovement = 0f // silly floats which are "almost zero"
|
||||
|
@ -34,13 +34,17 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
|
||||
val updatedTiles = ArrayList<TileInfo>()
|
||||
for (tileToCheck in tilesToCheck)
|
||||
for (neighbor in tileToCheck.neighbors) {
|
||||
if (neighbor.getOwner() != null && neighbor.getOwner() != unit.civInfo
|
||||
|
||||
var totalDistanceToTile:Float
|
||||
if (neighbor.getOwner() != unit.civInfo
|
||||
&& neighbor.isCityCenter())
|
||||
continue // Enemy city, can't move through it!
|
||||
totalDistanceToTile = unitMovement // Enemy city, can't move through it - we'll be "stuck" there
|
||||
|
||||
val distanceBetweenTiles = getMovementCostBetweenAdjacentTiles(tileToCheck, neighbor)
|
||||
else {
|
||||
val distanceBetweenTiles = getMovementCostBetweenAdjacentTiles(tileToCheck, neighbor)
|
||||
totalDistanceToTile = distanceToTiles[tileToCheck]!! + distanceBetweenTiles
|
||||
}
|
||||
|
||||
var totalDistanceToTile = distanceToTiles[tileToCheck]!! + distanceBetweenTiles
|
||||
if (!distanceToTiles.containsKey(neighbor) || distanceToTiles[neighbor]!! > totalDistanceToTile) {
|
||||
if (totalDistanceToTile < unitMovement)
|
||||
updatedTiles += neighbor
|
||||
@ -76,7 +80,7 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
|
||||
distanceToDestination[tileToCheck] = distanceToTilesThisTurn[reachableTile]!!
|
||||
else {
|
||||
if (movementTreeParents.containsKey(reachableTile)) continue // We cannot be faster than anything existing...
|
||||
if (reachableTile != destination && reachableTile.unit != null) continue // This is an intermediary tile that contains a unit - we can't go there!
|
||||
if (!unit.canMove(reachableTile)) continue // This is a tile that we can''t actually enter - either an intermediary tile containing our unit, or an enemy unit/city
|
||||
movementTreeParents[reachableTile] = tileToCheck
|
||||
newTilesToCheck.add(reachableTile)
|
||||
}
|
||||
@ -111,12 +115,13 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
|
||||
*/
|
||||
fun headTowards(destination: TileInfo): TileInfo {
|
||||
val currentTile = unit.getTile()
|
||||
if(currentTile==destination) return currentTile
|
||||
|
||||
val distanceToTiles = unit.getDistanceToTiles()
|
||||
|
||||
val destinationTileThisTurn: TileInfo
|
||||
if (distanceToTiles.containsKey(destination)) { // we can get there this turn
|
||||
if (destination.unit == null)
|
||||
if (unit.canMove(destination))
|
||||
destinationTileThisTurn = destination
|
||||
else // Someone is blocking to the path to the final tile...
|
||||
{
|
||||
@ -125,7 +130,7 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
|
||||
return currentTile
|
||||
|
||||
val reachableDestinationNeighbors = destinationNeighbors
|
||||
.filter { distanceToTiles.containsKey(it) && it.unit == null }
|
||||
.filter { distanceToTiles.containsKey(it) && unit.canMove(it)}
|
||||
if (reachableDestinationNeighbors.isEmpty()) // We can't get closer...
|
||||
return currentTile
|
||||
|
||||
|
@ -12,6 +12,12 @@ import com.unciv.ui.utils.center
|
||||
import com.unciv.ui.utils.colorFromRGB
|
||||
|
||||
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 var terrainFeatureImage:Image?=null
|
||||
@ -67,7 +73,8 @@ open class TileGroup(var tileInfo: TileInfo) : Group() {
|
||||
|
||||
open fun update(isViewable: Boolean) {
|
||||
hideCircle()
|
||||
if (!tileInfo.tileMap.gameInfo.getPlayerCivilization().exploredTiles.contains(tileInfo.position)) {
|
||||
if (!tileInfo.tileMap.gameInfo.getPlayerCivilization().exploredTiles.contains(tileInfo.position)
|
||||
&& !viewEntireMapForDebug) {
|
||||
hexagon.color = Color.BLACK
|
||||
return
|
||||
}
|
||||
@ -205,7 +212,7 @@ open class TileGroup(var tileInfo: TileInfo) : Group() {
|
||||
unitImage = null
|
||||
}
|
||||
|
||||
if (tileInfo.unit != null && isViewable) { // Tile is visible
|
||||
if (tileInfo.unit != null && (isViewable || viewEntireMapForDebug)) { // Tile is visible
|
||||
val unit = tileInfo.unit!!
|
||||
unitImage = getUnitImage(unit.name, unit.civInfo.getCivilization().getColor())
|
||||
addActor(unitImage!!)
|
||||
|
@ -27,7 +27,8 @@ class WorldTileGroup(tileInfo: TileInfo) : TileGroup(tileInfo) {
|
||||
|
||||
override fun update(isViewable: Boolean) {
|
||||
super.update(isViewable)
|
||||
if (!tileInfo.tileMap.gameInfo.getPlayerCivilization().exploredTiles.contains(tileInfo.position)) return
|
||||
if (!tileInfo.tileMap.gameInfo.getPlayerCivilization().exploredTiles.contains(tileInfo.position)
|
||||
&& !viewEntireMapForDebug) return
|
||||
|
||||
if (populationImage != null) removePopulationIcon()
|
||||
|
||||
|
@ -4,16 +4,15 @@ import com.badlogic.gdx.graphics.Color
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Label
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.TextButton
|
||||
import com.unciv.logic.automation.UnitAutomation
|
||||
import com.unciv.logic.battle.Battle
|
||||
import com.unciv.logic.battle.CityCombatant
|
||||
import com.unciv.logic.battle.ICombatant
|
||||
import com.unciv.logic.battle.MapUnitCombatant
|
||||
import com.unciv.logic.map.TileInfo
|
||||
import com.unciv.logic.map.UnitType
|
||||
import com.unciv.ui.cityscreen.addClickListener
|
||||
import com.unciv.ui.utils.CameraStageBaseScreen
|
||||
import com.unciv.ui.utils.ImageGetter
|
||||
import com.unciv.ui.utils.centerX
|
||||
import com.unciv.ui.utils.disable
|
||||
import kotlin.math.max
|
||||
|
||||
@ -121,37 +120,14 @@ class BattleTable(val worldScreen: WorldScreen): Table() {
|
||||
|
||||
val attackerDistanceToTiles = attacker.unit.getDistanceToTiles()
|
||||
|
||||
val attackerCanReachDefender:Boolean
|
||||
var tileToMoveTo:TileInfo? = null
|
||||
|
||||
if(attacker.isMelee()){
|
||||
if(attacker.getTile().neighbors.contains(defender.getTile())){
|
||||
attackerCanReachDefender=true
|
||||
}
|
||||
else {
|
||||
val tilesCanAttackFrom = attackerDistanceToTiles.filter {
|
||||
attacker.unit.currentMovement - it.value > 0 // once we reach it we'll still have energy to attack
|
||||
&& it.key.unit == null
|
||||
&& it.key.neighbors.contains(defender.getTile())
|
||||
}
|
||||
|
||||
if (tilesCanAttackFrom.isEmpty()) attackerCanReachDefender=false
|
||||
else {
|
||||
tileToMoveTo = tilesCanAttackFrom.minBy { it.value }!!.key // travel least distance
|
||||
attackerCanReachDefender=true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else { // ranged
|
||||
val tilesInRange = attacker.getTile().getTilesInDistance(2)
|
||||
attackerCanReachDefender = tilesInRange.contains(defender.getTile())
|
||||
}
|
||||
val attackerCanReachDefender = UnitAutomation().getAttackableEnemies(attacker.unit)
|
||||
.contains(defender.getTile())
|
||||
|
||||
if(!attackerCanReachDefender || attacker.unit.currentMovement==0f) attackButton.disable()
|
||||
else {
|
||||
attackButton.addClickListener {
|
||||
if(tileToMoveTo!=null) attacker.unit.moveToTile(tileToMoveTo)
|
||||
if(attacker.isMelee())
|
||||
attacker.unit.movementAlgs().headTowards(defender.getTile())
|
||||
battle.attack(attacker, defender)
|
||||
worldScreen.update()
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user