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:
Yair Morgenstern 2019-04-30 23:56:47 +03:00
parent 2ab8caeaea
commit 5952056518
5 changed files with 105 additions and 31 deletions

View File

@ -6,10 +6,12 @@ import com.unciv.logic.automation.UnitAutomation
import com.unciv.logic.city.CityInfo
import com.unciv.logic.civilization.AlertType
import com.unciv.logic.civilization.PopupAlert
import com.unciv.logic.civilization.diplomacy.DiplomaticModifiers
import com.unciv.logic.map.TileInfo
import com.unciv.models.gamebasics.unit.UnitType
import java.util.*
import kotlin.math.max
import kotlin.math.roundToInt
/**
* 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) {
val enemyCiv = city.civInfo
attacker.getCivInfo().addNotification("We have conquered the city of [${city.name}]!",city.location, Color.RED)
attacker.getCivInfo().popupAlerts.add(PopupAlert(AlertType.CityConquered,city.name))
val cityCiv = city.civInfo
val attackerCiv = attacker.getCivInfo()
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 {
if(militaryUnit!=null) militaryUnit!!.destroy()
if(civilianUnit!=null) captureCivilianUnit(attacker,MapUnitCombatant(civilianUnit!!))
}
if (attacker.getCivInfo().isBarbarianCivilization() || attacker.getCivInfo().isCityState()){
if (!attackerCiv.isMajorCiv()){
city.destroyCity()
}
else {
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.
city.population.unassignExtraPopulation()
@ -195,12 +214,12 @@ class Battle(val gameInfo:GameInfo) {
if(city.cityConstructions.isBuilt("Palace")){
city.cityConstructions.removeBuilding("Palace")
if(enemyCiv.isDefeated()) {
enemyCiv.destroy()
attacker.getCivInfo().popupAlerts.add(PopupAlert(AlertType.Defeated,enemyCiv.civName))
if(cityCiv.isDefeated()) {
cityCiv.destroy()
attacker.getCivInfo().popupAlerts.add(PopupAlert(AlertType.Defeated,cityCiv.civName))
}
else if(enemyCiv.cities.isNotEmpty()){
enemyCiv.cities.first().cityConstructions.addBuilding("Palace") // relocate palace
else if(cityCiv.cities.isNotEmpty()){
cityCiv.cities.first().cityConstructions.addBuilding("Palace") // relocate palace
}
}

View File

@ -123,7 +123,6 @@ class CivilizationInfo {
return translatedNation
}
fun isCityState(): Boolean = getNation().isCityState()
fun getDiplomacyManager(civInfo: CivilizationInfo) = diplomacy[civInfo.civName]!!
fun getKnownCivs() = diplomacy.values.map { it.otherCiv() }
@ -131,6 +130,8 @@ class CivilizationInfo {
fun isPlayerCivilization() = playerType==PlayerType.Human
fun isCurrentPlayer() = gameInfo.getCurrentPlayerCivilization()==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 getStatMapForNextTurn(): HashMap<String, Stats> {

View File

@ -16,6 +16,14 @@ enum class DiplomacyFlags{
DeclinedPeace
}
enum class DiplomaticModifiers{
DeclaredWarOnUs,
WarMongerer,
CapturedOurCities,
YearsOfPeace,
CapturedOurEnemiesCities
}
class DiplomacyManager() {
@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
@ -24,11 +32,19 @@ class DiplomacyManager() {
lateinit var otherCivName:String
var trades = ArrayList<Trade>()
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.
* 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 */
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 {
val toReturn = DiplomacyManager()
@ -48,6 +64,8 @@ class DiplomacyManager() {
}
//region pure functions
fun otherCiv() = civInfo.gameInfo.getCivilization(otherCivName)
fun turnsToPeaceTreaty(): Int {
for(trade in trades)
for(offer in trade.ourOffers)
@ -55,9 +73,10 @@ class DiplomacyManager() {
return 0
}
fun opinionOfOtherCiv() = diplomaticModifiers.values.sum()
fun canDeclareWar() = (turnsToPeaceTreaty()==0 && diplomaticStatus != DiplomaticStatus.War)
fun otherCiv() = civInfo.gameInfo.getCivilization(otherCivName)
fun goldPerTurn():Int{
var goldPerTurnForUs = 0
@ -140,9 +159,14 @@ class DiplomacyManager() {
removeUntenebleTrades()
updateHasOpenBorders()
if(diplomaticStatus==DiplomaticStatus.Peace)
addModifier(DiplomaticModifiers.YearsOfPeace,1f)
for(flag in flagsCountdown.keys.toList()) {
flagsCountdown[flag] = flagsCountdown[flag]!! - 1
if(flagsCountdown[flag]==0) flagsCountdown.remove(flag)
if(flagsCountdown[flag]==0) {
flagsCountdown.remove(flag)
}
}
if (influence > 1) {
@ -179,6 +203,29 @@ class DiplomacyManager() {
/// AI won't propose peace for 10 turns
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
}

View File

@ -96,11 +96,7 @@ class TradeLogic(val ourCivilization:CivilizationInfo, val otherCivilization: Ci
from.updateViewableTiles()
}
if(offer.type== TradeType.Treaty){
if(offer.name=="Peace Treaty"){
to.getDiplomacyManager(from).diplomaticStatus= DiplomaticStatus.Peace
for(unit in to.getCivUnits().filter { it.getTile().getOwner()==from })
unit.movementAlgs().teleportToClosestMoveableTile()
}
if(offer.name=="Peace Treaty") to.getDiplomacyManager(from).makePeace()
}
if(offer.type==TradeType.Introduction)
to.meetCivilization(to.gameInfo.getCivilization(offer.name.split(" ")[2]))

View File

@ -66,44 +66,44 @@ class DiplomacyScreen:CameraStageBaseScreen() {
return tradeTable
}
private fun getDiplomacyTable(civ: CivilizationInfo): Table {
private fun getDiplomacyTable(otherCiv: CivilizationInfo): Table {
val currentPlayerCiv = UnCivGame.Current.gameInfo.getCurrentPlayerCivilization()
val diplomacyTable = Table()
diplomacyTable.defaults().pad(10f)
val leaderName: String
if (civ.isCityState()) {
leaderName = "City State [" + civ.civName + "]"
if (otherCiv.isCityState()) {
leaderName = otherCiv.civName
} else {
leaderName = "[" + civ.getNation().leaderName + "] of [" + civ.civName + "]"
leaderName = "[" + otherCiv.getNation().leaderName + "] of [" + otherCiv.civName + "]"
}
diplomacyTable.add(leaderName.toLabel())
diplomacyTable.addSeparator()
if(!civ.isCityState()) {
if(!otherCiv.isCityState()) {
val tradeButton = TextButton("Trade".tr(), skin)
tradeButton.onClick { setTrade(civ) }
tradeButton.onClick { setTrade(otherCiv) }
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)
declareWarButton.color = Color.RED
val turnsToPeaceTreaty = civDiplomacy.turnsToPeaceTreaty()
val turnsToPeaceTreaty = diplomacyManager.turnsToPeaceTreaty()
if (turnsToPeaceTreaty > 0) {
declareWarButton.disable()
declareWarButton.setText(declareWarButton.text.toString() + " ($turnsToPeaceTreaty)")
}
declareWarButton.onClick {
YesNoPopupTable("Declare war on [${civ.civName}]?".tr(), {
civDiplomacy.declareWar()
YesNoPopupTable("Declare war on [${otherCiv.civName}]?".tr(), {
diplomacyManager.declareWar()
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.addSeparator()
responsePopup.addGoodSizedLabel(civ.getNation().attacked).row()
responsePopup.addGoodSizedLabel(otherCiv.getNation().attacked).row()
responsePopup.addButton("Very well.".tr()) { responsePopup.remove() }
responsePopup.open()
@ -112,6 +112,17 @@ class DiplomacyScreen:CameraStageBaseScreen() {
}
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
}
}