diff --git a/core/src/com/unciv/logic/GameStarter.kt b/core/src/com/unciv/logic/GameStarter.kt index 696f96cb9c..a17a905a75 100644 --- a/core/src/com/unciv/logic/GameStarter.kt +++ b/core/src/com/unciv/logic/GameStarter.kt @@ -136,13 +136,13 @@ object GameStarter { gameInfo.tileMap) // For later starting eras, or for civs like Polynesia with a different Warrior, we need different starting units - fun getWarriorEquivalent(civ: CivilizationInfo): String { + fun getWarriorEquivalent(civ: CivilizationInfo): String? { val availableMilitaryUnits = gameInfo.ruleSet.units.values.filter { it.isBuildable(civ) && it.unitType.isLandUnit() && !it.unitType.isCivilian() } - return availableMilitaryUnits.maxBy { max(it.strength, it.rangedStrength) }!!.name + return availableMilitaryUnits.maxBy { max(it.strength, it.rangedStrength) }?.name } // no starting units for Barbarians and Spectators for (civ in gameInfo.civilizations.filter { !it.isBarbarian() && !it.isSpectator() }) { @@ -155,12 +155,13 @@ object GameStarter { civ.placeUnitNearTile(startingLocation.position, unitName) } placeNearStartingPosition(Constants.settler) - placeNearStartingPosition(getWarriorEquivalent(civ)) + val warriorEquivalent = getWarriorEquivalent(civ) + if(warriorEquivalent!=null) placeNearStartingPosition(warriorEquivalent) if (!civ.isPlayerCivilization() && civ.isMajorCiv()) { for (unit in gameInfo.getDifficulty().aiFreeUnits) { - val unitToAdd = if (unit == "Warrior") getWarriorEquivalent(civ) else unit - placeNearStartingPosition(unitToAdd) + val unitToAdd = if (unit == "Warrior") warriorEquivalent else unit + if (unitToAdd != null) placeNearStartingPosition(unitToAdd) } } } diff --git a/core/src/com/unciv/ui/worldscreen/WorldMapHolder.kt b/core/src/com/unciv/ui/worldscreen/WorldMapHolder.kt index c4367b5d31..8fe5c8c151 100644 --- a/core/src/com/unciv/ui/worldscreen/WorldMapHolder.kt +++ b/core/src/com/unciv/ui/worldscreen/WorldMapHolder.kt @@ -23,7 +23,6 @@ import com.unciv.ui.map.TileGroupMap import com.unciv.ui.tilegroups.TileSetStrings import com.unciv.ui.tilegroups.WorldTileGroup import com.unciv.ui.utils.* -import com.unciv.ui.worldscreen.unit.UnitContextMenu import kotlin.concurrent.thread @@ -44,7 +43,7 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap val allTiles = TileGroupMap(daTileGroups,worldScreen.stage.width) - for(tileGroup in tileGroups.values){ + for(tileGroup in tileGroups.values) { tileGroup.cityButtonLayerGroup.onClick(UncivSound.Silent) { onTileClicked(tileGroup.tileInfo) } @@ -55,14 +54,13 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap init { button = Input.Buttons.RIGHT } + override fun clicked(event: InputEvent?, x: Float, y: Float) { val unit = worldScreen.bottomUnitTable.selectedUnit if (unit == null) return thread { val canUnitReachTile = unit.movement.canReach(tileGroup.tileInfo) - if (canUnitReachTile) { - UnitContextMenu(this@WorldMapHolder, unit, tileGroup.tileInfo).onMoveButtonClick() - } + if (canUnitReachTile) moveUnitToTargetTile(unit, tileGroup.tileInfo) } } }) @@ -111,6 +109,42 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap worldScreen.shouldUpdate = true } + + fun moveUnitToTargetTile(selectedUnit: MapUnit, targetTile:TileInfo) { + // this can take a long time, because of the unit-to-tile calculation needed, so we put it in a different thread + thread(name = "TileToMoveTo") { + // these are the heavy parts, finding where we want to go + // Since this runs in a different thread, even if we check movement.canReach() + // then it might change until we get to the getTileToMoveTo, so we just try/catch it + val tileToMoveTo: TileInfo + try { + tileToMoveTo = selectedUnit.movement.getTileToMoveToThisTurn(targetTile) + } catch (ex: Exception) { + return@thread + } // can't move here + + Gdx.app.postRunnable { + 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.movement.moveToTile(tileToMoveTo) + if (selectedUnit.action == Constants.unitActionExplore) selectedUnit.action = null // remove explore on manual move + Sounds.play(UncivSound.Whoosh) + if (selectedUnit.currentTile != targetTile) + selectedUnit.action = "moveTo " + targetTile.position.x.toInt() + "," + targetTile.position.y.toInt() + if (selectedUnit.currentMovement > 0) worldScreen.bottomUnitTable.selectedUnit = selectedUnit + + worldScreen.shouldUpdate = true + unitActionOverlay?.remove() + } catch (e: Exception) {} + } + } + } + + private fun addTileOverlaysWithUnitMovement(selectedUnit: MapUnit, tileInfo: TileInfo) { // some code is copied from canReach not to call getShortestPath on the main thread before calling it on this thread if (selectedUnit.type.isAirUnit() && selectedUnit.currentTile.aerialDistanceTo(tileInfo) > selectedUnit.getRange()*2) { @@ -195,7 +229,7 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap UncivGame.Current.settings.addCompletedTutorialTask("Move unit") if(dto.unit.type.isAirUnit()) UncivGame.Current.settings.addCompletedTutorialTask("Move an air unit") - UnitContextMenu(this, dto.unit, dto.tileInfo).onMoveButtonClick() + moveUnitToTargetTile(dto.unit, dto.tileInfo) } else moveHereButton.color.a = 0.5f diff --git a/core/src/com/unciv/ui/worldscreen/unit/UnitContextMenu.kt b/core/src/com/unciv/ui/worldscreen/unit/UnitContextMenu.kt deleted file mode 100644 index 63389485d0..0000000000 --- a/core/src/com/unciv/ui/worldscreen/unit/UnitContextMenu.kt +++ /dev/null @@ -1,98 +0,0 @@ -package com.unciv.ui.worldscreen.unit - -import com.badlogic.gdx.Gdx -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.Constants -import com.unciv.logic.map.MapUnit -import com.unciv.logic.map.TileInfo -import com.unciv.logic.map.action.BuildLongRoadAction -import com.unciv.logic.map.action.MapUnitAction -import com.unciv.models.UncivSound -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.WorldMapHolder -import kotlin.concurrent.thread - -class UnitContextMenu(val tileMapHolder: WorldMapHolder, val selectedUnit: MapUnit, val targetTile: TileInfo) : VerticalGroup() { - - init { - - space(10f) - - addButton(ImageGetter.getStatIcon("Movement"), "Move unit") { - onMoveButtonClick() - } - - // Basic scenarios sommetimes don't have roads - if (selectedUnit.civInfo.gameInfo.ruleSet.tileImprovements.containsKey("Road")) - 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.unitActionOverlay?.remove() - 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(name = "TileToMoveTo") { - // these are the heavy parts, finding where we want to go - // Since this runs in a different thread, even if we check movement.canReach() - // then it might change until we get to the getTileToMoveTo, so we just try/catch it - val tileToMoveTo: TileInfo - try { - tileToMoveTo = selectedUnit.movement.getTileToMoveToThisTurn(targetTile) - } catch (ex: Exception) { - return@thread - } // can't move here - - Gdx.app.postRunnable { - 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.movement.moveToTile(tileToMoveTo) - if (selectedUnit.action == Constants.unitActionExplore) selectedUnit.action = null // remove explore on manual move - Sounds.play(UncivSound.Whoosh) - if (selectedUnit.currentTile != targetTile) - selectedUnit.action = "moveTo " + targetTile.position.x.toInt() + "," + targetTile.position.y.toInt() - if (selectedUnit.currentMovement > 0) { - tileMapHolder.worldScreen.bottomUnitTable.selectedUnit = selectedUnit - } - - tileMapHolder.worldScreen.shouldUpdate = true - tileMapHolder.unitActionOverlay?.remove() - } catch (e: Exception) {} - - } - } - } - -} \ No newline at end of file