From 16874f5da6f84b0e46d741ff89e3afc878f6a986 Mon Sep 17 00:00:00 2001 From: SomeTroglodyte <63000004+SomeTroglodyte@users.noreply.github.com> Date: Wed, 25 May 2022 04:07:20 +0200 Subject: [PATCH] Implement a waiting command (#6884) (#6896) * Implement a waiting command (#6884) * Resolve misc. issues with commit 27a03bca8 * Resolve misc. issues with commit 93d9fe9cc * Resolve misc. issues with commit 2ca7ed154 Co-authored-by: Paul Pogonyshev --- .../logic/civilization/CivilizationInfo.kt | 54 +++++++++++++++---- core/src/com/unciv/models/UnitAction.kt | 2 + .../com/unciv/ui/worldscreen/WorldScreen.kt | 30 ++++++----- .../unciv/ui/worldscreen/unit/UnitActions.kt | 16 +++++- 4 files changed, 77 insertions(+), 25 deletions(-) diff --git a/core/src/com/unciv/logic/civilization/CivilizationInfo.kt b/core/src/com/unciv/logic/civilization/CivilizationInfo.kt index ddce02bd75503..2bc7db01d1ec2 100644 --- a/core/src/com/unciv/logic/civilization/CivilizationInfo.kt +++ b/core/src/com/unciv/logic/civilization/CivilizationInfo.kt @@ -74,6 +74,12 @@ class CivilizationInfo { @Transient private var units = listOf() + /** + * Index in the unit list above of the unit that is potentially due and is next up for button "Next unit". + */ + @Transient + private var nextPotentiallyDueAt = 0 + @Transient var viewableTiles = setOf() @@ -448,10 +454,19 @@ class CivilizationInfo { fun getCivUnits(): Sequence = units.asSequence() fun getCivGreatPeople(): Sequence = getCivUnits().filter { mapUnit -> mapUnit.isGreatPerson() } + // Similar to getCivUnits(), but the returned list is rotated so that the + // 'nextPotentiallyDueAt' unit is first here. + private fun getCivUnitsStartingAtNextDue(): Sequence = sequenceOf(units.subList(nextPotentiallyDueAt, units.size) + units.subList(0, nextPotentiallyDueAt)).flatten() + fun addUnit(mapUnit: MapUnit, updateCivInfo: Boolean = true) { - val newList = ArrayList(units) + // Since we create a new list anyway (otherwise some concurrent modification + // exception will happen), also rearrange existing units so that + // 'nextPotentiallyDueAt' becomes 0. This way new units are always last to be due + // (can be changed as wanted, just have a predictable place). + var newList = getCivUnitsStartingAtNextDue().toMutableList() newList.add(mapUnit) units = newList + nextPotentiallyDueAt = 0 if (updateCivInfo) { // Not relevant when updating TileInfo transients, since some info of the civ itself isn't yet available, @@ -462,27 +477,46 @@ class CivilizationInfo { } fun removeUnit(mapUnit: MapUnit) { - val newList = ArrayList(units) + // See comment in addUnit(). + var newList = getCivUnitsStartingAtNextDue().toMutableList() newList.remove(mapUnit) units = newList + nextPotentiallyDueAt = 0 + updateStatsForNextTurn() // unit upkeep updateDetailedCivResources() } fun getIdleUnits() = getCivUnits().filter { it.isIdle() } - private fun getDueUnits() = getCivUnits().filter { it.due && it.isIdle() } + fun getDueUnits(): Sequence = getCivUnitsStartingAtNextDue().filter { it.due && 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 + // Return the next due unit, but preferably not 'unitToSkip': this is returned only if it is the only remaining due unit. + fun cycleThroughDueUnits(unitToSkip: MapUnit? = null): MapUnit? { + if (units.none()) return null + + var returnAt = nextPotentiallyDueAt + var fallbackAt = -1 + + do { + if (units[returnAt].due && units[returnAt].isIdle()) { + if (units[returnAt] != unitToSkip) { + nextPotentiallyDueAt = (returnAt + 1) % units.size + return units[returnAt] + } + else fallbackAt = returnAt + } + + returnAt = (returnAt + 1) % units.size + } while (returnAt != nextPotentiallyDueAt) + + if (fallbackAt >= 0) { + nextPotentiallyDueAt = (fallbackAt + 1) % units.size + return units[fallbackAt] } - return null + else return null } //endregion diff --git a/core/src/com/unciv/models/UnitAction.kt b/core/src/com/unciv/models/UnitAction.kt index bf64172ed941f..540db322f9d95 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 bb19dbe6bb75f..584086203fc30 100644 --- a/core/src/com/unciv/ui/worldscreen/WorldScreen.kt +++ b/core/src/com/unciv/ui/worldscreen/WorldScreen.kt @@ -187,7 +187,6 @@ class WorldScreen(val gameInfo: GameInfo, val viewingCiv:CivilizationInfo) : Bas else mapHolder.setCenterPosition(tileToCenterOn, immediately = true, selectUnit = true) - tutorialController.allTutorialsShowedCallback = { shouldUpdate = true } onBackButtonClicked { backButtonAndESCHandler() } @@ -723,6 +722,22 @@ class WorldScreen(val gameInfo: GameInfo, val viewingCiv:CivilizationInfo) : Bas } } + fun switchToNextUnit() { + // Try to select something new if we already have the next pending unit selected. + val nextDueUnit = viewingCiv.cycleThroughDueUnits(bottomUnitTable.selectedUnit) + 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) @@ -737,18 +752,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 094eaf955c0db..150b888f79f64 100644 --- a/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt +++ b/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt @@ -68,6 +68,7 @@ object UnitActions { addTriggerUniqueActions(unit, actionList) addAddInCapitalAction(unit, actionList, tile) + addWaitAction(unit, actionList, worldScreen); addToggleActionsAction(unit, actionList, unitTable) @@ -821,6 +822,18 @@ object UnitActions { } } + private fun addWaitAction(unit: MapUnit, actionList: ArrayList, worldScreen: WorldScreen) { + if (!unit.isIdle()) return + if (worldScreen.viewingCiv.getDueUnits().filter { it != unit }.none()) return + actionList += UnitAction( + type = UnitActionType.Wait, + action = { + unit.due = true + worldScreen.switchToNextUnit() + } + ) + } + private fun addToggleActionsAction(unit: MapUnit, actionList: ArrayList, unitTable: UnitTable) { actionList += UnitAction( type = if (unit.showAdditionalActions) UnitActionType.HideAdditionalActions @@ -831,5 +844,4 @@ object UnitActions { } ) } - -} \ No newline at end of file +}