mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-21 05:09:25 +07:00
Resolved #3476 - captured civilian units can no longer move on the same turn
This commit is contained in:
@ -21,7 +21,7 @@ import kotlin.math.max
|
|||||||
*/
|
*/
|
||||||
object Battle {
|
object Battle {
|
||||||
|
|
||||||
fun moveAndAttack(attacker: ICombatant, attackableTile: AttackableTile){
|
fun moveAndAttack(attacker: ICombatant, attackableTile: AttackableTile) {
|
||||||
if (attacker is MapUnitCombatant) {
|
if (attacker is MapUnitCombatant) {
|
||||||
attacker.unit.movement.moveToTile(attackableTile.tileToAttackFrom)
|
attacker.unit.movement.moveToTile(attackableTile.tileToAttackFrom)
|
||||||
if (attacker.unit.hasUnique("Must set up to ranged attack") && attacker.unit.action != Constants.unitActionSetUp) {
|
if (attacker.unit.hasUnique("Must set up to ranged attack") && attacker.unit.action != Constants.unitActionSetUp) {
|
||||||
@ -38,18 +38,18 @@ object Battle {
|
|||||||
|
|
||||||
fun attack(attacker: ICombatant, defender: ICombatant) {
|
fun attack(attacker: ICombatant, defender: ICombatant) {
|
||||||
if (UncivGame.Current.alertBattle) {
|
if (UncivGame.Current.alertBattle) {
|
||||||
println(attacker.getCivInfo().civName+" "+attacker.getName()+" attacked "+
|
println(attacker.getCivInfo().civName + " " + attacker.getName() + " attacked " +
|
||||||
defender.getCivInfo().civName+" "+defender.getName())
|
defender.getCivInfo().civName + " " + defender.getName())
|
||||||
}
|
}
|
||||||
val attackedTile = defender.getTile()
|
val attackedTile = defender.getTile()
|
||||||
|
|
||||||
if(attacker is MapUnitCombatant && attacker.getUnitType().isAirUnit()) {
|
if (attacker is MapUnitCombatant && attacker.getUnitType().isAirUnit()) {
|
||||||
tryInterceptAirAttack(attacker, defender)
|
tryInterceptAirAttack(attacker, defender)
|
||||||
if (attacker.isDefeated()) return
|
if (attacker.isDefeated()) return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Withdraw from melee ability
|
// Withdraw from melee ability
|
||||||
if (attacker is MapUnitCombatant && attacker.isMelee() && defender is MapUnitCombatant ) {
|
if (attacker is MapUnitCombatant && attacker.isMelee() && defender is MapUnitCombatant) {
|
||||||
val withdraw = defender.unit.getMatchingUniques("May withdraw before melee ([]%)").firstOrNull()
|
val withdraw = defender.unit.getMatchingUniques("May withdraw before melee ([]%)").firstOrNull()
|
||||||
if (withdraw != null && doWithdrawFromMeleeAbility(attacker, defender, withdraw)) return
|
if (withdraw != null && doWithdrawFromMeleeAbility(attacker, defender, withdraw)) return
|
||||||
}
|
}
|
||||||
@ -71,7 +71,7 @@ object Battle {
|
|||||||
|
|
||||||
reduceAttackerMovementPointsAndAttacks(attacker, defender)
|
reduceAttackerMovementPointsAndAttacks(attacker, defender)
|
||||||
|
|
||||||
if(!isAlreadyDefeatedCity) postBattleAddXp(attacker, defender)
|
if (!isAlreadyDefeatedCity) postBattleAddXp(attacker, defender)
|
||||||
|
|
||||||
// Add culture when defeating a barbarian when Honor policy is adopted, gold from enemy killed when honor is complete
|
// Add culture when defeating a barbarian when Honor policy is adopted, gold from enemy killed when honor is complete
|
||||||
// or any enemy military unit with Sacrificial captives unique (can be either attacker or defender!)
|
// or any enemy military unit with Sacrificial captives unique (can be either attacker or defender!)
|
||||||
@ -91,7 +91,7 @@ object Battle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun tryEarnFromKilling(civUnit:ICombatant, defeatedUnit:MapUnitCombatant){
|
private fun tryEarnFromKilling(civUnit: ICombatant, defeatedUnit: MapUnitCombatant) {
|
||||||
val unitStr = max(defeatedUnit.unit.baseUnit.strength, defeatedUnit.unit.baseUnit.rangedStrength)
|
val unitStr = max(defeatedUnit.unit.baseUnit.strength, defeatedUnit.unit.baseUnit.rangedStrength)
|
||||||
val unitCost = defeatedUnit.unit.baseUnit.cost
|
val unitCost = defeatedUnit.unit.baseUnit.cost
|
||||||
val bonusUniquePlaceholderText = "Earn []% of [] opponent's [] as [] for kills"
|
val bonusUniquePlaceholderText = "Earn []% of [] opponent's [] as [] for kills"
|
||||||
@ -132,7 +132,7 @@ object Battle {
|
|||||||
var damageToAttacker = BattleDamage.calculateDamageToAttacker(attacker, attacker.getTile(), defender)
|
var damageToAttacker = BattleDamage.calculateDamageToAttacker(attacker, attacker.getTile(), defender)
|
||||||
|
|
||||||
if (defender.getUnitType().isCivilian() && attacker.isMelee()) {
|
if (defender.getUnitType().isCivilian() && attacker.isMelee()) {
|
||||||
captureCivilianUnit(attacker, defender)
|
captureCivilianUnit(attacker, defender as MapUnitCombatant)
|
||||||
} else if (attacker.isRanged()) {
|
} else if (attacker.isRanged()) {
|
||||||
defender.takeDamage(damageToDefender) // straight up
|
defender.takeDamage(damageToDefender) // straight up
|
||||||
} else {
|
} else {
|
||||||
@ -251,22 +251,22 @@ object Battle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// XP!
|
// XP!
|
||||||
private fun addXp(thisCombatant:ICombatant, amount:Int, otherCombatant:ICombatant){
|
private fun addXp(thisCombatant: ICombatant, amount: Int, otherCombatant: ICombatant) {
|
||||||
if(thisCombatant !is MapUnitCombatant) return
|
if (thisCombatant !is MapUnitCombatant) return
|
||||||
if(thisCombatant.unit.promotions.totalXpProduced() >= thisCombatant.unit.civInfo.gameInfo.ruleSet.modOptions.maxXPfromBarbarians
|
if (thisCombatant.unit.promotions.totalXpProduced() >= thisCombatant.unit.civInfo.gameInfo.ruleSet.modOptions.maxXPfromBarbarians
|
||||||
&& otherCombatant.getCivInfo().isBarbarian())
|
&& otherCombatant.getCivInfo().isBarbarian())
|
||||||
return
|
return
|
||||||
|
|
||||||
var XPModifier = 1f
|
var XPModifier = 1f
|
||||||
if (thisCombatant.getCivInfo().hasUnique("Military units gain 50% more Experience from combat")) XPModifier += 0.5f
|
if (thisCombatant.getCivInfo().hasUnique("Military units gain 50% more Experience from combat")) XPModifier += 0.5f
|
||||||
for (unique in thisCombatant.unit.getMatchingUniques("[]% Bonus XP gain"))
|
for (unique in thisCombatant.unit.getMatchingUniques("[]% Bonus XP gain"))
|
||||||
XPModifier += unique.params[0].toFloat() / 100
|
XPModifier += unique.params[0].toFloat() / 100
|
||||||
|
|
||||||
val XPGained = (amount * XPModifier).toInt()
|
val XPGained = (amount * XPModifier).toInt()
|
||||||
thisCombatant.unit.promotions.XP += XPGained
|
thisCombatant.unit.promotions.XP += XPGained
|
||||||
|
|
||||||
|
|
||||||
if(thisCombatant.getCivInfo().isMajorCiv()) {
|
if (thisCombatant.getCivInfo().isMajorCiv()) {
|
||||||
var greatGeneralPointsModifier = 1f
|
var greatGeneralPointsModifier = 1f
|
||||||
val unitUniques = thisCombatant.unit.getMatchingUniques("[] is earned []% faster")
|
val unitUniques = thisCombatant.unit.getMatchingUniques("[] is earned []% faster")
|
||||||
val civUniques = thisCombatant.getCivInfo().getMatchingUniques("[] is earned []% faster")
|
val civUniques = thisCombatant.getCivInfo().getMatchingUniques("[] is earned []% faster")
|
||||||
@ -311,17 +311,17 @@ object Battle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getMapCombatantOfTile(tile:TileInfo): ICombatant? {
|
fun getMapCombatantOfTile(tile: TileInfo): ICombatant? {
|
||||||
if (tile.isCityCenter()) return CityCombatant(tile.getCity()!!)
|
if (tile.isCityCenter()) return CityCombatant(tile.getCity()!!)
|
||||||
if (tile.militaryUnit != null) return MapUnitCombatant(tile.militaryUnit!!)
|
if (tile.militaryUnit != null) return MapUnitCombatant(tile.militaryUnit!!)
|
||||||
if (tile.civilianUnit != null) return MapUnitCombatant(tile.civilianUnit!!)
|
if (tile.civilianUnit != null) return MapUnitCombatant(tile.civilianUnit!!)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun captureCivilianUnit(attacker: ICombatant, defender: ICombatant){
|
private fun captureCivilianUnit(attacker: ICombatant, defender: MapUnitCombatant) {
|
||||||
// barbarians don't capture civilians
|
// barbarians don't capture civilians
|
||||||
if(attacker.getCivInfo().isBarbarian()
|
if (attacker.getCivInfo().isBarbarian()
|
||||||
||(defender as MapUnitCombatant).unit.hasUnique("Uncapturable")){
|
|| (defender as MapUnitCombatant).unit.hasUnique("Uncapturable")) {
|
||||||
defender.takeDamage(100)
|
defender.takeDamage(100)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -330,25 +330,25 @@ object Battle {
|
|||||||
val defenderCiv = defender.getCivInfo()
|
val defenderCiv = defender.getCivInfo()
|
||||||
|
|
||||||
val capturedUnit = defender.unit
|
val capturedUnit = defender.unit
|
||||||
capturedUnit.civInfo.addNotification("An enemy ["+attacker.getName()+"] has captured our ["+defender.getName()+"]",
|
capturedUnit.civInfo.addNotification("An enemy [" + attacker.getName() + "] has captured our [" + defender.getName() + "]",
|
||||||
defender.getTile().position, Color.RED)
|
defender.getTile().position, Color.RED)
|
||||||
|
|
||||||
// Apparently in Civ V, captured settlers are converted to workers.
|
// Apparently in Civ V, captured settlers are converted to workers.
|
||||||
if(capturedUnit.name==Constants.settler) {
|
if (capturedUnit.name == Constants.settler) {
|
||||||
val tile = capturedUnit.getTile()
|
val tile = capturedUnit.getTile()
|
||||||
capturedUnit.destroy()
|
capturedUnit.destroy()
|
||||||
attacker.getCivInfo().placeUnitNearTile(tile.position, Constants.worker)
|
attacker.getCivInfo().placeUnitNearTile(tile.position, Constants.worker)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
capturedUnit.civInfo.removeUnit(capturedUnit)
|
capturedUnit.civInfo.removeUnit(capturedUnit)
|
||||||
capturedUnit.assignOwner(attacker.getCivInfo())
|
capturedUnit.assignOwner(attacker.getCivInfo())
|
||||||
|
capturedUnit.currentMovement = 0f
|
||||||
}
|
}
|
||||||
|
|
||||||
destroyIfDefeated(defenderCiv, attacker.getCivInfo())
|
destroyIfDefeated(defenderCiv, attacker.getCivInfo())
|
||||||
capturedUnit.updateVisibleTiles()
|
capturedUnit.updateVisibleTiles()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun destroyIfDefeated(attackedCiv:CivilizationInfo, attacker: CivilizationInfo){
|
fun destroyIfDefeated(attackedCiv: CivilizationInfo, attacker: CivilizationInfo) {
|
||||||
if (attackedCiv.isDefeated()) {
|
if (attackedCiv.isDefeated()) {
|
||||||
attackedCiv.destroy()
|
attackedCiv.destroy()
|
||||||
attacker.popupAlerts.add(PopupAlert(AlertType.Defeated, attackedCiv.civName))
|
attacker.popupAlerts.add(PopupAlert(AlertType.Defeated, attackedCiv.civName))
|
||||||
@ -366,11 +366,11 @@ object Battle {
|
|||||||
if (city.population.population <= 5 && !city.isOriginalCapital) {
|
if (city.population.population <= 5 && !city.isOriginalCapital) {
|
||||||
city.destroyCity()
|
city.destroyCity()
|
||||||
} else {
|
} else {
|
||||||
city.population.population = max(city.population.population-5, 1)
|
city.population.population = max(city.population.population - 5, 1)
|
||||||
city.population.unassignExtraPopulation()
|
city.population.unassignExtraPopulation()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
destroyIfDefeated(city.civInfo,attackingCiv)
|
destroyIfDefeated(city.civInfo, attackingCiv)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun declareWar(civSuffered: CivilizationInfo) {
|
fun declareWar(civSuffered: CivilizationInfo) {
|
||||||
@ -381,7 +381,7 @@ object Battle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for(unit in tile.getUnits()){
|
for (unit in tile.getUnits()) {
|
||||||
unit.destroy()
|
unit.destroy()
|
||||||
postBattleNotifications(attacker, MapUnitCombatant(unit), unit.currentTile)
|
postBattleNotifications(attacker, MapUnitCombatant(unit), unit.currentTile)
|
||||||
declareWar(unit.civInfo)
|
declareWar(unit.civInfo)
|
||||||
@ -399,16 +399,16 @@ object Battle {
|
|||||||
if (tile.isLand && !tile.isImpassible()) tile.terrainFeature = "Fallout"
|
if (tile.isLand && !tile.isImpassible()) tile.terrainFeature = "Fallout"
|
||||||
}
|
}
|
||||||
|
|
||||||
for(civ in attacker.getCivInfo().getKnownCivs()){
|
for (civ in attacker.getCivInfo().getKnownCivs()) {
|
||||||
civ.getDiplomacyManager(attackingCiv)
|
civ.getDiplomacyManager(attackingCiv)
|
||||||
.setModifier(DiplomaticModifiers.UsedNuclearWeapons,-50f)
|
.setModifier(DiplomaticModifiers.UsedNuclearWeapons, -50f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Instead of postBattleAction() just destroy the missile, all other functions are not relevant
|
// Instead of postBattleAction() just destroy the missile, all other functions are not relevant
|
||||||
(attacker as MapUnitCombatant).unit.destroy()
|
(attacker as MapUnitCombatant).unit.destroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun tryInterceptAirAttack(attacker:MapUnitCombatant, defender: ICombatant) {
|
private fun tryInterceptAirAttack(attacker: MapUnitCombatant, defender: ICombatant) {
|
||||||
val attackedTile = defender.getTile()
|
val attackedTile = defender.getTile()
|
||||||
for (interceptor in defender.getCivInfo().getCivUnits().filter { it.canIntercept(attackedTile) }) {
|
for (interceptor in defender.getCivInfo().getCivUnits().filter { it.canIntercept(attackedTile) }) {
|
||||||
if (Random().nextFloat() > 100f / interceptor.interceptChance()) continue
|
if (Random().nextFloat() > 100f / interceptor.interceptChance()) continue
|
||||||
@ -426,17 +426,17 @@ object Battle {
|
|||||||
if (attacker.isDefeated()) {
|
if (attacker.isDefeated()) {
|
||||||
attacker.getCivInfo()
|
attacker.getCivInfo()
|
||||||
.addNotification("Our [$attackerName] was destroyed by an intercepting [$interceptorName]",
|
.addNotification("Our [$attackerName] was destroyed by an intercepting [$interceptorName]",
|
||||||
Color.RED)
|
Color.RED)
|
||||||
defender.getCivInfo()
|
defender.getCivInfo()
|
||||||
.addNotification("Our [$interceptorName] intercepted and destroyed an enemy [$attackerName]",
|
.addNotification("Our [$interceptorName] intercepted and destroyed an enemy [$attackerName]",
|
||||||
interceptor.currentTile.position, Color.RED)
|
interceptor.currentTile.position, Color.RED)
|
||||||
} else {
|
} else {
|
||||||
attacker.getCivInfo()
|
attacker.getCivInfo()
|
||||||
.addNotification("Our [$attackerName] was attacked by an intercepting [$interceptorName]",
|
.addNotification("Our [$attackerName] was attacked by an intercepting [$interceptorName]",
|
||||||
Color.RED)
|
Color.RED)
|
||||||
defender.getCivInfo()
|
defender.getCivInfo()
|
||||||
.addNotification("Our [$interceptorName] intercepted and attacked an enemy [$attackerName]",
|
.addNotification("Our [$interceptorName] intercepted and attacked an enemy [$attackerName]",
|
||||||
interceptor.currentTile.position, Color.RED)
|
interceptor.currentTile.position, Color.RED)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -460,8 +460,8 @@ object Battle {
|
|||||||
val defendBaseUnit = defender.unit.baseUnit
|
val defendBaseUnit = defender.unit.baseUnit
|
||||||
val baseChance = withdrawUnique.params[0].toFloat()
|
val baseChance = withdrawUnique.params[0].toFloat()
|
||||||
val percentChance = (baseChance
|
val percentChance = (baseChance
|
||||||
* defendBaseUnit.strength / attackBaseUnit.strength
|
* defendBaseUnit.strength / attackBaseUnit.strength
|
||||||
* defendBaseUnit.movement / attackBaseUnit.movement).toInt()
|
* defendBaseUnit.movement / attackBaseUnit.movement).toInt()
|
||||||
// Roll the dice - note the effect of the surroundings, namely how much room there is to evade to,
|
// Roll the dice - note the effect of the surroundings, namely how much room there is to evade to,
|
||||||
// isn't yet factored in. But it should, and that's factored in by allowing the dice to choose
|
// isn't yet factored in. But it should, and that's factored in by allowing the dice to choose
|
||||||
// any geometrically fitting tile first and *then* fail when checking the tile for viability.
|
// any geometrically fitting tile first and *then* fail when checking the tile for viability.
|
||||||
@ -494,7 +494,7 @@ object Battle {
|
|||||||
defender.unit.removeFromTile()
|
defender.unit.removeFromTile()
|
||||||
defender.unit.putInTile(toTile)
|
defender.unit.putInTile(toTile)
|
||||||
// and count 1 attack for attacker but leave it in place
|
// and count 1 attack for attacker but leave it in place
|
||||||
reduceAttackerMovementPointsAndAttacks(attacker,defender)
|
reduceAttackerMovementPointsAndAttacks(attacker, defender)
|
||||||
val notificationString = "[" + defendBaseUnit.name + "] withdrew from a [" + attackBaseUnit.name + "]"
|
val notificationString = "[" + defendBaseUnit.name + "] withdrew from a [" + attackBaseUnit.name + "]"
|
||||||
defender.getCivInfo().addNotification(notificationString, toTile.position, Color.GREEN)
|
defender.getCivInfo().addNotification(notificationString, toTile.position, Color.GREEN)
|
||||||
attacker.getCivInfo().addNotification(notificationString, toTile.position, Color.RED)
|
attacker.getCivInfo().addNotification(notificationString, toTile.position, Color.RED)
|
||||||
|
Reference in New Issue
Block a user