Return Civilians captured by Barbarians to original owner; Capture stacked civilians (#5437)

* return captured civilians

* stacked civilian capture bug

* works on old saves

* diplomacy bonus expires on war declared

* vector bug
This commit is contained in:
SimonCeder 2021-10-09 19:44:03 +02:00 committed by GitHub
parent fd3bfbade4
commit 69e1792fa9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 109 additions and 17 deletions

View File

@ -165,6 +165,7 @@ You destroyed City States that were under our protection! =
You attacked City States that were under our protection! =
You demanded tribute from City States that were under our protection! =
You sided with a City State over us =
You returned captured units to us =
Demands =
Please don't settle new cities near us. =
@ -176,6 +177,9 @@ We asked [civName] for a tribute recently and they gave in.\nYou promised to pro
It's come to my attention that I may have attacked [civName], a city-state under your protection.\nWhile it was not my goal to be at odds with your empire, this was deemed a necessary course of action. =
I thought you might like to know that I've launched an invasion of one of your little pet states.\nThe lands of [civName] will make a fine addition to my own. =
Return [unitName] to [civName]? =
The [unitName] we liberated originally belonged to [civName]. They will be grateful if we return it to them. =
Enter the amount of gold =
# City-States

View File

@ -347,17 +347,20 @@ object Battle {
private fun postBattleMoveToAttackedTile(attacker: ICombatant, defender: ICombatant, attackedTile: TileInfo) {
if (attacker.isMelee()
&& (defender.isDefeated() || defender.getCivInfo() == attacker.getCivInfo())
// This is so that if we attack e.g. a barbarian in enemy territory that we can't enter, we won't enter it
&& (attacker as MapUnitCombatant).unit.movement.canMoveTo(attackedTile)) {
&& (defender.isDefeated() || defender.getCivInfo() == attacker.getCivInfo())) {
// we destroyed an enemy military unit and there was a civilian unit in the same tile as well
// this has to be checked before canMoveTo, otherwise it will return false
if (attackedTile.civilianUnit != null && attackedTile.civilianUnit!!.civInfo != attacker.getCivInfo())
captureCivilianUnit(attacker, MapUnitCombatant(attackedTile.civilianUnit!!))
// Units that can move after attacking are not affected by zone of control if the
// movement is caused by killing a unit. Effectively, this means that attack movements
// are exempt from zone of control, since units that cannot move after attacking already
// lose all remaining movement points anyway.
attacker.unit.movement.moveToTile(attackedTile, considerZoneOfControl = false)
// This is so that if we attack e.g. a barbarian in enemy territory that we can't enter, we won't enter it
if ((attacker as MapUnitCombatant).unit.movement.canMoveTo(attackedTile)) {
// Units that can move after attacking are not affected by zone of control if the
// movement is caused by killing a unit. Effectively, this means that attack movements
// are exempt from zone of control, since units that cannot move after attacking already
// lose all remaining movement points anyway.
attacker.unit.movement.moveToTile(attackedTile, considerZoneOfControl = false)
}
}
}
@ -477,14 +480,40 @@ object Battle {
defender.getTile().position, attacker.getName(), NotificationIcon.War, defender.getName())
val capturedUnitTile = capturedUnit.getTile()
val originalOwner = if (capturedUnit.originalOwner != null)
capturedUnit.civInfo.gameInfo.getCivilization(capturedUnit.originalOwner!!)
else null
when {
// Uncapturable units are destroyed
defender.unit.hasUnique("Uncapturable") -> {
capturedUnit.destroy()
}
// City states can never capture settlers at all
capturedUnit.hasUnique("Founds a new city") && attacker.getCivInfo().isCityState() -> {
capturedUnit.destroy()
}
// Is it our old unit?
attacker.getCivInfo() == originalOwner -> {
// Then it is recaptured without converting settlers to workers
capturedUnit.capturedBy(attacker.getCivInfo())
}
// Return captured civilian to its original owner?
defender.getCivInfo().isBarbarian()
&& originalOwner != null
&& !originalOwner.isBarbarian()
&& attacker.getCivInfo() != originalOwner
&& attacker.getCivInfo().knows(originalOwner)
&& originalOwner.isAlive()
&& !attacker.getCivInfo().isAtWarWith(originalOwner)
&& attacker.getCivInfo().playerType == PlayerType.Human // Only humans get the choice
-> {
capturedUnit.capturedBy(attacker.getCivInfo())
attacker.getCivInfo().popupAlerts.add(PopupAlert(AlertType.RecapturedCivilian, capturedUnitTile.position.toString()))
}
// Captured settlers are converted to workers unless captured by barbarians (so they can be returned later).
capturedUnit.name == Constants.settler && !attacker.getCivInfo().isBarbarian() -> {
capturedUnit.hasUnique("Founds a new city") && !attacker.getCivInfo().isBarbarian() -> {
capturedUnit.destroy()
// This is so that future checks which check if a unit has been captured are caught give the right answer
// For example, in postBattleMoveToAttackedTile
@ -492,14 +521,7 @@ object Battle {
attacker.getCivInfo().placeUnitNearTile(capturedUnitTile.position, Constants.worker)
}
else -> {
capturedUnit.civInfo.removeUnit(capturedUnit)
capturedUnit.assignOwner(attacker.getCivInfo())
capturedUnit.currentMovement = 0f
// It's possible that the unit can no longer stand on the tile it was captured on.
// For example, because it's embarked and the capturing civ cannot embark units yet.
if (!capturedUnit.movement.canPassThrough(capturedUnitTile)) {
capturedUnit.movement.teleportToClosestMoveableTile()
}
capturedUnit.capturedBy(attacker.getCivInfo())
}
}

View File

@ -16,6 +16,7 @@ enum class AlertType {
DiplomaticMarriage,
BulliedProtectedMinor,
AttackedProtectedMinor,
RecapturedCivilian,
}
class PopupAlert {

View File

@ -81,6 +81,7 @@ enum class DiplomaticModifiers {
AttackedProtectedMinor,
BulliedProtectedMinor,
SidedWithProtectedMinor,
ReturnedCapturedUnits,
}
class DiplomacyManager() {
@ -645,6 +646,7 @@ class DiplomacyManager() {
}
otherCivDiplomacy.setModifier(DiplomaticModifiers.DeclaredWarOnUs, -20f)
otherCivDiplomacy.removeModifier(DiplomaticModifiers.ReturnedCapturedUnits)
if (otherCiv.isCityState()) {
otherCivDiplomacy.setInfluence(-60f)
civInfo.changeMinorCivsAttacked(1)

View File

@ -114,6 +114,9 @@ class MapUnit {
/** civName owning the unit */
lateinit var owner: String
/** civName of original owner - relevant for returning captured workers from barbarians */
var originalOwner: String? = null
/**
* Name key of the unit, used for serialization
*/
@ -169,6 +172,7 @@ class MapUnit {
toReturn.name = name
toReturn.civInfo = civInfo
toReturn.owner = owner
toReturn.originalOwner = originalOwner
toReturn.instanceName = instanceName
toReturn.currentMovement = currentMovement
toReturn.health = health
@ -916,6 +920,17 @@ class MapUnit {
civInfo.addUnit(this, updateCivInfo)
}
fun capturedBy(captor: CivilizationInfo) {
civInfo.removeUnit(this)
assignOwner(captor)
currentMovement = 0f
// It's possible that the unit can no longer stand on the tile it was captured on.
// For example, because it's embarked and the capturing civ cannot embark units yet.
if (!movement.canPassThrough(getTile())) {
movement.teleportToClosestMoveableTile()
}
}
fun canIntercept(attackedTile: TileInfo): Boolean {
if (!canIntercept()) return false
if (currentTile.aerialDistanceTo(attackedTile) > baseUnit.interceptRange) return false

View File

@ -410,6 +410,8 @@ class TileMap {
// both the civ name and actual civ need to be in here in order to calculate the canMoveTo...Darn
unit.assignOwner(civInfo, false)
// remember our first owner
unit.originalOwner = civInfo.civName
var unitToPlaceTile: TileInfo? = null
// try to place at the original point (this is the most probable scenario)

View File

@ -759,6 +759,7 @@ class DiplomacyScreen(val viewingCiv:CivilizationInfo): CameraStageBaseScreen()
AttackedProtectedMinor -> "You attacked City States that were under our protection!"
BulliedProtectedMinor -> "You demanded tribute from City States that were under our protection!"
SidedWithProtectedMinor -> "You sided with a City State over us"
ReturnedCapturedUnits -> "You returned captured units to us"
}
text = text.tr() + " "
if (modifier.value > 0) text += "+"

View File

@ -1,11 +1,13 @@
package com.unciv.ui.worldscreen
import com.badlogic.gdx.math.Vector2
import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane
import com.badlogic.gdx.scenes.scene2d.ui.Table
import com.badlogic.gdx.scenes.scene2d.ui.TextButton
import com.unciv.Constants
import com.unciv.UncivGame
import com.unciv.logic.civilization.*
import com.unciv.logic.civilization.diplomacy.DiplomaticModifiers
import com.unciv.logic.civilization.diplomacy.RelationshipLevel
import com.unciv.models.translations.fillPlaceholders
import com.unciv.models.translations.tr
@ -321,6 +323,49 @@ class AlertPopup(val worldScreen: WorldScreen, val popupAlert: PopupAlert): Popu
cityState.removeProtectorCiv(player, forced = true)
}).row()
}
AlertType.RecapturedCivilian -> {
val position = Vector2().fromString(popupAlert.value)
val tile = worldScreen.gameInfo.tileMap[position]
val capturedUnit = tile.civilianUnit!! // This has got to be it
val originalOwner = worldScreen.gameInfo.getCivilization(capturedUnit.originalOwner!!)
val captor = worldScreen.viewingCiv
addGoodSizedLabel("Return [${capturedUnit.name}] to [${originalOwner.civName}]?")
addSeparator()
addGoodSizedLabel("The [${capturedUnit.name}] we liberated originally belonged to [${originalOwner.civName}]. They will be grateful if we return it to them.").row()
val responseTable = Table()
responseTable.defaults().pad(0f, 30f) // Small buttons, plenty of pad so we don't fat-finger it
responseTable.add(getCloseButton("Yes", 'y') {
// Return it to original owner
val unitName = capturedUnit.baseUnit.name
capturedUnit.destroy()
val closestCity = originalOwner.cities.minByOrNull { it.getCenterTile().aerialDistanceTo(tile) }
if (closestCity != null) {
// Attempt to place the unit near their nearest city
originalOwner.placeUnitNearTile(closestCity.location, unitName)
}
if (originalOwner.isCityState()) {
originalOwner.getDiplomacyManager(captor).addInfluence(45f)
} else if (originalOwner.isMajorCiv()) {
// No extra bonus from doing it several times
originalOwner.getDiplomacyManager(captor).setModifier(DiplomaticModifiers.ReturnedCapturedUnits, 20f)
}
})
responseTable.add(getCloseButton("No", 'n') {
// Take it for ourselves
// Settlers become workers at this point
if (capturedUnit.hasUnique("Founds a new city")) {
capturedUnit.destroy()
// This is so that future checks which check if a unit has been captured are caught give the right answer
// For example, in postBattleMoveToAttackedTile
capturedUnit.civInfo = captor
captor.placeUnitNearTile(tile.position, Constants.worker)
} else
capturedUnit.capturedBy(captor)
}).row()
add(responseTable)
}
}
}