diff --git a/core/src/com/unciv/logic/civilization/CivilizationInfo.kt b/core/src/com/unciv/logic/civilization/CivilizationInfo.kt index 41c6810547..39b49d4a67 100644 --- a/core/src/com/unciv/logic/civilization/CivilizationInfo.kt +++ b/core/src/com/unciv/logic/civilization/CivilizationInfo.kt @@ -211,6 +211,12 @@ class CivilizationInfo { */ var attacksSinceTurnStart = ArrayList() + /** + * Queue of all civilization units that have their actions possibly pending. Internally + * might include e.g. non-idle units, so must be filtered before being given to any outside code. + * (Should be small enough that using ArrayList is fine.) + */ + private var dueUnits = ArrayList() var hasMovedAutomatedUnits = false @Transient @@ -264,6 +270,7 @@ class CivilizationInfo { toReturn.totalCultureForContests = totalCultureForContests toReturn.totalFaithForContests = totalFaithForContests toReturn.attacksSinceTurnStart = attacksSinceTurnStart.copy() + toReturn.dueUnits = ArrayList(dueUnits) toReturn.hasMovedAutomatedUnits = hasMovedAutomatedUnits return toReturn } @@ -453,6 +460,8 @@ class CivilizationInfo { newList.add(mapUnit) units = newList + dueUnits.add(mapUnit) + if (updateCivInfo) { // Not relevant when updating TileInfo transients, since some info of the civ itself isn't yet available, // and in any case it'll be updated once civ info transients are @@ -471,18 +480,23 @@ class CivilizationInfo { fun getIdleUnits() = getCivUnits().filter { it.isIdle() } - private fun getDueUnits() = getCivUnits().filter { it.due && it.isIdle() } + // Drop all units that are not really 'due' anymore. We do it here to avoid caring how and where it happened. + fun getDueUnits() = dueUnits.filter { it.due && !it.isDestroyed && it.isIdle() } fun shouldGoToDueUnit() = UncivGame.Current.settings.checkForDueUnits && getDueUnits().any() - fun getNextDueUnit(): MapUnit? { - val dueUnits = getDueUnits() - if (dueUnits.any()) { - val unit = dueUnits.first() - unit.due = false - return unit + fun getNextDueUnit() = getDueUnits().firstOrNull() + + fun cycleThroughDueUnits(): MapUnit? { + var realDueUnits = getDueUnits(); + if (realDueUnits.any()) { + var unit = realDueUnits.first(); + // We shift the unit to the back of the queue. However, the caller may clear its 'due' state if it wants. + dueUnits.remove(unit); + dueUnits.add(unit); + return unit; } - return null + else return null; } //endregion @@ -828,6 +842,8 @@ class CivilizationInfo { fun startTurn() { civConstructions.startTurn() attacksSinceTurnStart.clear() + dueUnits.clear() + dueUnits.addAll(getCivUnits()) updateStatsForNextTurn() // for things that change when turn passes e.g. golden age, city state influence // Do this after updateStatsForNextTurn but before cities.startTurn diff --git a/core/src/com/unciv/models/UnitAction.kt b/core/src/com/unciv/models/UnitAction.kt index bf64172ed9..540db322f9 100644 --- a/core/src/com/unciv/models/UnitAction.kt +++ b/core/src/com/unciv/models/UnitAction.kt @@ -137,6 +137,8 @@ enum class UnitActionType( { ImageGetter.getImage("OtherIcons/DisbandUnit") }, KeyCharAndCode.DEL), GiftUnit("Gift unit", { ImageGetter.getImage("OtherIcons/Present") }, UncivSound.Silent), + Wait("Wait", + null, 'z', UncivSound.Silent), ShowAdditionalActions("Show more", { imageGetShowMore() }, KeyCharAndCode(Input.Keys.PAGE_DOWN)), HideAdditionalActions("Back", diff --git a/core/src/com/unciv/ui/worldscreen/WorldScreen.kt b/core/src/com/unciv/ui/worldscreen/WorldScreen.kt index ab049cd038..aef69c8b3a 100644 --- a/core/src/com/unciv/ui/worldscreen/WorldScreen.kt +++ b/core/src/com/unciv/ui/worldscreen/WorldScreen.kt @@ -184,9 +184,11 @@ class WorldScreen(val gameInfo: GameInfo, val viewingCiv:CivilizationInfo) : Bas // Don't select unit and change selectedCiv when centering as spectator if (viewingCiv.isSpectator()) mapHolder.setCenterPosition(tileToCenterOn, immediately = true, selectUnit = false) - else + else { mapHolder.setCenterPosition(tileToCenterOn, immediately = true, selectUnit = true) - + if (viewingCiv.getNextDueUnit() == bottomUnitTable.selectedUnit) + viewingCiv.cycleThroughDueUnits() + } tutorialController.allTutorialsShowedCallback = { shouldUpdate = true } @@ -727,6 +729,21 @@ class WorldScreen(val gameInfo: GameInfo, val viewingCiv:CivilizationInfo) : Bas } } + fun switchToNextUnit() { + val nextDueUnit = viewingCiv.cycleThroughDueUnits() + if (nextDueUnit != null) { + mapHolder.setCenterPosition( + nextDueUnit.currentTile.position, + immediately = false, + selectUnit = false + ) + bottomUnitTable.selectUnit(nextDueUnit) + shouldUpdate = true + // Unless 'wait' action is chosen, the unit will not be considered due anymore. + nextDueUnit.due = false + } + } + private fun updateNextTurnButton(isSomethingOpen: Boolean) { nextTurnButton.update(isSomethingOpen, isPlayersTurn, waitingForAutosave, getNextTurnAction()) nextTurnButton.setPosition(stage.width - nextTurnButton.width - 10f, topBar.y - nextTurnButton.height - 10f) @@ -741,18 +758,7 @@ class WorldScreen(val gameInfo: GameInfo, val viewingCiv:CivilizationInfo) : Bas NextTurnAction("Waiting for other players...",Color.GRAY) {} viewingCiv.shouldGoToDueUnit() -> - NextTurnAction("Next unit", Color.LIGHT_GRAY) { - val nextDueUnit = viewingCiv.getNextDueUnit() - if (nextDueUnit != null) { - mapHolder.setCenterPosition( - nextDueUnit.currentTile.position, - immediately = false, - selectUnit = false - ) - bottomUnitTable.selectUnit(nextDueUnit) - shouldUpdate = true - } - } + NextTurnAction("Next unit", Color.LIGHT_GRAY) { switchToNextUnit() } viewingCiv.cities.any { it.cityConstructions.currentConstructionFromQueue == "" } -> NextTurnAction("Pick construction", Color.CORAL) { diff --git a/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt b/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt index 094eaf955c..71c2479801 100644 --- a/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt +++ b/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt @@ -68,9 +68,10 @@ object UnitActions { addTriggerUniqueActions(unit, actionList) addAddInCapitalAction(unit, actionList, tile) - addToggleActionsAction(unit, actionList, unitTable) + addWaitAction(unit, actionList, worldScreen); + return actionList } @@ -832,4 +833,17 @@ object UnitActions { ) } -} \ No newline at end of file + private fun addWaitAction(unit: MapUnit, actionList: ArrayList, worldScreen: WorldScreen) { + // This is only for idle units. + if (!unit.isIdle()) return + // Don't add if there are no idle units we could switch to, + if (!worldScreen.viewingCiv.getDueUnits().any()) return + actionList += UnitAction( + type = UnitActionType.Wait, + action = { + unit.due = true + worldScreen.switchToNextUnit() + } + ) + } +}