mirror of
https://github.com/yairm210/Unciv.git
synced 2025-09-28 22:37:02 -04:00
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:
parent
fd3bfbade4
commit
69e1792fa9
@ -165,6 +165,7 @@ You destroyed City States that were under our protection! =
|
|||||||
You attacked 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 demanded tribute from City States that were under our protection! =
|
||||||
You sided with a City State over us =
|
You sided with a City State over us =
|
||||||
|
You returned captured units to us =
|
||||||
|
|
||||||
Demands =
|
Demands =
|
||||||
Please don't settle new cities near us. =
|
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. =
|
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. =
|
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 =
|
Enter the amount of gold =
|
||||||
|
|
||||||
# City-States
|
# City-States
|
||||||
|
@ -347,12 +347,14 @@ object Battle {
|
|||||||
|
|
||||||
private fun postBattleMoveToAttackedTile(attacker: ICombatant, defender: ICombatant, attackedTile: TileInfo) {
|
private fun postBattleMoveToAttackedTile(attacker: ICombatant, defender: ICombatant, attackedTile: TileInfo) {
|
||||||
if (attacker.isMelee()
|
if (attacker.isMelee()
|
||||||
&& (defender.isDefeated() || defender.getCivInfo() == attacker.getCivInfo())
|
&& (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)) {
|
|
||||||
// we destroyed an enemy military unit and there was a civilian unit in the same tile as well
|
// 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())
|
if (attackedTile.civilianUnit != null && attackedTile.civilianUnit!!.civInfo != attacker.getCivInfo())
|
||||||
captureCivilianUnit(attacker, MapUnitCombatant(attackedTile.civilianUnit!!))
|
captureCivilianUnit(attacker, MapUnitCombatant(attackedTile.civilianUnit!!))
|
||||||
|
|
||||||
|
// 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
|
// 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
|
// 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
|
// are exempt from zone of control, since units that cannot move after attacking already
|
||||||
@ -360,6 +362,7 @@ object Battle {
|
|||||||
attacker.unit.movement.moveToTile(attackedTile, considerZoneOfControl = false)
|
attacker.unit.movement.moveToTile(attackedTile, considerZoneOfControl = false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun postBattleAddXp(attacker: ICombatant, defender: ICombatant) {
|
private fun postBattleAddXp(attacker: ICombatant, defender: ICombatant) {
|
||||||
if (attacker.isMelee()) {
|
if (attacker.isMelee()) {
|
||||||
@ -477,14 +480,40 @@ object Battle {
|
|||||||
defender.getTile().position, attacker.getName(), NotificationIcon.War, defender.getName())
|
defender.getTile().position, attacker.getName(), NotificationIcon.War, defender.getName())
|
||||||
|
|
||||||
val capturedUnitTile = capturedUnit.getTile()
|
val capturedUnitTile = capturedUnit.getTile()
|
||||||
|
val originalOwner = if (capturedUnit.originalOwner != null)
|
||||||
|
capturedUnit.civInfo.gameInfo.getCivilization(capturedUnit.originalOwner!!)
|
||||||
|
else null
|
||||||
|
|
||||||
|
|
||||||
when {
|
when {
|
||||||
// Uncapturable units are destroyed
|
// Uncapturable units are destroyed
|
||||||
defender.unit.hasUnique("Uncapturable") -> {
|
defender.unit.hasUnique("Uncapturable") -> {
|
||||||
capturedUnit.destroy()
|
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).
|
// 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()
|
capturedUnit.destroy()
|
||||||
// This is so that future checks which check if a unit has been captured are caught give the right answer
|
// This is so that future checks which check if a unit has been captured are caught give the right answer
|
||||||
// For example, in postBattleMoveToAttackedTile
|
// For example, in postBattleMoveToAttackedTile
|
||||||
@ -492,14 +521,7 @@ object Battle {
|
|||||||
attacker.getCivInfo().placeUnitNearTile(capturedUnitTile.position, Constants.worker)
|
attacker.getCivInfo().placeUnitNearTile(capturedUnitTile.position, Constants.worker)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
capturedUnit.civInfo.removeUnit(capturedUnit)
|
capturedUnit.capturedBy(attacker.getCivInfo())
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ enum class AlertType {
|
|||||||
DiplomaticMarriage,
|
DiplomaticMarriage,
|
||||||
BulliedProtectedMinor,
|
BulliedProtectedMinor,
|
||||||
AttackedProtectedMinor,
|
AttackedProtectedMinor,
|
||||||
|
RecapturedCivilian,
|
||||||
}
|
}
|
||||||
|
|
||||||
class PopupAlert {
|
class PopupAlert {
|
||||||
|
@ -81,6 +81,7 @@ enum class DiplomaticModifiers {
|
|||||||
AttackedProtectedMinor,
|
AttackedProtectedMinor,
|
||||||
BulliedProtectedMinor,
|
BulliedProtectedMinor,
|
||||||
SidedWithProtectedMinor,
|
SidedWithProtectedMinor,
|
||||||
|
ReturnedCapturedUnits,
|
||||||
}
|
}
|
||||||
|
|
||||||
class DiplomacyManager() {
|
class DiplomacyManager() {
|
||||||
@ -645,6 +646,7 @@ class DiplomacyManager() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
otherCivDiplomacy.setModifier(DiplomaticModifiers.DeclaredWarOnUs, -20f)
|
otherCivDiplomacy.setModifier(DiplomaticModifiers.DeclaredWarOnUs, -20f)
|
||||||
|
otherCivDiplomacy.removeModifier(DiplomaticModifiers.ReturnedCapturedUnits)
|
||||||
if (otherCiv.isCityState()) {
|
if (otherCiv.isCityState()) {
|
||||||
otherCivDiplomacy.setInfluence(-60f)
|
otherCivDiplomacy.setInfluence(-60f)
|
||||||
civInfo.changeMinorCivsAttacked(1)
|
civInfo.changeMinorCivsAttacked(1)
|
||||||
|
@ -114,6 +114,9 @@ class MapUnit {
|
|||||||
/** civName owning the unit */
|
/** civName owning the unit */
|
||||||
lateinit var owner: String
|
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
|
* Name key of the unit, used for serialization
|
||||||
*/
|
*/
|
||||||
@ -169,6 +172,7 @@ class MapUnit {
|
|||||||
toReturn.name = name
|
toReturn.name = name
|
||||||
toReturn.civInfo = civInfo
|
toReturn.civInfo = civInfo
|
||||||
toReturn.owner = owner
|
toReturn.owner = owner
|
||||||
|
toReturn.originalOwner = originalOwner
|
||||||
toReturn.instanceName = instanceName
|
toReturn.instanceName = instanceName
|
||||||
toReturn.currentMovement = currentMovement
|
toReturn.currentMovement = currentMovement
|
||||||
toReturn.health = health
|
toReturn.health = health
|
||||||
@ -916,6 +920,17 @@ class MapUnit {
|
|||||||
civInfo.addUnit(this, updateCivInfo)
|
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 {
|
fun canIntercept(attackedTile: TileInfo): Boolean {
|
||||||
if (!canIntercept()) return false
|
if (!canIntercept()) return false
|
||||||
if (currentTile.aerialDistanceTo(attackedTile) > baseUnit.interceptRange) return false
|
if (currentTile.aerialDistanceTo(attackedTile) > baseUnit.interceptRange) return false
|
||||||
|
@ -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
|
// both the civ name and actual civ need to be in here in order to calculate the canMoveTo...Darn
|
||||||
unit.assignOwner(civInfo, false)
|
unit.assignOwner(civInfo, false)
|
||||||
|
// remember our first owner
|
||||||
|
unit.originalOwner = civInfo.civName
|
||||||
|
|
||||||
var unitToPlaceTile: TileInfo? = null
|
var unitToPlaceTile: TileInfo? = null
|
||||||
// try to place at the original point (this is the most probable scenario)
|
// try to place at the original point (this is the most probable scenario)
|
||||||
|
@ -759,6 +759,7 @@ class DiplomacyScreen(val viewingCiv:CivilizationInfo): CameraStageBaseScreen()
|
|||||||
AttackedProtectedMinor -> "You attacked City States that were under our protection!"
|
AttackedProtectedMinor -> "You attacked City States that were under our protection!"
|
||||||
BulliedProtectedMinor -> "You demanded tribute from 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"
|
SidedWithProtectedMinor -> "You sided with a City State over us"
|
||||||
|
ReturnedCapturedUnits -> "You returned captured units to us"
|
||||||
}
|
}
|
||||||
text = text.tr() + " "
|
text = text.tr() + " "
|
||||||
if (modifier.value > 0) text += "+"
|
if (modifier.value > 0) text += "+"
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
package com.unciv.ui.worldscreen
|
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.ScrollPane
|
||||||
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
||||||
import com.badlogic.gdx.scenes.scene2d.ui.TextButton
|
import com.badlogic.gdx.scenes.scene2d.ui.TextButton
|
||||||
import com.unciv.Constants
|
import com.unciv.Constants
|
||||||
import com.unciv.UncivGame
|
import com.unciv.UncivGame
|
||||||
import com.unciv.logic.civilization.*
|
import com.unciv.logic.civilization.*
|
||||||
|
import com.unciv.logic.civilization.diplomacy.DiplomaticModifiers
|
||||||
import com.unciv.logic.civilization.diplomacy.RelationshipLevel
|
import com.unciv.logic.civilization.diplomacy.RelationshipLevel
|
||||||
import com.unciv.models.translations.fillPlaceholders
|
import com.unciv.models.translations.fillPlaceholders
|
||||||
import com.unciv.models.translations.tr
|
import com.unciv.models.translations.tr
|
||||||
@ -321,6 +323,49 @@ class AlertPopup(val worldScreen: WorldScreen, val popupAlert: PopupAlert): Popu
|
|||||||
cityState.removeProtectorCiv(player, forced = true)
|
cityState.removeProtectorCiv(player, forced = true)
|
||||||
}).row()
|
}).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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user