mirror of
https://github.com/yairm210/Unciv.git
synced 2025-09-27 22:06:05 -04:00
Merge branch 'roadOnLongClick'
This commit is contained in:
commit
7e6b102f27
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Before Width: | Height: | Size: 988 KiB After Width: | Height: | Size: 988 KiB |
@ -227,6 +227,10 @@
|
||||
Japanese:"自動化を停止"
|
||||
}
|
||||
|
||||
"Construct road": {
|
||||
"German": "Straße bauen"
|
||||
}
|
||||
|
||||
"Fortify":{
|
||||
Italian:"Fortifica"
|
||||
Russian:"Укрепить"
|
||||
@ -258,7 +262,7 @@
|
||||
Romanian:"Dormi"
|
||||
Simplified_Chinese:"休眠"
|
||||
Portuguese:"Dormir"
|
||||
German:"Schlafen legen"
|
||||
German:"Schlafen"
|
||||
French:"Dormir"
|
||||
Japanese:"睡眠"
|
||||
Russian:"Спать"
|
||||
@ -266,6 +270,10 @@
|
||||
Spanish:"Dormir"
|
||||
}
|
||||
|
||||
"Moving": {
|
||||
"German": "Bewegen"
|
||||
}
|
||||
|
||||
"Set up":{ // For siege units
|
||||
Italian:"Monta"
|
||||
Russian:"Подготовится"
|
||||
|
@ -49,23 +49,20 @@ class WorkerAutomation(val unit: MapUnit) {
|
||||
|
||||
|
||||
|
||||
fun tryConnectingCities():Boolean{ // returns whether we actually did anything
|
||||
val techEnablingRailroad = GameBasics.TileImprovements["Railroad"]!!.techRequired!!
|
||||
val canBuildRailroad = unit.civInfo.tech.isResearched(techEnablingRailroad)
|
||||
fun tryConnectingCities():Boolean { // returns whether we actually did anything
|
||||
|
||||
val targetRoadName = if (canBuildRailroad) "Railroad" else "Road"
|
||||
val targetStatus = if (canBuildRailroad) RoadStatus.Railroad else RoadStatus.Road
|
||||
val targetRoad = unit.civInfo.tech.getBestRoadAvailable()
|
||||
|
||||
val citiesThatNeedConnecting = unit.civInfo.cities
|
||||
.filter { it.population.population>3 && !it.isCapital()
|
||||
&& !it.cityStats.isConnectedToCapital(targetStatus) }
|
||||
&& !it.cityStats.isConnectedToCapital(targetRoad) }
|
||||
if(citiesThatNeedConnecting.isEmpty()) return false // do nothing.
|
||||
|
||||
val citiesThatNeedConnectingBfs = citiesThatNeedConnecting
|
||||
.map { city -> BFS(city.getCenterTile()){it.isLand && unit.canPassThrough(it)} }
|
||||
.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() }
|
||||
|
||||
while(citiesThatNeedConnectingBfs.any()){
|
||||
@ -78,7 +75,7 @@ class WorkerAutomation(val unit: MapUnit) {
|
||||
for(city in connectedCities)
|
||||
if(bfs.tilesToCheck.contains(city)) { // we have a winner!
|
||||
val pathToCity = bfs.getPathTo(city)
|
||||
val roadableTiles = pathToCity.filter { it.roadStatus < targetStatus }
|
||||
val roadableTiles = pathToCity.filter { it.roadStatus < targetRoad }
|
||||
val tileToConstructRoadOn :TileInfo
|
||||
if(unit.currentTile in roadableTiles) tileToConstructRoadOn = unit.currentTile
|
||||
else{
|
||||
@ -88,8 +85,8 @@ class WorkerAutomation(val unit: MapUnit) {
|
||||
unit.movementAlgs().headTowards(tileToConstructRoadOn)
|
||||
}
|
||||
if(unit.currentMovement>0 && unit.currentTile==tileToConstructRoadOn
|
||||
&& unit.currentTile.improvementInProgress!=targetRoadName)
|
||||
tileToConstructRoadOn.startWorkingOnImprovement(GameBasics.TileImprovements[targetRoadName]!!,unit.civInfo)
|
||||
&& unit.currentTile.improvementInProgress!=targetRoad.name)
|
||||
tileToConstructRoadOn.startWorkingOnImprovement(targetRoad.improvement()!!,unit.civInfo)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package com.unciv.logic.civilization
|
||||
|
||||
|
||||
import com.badlogic.gdx.graphics.Color
|
||||
import com.unciv.logic.map.RoadStatus
|
||||
import com.unciv.models.gamebasics.GameBasics
|
||||
import com.unciv.models.gamebasics.tech.Technology
|
||||
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("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()
|
||||
}
|
||||
|
||||
fun stepUntilDestination(destination: TileInfo){
|
||||
fun stepUntilDestination(destination: TileInfo): BFS {
|
||||
while(!tilesReached.containsKey(destination) && tilesToCheck.isNotEmpty())
|
||||
nextStep()
|
||||
return this
|
||||
}
|
||||
|
||||
fun nextStep(){
|
||||
@ -39,8 +40,10 @@ class BFS(val startingPoint: TileInfo, val predicate : (TileInfo) -> Boolean){
|
||||
path.add(destination)
|
||||
var currentNode = destination
|
||||
while(currentNode != startingPoint){
|
||||
currentNode = tilesReached[currentNode]!!
|
||||
path.add(currentNode)
|
||||
tilesReached[currentNode]?.let {
|
||||
currentNode = it
|
||||
path.add(currentNode)
|
||||
} ?: return ArrayList() // destination is not in our path
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
@ -6,6 +6,8 @@ import com.unciv.Constants
|
||||
import com.unciv.logic.automation.UnitAutomation
|
||||
import com.unciv.logic.automation.WorkerAutomation
|
||||
import com.unciv.logic.civilization.CivilizationInfo
|
||||
import com.unciv.logic.map.action.MapUnitAction
|
||||
import com.unciv.logic.map.action.StringAction
|
||||
import com.unciv.models.gamebasics.GameBasics
|
||||
import com.unciv.models.gamebasics.tech.TechEra
|
||||
import com.unciv.models.gamebasics.tile.TerrainType
|
||||
@ -16,6 +18,7 @@ import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
class MapUnit {
|
||||
|
||||
@Transient lateinit var civInfo: CivilizationInfo
|
||||
@Transient lateinit var baseUnit: BaseUnit
|
||||
@Transient internal lateinit var currentTile :TileInfo
|
||||
@ -32,7 +35,15 @@ class MapUnit {
|
||||
lateinit var name: String
|
||||
var currentMovement: Float = 0f
|
||||
var health:Int = 100
|
||||
var action: String? = null // work, automation, fortifying, I dunno what.
|
||||
|
||||
var mapUnitAction : MapUnitAction? = null
|
||||
|
||||
var action: String? // work, automation, fortifying, I dunno what.
|
||||
// getter and setter for compatibility: make sure string-based actions still work
|
||||
get() = (mapUnitAction as? StringAction)?.action ?: mapUnitAction?.let { "" } // null if no action is assigned.
|
||||
set(value) { mapUnitAction = value?.let{ StringAction(this, value) } } // wrap traditional string-encoded actions into StringAction
|
||||
|
||||
|
||||
var attacksThisTurn = 0
|
||||
var promotions = UnitPromotions()
|
||||
var due: Boolean = true
|
||||
@ -169,7 +180,8 @@ class MapUnit {
|
||||
}
|
||||
|
||||
/**
|
||||
* Designates whether we can walk to the tile - without attacking
|
||||
* Designates whether we can enter the tile - without attacking
|
||||
* DOES NOT designate whether we can reach that tile in the current turn
|
||||
*/
|
||||
fun canMoveTo(tile: TileInfo): Boolean {
|
||||
if(!canPassThrough(tile)) return false
|
||||
@ -275,6 +287,7 @@ class MapUnit {
|
||||
//region state-changing functions
|
||||
fun setTransients(){
|
||||
promotions.unit=this
|
||||
mapUnitAction?.unit = this
|
||||
baseUnit=GameBasics.Units[name]!!
|
||||
updateUniques()
|
||||
}
|
||||
@ -292,10 +305,13 @@ class MapUnit {
|
||||
val enemyUnitsInWalkingDistance = getDistanceToTiles().keys
|
||||
.filter { it.militaryUnit!=null && civInfo.isAtWarWith(it.militaryUnit!!.civInfo)}
|
||||
if(enemyUnitsInWalkingDistance.isNotEmpty()) {
|
||||
if (action != null && action!!.startsWith("moveTo")) action=null
|
||||
if (mapUnitAction?.shouldStopOnEnemyInSight()==true)
|
||||
mapUnitAction=null
|
||||
return // Don't you dare move.
|
||||
}
|
||||
|
||||
mapUnitAction?.doPreTurnAction()
|
||||
|
||||
if (action != null && action!!.startsWith("moveTo")) {
|
||||
val destination = action!!.replace("moveTo ", "").split(",").dropLastWhile { it.isEmpty() }.toTypedArray()
|
||||
val destinationVector = Vector2(Integer.parseInt(destination[0]).toFloat(), Integer.parseInt(destination[1]).toFloat())
|
||||
@ -368,22 +384,19 @@ class MapUnit {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The tile that we reached this turn
|
||||
*/
|
||||
fun moveToTile(otherTile: TileInfo) {
|
||||
if(otherTile==getTile()) return // already here!
|
||||
val distanceToTiles = getDistanceToTiles()
|
||||
|
||||
class YouCantGetThereFromHereException : Exception()
|
||||
class YouCantGetThereFromHereException(msg: String) : Exception(msg)
|
||||
if (!distanceToTiles.containsKey(otherTile))
|
||||
throw YouCantGetThereFromHereException("$this can't get from ${currentTile.position} to ${otherTile.position}.")
|
||||
|
||||
throw YouCantGetThereFromHereException()
|
||||
|
||||
class CantEnterThisTileException : Exception()
|
||||
class CantEnterThisTileException(msg: String) : Exception(msg)
|
||||
if(!canMoveTo(otherTile))
|
||||
throw CantEnterThisTileException()
|
||||
if(otherTile.isCityCenter() && otherTile.getOwner()!=civInfo) throw Exception("This is an enemy city, you can't go here!")
|
||||
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!")
|
||||
|
||||
currentMovement -= distanceToTiles[otherTile]!!
|
||||
if (currentMovement < 0.1) currentMovement = 0f // silly floats which are "almost zero"
|
||||
|
@ -1,7 +1,18 @@
|
||||
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 {
|
||||
|
||||
None,
|
||||
Road,
|
||||
Railroad
|
||||
Railroad;
|
||||
|
||||
/** returns null for [None] */
|
||||
fun improvement() = GameBasics.TileImprovements[this.name]
|
||||
|
||||
}
|
||||
|
@ -137,7 +137,7 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
|
||||
*/
|
||||
fun headTowards(destination: TileInfo): TileInfo {
|
||||
val currentTile = unit.getTile()
|
||||
if(currentTile==destination) return currentTile
|
||||
if (currentTile == destination) return currentTile
|
||||
|
||||
val distanceToTiles = unit.getDistanceToTiles()
|
||||
|
||||
@ -152,7 +152,7 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
|
||||
return currentTile
|
||||
|
||||
val reachableDestinationNeighbors = destinationNeighbors
|
||||
.filter { distanceToTiles.containsKey(it) && unit.canMoveTo(it)}
|
||||
.filter { distanceToTiles.containsKey(it) && unit.canMoveTo(it) }
|
||||
if (reachableDestinationNeighbors.isEmpty()) // We can't get closer...
|
||||
return currentTile
|
||||
|
||||
@ -160,8 +160,8 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
|
||||
}
|
||||
} else { // If the tile is far away, we need to build a path how to get there, and then take the first step
|
||||
val path = getShortestPath(destination)
|
||||
class UnreachableDestinationException:Exception()
|
||||
if(path.isEmpty()) throw UnreachableDestinationException()
|
||||
class UnreachableDestinationException : Exception()
|
||||
if (path.isEmpty()) throw UnreachableDestinationException()
|
||||
destinationTileThisTurn = path.first()
|
||||
}
|
||||
|
||||
|
103
core/src/com/unciv/logic/map/action/BuildLongRoadAction.kt
Normal file
103
core/src/com/unciv/logic/map/action/BuildLongRoadAction.kt
Normal file
@ -0,0 +1,103 @@
|
||||
package com.unciv.logic.map.action
|
||||
|
||||
import com.badlogic.gdx.graphics.Color
|
||||
import com.unciv.logic.map.BFS
|
||||
import com.unciv.logic.map.MapUnit
|
||||
import com.unciv.logic.map.RoadStatus
|
||||
import com.unciv.logic.map.TileInfo
|
||||
|
||||
class BuildLongRoadAction(
|
||||
mapUnit: MapUnit = MapUnit(),
|
||||
val target: TileInfo = TileInfo()
|
||||
) : MapUnitAction(mapUnit) {
|
||||
|
||||
override fun name(): String = "Build Long Road"
|
||||
|
||||
override fun shouldStopOnEnemyInSight(): Boolean = true
|
||||
|
||||
override fun isAvailable(): Boolean
|
||||
= unit.hasUnique("Can build improvements on tiles")
|
||||
&& getPath(target).isNotEmpty()
|
||||
&& unit.civInfo.tech.getBestRoadAvailable() != RoadStatus.None
|
||||
|
||||
override fun doPreTurnAction() {
|
||||
|
||||
// we're working!
|
||||
if (unit.currentTile.improvementInProgress != null)
|
||||
return
|
||||
|
||||
if (startWorkingOnRoad())
|
||||
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)) {
|
||||
startWorkingOnRoad()
|
||||
} else if (unit.currentMovement > 1f) {
|
||||
unit.civInfo.addNotification("[${unit.name}] canceled building road: can't move forward.", unit.currentTile.position, Color.GRAY)
|
||||
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
|
||||
val tilesUnitCanCurrentlyReach = unit.getDistanceToTiles().keys
|
||||
for (step in getPath(destination).drop(1)) {
|
||||
if(step !in tilesUnitCanCurrentlyReach) return false // we're out of tiles in reachable distance, no need to check any further
|
||||
|
||||
if (unit.currentMovement > 0f) {
|
||||
if(unit.canMoveTo(step)) {
|
||||
unit.moveToTile(step)
|
||||
success = true
|
||||
// if there is a road already, take multiple steps, otherwise this is where we're going to build a road
|
||||
if (!isRoadFinished(step)) return true
|
||||
}
|
||||
else if(!isRoadFinished(step)){
|
||||
unit.civInfo.addNotification("[${unit.name}] skipped building road. It can't move here.", step.position, Color.GRAY)
|
||||
}
|
||||
// worker moves on even if the current step is blocked
|
||||
} 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 startWorkingOnRoad(): 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()
|
||||
) {
|
||||
open fun 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 = false
|
||||
}
|
||||
|
||||
|
27
core/src/com/unciv/logic/map/action/StringAction.kt
Normal file
27
core/src/com/unciv/logic/map/action/StringAction.kt
Normal file
@ -0,0 +1,27 @@
|
||||
package com.unciv.logic.map.action
|
||||
|
||||
import com.unciv.logic.map.MapUnit
|
||||
|
||||
/**
|
||||
* this class represents all actions that are identified by string only.
|
||||
* this is the traditional way of handling actions in UnCiv: by coding relevant information
|
||||
* into a string. This class is here to maintain compatibility to that method, preventing from a huge
|
||||
* refactoring going on here.
|
||||
*/
|
||||
class StringAction(
|
||||
unit: MapUnit = MapUnit(),
|
||||
val action: String = "" // traditional string-encoded action like "moveTo x,y"
|
||||
) : MapUnitAction(unit) {
|
||||
|
||||
override fun shouldStopOnEnemyInSight(): Boolean = action.startsWith("moveTo")
|
||||
|
||||
override fun name(): String {
|
||||
return when {
|
||||
// translate string-encoded actions to user-readable names
|
||||
action.startsWith("moveTo") -> "Moving"
|
||||
else -> action
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -103,7 +103,7 @@ class EmpireOverviewScreen : CameraStageBaseScreen(){
|
||||
generalTable.add(createOffersTable(otherCiv, trade.theirOffers, trade.ourOffers.size))
|
||||
return generalTable
|
||||
}
|
||||
|
||||
|
||||
private fun createOffersTable(civ: CivilizationInfo, offersList: TradeOffersList, numberOfOtherSidesOffers: Int): Table {
|
||||
val table = Table()
|
||||
table.defaults().pad(10f)
|
||||
@ -259,6 +259,7 @@ class EmpireOverviewScreen : CameraStageBaseScreen(){
|
||||
fun getUnitTable(): Table {
|
||||
val table=Table(skin).apply { defaults().pad(5f) }
|
||||
table.add("Name".tr())
|
||||
table.add("Action".tr())
|
||||
table.add("Strength".tr())
|
||||
table.add("Ranged strength".tr())
|
||||
table.add("Movement".tr())
|
||||
@ -274,6 +275,8 @@ class EmpireOverviewScreen : CameraStageBaseScreen(){
|
||||
UnCivGame.Current.worldScreen.tileMapHolder.setCenterPosition(unit.currentTile.position)
|
||||
}
|
||||
table.add(button).left()
|
||||
val mapUnitAction = unit.mapUnitAction
|
||||
if (mapUnitAction != null) table.add(mapUnitAction.name().tr()) else table.add()
|
||||
if(baseUnit.strength>0) table.add(baseUnit.strength.toString()) else table.add()
|
||||
if(baseUnit.rangedStrength>0) table.add(baseUnit.rangedStrength.toString()) else table.add()
|
||||
table.add(DecimalFormat("0.#").format(unit.currentMovement)+"/"+unit.getMaxMovement())
|
||||
|
@ -30,7 +30,7 @@ class GreatPersonPickerScreen : PickerScreen() {
|
||||
pick("Get " +unit.name)
|
||||
descriptionLabel.setText(unit.uniques.joinToString())
|
||||
}
|
||||
topTable.add(button).pad(10f)
|
||||
topTable.add(button).pad(10f).row()
|
||||
}
|
||||
|
||||
rightSideButton.onClick("choir") {
|
||||
|
@ -20,6 +20,7 @@ import com.unciv.logic.map.TileMap
|
||||
import com.unciv.models.gamebasics.unit.UnitType
|
||||
import com.unciv.ui.tilegroups.WorldTileGroup
|
||||
import com.unciv.ui.utils.*
|
||||
import com.unciv.ui.worldscreen.unit.UnitContextMenu
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
class TileMapHolder(internal val worldScreen: WorldScreen, internal val tileMap: TileMap) : ScrollPane(null) {
|
||||
@ -41,7 +42,15 @@ class TileMapHolder(internal val worldScreen: WorldScreen, internal val tileMap:
|
||||
val allTiles = TileGroupMap(daTileGroups,worldScreen.stage.width)
|
||||
|
||||
for(tileGroup in tileGroups.values){
|
||||
tileGroup.onClick{ onTileClicked(tileGroup.tileInfo)}
|
||||
tileGroup.addListener (object: ActorGestureListener() {
|
||||
override fun tap(event: InputEvent?, x: Float, y: Float, count: Int, button: Int) {
|
||||
onTileClicked(tileGroup.tileInfo)
|
||||
}
|
||||
override fun longPress(actor: Actor?, x: Float, y: Float): Boolean {
|
||||
return onTileLongClicked(tileGroup.tileInfo)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
actor = allTiles
|
||||
@ -77,7 +86,7 @@ class TileMapHolder(internal val worldScreen: WorldScreen, internal val tileMap:
|
||||
|
||||
private fun onTileClicked(tileInfo: TileInfo) {
|
||||
worldScreen.displayTutorials("TileClicked")
|
||||
if (unitActionOverlay != null) unitActionOverlay!!.remove()
|
||||
unitActionOverlay?.remove()
|
||||
selectedTile = tileInfo
|
||||
|
||||
val selectedUnit = worldScreen.bottomBar.unitTable.selectedUnit
|
||||
@ -121,7 +130,7 @@ class TileMapHolder(internal val worldScreen: WorldScreen, internal val tileMap:
|
||||
} else {
|
||||
// add "move to" button
|
||||
val moveHereButtonDto = MoveHereButtonDto(selectedUnit, tileInfo, turnsToGetThere)
|
||||
addMoveHereButtonToTile(moveHereButtonDto, tileGroups[moveHereButtonDto.tileInfo]!!)
|
||||
addMoveHereButtonToTile(moveHereButtonDto)
|
||||
}
|
||||
worldScreen.shouldUpdate = true
|
||||
}
|
||||
@ -130,7 +139,7 @@ class TileMapHolder(internal val worldScreen: WorldScreen, internal val tileMap:
|
||||
}
|
||||
|
||||
|
||||
private fun addMoveHereButtonToTile(dto: MoveHereButtonDto, tileGroup: WorldTileGroup) {
|
||||
private fun addMoveHereButtonToTile(dto: MoveHereButtonDto) {
|
||||
val size = 60f
|
||||
val moveHereButton = Group().apply { width = size;height = size; }
|
||||
moveHereButton.addActor(ImageGetter.getCircle().apply { width = size; height = size })
|
||||
@ -147,48 +156,45 @@ class TileMapHolder(internal val worldScreen: WorldScreen, internal val tileMap:
|
||||
moveHereButton.addActor(unitIcon)
|
||||
|
||||
if (dto.unit.currentMovement > 0)
|
||||
moveHereButton.onClick(""){onMoveButtonClick(dto)}
|
||||
|
||||
else moveHereButton.color.a = 0.5f
|
||||
addOverlayOnTileGroup(tileGroup, moveHereButton)
|
||||
moveHereButton.y += tileGroup.height
|
||||
unitActionOverlay = moveHereButton
|
||||
}
|
||||
|
||||
private fun onMoveButtonClick(dto: MoveHereButtonDto) {
|
||||
// this can take a long time, because of the unit-to-tile calculation needed, so we put it in a different thread
|
||||
thread {
|
||||
if (dto.unit.movementAlgs().canReach(dto.tileInfo)) {
|
||||
try {
|
||||
// Because this is darned concurrent (as it MUST be to avoid ANRs),
|
||||
// there are edge cases where the canReach is true,
|
||||
// but until it reaches the headTowards the board has changed and so the headTowards fails.
|
||||
// I can't think of any way to avoid this,
|
||||
// but it's so rare and edge-case-y that ignoring its failure is actually acceptable, hence the empty catch
|
||||
dto.unit.movementAlgs().headTowards(dto.tileInfo)
|
||||
Sounds.play("whoosh")
|
||||
if (dto.unit.currentTile != dto.tileInfo)
|
||||
dto.unit.action = "moveTo " + dto.tileInfo.position.x.toInt() + "," + dto.tileInfo.position.y.toInt()
|
||||
if(dto.unit.currentMovement>0){
|
||||
worldScreen.bottomBar.unitTable.selectedUnit=dto.unit
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
}
|
||||
moveHereButton.onClick(""){
|
||||
UnitContextMenu(this, dto.unit, dto.tileInfo).onMoveButtonClick()
|
||||
}
|
||||
|
||||
// we don't update it directly because we're on a different thread; instead, we tell it to update itself
|
||||
worldScreen.shouldUpdate = true
|
||||
|
||||
removeUnitActionOverlay=true
|
||||
}
|
||||
else moveHereButton.color.a = 0.5f
|
||||
addOverlayOnTileGroup(dto.tileInfo, moveHereButton)
|
||||
}
|
||||
|
||||
private fun addOverlayOnTileGroup(group:WorldTileGroup, actor: Actor) {
|
||||
|
||||
|
||||
fun onTileLongClicked(tileInfo: TileInfo): Boolean {
|
||||
|
||||
unitActionOverlay?.remove()
|
||||
selectedTile = tileInfo
|
||||
val selectedUnit = worldScreen.bottomBar.unitTable.selectedUnit
|
||||
worldScreen.bottomBar.unitTable.tileSelected(tileInfo)
|
||||
worldScreen.shouldUpdate = true
|
||||
|
||||
if (selectedUnit != null) {
|
||||
addOverlayOnTileGroup(tileInfo, UnitContextMenu(this, selectedUnit, tileInfo))
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private fun addOverlayOnTileGroup(tileInfo: TileInfo, actor: Actor) {
|
||||
|
||||
val group = tileGroups[tileInfo]!!
|
||||
|
||||
actor.center(group)
|
||||
actor.x+=group.x
|
||||
actor.y+=group.y
|
||||
group.parent.addActor(actor)
|
||||
actor.toFront()
|
||||
|
||||
actor.y += actor.height
|
||||
unitActionOverlay = actor
|
||||
|
||||
}
|
||||
|
||||
internal fun updateTiles(civInfo: CivilizationInfo) {
|
||||
@ -216,20 +222,23 @@ class TileMapHolder(internal val worldScreen: WorldScreen, internal val tileMap:
|
||||
tileGroup.showCircle(Color.RED) // Display ALL viewable enemies with a red circle so that users don't need to go "hunting" for enemy units
|
||||
}
|
||||
|
||||
if (worldScreen.bottomBar.unitTable.selectedCity!=null){
|
||||
val city = worldScreen.bottomBar.unitTable.selectedCity!!
|
||||
updateTilegroupsForSelectedCity(city, playerViewableTilePositions)
|
||||
} else if(worldScreen.bottomBar.unitTable.selectedUnit!=null){
|
||||
val unit = worldScreen.bottomBar.unitTable.selectedUnit!!
|
||||
updateTilegroupsForSelectedUnit(unit, playerViewableTilePositions)
|
||||
}
|
||||
else if(unitActionOverlay!=null){
|
||||
unitActionOverlay!!.remove()
|
||||
unitActionOverlay=null
|
||||
val unitTable = worldScreen.bottomBar.unitTable
|
||||
when {
|
||||
unitTable.selectedCity!=null -> {
|
||||
val city = unitTable.selectedCity!!
|
||||
updateTilegroupsForSelectedCity(city, playerViewableTilePositions)
|
||||
}
|
||||
unitTable.selectedUnit!=null -> {
|
||||
val unit = unitTable.selectedUnit!!
|
||||
updateTilegroupsForSelectedUnit(unit, playerViewableTilePositions)
|
||||
}
|
||||
unitActionOverlay!=null -> {
|
||||
unitActionOverlay!!.remove()
|
||||
unitActionOverlay=null
|
||||
}
|
||||
}
|
||||
|
||||
if(selectedTile!=null)
|
||||
tileGroups[selectedTile!!]!!.showCircle(Color.WHITE)
|
||||
tileGroups[selectedTile]?.showCircle(Color.WHITE)
|
||||
}
|
||||
|
||||
private fun updateTilegroupsForSelectedUnit(unit: MapUnit, playerViewableTilePositions: HashSet<Vector2>) {
|
||||
|
@ -246,8 +246,6 @@ class WorldScreen : CameraStageBaseScreen() {
|
||||
return@onClick
|
||||
}
|
||||
|
||||
bottomBar.unitTable.currentlyExecutingAction = null
|
||||
|
||||
Gdx.input.inputProcessor = null // remove input processing - nothing will be clicked!
|
||||
nextTurnButton.disable()
|
||||
nextTurnButton.setText("Working...".tr())
|
||||
|
@ -34,7 +34,6 @@ class UnitActions {
|
||||
if(unit.action!=null && unit.action!!.startsWith("moveTo")) {
|
||||
actionList +=
|
||||
UnitAction("Stop movement", true) {
|
||||
unitTable.currentlyExecutingAction = null
|
||||
unit.action = null
|
||||
}
|
||||
}
|
||||
@ -146,7 +145,6 @@ class UnitActions {
|
||||
|
||||
unit.civInfo.addCity(tile.position)
|
||||
tile.improvement = null
|
||||
unitTable.currentlyExecutingAction = null // In case the settler was in the middle of doing something and we then founded a city with it
|
||||
unit.destroy()
|
||||
}.sound("chimes")
|
||||
}
|
||||
|
85
core/src/com/unciv/ui/worldscreen/unit/UnitContextMenu.kt
Normal file
85
core/src/com/unciv/ui/worldscreen/unit/UnitContextMenu.kt
Normal file
@ -0,0 +1,85 @@
|
||||
package com.unciv.ui.worldscreen.unit
|
||||
|
||||
import com.badlogic.gdx.scenes.scene2d.Actor
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Button
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.VerticalGroup
|
||||
import com.unciv.logic.map.MapUnit
|
||||
import com.unciv.logic.map.TileInfo
|
||||
import com.unciv.ui.utils.CameraStageBaseScreen
|
||||
import com.unciv.ui.utils.ImageGetter
|
||||
import com.unciv.ui.utils.Sounds
|
||||
import com.unciv.ui.utils.onClick
|
||||
import com.unciv.ui.worldscreen.TileMapHolder
|
||||
import com.unciv.logic.map.action.BuildLongRoadAction
|
||||
import com.unciv.logic.map.action.MapUnitAction
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
class UnitContextMenu(val tileMapHolder: TileMapHolder, val selectedUnit: MapUnit, val targetTile: TileInfo) : VerticalGroup() {
|
||||
|
||||
init {
|
||||
|
||||
space(10f)
|
||||
|
||||
addButton(ImageGetter.getStatIcon("Movement"), "Move unit") {
|
||||
onMoveButtonClick()
|
||||
}
|
||||
|
||||
addButton(
|
||||
ImageGetter.getImprovementIcon("Road"),
|
||||
"Construct road",
|
||||
BuildLongRoadAction(selectedUnit, targetTile)
|
||||
)
|
||||
|
||||
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) {
|
||||
val skin = CameraStageBaseScreen.skin
|
||||
val button = Button(skin)
|
||||
button.add(icon).size(20f).padRight(10f)
|
||||
button.add(label)
|
||||
addActor(button)
|
||||
button.onClick { action() }
|
||||
}
|
||||
|
||||
fun onMoveButtonClick() {
|
||||
// this can take a long time, because of the unit-to-tile calculation needed, so we put it in a different thread
|
||||
thread {
|
||||
if (selectedUnit.movementAlgs().canReach(targetTile)) {
|
||||
try {
|
||||
// Because this is darned concurrent (as it MUST be to avoid ANRs),
|
||||
// there are edge cases where the canReach is true,
|
||||
// but until it reaches the headTowards the board has changed and so the headTowards fails.
|
||||
// I can't think of any way to avoid this,
|
||||
// but it's so rare and edge-case-y that ignoring its failure is actually acceptable, hence the empty catch
|
||||
selectedUnit.movementAlgs().headTowards(targetTile)
|
||||
Sounds.play("whoosh")
|
||||
if (selectedUnit.currentTile != targetTile)
|
||||
selectedUnit.action = "moveTo " + targetTile.position.x.toInt() + "," + targetTile.position.y.toInt()
|
||||
if(selectedUnit.currentMovement>0){
|
||||
tileMapHolder.worldScreen.bottomBar.unitTable.selectedUnit=selectedUnit
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
}
|
||||
}
|
||||
|
||||
// we don't update it directly because we're on a different thread; instead, we tell it to update itself
|
||||
tileMapHolder.worldScreen.shouldUpdate = true
|
||||
|
||||
tileMapHolder.removeUnitActionOverlay=true
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -22,8 +22,6 @@ class UnitTable(val worldScreen: WorldScreen) : Table(){
|
||||
private val unitDescriptionTable = Table(CameraStageBaseScreen.skin)
|
||||
var selectedUnit : MapUnit? = null
|
||||
var selectedCity : CityInfo? = null
|
||||
var currentlyExecutingAction : String? = null
|
||||
var lastSelectedCityButton : Boolean = false
|
||||
val deselectUnitButton = Table()
|
||||
val helpUnitButton = Table()
|
||||
|
||||
@ -88,12 +86,10 @@ class UnitTable(val worldScreen: WorldScreen) : Table(){
|
||||
if (selectedUnit!!.civInfo != worldScreen.currentPlayerCiv) { // The unit that was selected, was captured. It exists but is no longer ours.
|
||||
selectedUnit = null
|
||||
selectedCity = null
|
||||
currentlyExecutingAction = null
|
||||
selectedUnitHasChanged = true
|
||||
} else if (selectedUnit!! !in selectedUnit!!.getTile().getUnits()) { // The unit that was there no longer exists}
|
||||
selectedUnit = null
|
||||
selectedCity = null
|
||||
currentlyExecutingAction = null
|
||||
selectedUnitHasChanged = true
|
||||
}
|
||||
}
|
||||
@ -190,20 +186,7 @@ class UnitTable(val worldScreen: WorldScreen) : Table(){
|
||||
fun tileSelected(selectedTile: TileInfo) {
|
||||
|
||||
val previouslySelectedUnit = selectedUnit
|
||||
if(currentlyExecutingAction=="moveTo"){
|
||||
if(selectedUnit!!.movementAlgs()
|
||||
.getShortestPath(selectedTile).isEmpty())
|
||||
return // can't reach there with the selected unit, watcha want me to do?
|
||||
|
||||
val reachedTile = selectedUnit!!.movementAlgs().headTowards(selectedTile)
|
||||
|
||||
selectedUnit!!.action=null // Disable any prior action (automation, fortification...)
|
||||
if(reachedTile!=selectedTile) // Didn't get all the way there
|
||||
selectedUnit!!.action = "moveTo " + selectedTile.position.x.toInt() + "," + selectedTile.position.y.toInt()
|
||||
currentlyExecutingAction = null
|
||||
}
|
||||
|
||||
else if(selectedTile.militaryUnit!=null && selectedTile.militaryUnit!!.civInfo == worldScreen.currentPlayerCiv
|
||||
if(selectedTile.militaryUnit!=null && selectedTile.militaryUnit!!.civInfo == worldScreen.currentPlayerCiv
|
||||
&& selectedUnit!=selectedTile.militaryUnit
|
||||
&& (selectedTile.civilianUnit==null || selectedUnit!=selectedTile.civilianUnit)){
|
||||
selectedUnit = selectedTile.militaryUnit
|
||||
|
@ -17,7 +17,7 @@ class DesktopLauncher {
|
||||
// This is so they don't look all pixelated
|
||||
settings.filterMag = Texture.TextureFilter.MipMapLinearLinear;
|
||||
settings.filterMin = Texture.TextureFilter.MipMapLinearLinear;
|
||||
TexturePacker.process(settings, "../images", ".", "game");
|
||||
TexturePacker.process(settings, "../Images", ".", "game");
|
||||
|
||||
LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
|
||||
new LwjglApplication(new UnCivGame("Desktop"), config);
|
||||
|
Loading…
x
Reference in New Issue
Block a user