mirror of
https://github.com/yairm210/Unciv.git
synced 2025-09-30 07:21:34 -04:00
implementing BuildLongRoadAction
This commit is contained in:
parent
a8c3b3755b
commit
98803f0454
@ -227,6 +227,10 @@
|
|||||||
Japanese:"自動化を停止"
|
Japanese:"自動化を停止"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"Construct road": {
|
||||||
|
"German": "Straße bauen"
|
||||||
|
}
|
||||||
|
|
||||||
"Fortify":{
|
"Fortify":{
|
||||||
Italian:"Fortifica"
|
Italian:"Fortifica"
|
||||||
Russian:"Укрепить"
|
Russian:"Укрепить"
|
||||||
|
@ -50,22 +50,19 @@ class WorkerAutomation(val unit: MapUnit) {
|
|||||||
|
|
||||||
|
|
||||||
fun tryConnectingCities():Boolean { // returns whether we actually did anything
|
fun tryConnectingCities():Boolean { // returns whether we actually did anything
|
||||||
val techEnablingRailroad = GameBasics.TileImprovements["Railroad"]!!.techRequired!!
|
|
||||||
val canBuildRailroad = unit.civInfo.tech.isResearched(techEnablingRailroad)
|
|
||||||
|
|
||||||
val targetRoadName = if (canBuildRailroad) "Railroad" else "Road"
|
val targetRoad = unit.civInfo.tech.getBestRoadAvailable()
|
||||||
val targetStatus = if (canBuildRailroad) RoadStatus.Railroad else RoadStatus.Road
|
|
||||||
|
|
||||||
val citiesThatNeedConnecting = unit.civInfo.cities
|
val citiesThatNeedConnecting = unit.civInfo.cities
|
||||||
.filter { it.population.population>3 && !it.isCapital()
|
.filter { it.population.population>3 && !it.isCapital()
|
||||||
&& !it.cityStats.isConnectedToCapital(targetStatus) }
|
&& !it.cityStats.isConnectedToCapital(targetRoad) }
|
||||||
if(citiesThatNeedConnecting.isEmpty()) return false // do nothing.
|
if(citiesThatNeedConnecting.isEmpty()) return false // do nothing.
|
||||||
|
|
||||||
val citiesThatNeedConnectingBfs = citiesThatNeedConnecting
|
val citiesThatNeedConnectingBfs = citiesThatNeedConnecting
|
||||||
.map { city -> BFS(city.getCenterTile()){it.isLand && unit.canPassThrough(it)} }
|
.map { city -> BFS(city.getCenterTile()){it.isLand && unit.canPassThrough(it)} }
|
||||||
.toMutableList()
|
.toMutableList()
|
||||||
|
|
||||||
val connectedCities = unit.civInfo.cities.filter { it.isCapital() || it.cityStats.isConnectedToCapital(targetStatus) }
|
val connectedCities = unit.civInfo.cities.filter { it.isCapital() || it.cityStats.isConnectedToCapital(targetRoad) }
|
||||||
.map { it.getCenterTile() }
|
.map { it.getCenterTile() }
|
||||||
|
|
||||||
while(citiesThatNeedConnectingBfs.any()){
|
while(citiesThatNeedConnectingBfs.any()){
|
||||||
@ -78,7 +75,7 @@ class WorkerAutomation(val unit: MapUnit) {
|
|||||||
for(city in connectedCities)
|
for(city in connectedCities)
|
||||||
if(bfs.tilesToCheck.contains(city)) { // we have a winner!
|
if(bfs.tilesToCheck.contains(city)) { // we have a winner!
|
||||||
val pathToCity = bfs.getPathTo(city)
|
val pathToCity = bfs.getPathTo(city)
|
||||||
val roadableTiles = pathToCity.filter { it.roadStatus < targetStatus }
|
val roadableTiles = pathToCity.filter { it.roadStatus < targetRoad }
|
||||||
val tileToConstructRoadOn :TileInfo
|
val tileToConstructRoadOn :TileInfo
|
||||||
if(unit.currentTile in roadableTiles) tileToConstructRoadOn = unit.currentTile
|
if(unit.currentTile in roadableTiles) tileToConstructRoadOn = unit.currentTile
|
||||||
else{
|
else{
|
||||||
@ -88,8 +85,8 @@ class WorkerAutomation(val unit: MapUnit) {
|
|||||||
unit.movementAlgs().headTowards(tileToConstructRoadOn)
|
unit.movementAlgs().headTowards(tileToConstructRoadOn)
|
||||||
}
|
}
|
||||||
if(unit.currentMovement>0 && unit.currentTile==tileToConstructRoadOn
|
if(unit.currentMovement>0 && unit.currentTile==tileToConstructRoadOn
|
||||||
&& unit.currentTile.improvementInProgress!=targetRoadName)
|
&& unit.currentTile.improvementInProgress!=targetRoad.name)
|
||||||
tileToConstructRoadOn.startWorkingOnImprovement(GameBasics.TileImprovements[targetRoadName]!!,unit.civInfo)
|
tileToConstructRoadOn.startWorkingOnImprovement(targetRoad.improvement()!!,unit.civInfo)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package com.unciv.logic.civilization
|
|||||||
|
|
||||||
|
|
||||||
import com.badlogic.gdx.graphics.Color
|
import com.badlogic.gdx.graphics.Color
|
||||||
|
import com.unciv.logic.map.RoadStatus
|
||||||
import com.unciv.models.gamebasics.GameBasics
|
import com.unciv.models.gamebasics.GameBasics
|
||||||
import com.unciv.models.gamebasics.tech.Technology
|
import com.unciv.models.gamebasics.tech.Technology
|
||||||
import com.unciv.models.gamebasics.tr
|
import com.unciv.models.gamebasics.tr
|
||||||
@ -196,4 +197,13 @@ class TechManager {
|
|||||||
if(researchedTechUniques.contains("Enables embarked units to enter ocean tiles")) embarkedUnitsCanEnterOcean=true
|
if(researchedTechUniques.contains("Enables embarked units to enter ocean tiles")) embarkedUnitsCanEnterOcean=true
|
||||||
if(researchedTechUniques.contains("Improves movement speed on roads")) movementSpeedOnRoadsImproved = true
|
if(researchedTechUniques.contains("Improves movement speed on roads")) movementSpeedOnRoadsImproved = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getBestRoadAvailable(): RoadStatus {
|
||||||
|
if (!isResearched(RoadStatus.Road.improvement()!!.techRequired!!)) return RoadStatus.None
|
||||||
|
|
||||||
|
val techEnablingRailroad = RoadStatus.Railroad.improvement()!!.techRequired!!
|
||||||
|
val canBuildRailroad = isResearched(techEnablingRailroad)
|
||||||
|
|
||||||
|
return if (canBuildRailroad) RoadStatus.Railroad else RoadStatus.Road
|
||||||
|
}
|
||||||
}
|
}
|
@ -18,9 +18,10 @@ class BFS(val startingPoint: TileInfo, val predicate : (TileInfo) -> Boolean){
|
|||||||
nextStep()
|
nextStep()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun stepUntilDestination(destination: TileInfo){
|
fun stepUntilDestination(destination: TileInfo): BFS {
|
||||||
while(!tilesReached.containsKey(destination) && tilesToCheck.isNotEmpty())
|
while(!tilesReached.containsKey(destination) && tilesToCheck.isNotEmpty())
|
||||||
nextStep()
|
nextStep()
|
||||||
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
fun nextStep(){
|
fun nextStep(){
|
||||||
@ -39,8 +40,10 @@ class BFS(val startingPoint: TileInfo, val predicate : (TileInfo) -> Boolean){
|
|||||||
path.add(destination)
|
path.add(destination)
|
||||||
var currentNode = destination
|
var currentNode = destination
|
||||||
while(currentNode != startingPoint){
|
while(currentNode != startingPoint){
|
||||||
currentNode = tilesReached[currentNode]!!
|
tilesReached[currentNode]?.let {
|
||||||
|
currentNode = it
|
||||||
path.add(currentNode)
|
path.add(currentNode)
|
||||||
|
} ?: return ArrayList() // destination is not in our path
|
||||||
}
|
}
|
||||||
return path
|
return path
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import com.unciv.Constants
|
|||||||
import com.unciv.logic.automation.UnitAutomation
|
import com.unciv.logic.automation.UnitAutomation
|
||||||
import com.unciv.logic.automation.WorkerAutomation
|
import com.unciv.logic.automation.WorkerAutomation
|
||||||
import com.unciv.logic.civilization.CivilizationInfo
|
import com.unciv.logic.civilization.CivilizationInfo
|
||||||
|
import com.unciv.logic.map.action.MapUnitAction
|
||||||
import com.unciv.models.gamebasics.GameBasics
|
import com.unciv.models.gamebasics.GameBasics
|
||||||
import com.unciv.models.gamebasics.tech.TechEra
|
import com.unciv.models.gamebasics.tech.TechEra
|
||||||
import com.unciv.models.gamebasics.tile.TerrainType
|
import com.unciv.models.gamebasics.tile.TerrainType
|
||||||
@ -300,10 +301,13 @@ class MapUnit {
|
|||||||
val enemyUnitsInWalkingDistance = getDistanceToTiles().keys
|
val enemyUnitsInWalkingDistance = getDistanceToTiles().keys
|
||||||
.filter { it.militaryUnit!=null && civInfo.isAtWarWith(it.militaryUnit!!.civInfo)}
|
.filter { it.militaryUnit!=null && civInfo.isAtWarWith(it.militaryUnit!!.civInfo)}
|
||||||
if(enemyUnitsInWalkingDistance.isNotEmpty()) {
|
if(enemyUnitsInWalkingDistance.isNotEmpty()) {
|
||||||
if (action != null && action!!.startsWith("moveTo")) action=null
|
if (mapUnitAction?.shouldStopOnEnemyInSight()==true)
|
||||||
|
mapUnitAction=null
|
||||||
return // Don't you dare move.
|
return // Don't you dare move.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mapUnitAction?.doPreTurnAction()
|
||||||
|
|
||||||
if (action != null && action!!.startsWith("moveTo")) {
|
if (action != null && action!!.startsWith("moveTo")) {
|
||||||
val destination = action!!.replace("moveTo ", "").split(",").dropLastWhile { it.isEmpty() }.toTypedArray()
|
val destination = action!!.replace("moveTo ", "").split(",").dropLastWhile { it.isEmpty() }.toTypedArray()
|
||||||
val destinationVector = Vector2(Integer.parseInt(destination[0]).toFloat(), Integer.parseInt(destination[1]).toFloat())
|
val destinationVector = Vector2(Integer.parseInt(destination[0]).toFloat(), Integer.parseInt(destination[1]).toFloat())
|
||||||
@ -383,14 +387,13 @@ class MapUnit {
|
|||||||
if(otherTile==getTile()) return // already here!
|
if(otherTile==getTile()) return // already here!
|
||||||
val distanceToTiles = getDistanceToTiles()
|
val distanceToTiles = getDistanceToTiles()
|
||||||
|
|
||||||
class YouCantGetThereFromHereException : Exception()
|
class YouCantGetThereFromHereException(msg: String) : Exception(msg)
|
||||||
if (!distanceToTiles.containsKey(otherTile))
|
if (!distanceToTiles.containsKey(otherTile))
|
||||||
|
throw YouCantGetThereFromHereException("$this can't get from ${currentTile.position} to ${otherTile.position}.")
|
||||||
|
|
||||||
throw YouCantGetThereFromHereException()
|
class CantEnterThisTileException(msg: String) : Exception(msg)
|
||||||
|
|
||||||
class CantEnterThisTileException : Exception()
|
|
||||||
if(!canMoveTo(otherTile))
|
if(!canMoveTo(otherTile))
|
||||||
throw CantEnterThisTileException()
|
throw CantEnterThisTileException("$this can't enter $otherTile")
|
||||||
if(otherTile.isCityCenter() && otherTile.getOwner()!=civInfo) throw Exception("This is an enemy city, you can't go here!")
|
if(otherTile.isCityCenter() && otherTile.getOwner()!=civInfo) throw Exception("This is an enemy city, you can't go here!")
|
||||||
|
|
||||||
currentMovement -= distanceToTiles[otherTile]!!
|
currentMovement -= distanceToTiles[otherTile]!!
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
package com.unciv.logic.map
|
|
||||||
|
|
||||||
open class MapUnitAction(
|
|
||||||
@Transient var unit: MapUnit = MapUnit(),
|
|
||||||
var name: String = ""
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class BuildLongRoadAction(
|
|
||||||
mapUnit: MapUnit = MapUnit()
|
|
||||||
) : MapUnitAction(mapUnit, "Build Long Road") {
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
@ -1,7 +1,18 @@
|
|||||||
package com.unciv.logic.map
|
package com.unciv.logic.map
|
||||||
|
|
||||||
|
import com.unciv.models.gamebasics.GameBasics
|
||||||
|
|
||||||
|
/**
|
||||||
|
* You can use RoadStatus.name to identify [Road] and [Railroad]
|
||||||
|
* in string-based identification, as done in [improvement].
|
||||||
|
*/
|
||||||
enum class RoadStatus {
|
enum class RoadStatus {
|
||||||
|
|
||||||
None,
|
None,
|
||||||
Road,
|
Road,
|
||||||
Railroad
|
Railroad;
|
||||||
|
|
||||||
|
/** returns null for [None] */
|
||||||
|
fun improvement() = GameBasics.TileImprovements[this.name]
|
||||||
|
|
||||||
}
|
}
|
||||||
|
89
core/src/com/unciv/logic/map/action/BuildLongRoadAction.kt
Normal file
89
core/src/com/unciv/logic/map/action/BuildLongRoadAction.kt
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
package com.unciv.logic.map.action
|
||||||
|
|
||||||
|
import com.unciv.logic.map.BFS
|
||||||
|
import com.unciv.logic.map.MapUnit
|
||||||
|
import com.unciv.logic.map.TileInfo
|
||||||
|
|
||||||
|
class BuildLongRoadAction(
|
||||||
|
mapUnit: MapUnit = MapUnit(),
|
||||||
|
val target: TileInfo = TileInfo()
|
||||||
|
) : MapUnitAction(mapUnit, "Build Long Road") {
|
||||||
|
|
||||||
|
override fun shouldStopOnEnemyInSight(): Boolean = true
|
||||||
|
|
||||||
|
override fun isAvailable(): Boolean
|
||||||
|
= unit.hasUnique("Can build improvements on tiles")
|
||||||
|
&& getPath(target).isNotEmpty()
|
||||||
|
|
||||||
|
override fun doPreTurnAction() {
|
||||||
|
|
||||||
|
// we're working!
|
||||||
|
if (unit.currentTile.improvementInProgress != null)
|
||||||
|
return
|
||||||
|
else if (startWorking()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// we reached our target? And road is finished?
|
||||||
|
if (unit.currentTile.position == target.position
|
||||||
|
&& isRoadFinished(unit.currentTile)) {
|
||||||
|
unit.action = null
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// move one step forward - and start building
|
||||||
|
if (stepForward(target)) {
|
||||||
|
startWorking()
|
||||||
|
} else if (unit.currentMovement > 1f) {
|
||||||
|
unit.action = null
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// because the unit is building a road, we need to use a shortest path that is
|
||||||
|
// independent of movement costs, but should respect impassable terrain like water and enemy territory
|
||||||
|
private fun stepForward(destination: TileInfo): Boolean {
|
||||||
|
var success = false
|
||||||
|
for (step in getPath(destination).drop(1)) {
|
||||||
|
if (unit.currentMovement > 0f && unit.canMoveTo(step)) {
|
||||||
|
unit.moveToTile(step)
|
||||||
|
success = true
|
||||||
|
// if there is a road already, take multiple steps, otherwise break
|
||||||
|
if (!isRoadFinished(step)) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else break
|
||||||
|
}
|
||||||
|
return success
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isRoadFinished(tile: TileInfo): Boolean {
|
||||||
|
return tile.roadStatus == unit.civInfo.tech.getBestRoadAvailable()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getPath(destination: TileInfo): List<TileInfo> {
|
||||||
|
// BFS is not very efficient
|
||||||
|
return BFS(unit.currentTile) { isRoadableTile(it) }
|
||||||
|
.stepUntilDestination(destination)
|
||||||
|
.getPathTo(destination).reversed()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isRoadableTile(it: TileInfo) = it.isLand && unit.canPassThrough(it)
|
||||||
|
|
||||||
|
private fun startWorking(): Boolean {
|
||||||
|
val tile = unit.currentTile
|
||||||
|
if (unit.currentMovement > 0 && isRoadableTile(tile)) {
|
||||||
|
val roadToBuild = unit.civInfo.tech.getBestRoadAvailable()
|
||||||
|
roadToBuild.improvement()?.let { improvement ->
|
||||||
|
if (tile.roadStatus != roadToBuild && tile.improvementInProgress != improvement.name) {
|
||||||
|
tile.startWorkingOnImprovement(improvement, unit.civInfo)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
15
core/src/com/unciv/logic/map/action/MapUnitAction.kt
Normal file
15
core/src/com/unciv/logic/map/action/MapUnitAction.kt
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package com.unciv.logic.map.action
|
||||||
|
|
||||||
|
import com.unciv.logic.map.MapUnit
|
||||||
|
|
||||||
|
open class MapUnitAction(
|
||||||
|
@Transient var unit: MapUnit = MapUnit(),
|
||||||
|
var name: String = ""
|
||||||
|
) {
|
||||||
|
/** return true if this action is possible in the given conditions */
|
||||||
|
open fun isAvailable(): Boolean = true
|
||||||
|
open fun doPreTurnAction() {}
|
||||||
|
open fun shouldStopOnEnemyInSight(): Boolean = name.startsWith("moveTo")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -30,7 +30,7 @@ class GreatPersonPickerScreen : PickerScreen() {
|
|||||||
pick("Get " +unit.name)
|
pick("Get " +unit.name)
|
||||||
descriptionLabel.setText(unit.uniques.joinToString())
|
descriptionLabel.setText(unit.uniques.joinToString())
|
||||||
}
|
}
|
||||||
topTable.add(button).pad(10f)
|
topTable.add(button).pad(10f).row()
|
||||||
}
|
}
|
||||||
|
|
||||||
rightSideButton.onClick("choir") {
|
rightSideButton.onClick("choir") {
|
||||||
|
@ -10,7 +10,8 @@ import com.unciv.ui.utils.ImageGetter
|
|||||||
import com.unciv.ui.utils.Sounds
|
import com.unciv.ui.utils.Sounds
|
||||||
import com.unciv.ui.utils.onClick
|
import com.unciv.ui.utils.onClick
|
||||||
import com.unciv.ui.worldscreen.TileMapHolder
|
import com.unciv.ui.worldscreen.TileMapHolder
|
||||||
import com.unciv.logic.map.BuildLongRoadAction
|
import com.unciv.logic.map.action.BuildLongRoadAction
|
||||||
|
import com.unciv.logic.map.action.MapUnitAction
|
||||||
import kotlin.concurrent.thread
|
import kotlin.concurrent.thread
|
||||||
|
|
||||||
class UnitContextMenu(val tileMapHolder: TileMapHolder, val selectedUnit: MapUnit, val targetTile: TileInfo) : VerticalGroup() {
|
class UnitContextMenu(val tileMapHolder: TileMapHolder, val selectedUnit: MapUnit, val targetTile: TileInfo) : VerticalGroup() {
|
||||||
@ -19,17 +20,31 @@ class UnitContextMenu(val tileMapHolder: TileMapHolder, val selectedUnit: MapUni
|
|||||||
|
|
||||||
space(10f)
|
space(10f)
|
||||||
|
|
||||||
addButton(ImageGetter.getStatIcon("Movement"), "Move here") {
|
addButton(ImageGetter.getStatIcon("Movement"), "Move unit") {
|
||||||
onMoveButtonClick()
|
onMoveButtonClick()
|
||||||
}
|
}
|
||||||
addButton(ImageGetter.getImprovementIcon("Road"), "Construct Road this way") {
|
|
||||||
onConstructRoadButtonClick()
|
addButton(
|
||||||
}
|
ImageGetter.getImprovementIcon("Road"),
|
||||||
|
"Construct road",
|
||||||
|
BuildLongRoadAction(selectedUnit, targetTile)
|
||||||
|
)
|
||||||
|
|
||||||
pack()
|
pack()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun addButton(icon: Actor, label: String, action: MapUnitAction) {
|
||||||
|
if (action.isAvailable()) {
|
||||||
|
addButton(icon, label) {
|
||||||
|
selectedUnit.mapUnitAction = action
|
||||||
|
selectedUnit.mapUnitAction?.doPreTurnAction()
|
||||||
|
tileMapHolder.removeUnitActionOverlay = true
|
||||||
|
tileMapHolder.worldScreen.shouldUpdate = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun addButton(icon: Actor, label: String, action: () -> Unit) {
|
fun addButton(icon: Actor, label: String, action: () -> Unit) {
|
||||||
val skin = CameraStageBaseScreen.skin
|
val skin = CameraStageBaseScreen.skin
|
||||||
val button = Button(skin)
|
val button = Button(skin)
|
||||||
@ -67,7 +82,4 @@ class UnitContextMenu(val tileMapHolder: TileMapHolder, val selectedUnit: MapUni
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onConstructRoadButtonClick() {
|
|
||||||
selectedUnit.mapUnitAction = BuildLongRoadAction()
|
|
||||||
}
|
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user