mirror of
https://github.com/yairm210/Unciv.git
synced 2025-09-29 06:51:30 -04:00
Resolved #506 - Added real diplomatic relations!
Doesn't affect much now, but the platform is there to add what we want!
This commit is contained in:
parent
2ab8caeaea
commit
5952056518
@ -6,10 +6,12 @@ import com.unciv.logic.automation.UnitAutomation
|
|||||||
import com.unciv.logic.city.CityInfo
|
import com.unciv.logic.city.CityInfo
|
||||||
import com.unciv.logic.civilization.AlertType
|
import com.unciv.logic.civilization.AlertType
|
||||||
import com.unciv.logic.civilization.PopupAlert
|
import com.unciv.logic.civilization.PopupAlert
|
||||||
|
import com.unciv.logic.civilization.diplomacy.DiplomaticModifiers
|
||||||
import com.unciv.logic.map.TileInfo
|
import com.unciv.logic.map.TileInfo
|
||||||
import com.unciv.models.gamebasics.unit.UnitType
|
import com.unciv.models.gamebasics.unit.UnitType
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Damage calculations according to civ v wiki and https://steamcommunity.com/sharedfiles/filedetails/?id=170194443
|
* Damage calculations according to civ v wiki and https://steamcommunity.com/sharedfiles/filedetails/?id=170194443
|
||||||
@ -160,20 +162,37 @@ class Battle(val gameInfo:GameInfo) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun conquerCity(city: CityInfo, attacker: ICombatant) {
|
private fun conquerCity(city: CityInfo, attacker: ICombatant) {
|
||||||
val enemyCiv = city.civInfo
|
val cityCiv = city.civInfo
|
||||||
attacker.getCivInfo().addNotification("We have conquered the city of [${city.name}]!",city.location, Color.RED)
|
val attackerCiv = attacker.getCivInfo()
|
||||||
attacker.getCivInfo().popupAlerts.add(PopupAlert(AlertType.CityConquered,city.name))
|
|
||||||
|
attackerCiv.addNotification("We have conquered the city of [${city.name}]!",city.location, Color.RED)
|
||||||
|
attackerCiv.popupAlerts.add(PopupAlert(AlertType.CityConquered,city.name))
|
||||||
|
|
||||||
city.getCenterTile().apply {
|
city.getCenterTile().apply {
|
||||||
if(militaryUnit!=null) militaryUnit!!.destroy()
|
if(militaryUnit!=null) militaryUnit!!.destroy()
|
||||||
if(civilianUnit!=null) captureCivilianUnit(attacker,MapUnitCombatant(civilianUnit!!))
|
if(civilianUnit!=null) captureCivilianUnit(attacker,MapUnitCombatant(civilianUnit!!))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (attacker.getCivInfo().isBarbarianCivilization() || attacker.getCivInfo().isCityState()){
|
if (!attackerCiv.isMajorCiv()){
|
||||||
city.destroyCity()
|
city.destroyCity()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
val currentPopulation = city.population.population
|
val currentPopulation = city.population.population
|
||||||
|
|
||||||
|
val percentageOfCivPopulationInThatCity = currentPopulation*100f / cityCiv.cities.sumBy { it.population.population }
|
||||||
|
val aggroGenerated = 10f+percentageOfCivPopulationInThatCity.roundToInt()
|
||||||
|
cityCiv.getDiplomacyManager(attacker.getCivInfo())
|
||||||
|
.addModifier(DiplomaticModifiers.CapturedOurCities, -aggroGenerated)
|
||||||
|
|
||||||
|
for(thirdPartyCiv in attackerCiv.getKnownCivs().filter { it.isMajorCiv() }){
|
||||||
|
val aggroGeneratedForOtherCivs = (aggroGenerated/10).roundToInt().toFloat()
|
||||||
|
if(thirdPartyCiv.isAtWarWith(cityCiv)) // You annoyed our enemy?
|
||||||
|
thirdPartyCiv.getDiplomacyManager(attackerCiv)
|
||||||
|
.addModifier(DiplomaticModifiers.CapturedOurEnemiesCities,aggroGeneratedForOtherCivs) // Cool, keep at at! =D
|
||||||
|
else thirdPartyCiv.getDiplomacyManager(attackerCiv)
|
||||||
|
.addModifier(DiplomaticModifiers.WarMongerer, -aggroGeneratedForOtherCivs) // Uncool bro.
|
||||||
|
}
|
||||||
|
|
||||||
if(currentPopulation>1) city.population.population -= 1 + currentPopulation/4 // so from 2-4 population, remove 1, from 5-8, remove 2, etc.
|
if(currentPopulation>1) city.population.population -= 1 + currentPopulation/4 // so from 2-4 population, remove 1, from 5-8, remove 2, etc.
|
||||||
city.population.unassignExtraPopulation()
|
city.population.unassignExtraPopulation()
|
||||||
|
|
||||||
@ -195,12 +214,12 @@ class Battle(val gameInfo:GameInfo) {
|
|||||||
|
|
||||||
if(city.cityConstructions.isBuilt("Palace")){
|
if(city.cityConstructions.isBuilt("Palace")){
|
||||||
city.cityConstructions.removeBuilding("Palace")
|
city.cityConstructions.removeBuilding("Palace")
|
||||||
if(enemyCiv.isDefeated()) {
|
if(cityCiv.isDefeated()) {
|
||||||
enemyCiv.destroy()
|
cityCiv.destroy()
|
||||||
attacker.getCivInfo().popupAlerts.add(PopupAlert(AlertType.Defeated,enemyCiv.civName))
|
attacker.getCivInfo().popupAlerts.add(PopupAlert(AlertType.Defeated,cityCiv.civName))
|
||||||
}
|
}
|
||||||
else if(enemyCiv.cities.isNotEmpty()){
|
else if(cityCiv.cities.isNotEmpty()){
|
||||||
enemyCiv.cities.first().cityConstructions.addBuilding("Palace") // relocate palace
|
cityCiv.cities.first().cityConstructions.addBuilding("Palace") // relocate palace
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,7 +123,6 @@ class CivilizationInfo {
|
|||||||
return translatedNation
|
return translatedNation
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isCityState(): Boolean = getNation().isCityState()
|
|
||||||
fun getDiplomacyManager(civInfo: CivilizationInfo) = diplomacy[civInfo.civName]!!
|
fun getDiplomacyManager(civInfo: CivilizationInfo) = diplomacy[civInfo.civName]!!
|
||||||
fun getKnownCivs() = diplomacy.values.map { it.otherCiv() }
|
fun getKnownCivs() = diplomacy.values.map { it.otherCiv() }
|
||||||
|
|
||||||
@ -131,6 +130,8 @@ class CivilizationInfo {
|
|||||||
fun isPlayerCivilization() = playerType==PlayerType.Human
|
fun isPlayerCivilization() = playerType==PlayerType.Human
|
||||||
fun isCurrentPlayer() = gameInfo.getCurrentPlayerCivilization()==this
|
fun isCurrentPlayer() = gameInfo.getCurrentPlayerCivilization()==this
|
||||||
fun isBarbarianCivilization() = gameInfo.getBarbarianCivilization()==this
|
fun isBarbarianCivilization() = gameInfo.getBarbarianCivilization()==this
|
||||||
|
fun isCityState(): Boolean = getNation().isCityState()
|
||||||
|
fun isMajorCiv() = !isBarbarianCivilization() && !isCityState()
|
||||||
fun getStatsForNextTurn():Stats = getStatMapForNextTurn().values.toList().reduce{a,b->a+b}
|
fun getStatsForNextTurn():Stats = getStatMapForNextTurn().values.toList().reduce{a,b->a+b}
|
||||||
|
|
||||||
fun getStatMapForNextTurn(): HashMap<String, Stats> {
|
fun getStatMapForNextTurn(): HashMap<String, Stats> {
|
||||||
|
@ -16,6 +16,14 @@ enum class DiplomacyFlags{
|
|||||||
DeclinedPeace
|
DeclinedPeace
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class DiplomaticModifiers{
|
||||||
|
DeclaredWarOnUs,
|
||||||
|
WarMongerer,
|
||||||
|
CapturedOurCities,
|
||||||
|
YearsOfPeace,
|
||||||
|
CapturedOurEnemiesCities
|
||||||
|
}
|
||||||
|
|
||||||
class DiplomacyManager() {
|
class DiplomacyManager() {
|
||||||
@Transient lateinit var civInfo: CivilizationInfo
|
@Transient lateinit var civInfo: CivilizationInfo
|
||||||
// since this needs to be checked a lot during travel, putting it in a transient is a good performance booster
|
// since this needs to be checked a lot during travel, putting it in a transient is a good performance booster
|
||||||
@ -24,11 +32,19 @@ class DiplomacyManager() {
|
|||||||
lateinit var otherCivName:String
|
lateinit var otherCivName:String
|
||||||
var trades = ArrayList<Trade>()
|
var trades = ArrayList<Trade>()
|
||||||
var diplomaticStatus = DiplomaticStatus.War
|
var diplomaticStatus = DiplomaticStatus.War
|
||||||
|
|
||||||
/** Contains various flags (declared war, promised to not settle, declined luxury trade) and the number of turns in which they will expire.
|
/** Contains various flags (declared war, promised to not settle, declined luxury trade) and the number of turns in which they will expire.
|
||||||
* The JSON serialize/deserialize REFUSES to deserialize hashmap keys as Enums, so I'm forced to use strings instead =(
|
* The JSON serialize/deserialize REFUSES to deserialize hashmap keys as Enums, so I'm forced to use strings instead =(
|
||||||
* This is so sad Alexa play Despacito */
|
* This is so sad Alexa play Despacito */
|
||||||
var flagsCountdown = HashMap<String,Int>()
|
var flagsCountdown = HashMap<String,Int>()
|
||||||
var influence = 0f // For city states
|
|
||||||
|
/** For AI. Positive is good relations, negative is bad.
|
||||||
|
* Baseline is 1 point for each turn of peace - so declaring a war upends 40 years of peace, and e.g. capturing a city can be another 30 or 40.
|
||||||
|
* As for why it's String and not DiplomaticModifier see FlagsCountdown comment */
|
||||||
|
var diplomaticModifiers = HashMap<String,Float>()
|
||||||
|
|
||||||
|
/** For city states */
|
||||||
|
var influence = 0f
|
||||||
|
|
||||||
fun clone(): DiplomacyManager {
|
fun clone(): DiplomacyManager {
|
||||||
val toReturn = DiplomacyManager()
|
val toReturn = DiplomacyManager()
|
||||||
@ -48,6 +64,8 @@ class DiplomacyManager() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//region pure functions
|
//region pure functions
|
||||||
|
fun otherCiv() = civInfo.gameInfo.getCivilization(otherCivName)
|
||||||
|
|
||||||
fun turnsToPeaceTreaty(): Int {
|
fun turnsToPeaceTreaty(): Int {
|
||||||
for(trade in trades)
|
for(trade in trades)
|
||||||
for(offer in trade.ourOffers)
|
for(offer in trade.ourOffers)
|
||||||
@ -55,9 +73,10 @@ class DiplomacyManager() {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun opinionOfOtherCiv() = diplomaticModifiers.values.sum()
|
||||||
|
|
||||||
fun canDeclareWar() = (turnsToPeaceTreaty()==0 && diplomaticStatus != DiplomaticStatus.War)
|
fun canDeclareWar() = (turnsToPeaceTreaty()==0 && diplomaticStatus != DiplomaticStatus.War)
|
||||||
|
|
||||||
fun otherCiv() = civInfo.gameInfo.getCivilization(otherCivName)
|
|
||||||
|
|
||||||
fun goldPerTurn():Int{
|
fun goldPerTurn():Int{
|
||||||
var goldPerTurnForUs = 0
|
var goldPerTurnForUs = 0
|
||||||
@ -140,9 +159,14 @@ class DiplomacyManager() {
|
|||||||
removeUntenebleTrades()
|
removeUntenebleTrades()
|
||||||
updateHasOpenBorders()
|
updateHasOpenBorders()
|
||||||
|
|
||||||
|
if(diplomaticStatus==DiplomaticStatus.Peace)
|
||||||
|
addModifier(DiplomaticModifiers.YearsOfPeace,1f)
|
||||||
|
|
||||||
for(flag in flagsCountdown.keys.toList()) {
|
for(flag in flagsCountdown.keys.toList()) {
|
||||||
flagsCountdown[flag] = flagsCountdown[flag]!! - 1
|
flagsCountdown[flag] = flagsCountdown[flag]!! - 1
|
||||||
if(flagsCountdown[flag]==0) flagsCountdown.remove(flag)
|
if(flagsCountdown[flag]==0) {
|
||||||
|
flagsCountdown.remove(flag)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (influence > 1) {
|
if (influence > 1) {
|
||||||
@ -179,6 +203,29 @@ class DiplomacyManager() {
|
|||||||
/// AI won't propose peace for 10 turns
|
/// AI won't propose peace for 10 turns
|
||||||
flagsCountdown[DiplomacyFlags.DeclinedPeace.toString()]=10
|
flagsCountdown[DiplomacyFlags.DeclinedPeace.toString()]=10
|
||||||
otherCiv.getDiplomacyManager(civInfo).flagsCountdown[DiplomacyFlags.DeclinedPeace.toString()]=10
|
otherCiv.getDiplomacyManager(civInfo).flagsCountdown[DiplomacyFlags.DeclinedPeace.toString()]=10
|
||||||
|
|
||||||
|
otherCivDiplomacy.diplomaticModifiers[DiplomaticModifiers.DeclaredWarOnUs.toString()] = -20f
|
||||||
|
for(thirdCiv in civInfo.getKnownCivs()){
|
||||||
|
thirdCiv.getDiplomacyManager(civInfo).addModifier(DiplomaticModifiers.WarMongerer,-5f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun makePeace(){
|
||||||
|
diplomaticStatus= DiplomaticStatus.Peace
|
||||||
|
val otherCiv = otherCiv()
|
||||||
|
// We get out of their territory
|
||||||
|
for(unit in civInfo.getCivUnits().filter { it.getTile().getOwner()== otherCiv})
|
||||||
|
unit.movementAlgs().teleportToClosestMoveableTile()
|
||||||
|
|
||||||
|
// And we get out of theirs
|
||||||
|
for(unit in otherCiv.getCivUnits().filter { it.getTile().getOwner()== civInfo})
|
||||||
|
unit.movementAlgs().teleportToClosestMoveableTile()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addModifier(modifier: DiplomaticModifiers, amount:Float){
|
||||||
|
val modifierString = modifier.toString()
|
||||||
|
if(!diplomaticModifiers.containsKey(modifierString)) diplomaticModifiers[modifierString]=0f
|
||||||
|
diplomaticModifiers[modifierString] = diplomaticModifiers[modifierString]!!+amount
|
||||||
}
|
}
|
||||||
//endregion
|
//endregion
|
||||||
}
|
}
|
||||||
|
@ -96,11 +96,7 @@ class TradeLogic(val ourCivilization:CivilizationInfo, val otherCivilization: Ci
|
|||||||
from.updateViewableTiles()
|
from.updateViewableTiles()
|
||||||
}
|
}
|
||||||
if(offer.type== TradeType.Treaty){
|
if(offer.type== TradeType.Treaty){
|
||||||
if(offer.name=="Peace Treaty"){
|
if(offer.name=="Peace Treaty") to.getDiplomacyManager(from).makePeace()
|
||||||
to.getDiplomacyManager(from).diplomaticStatus= DiplomaticStatus.Peace
|
|
||||||
for(unit in to.getCivUnits().filter { it.getTile().getOwner()==from })
|
|
||||||
unit.movementAlgs().teleportToClosestMoveableTile()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if(offer.type==TradeType.Introduction)
|
if(offer.type==TradeType.Introduction)
|
||||||
to.meetCivilization(to.gameInfo.getCivilization(offer.name.split(" ")[2]))
|
to.meetCivilization(to.gameInfo.getCivilization(offer.name.split(" ")[2]))
|
||||||
|
@ -66,44 +66,44 @@ class DiplomacyScreen:CameraStageBaseScreen() {
|
|||||||
return tradeTable
|
return tradeTable
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getDiplomacyTable(civ: CivilizationInfo): Table {
|
private fun getDiplomacyTable(otherCiv: CivilizationInfo): Table {
|
||||||
val currentPlayerCiv = UnCivGame.Current.gameInfo.getCurrentPlayerCivilization()
|
val currentPlayerCiv = UnCivGame.Current.gameInfo.getCurrentPlayerCivilization()
|
||||||
val diplomacyTable = Table()
|
val diplomacyTable = Table()
|
||||||
diplomacyTable.defaults().pad(10f)
|
diplomacyTable.defaults().pad(10f)
|
||||||
val leaderName: String
|
val leaderName: String
|
||||||
if (civ.isCityState()) {
|
if (otherCiv.isCityState()) {
|
||||||
leaderName = "City State [" + civ.civName + "]"
|
leaderName = otherCiv.civName
|
||||||
} else {
|
} else {
|
||||||
leaderName = "[" + civ.getNation().leaderName + "] of [" + civ.civName + "]"
|
leaderName = "[" + otherCiv.getNation().leaderName + "] of [" + otherCiv.civName + "]"
|
||||||
}
|
}
|
||||||
diplomacyTable.add(leaderName.toLabel())
|
diplomacyTable.add(leaderName.toLabel())
|
||||||
diplomacyTable.addSeparator()
|
diplomacyTable.addSeparator()
|
||||||
|
|
||||||
if(!civ.isCityState()) {
|
if(!otherCiv.isCityState()) {
|
||||||
val tradeButton = TextButton("Trade".tr(), skin)
|
val tradeButton = TextButton("Trade".tr(), skin)
|
||||||
tradeButton.onClick { setTrade(civ) }
|
tradeButton.onClick { setTrade(otherCiv) }
|
||||||
diplomacyTable.add(tradeButton).row()
|
diplomacyTable.add(tradeButton).row()
|
||||||
}
|
}
|
||||||
|
|
||||||
val civDiplomacy = currentPlayerCiv.getDiplomacyManager(civ)
|
val diplomacyManager = currentPlayerCiv.getDiplomacyManager(otherCiv)
|
||||||
|
|
||||||
if (!currentPlayerCiv.isAtWarWith(civ)) {
|
if (!currentPlayerCiv.isAtWarWith(otherCiv)) {
|
||||||
val declareWarButton = TextButton("Declare war".tr(), skin)
|
val declareWarButton = TextButton("Declare war".tr(), skin)
|
||||||
declareWarButton.color = Color.RED
|
declareWarButton.color = Color.RED
|
||||||
val turnsToPeaceTreaty = civDiplomacy.turnsToPeaceTreaty()
|
val turnsToPeaceTreaty = diplomacyManager.turnsToPeaceTreaty()
|
||||||
if (turnsToPeaceTreaty > 0) {
|
if (turnsToPeaceTreaty > 0) {
|
||||||
declareWarButton.disable()
|
declareWarButton.disable()
|
||||||
declareWarButton.setText(declareWarButton.text.toString() + " ($turnsToPeaceTreaty)")
|
declareWarButton.setText(declareWarButton.text.toString() + " ($turnsToPeaceTreaty)")
|
||||||
}
|
}
|
||||||
declareWarButton.onClick {
|
declareWarButton.onClick {
|
||||||
YesNoPopupTable("Declare war on [${civ.civName}]?".tr(), {
|
YesNoPopupTable("Declare war on [${otherCiv.civName}]?".tr(), {
|
||||||
civDiplomacy.declareWar()
|
diplomacyManager.declareWar()
|
||||||
|
|
||||||
val responsePopup = PopupTable(this)
|
val responsePopup = PopupTable(this)
|
||||||
val otherCivLeaderName = civ.getNation().leaderName + " of " + civ.civName
|
val otherCivLeaderName = otherCiv.getNation().leaderName + " of " + otherCiv.civName
|
||||||
responsePopup.add(otherCivLeaderName.toLabel())
|
responsePopup.add(otherCivLeaderName.toLabel())
|
||||||
responsePopup.addSeparator()
|
responsePopup.addSeparator()
|
||||||
responsePopup.addGoodSizedLabel(civ.getNation().attacked).row()
|
responsePopup.addGoodSizedLabel(otherCiv.getNation().attacked).row()
|
||||||
responsePopup.addButton("Very well.".tr()) { responsePopup.remove() }
|
responsePopup.addButton("Very well.".tr()) { responsePopup.remove() }
|
||||||
responsePopup.open()
|
responsePopup.open()
|
||||||
|
|
||||||
@ -112,6 +112,17 @@ class DiplomacyScreen:CameraStageBaseScreen() {
|
|||||||
}
|
}
|
||||||
diplomacyTable.add(declareWarButton).row()
|
diplomacyTable.add(declareWarButton).row()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!otherCiv.isCityState()){
|
||||||
|
val diplomacyModifiersTable = Table()
|
||||||
|
val otherCivDiplomacyManager = otherCiv.getDiplomacyManager(currentPlayerCiv)
|
||||||
|
diplomacyModifiersTable.add(("Current opinion: "+otherCivDiplomacyManager.opinionOfOtherCiv()).toLabel()).row()
|
||||||
|
for(modifier in otherCivDiplomacyManager.diplomaticModifiers){
|
||||||
|
diplomacyModifiersTable.add((modifier.key+" "+modifier.value).toLabel()).row()
|
||||||
|
}
|
||||||
|
diplomacyTable.add(diplomacyModifiersTable).row()
|
||||||
|
}
|
||||||
|
|
||||||
return diplomacyTable
|
return diplomacyTable
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user