From 0309e51afd6680e0aa8c92b07be300f08dd24cf9 Mon Sep 17 00:00:00 2001 From: yairm210 Date: Tue, 19 Oct 2021 21:16:16 +0300 Subject: [PATCH] Solved movement bug where extra tiles you could move through, but not *to*, would end up *adding* movememnt. Movement bugs are hard. So hard, in fact, that most of this commit is just adding the "amount of movement left after attacking" so I could ensure that there wasn't a problem in the "calculating attackable tiles" part. The actual fix is in UnitMovementAlgorithms. --- .../unciv/logic/automation/BattleHelper.kt | 41 +++++++++++-------- core/src/com/unciv/logic/battle/Battle.kt | 12 +++--- .../unciv/logic/map/UnitMovementAlgorithms.kt | 10 +++-- core/src/com/unciv/models/AttackableTile.kt | 3 +- core/src/com/unciv/models/ruleset/Ruleset.kt | 2 +- .../ui/worldscreen/bottombar/BattleTable.kt | 2 +- 6 files changed, 41 insertions(+), 29 deletions(-) diff --git a/core/src/com/unciv/logic/automation/BattleHelper.kt b/core/src/com/unciv/logic/automation/BattleHelper.kt index 2a429492ad..24e3335fbf 100644 --- a/core/src/com/unciv/logic/automation/BattleHelper.kt +++ b/core/src/com/unciv/logic/automation/BattleHelper.kt @@ -38,7 +38,7 @@ object BattleHelper { stayOnTile: Boolean = false ): ArrayList { val tilesWithEnemies = (tilesToCheck ?: unit.civInfo.viewableTiles) - .filter { containsAttackableEnemy(it, MapUnitCombatant(unit)) } + .filter { containsAttackableEnemy(it, MapUnitCombatant(unit)) } val rangeOfAttack = unit.getRange() @@ -50,27 +50,36 @@ object BattleHelper { // Silly floats, basically val unitMustBeSetUp = unit.hasUnique("Must set up to ranged attack") - val tilesToAttackFrom = if (stayOnTile || unit.baseUnit.movesLikeAirUnits()) sequenceOf(unit.currentTile) + val tilesToAttackFrom = if (stayOnTile || unit.baseUnit.movesLikeAirUnits()) + sequenceOf(Pair(unit.currentTile, unit.currentMovement)) else unitDistanceToTiles.asSequence() - .filter { - val movementPointsToExpendAfterMovement = if (unitMustBeSetUp) 1 else 0 - val movementPointsToExpendHere = if (unitMustBeSetUp && !unit.isSetUpForSiege()) 1 else 0 - val movementPointsToExpendBeforeAttack = if (it.key == unit.currentTile) movementPointsToExpendHere else movementPointsToExpendAfterMovement - unit.currentMovement - it.value.totalDistance - movementPointsToExpendBeforeAttack > 0.1 - } // still got leftover movement points after all that, to attack (0.1 is because of Float nonsense, see MapUnit.moveToTile(...) - .map { it.key } - .filter { unit.movement.canMoveTo(it) || it == unit.getTile() } + .map { + val tile = it.key + val movementPointsToExpendAfterMovement = if (unitMustBeSetUp) 1 else 0 + val movementPointsToExpendHere = + if (unitMustBeSetUp && !unit.isSetUpForSiege()) 1 else 0 + val movementPointsToExpendBeforeAttack = + if (it.key == unit.currentTile) movementPointsToExpendHere else movementPointsToExpendAfterMovement + val movementLeft = + unit.currentMovement - it.value.totalDistance - movementPointsToExpendBeforeAttack + Pair(tile, movementLeft) + } + // still got leftover movement points after all that, to attack (0.1 is because of Float nonsense, see MapUnit.moveToTile(...) + .filter { it.second > 0.1f } + .filter { + it.first == unit.getTile() || unit.movement.canMoveTo(it.first) + } - for (reachableTile in tilesToAttackFrom) { // tiles we'll still have energy after we reach there + for ((reachableTile, movementLeft) in tilesToAttackFrom) { // tiles we'll still have energy after we reach there val tilesInAttackRange = - if (unit.hasUnique("Ranged attacks may be performed over obstacles") || unit.baseUnit.movesLikeAirUnits()) - reachableTile.getTilesInDistance(rangeOfAttack) - else reachableTile.getViewableTilesList(rangeOfAttack) - .asSequence() + if (unit.hasUnique("Ranged attacks may be performed over obstacles") || unit.baseUnit.movesLikeAirUnits()) + reachableTile.getTilesInDistance(rangeOfAttack) + else reachableTile.getViewableTilesList(rangeOfAttack) + .asSequence() attackableTiles += tilesInAttackRange.filter { it in tilesWithEnemies } - .map { AttackableTile(reachableTile, it) } + .map { AttackableTile(reachableTile, it, movementLeft) } } return attackableTiles } diff --git a/core/src/com/unciv/logic/battle/Battle.kt b/core/src/com/unciv/logic/battle/Battle.kt index e49eaed1d1..e6d52ba972 100644 --- a/core/src/com/unciv/logic/battle/Battle.kt +++ b/core/src/com/unciv/logic/battle/Battle.kt @@ -372,15 +372,13 @@ object Battle { } private fun postBattleAddXp(attacker: ICombatant, defender: ICombatant) { - if (attacker.isMelee()) { - if (!defender.isCivilian()) // unit was not captured but actually attacked - { - addXp(attacker, 5, defender) - addXp(defender, 4, attacker) - } - } else { // ranged attack + if (!attacker.isMelee()) { // ranged attack addXp(attacker, 2, defender) addXp(defender, 2, attacker) + } else if (!defender.isCivilian()) // unit was not captured but actually attacked + { + addXp(attacker, 5, defender) + addXp(defender, 4, attacker) } } diff --git a/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt b/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt index ddd06bbb07..15419fafa0 100644 --- a/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt +++ b/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt @@ -470,15 +470,19 @@ class UnitMovementAlgorithms(val unit:MapUnit) { passingMovementSpent = 0f } - previousTile = tile - + + // We can't continue, stop here. if (unit.isDestroyed || unit.currentMovement - passingMovementSpent < Constants.minimumMovementEpsilon) { - unit.currentMovement = passingMovementSpent // silly floats which are "almost zero" break } } + // Silly floats which are almost zero + if (unit.currentMovement < Constants.minimumMovementEpsilon) + unit.currentMovement = 0f + + if (!unit.isDestroyed) unit.putInTile(lastReachedEnterableTile) diff --git a/core/src/com/unciv/models/AttackableTile.kt b/core/src/com/unciv/models/AttackableTile.kt index 0fb612bb92..01ceed2e23 100644 --- a/core/src/com/unciv/models/AttackableTile.kt +++ b/core/src/com/unciv/models/AttackableTile.kt @@ -2,4 +2,5 @@ package com.unciv.models import com.unciv.logic.map.TileInfo -class AttackableTile(val tileToAttackFrom: TileInfo, val tileToAttack: TileInfo) \ No newline at end of file +class AttackableTile(val tileToAttackFrom: TileInfo, val tileToAttack: TileInfo, + val movementLeftAfterMovingToAttackTile:Float) \ No newline at end of file diff --git a/core/src/com/unciv/models/ruleset/Ruleset.kt b/core/src/com/unciv/models/ruleset/Ruleset.kt index 661a291709..938db276ca 100644 --- a/core/src/com/unciv/models/ruleset/Ruleset.kt +++ b/core/src/com/unciv/models/ruleset/Ruleset.kt @@ -588,7 +588,7 @@ class Ruleset { lines.add("${promotion.name} requires promotion $prereq which does not exist!", RulesetErrorSeverity.Warning) for (unitType in promotion.unitTypes) if (!unitTypes.containsKey(unitType) && (unitTypes.isNotEmpty() || !baseRuleset.unitTypes.containsKey(unitType))) - lines.add("${promotion.name} references unit type ${unitType}, which does not exist!", RulesetErrorSeverity.Warning) + lines.add("${promotion.name} references unit type $unitType, which does not exist!", RulesetErrorSeverity.Warning) checkUniques(promotion, lines, UniqueType.UniqueComplianceErrorSeverity.RulesetSpecific) } for (unitType in unitTypes.values) { diff --git a/core/src/com/unciv/ui/worldscreen/bottombar/BattleTable.kt b/core/src/com/unciv/ui/worldscreen/bottombar/BattleTable.kt index 357e73091f..78390648d8 100644 --- a/core/src/com/unciv/ui/worldscreen/bottombar/BattleTable.kt +++ b/core/src/com/unciv/ui/worldscreen/bottombar/BattleTable.kt @@ -196,7 +196,7 @@ class BattleTable(val worldScreen: WorldScreen): Table() { { val canBombard = UnitAutomation.getBombardTargets(attacker.city).contains(defender.getTile()) if (canBombard) { - attackableTile = AttackableTile(attacker.getTile(), defender.getTile()) + attackableTile = AttackableTile(attacker.getTile(), defender.getTile(), 0f) } } }