diff --git a/android/assets/jsons/Translations/Diplomacy,Trade,Nations.json b/android/assets/jsons/Translations/Diplomacy,Trade,Nations.json index 24234a7e3b..af85c4c0ad 100644 --- a/android/assets/jsons/Translations/Diplomacy,Trade,Nations.json +++ b/android/assets/jsons/Translations/Diplomacy,Trade,Nations.json @@ -669,6 +669,15 @@ Czech:"Vliv: " } + "Reach 30 for friendship.":{ +} + + "Reach highest influence above 60 for alliance.":{ +} + + "Ally: ":{ +} + ////// Trade "Trade":{ diff --git a/android/assets/jsons/Translations/Notifications.json b/android/assets/jsons/Translations/Notifications.json index b35aa37d2e..7499778e25 100644 --- a/android/assets/jsons/Translations/Notifications.json +++ b/android/assets/jsons/Translations/Notifications.json @@ -778,4 +778,9 @@ Ukrainian:"[nation]: відмова не будувати міста поруч з нами!" } + "We have allied with [nation].": { + } + + "We have lost alliance with [nation].": { + } } diff --git a/core/src/com/unciv/logic/GameInfo.kt b/core/src/com/unciv/logic/GameInfo.kt index 226d36d12f..56090deb34 100644 --- a/core/src/com/unciv/logic/GameInfo.kt +++ b/core/src/com/unciv/logic/GameInfo.kt @@ -246,9 +246,9 @@ class GameInfo { } } - for (civInfo in civilizations) civInfo.setNationTransient() for (civInfo in civilizations) civInfo.setTransients() + for (civInfo in civilizations) civInfo.updateSightAndResources() for (civInfo in civilizations){ for(unit in civInfo.getCivUnits()) diff --git a/core/src/com/unciv/logic/city/CityInfo.kt b/core/src/com/unciv/logic/city/CityInfo.kt index d43ce7f2a5..5c627bb0e3 100644 --- a/core/src/com/unciv/logic/city/CityInfo.kt +++ b/core/src/com/unciv/logic/city/CityInfo.kt @@ -125,32 +125,9 @@ class CityInfo { for (tileInfo in getTiles().filter { it.resource != null }) { val resource = tileInfo.getTileResource() - if (resource.revealedBy!=null && !civInfo.tech.isResearched(resource.revealedBy!!)) continue - - // Even if the improvement exists (we conquered an enemy city or somesuch) or we have a city on it, we won't get the resource until the correct tech is researched - if (resource.improvement!=null){ - val improvement = GameBasics.TileImprovements[resource.improvement!!]!! - if(improvement.techRequired!=null && !civInfo.tech.isResearched(improvement.techRequired!!)) continue - } - - if (resource.improvement == tileInfo.improvement || tileInfo.isCityCenter() - // Per https://gaming.stackexchange.com/questions/53155/do-manufactories-and-customs-houses-sacrifice-the-strategic-or-luxury-resources - || (resource.resourceType==ResourceType.Strategic && tileInfo.containsGreatImprovement())){ - var amountToAdd = 1 - if(resource.resourceType == ResourceType.Strategic){ - amountToAdd = 2 - if(civInfo.policies.isAdopted("Facism")) amountToAdd*=2 - if(civInfo.nation.unique=="Strategic Resources provide +1 Production, and Horses, Iron and Uranium Resources provide double quantity" - && resource.name in listOf("Horses","Iron","Uranium")) - amountToAdd *= 2 - if(resource.name=="Oil" && civInfo.nation.unique=="+1 Gold from each Trade Route, Oil resources provide double quantity") - amountToAdd *= 2 - } - if(resource.resourceType == ResourceType.Luxury - && containsBuildingUnique("Provides 1 extra copy of each improved luxury resource near this City")) - amountToAdd*=2 - - cityResources.add(resource, amountToAdd, "Tiles") + val amount = getTileResourceAmount(tileInfo) + if (amount > 0) { + cityResources.add(resource, amount, "Tiles") } } @@ -163,6 +140,52 @@ class CityInfo { return cityResources } + fun getCityResourcesForAlly(): ResourceSupplyList { + val cityResources = ResourceSupplyList() + + for (tileInfo in getTiles().filter { it.resource != null }) { + val resource = tileInfo.getTileResource() + val amount = getTileResourceAmount(tileInfo) + if (amount > 0) { + cityResources.add(resource, amount, "City-States") + } + } + return cityResources + } + + fun getTileResourceAmount(tileInfo: TileInfo): Int { + if (tileInfo.resource == null) return 0 + val resource = tileInfo.getTileResource() + if (resource.revealedBy!=null && !civInfo.tech.isResearched(resource.revealedBy!!)) return 0 + + // Even if the improvement exists (we conquered an enemy city or somesuch) or we have a city on it, we won't get the resource until the correct tech is researched + if (resource.improvement!=null){ + val improvement = GameBasics.TileImprovements[resource.improvement!!]!! + if(improvement.techRequired!=null && !civInfo.tech.isResearched(improvement.techRequired!!)) return 0 + } + + if (resource.improvement == tileInfo.improvement || tileInfo.isCityCenter() + // Per https://gaming.stackexchange.com/questions/53155/do-manufactories-and-customs-houses-sacrifice-the-strategic-or-luxury-resources + || (resource.resourceType==ResourceType.Strategic && tileInfo.containsGreatImprovement())){ + var amountToAdd = 1 + if(resource.resourceType == ResourceType.Strategic){ + amountToAdd = 2 + if(civInfo.policies.isAdopted("Facism")) amountToAdd*=2 + if(civInfo.nation.unique=="Strategic Resources provide +1 Production, and Horses, Iron and Uranium Resources provide double quantity" + && resource.name in listOf("Horses","Iron","Uranium")) + amountToAdd *= 2 + if(resource.name=="Oil" && civInfo.nation.unique=="+1 Gold from each Trade Route, Oil resources provide double quantity") + amountToAdd *= 2 + } + if(resource.resourceType == ResourceType.Luxury + && containsBuildingUnique("Provides 1 extra copy of each improved luxury resource near this City")) + amountToAdd*=2 + + return amountToAdd + } + return 0 + } + fun getBuildingUniques(): List = cityConstructions.getBuiltBuildings().flatMap { it.uniques } fun containsBuildingUnique(unique:String) = cityConstructions.getBuiltBuildings().any { it.uniques.contains(unique) } diff --git a/core/src/com/unciv/logic/civilization/CivInfoTransientUpdater.kt b/core/src/com/unciv/logic/civilization/CivInfoTransientUpdater.kt index 5cc71ebde6..147bd88c0f 100644 --- a/core/src/com/unciv/logic/civilization/CivInfoTransientUpdater.kt +++ b/core/src/com/unciv/logic/civilization/CivInfoTransientUpdater.kt @@ -25,6 +25,15 @@ class CivInfoTransientUpdater(val civInfo: CivilizationInfo){ val neighboringUnownedTiles = ownedTiles.flatMap { it.neighbors.asSequence().filter { it.getOwner()!=civInfo } } newViewableTiles.addAll(neighboringUnownedTiles) newViewableTiles.addAll(civInfo.getCivUnits().asSequence().flatMap { it.viewableTiles.asSequence()}) + + if (!civInfo.isCityState()) { + for (otherCiv in civInfo.getKnownCivs()) { + if (otherCiv.getAllyCiv() == civInfo.civName) { + newViewableTiles.addAll(otherCiv.cities.asSequence().flatMap { it.getTiles().asSequence() }) + } + } + } + civInfo.viewableTiles = newViewableTiles // to avoid concurrent modification problems val newViewableInvisibleTiles = HashSet() @@ -138,6 +147,17 @@ class CivInfoTransientUpdater(val civInfo: CivilizationInfo){ fun updateDetailedCivResources() { val newDetailedCivResources = ResourceSupplyList() for (city in civInfo.cities) newDetailedCivResources.add(city.getCityResources()) + + if (!civInfo.isCityState()) { + for (otherCiv in civInfo.getKnownCivs()) { + if (otherCiv.getAllyCiv() == civInfo.civName) { + for (city in otherCiv.cities) { + newDetailedCivResources.add(city.getCityResourcesForAlly()) + } + } + } + } + for (dip in civInfo.diplomacy.values) newDetailedCivResources.add(dip.resourcesFromTrade()) for(resource in civInfo.getCivUnits().mapNotNull { it.baseUnit.requiredResource }.map { GameBasics.TileResources[it]!! }) newDetailedCivResources.add(resource,-1,"Units") diff --git a/core/src/com/unciv/logic/civilization/CivilizationInfo.kt b/core/src/com/unciv/logic/civilization/CivilizationInfo.kt index 177b25e778..69dd7db830 100644 --- a/core/src/com/unciv/logic/civilization/CivilizationInfo.kt +++ b/core/src/com/unciv/logic/civilization/CivilizationInfo.kt @@ -58,6 +58,7 @@ class CivilizationInfo { var diplomacy = HashMap() var notifications = ArrayList() val popupAlerts = ArrayList() + var allyCivName = "" //** for trades here, ourOffers is the current civ's offers, and theirOffers is what the requesting civ offers */ val tradeRequests = ArrayList() @@ -87,6 +88,7 @@ class CivilizationInfo { toReturn.goldenAges = goldenAges.clone() toReturn.greatPeople = greatPeople.clone() toReturn.victoryManager = victoryManager.clone() + toReturn.allyCivName = allyCivName for (diplomacyManager in diplomacy.values.map { it.clone() }) toReturn.diplomacy.put(diplomacyManager.otherCivName, diplomacyManager) toReturn.cities = cities.map { it.clone() } @@ -325,6 +327,9 @@ class CivilizationInfo { cityInfo.civInfo = this // must be before the city's setTransients because it depends on the tilemap, that comes from the currentPlayerCivInfo cityInfo.setTransients() } + } + + fun updateSightAndResources() { updateViewableTiles() updateHasActiveGreatWall() updateDetailedCivResources() @@ -394,6 +399,7 @@ class CivilizationInfo { goldenAges.endTurn(getHappiness()) getCivUnits().forEach { it.endTurn() } diplomacy.values.forEach{it.nextTurn()} + updateAllyCivForCityState() updateHasActiveGreatWall() } @@ -459,6 +465,7 @@ class CivilizationInfo { if(!otherCiv.isCityState()) throw Exception("You can only gain influence with city states!") gold -= giftAmount otherCiv.getDiplomacyManager(this).influence += giftAmount/10 + otherCiv.updateAllyCivForCityState() updateStatsForNextTurn() } @@ -471,5 +478,36 @@ class CivilizationInfo { addNotification("[${otherCiv.civName}] gave us a [${militaryUnit.name}] as gift near [${city.name}]!", null, Color.GREEN) } + fun getAllyCiv(): String { + return allyCivName + } + + fun updateAllyCivForCityState() { + var newAllyName = "" + if (!isCityState()) return + val maxInfluence = diplomacy.filter{ !it.value.otherCiv().isCityState() && !it.value.otherCiv().isDefeated() }.maxBy { it.value.influence } + if (maxInfluence != null && maxInfluence.value.influence >= 60) { + newAllyName = maxInfluence.key + } + + if (allyCivName != newAllyName) { + val oldAllyName = allyCivName + allyCivName = newAllyName + + if (newAllyName != "") { + val newAllyCiv = gameInfo.getCivilization(newAllyName) + newAllyCiv.addNotification("We have allied with [${civName}].", Color.GREEN) + newAllyCiv.updateViewableTiles() + newAllyCiv.updateDetailedCivResources() + } + if (oldAllyName != "") { + val oldAllyCiv = gameInfo.getCivilization(oldAllyName) + oldAllyCiv.addNotification("We have lost alliance with [${civName}].", Color.RED) + oldAllyCiv.updateViewableTiles() + oldAllyCiv.updateDetailedCivResources() + } + } + } + //endregion } diff --git a/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyManager.kt b/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyManager.kt index 7882a8af94..2b7cd7cf30 100644 --- a/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyManager.kt +++ b/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyManager.kt @@ -342,6 +342,22 @@ class DiplomacyManager() { } } otherCivDiplomacy.removeFlag(DiplomacyFlags.DeclarationOfFriendship) + if (otherCiv.isCityState()) otherCiv.updateAllyCivForCityState() + + if (!civInfo.isCityState()) { + for (thirdCiv in civInfo.getKnownCivs()) { + if (thirdCiv.isCityState() && thirdCiv.getAllyCiv() == civInfo.civName && thirdCiv.getDiplomacyManager(otherCiv).canDeclareWar()) { + thirdCiv.getDiplomacyManager(otherCiv).declareWar() + } + } + } + if (!otherCiv.isCityState()) { + for (thirdCiv in otherCiv.getKnownCivs()) { + if (thirdCiv.isCityState() && thirdCiv.getAllyCiv() == otherCiv.civName && thirdCiv.getDiplomacyManager(civInfo).canDeclareWar()) { + thirdCiv.getDiplomacyManager(civInfo).declareWar() + } + } + } } fun makePeace(){ diff --git a/core/src/com/unciv/models/gamebasics/Nation.kt b/core/src/com/unciv/models/gamebasics/Nation.kt index 9d348f3a8c..11f2685653 100644 --- a/core/src/com/unciv/models/gamebasics/Nation.kt +++ b/core/src/com/unciv/models/gamebasics/Nation.kt @@ -2,6 +2,7 @@ package com.unciv.models.gamebasics import com.badlogic.gdx.graphics.Color import com.unciv.logic.civilization.CityStateType +import com.unciv.logic.civilization.CivilizationInfo import com.unciv.models.stats.INamed import com.unciv.ui.utils.colorFromRGB diff --git a/core/src/com/unciv/ui/trade/DiplomacyScreen.kt b/core/src/com/unciv/ui/trade/DiplomacyScreen.kt index 5114ac1d61..e9a43797c8 100644 --- a/core/src/com/unciv/ui/trade/DiplomacyScreen.kt +++ b/core/src/com/unciv/ui/trade/DiplomacyScreen.kt @@ -95,9 +95,25 @@ class DiplomacyScreen(val viewingCiv:CivilizationInfo):CameraStageBaseScreen() { diplomacyTable.defaults().pad(10f) diplomacyTable.add(otherCiv.getLeaderDisplayName().toLabel(fontSize = 24)).row() diplomacyTable.add(("Type: ".tr() + otherCiv.getCityStateType().toString().tr()).toLabel()).row() - diplomacyTable.add(("Influence: ".tr() + otherCivDiplomacyManager.influence.toInt() + "/30").toLabel()).row() - + otherCiv.updateAllyCivForCityState() + val ally = otherCiv.getAllyCiv() + if (ally != "") + { + diplomacyTable.add(("Ally: ".tr() + ally!!.tr() + " " + "Influence: ".tr() + + otherCiv.getDiplomacyManager(ally).influence.toString().tr()).toLabel()).row() + } + val nextLevelString: String + if (otherCivDiplomacyManager.influence.toInt() < 30) { + nextLevelString = "Reach 30 for friendship." + } else if (ally == viewingCiv.civName) { + nextLevelString = "" + } else { + nextLevelString = "Reach highest influence above 60 for alliance." + } diplomacyTable.add(getRelationshipTable(otherCivDiplomacyManager)).row() + if (nextLevelString != "") { + diplomacyTable.add(nextLevelString.tr().toLabel()).row() + } val friendBonusText = when (otherCiv.getCityStateType()) { CityStateType.Cultured -> ("Provides [" + (3 * (viewingCiv.getEra().ordinal + 1)).toString() + "] culture at [30] Influence").tr()