Updated Freedom branch to G&K (#4142)

* Updated Freedom branch to G&K

* Hotfix for autocracy policy that I accidentally added already

* Implemented recommended changes (mostly)

* Implemented recommended changes

* Implemented recommended changes better
This commit is contained in:
Xander Lenstra 2021-06-18 08:42:48 +02:00 committed by GitHub
parent 87810b8ce8
commit 1d18c418e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 145 additions and 87 deletions

View File

@ -360,33 +360,33 @@
},
{
"name": "Universal Suffrage",
"uniques": ["[+1 Production] per [5] population in all cities"],
"uniques": ["+[33]% Defensive Strength for cities"],
"row": 1,
"column": 3
},
{
"name": "Civil Society",
"uniques": ["-50% food consumption by specialists"],
"uniques": ["-[50]% food consumption by specialists"],
"row": 1,
"column": 5
},
{
"name": "Free Speech",
"uniques": ["[+1 Culture] per [2] population in all cities"],
"uniques": ["[8] units cost no maintenance"],
"requires": ["Constitution"],
"row": 2,
"column": 1
},
{
"name": "Democracy",
"uniques": ["Specialists produce half normal unhappiness"],
"uniques": ["Specialists only produce [50]% of normal unhappiness"],
"requires": ["Civil Society"],
"row": 2,
"column": 5
},
{
"name": "Freedom Complete",
"uniques": ["Tile yield from Great Improvements +100%", "Golden Age length increased by [50]%"]
"uniques": ["+[100]% yield from [Great Improvements]", "Golden Age length increased by [50]%"]
}
]
},

View File

@ -167,50 +167,57 @@ object BattleDamage {
return modifiers
}
fun getDefenceModifiers(attacker: ICombatant, defender: MapUnitCombatant): Counter<String> {
fun getDefenceModifiers(attacker: ICombatant, defender: ICombatant): Counter<String> {
val modifiers = getGeneralModifiers(defender, attacker)
val tile = defender.getTile()
if (defender is MapUnitCombatant) {
if (defender.unit.isEmbarked()) {
// embarked units get no defensive modifiers apart from this unique
if (defender.unit.hasUnique("Defense bonus when embarked") ||
defender.getCivInfo().hasUnique("Embarked units can defend themselves")
if (defender.unit.isEmbarked()) {
// embarked units get no defensive modifiers apart from this unique
if (defender.unit.hasUnique("Defense bonus when embarked") ||
defender.getCivInfo().hasUnique("Embarked units can defend themselves")
)
modifiers["Embarked"] = 100
return modifiers
}
modifiers.putAll(getTileSpecificModifiers(defender, tile))
val tileDefenceBonus = tile.getDefensiveBonus()
if (!defender.unit.hasUnique("No defensive terrain bonus") && tileDefenceBonus > 0
|| !defender.unit.hasUnique("No defensive terrain penalty") && tileDefenceBonus < 0
)
modifiers["Embarked"] = 100
modifiers["Tile"] = (tileDefenceBonus * 100).toInt()
return modifiers
if (attacker.isRanged()) {
val defenceVsRanged = 25 * defender.unit.getUniques()
.count { it.text == "+25% Defence against ranged attacks" }
if (defenceVsRanged > 0) modifiers["defence vs ranged"] = defenceVsRanged
}
for (unique in defender.unit.getMatchingUniques("+[]% Strength when defending")) {
modifiers.add("Defender Bonus", unique.params[0].toInt())
}
for (unique in defender.unit.getMatchingUniques("+[]% defence in [] tiles")) {
if (tile.matchesUniqueFilter(unique.params[1]))
modifiers["[${unique.params[1]}] defence"] = unique.params[0].toInt()
}
if (defender.unit.isFortified())
modifiers["Fortification"] = 20 * defender.unit.getFortificationTurns()
} else if (defender is CityCombatant) {
modifiers["Defensive Bonus"] = defender.city.civInfo.getMatchingUniques("+[]% Defensive strength for cities")
.map { it.params[0].toFloat() / 100f }.sum().toInt()
}
modifiers.putAll(getTileSpecificModifiers(defender, tile))
val tileDefenceBonus = tile.getDefensiveBonus()
if (!defender.unit.hasUnique("No defensive terrain bonus") && tileDefenceBonus > 0
|| !defender.unit.hasUnique("No defensive terrain penalty") && tileDefenceBonus < 0
)
modifiers["Tile"] = (tileDefenceBonus * 100).toInt()
if (attacker.isRanged()) {
val defenceVsRanged = 25 * defender.unit.getUniques()
.count { it.text == "+25% Defence against ranged attacks" }
if (defenceVsRanged > 0) modifiers["defence vs ranged"] = defenceVsRanged
}
for (unique in defender.unit.getMatchingUniques("+[]% Strength when defending")) {
modifiers.add("Defender Bonus", unique.params[0].toInt())
}
for (unique in defender.unit.getMatchingUniques("+[]% defence in [] tiles")) {
if (tile.matchesUniqueFilter(unique.params[1]))
modifiers["[${unique.params[1]}] defence"] = unique.params[0].toInt()
}
if (defender.unit.isFortified())
modifiers["Fortification"] = 20 * defender.unit.getFortificationTurns()
return modifiers
}
private fun getTileSpecificModifiers(unit: MapUnitCombatant, tile: TileInfo): Counter<String> {
val modifiers = Counter<String>()
@ -269,8 +276,7 @@ object BattleDamage {
tileToAttackFrom: TileInfo?,
defender: ICombatant
): Float {
val attackModifier =
modifiersToMultiplicationBonus(getAttackModifiers(attacker, defender))
val attackModifier = modifiersToMultiplicationBonus(getAttackModifiers(attacker, defender))
return attacker.getAttackingStrength() * attackModifier
}
@ -279,9 +285,7 @@ object BattleDamage {
* Includes defence modifiers
*/
private fun getDefendingStrength(attacker: ICombatant, defender: ICombatant): Float {
var defenceModifier = 1f
if (defender is MapUnitCombatant) defenceModifier =
modifiersToMultiplicationBonus(getDefenceModifiers(attacker, defender))
val defenceModifier = modifiersToMultiplicationBonus(getDefenceModifiers(attacker, defender))
return defender.getDefendingStrength() * defenceModifier
}

View File

@ -174,15 +174,24 @@ class CityStats {
if (!civInfo.isPlayerCivilization())
unhappinessModifier *= civInfo.gameInfo.getDifficulty().aiUnhappinessModifier
var unhappinessFromCity = -3f // -3 happiness per city
var unhappinessFromCity = -3f // -3 happiness per city
if (civInfo.hasUnique("Unhappiness from number of Cities doubled"))
unhappinessFromCity *= 2f//doubled for the Indian
unhappinessFromCity *= 2f //doubled for the Indian
newHappinessList["Cities"] = unhappinessFromCity * unhappinessModifier
var unhappinessFromCitizens = cityInfo.population.population.toFloat()
if (civInfo.hasUnique("Specialists produce half normal unhappiness"))
unhappinessFromCitizens -= cityInfo.population.getNumberOfSpecialists() * 0.5f
var unhappinessFromSpecialists = cityInfo.population.getNumberOfSpecialists().toFloat()
for (unique in civInfo.getMatchingUniques("Specialists only produce []% of normal unhappiness")) {
unhappinessFromSpecialists *= (1f - unique.params[0].toFloat() / 100f)
}
// Deprecated since 3.15
if (civInfo.hasUnique("Specialists produce half normal unhappiness"))
unhappinessFromSpecialists *= 0.5f
//
unhappinessFromCitizens -= cityInfo.population.getNumberOfSpecialists().toFloat() - unhappinessFromSpecialists
if (cityInfo.isPuppet)
unhappinessFromCitizens *= 1.5f
@ -297,8 +306,7 @@ class CityStats {
for (unique in uniques.filter { it.placeholderText == "+[]% [] []"})
if (cityInfo.matchesFilter(unique.params[2]))
stats.add(Stat.valueOf(unique.params[1]), unique.params[0].toFloat())
for (unique in uniques.filter { it.placeholderText == "+[]% Production when constructing []" }) {
if (constructionMatchesFilter(currentConstruction, unique.params[1]))
stats.production += unique.params[0].toInt()
@ -391,7 +399,7 @@ class CityStats {
fun update(currentConstruction: IConstruction = cityInfo.cityConstructions.getCurrentConstruction()) {
val citySpecificUniques: Sequence<Unique> = cityInfo.cityConstructions.builtBuildingUniqueMap.getAllUniques()
.filter { it.params.isNotEmpty() && it.params.last()=="in this city" }
.filter { it.params.isNotEmpty() && it.params.last() == "in this city" }
// We need to compute Tile yields before happiness
updateBaseStatList()
updateCityHappiness()
@ -505,8 +513,16 @@ class CityStats {
private fun updateFoodEaten() {
foodEaten = cityInfo.population.population.toFloat() * 2
if (cityInfo.civInfo.hasUnique("-50% food consumption by specialists"))
foodEaten -= cityInfo.population.getNumberOfSpecialists()
var foodEatenBySpecialists = 2f * cityInfo.population.getNumberOfSpecialists()
for (unique in cityInfo.civInfo.getMatchingUniques("-[]% food consumption by specialists"))
foodEatenBySpecialists *= 1f - unique.params[0].toFloat() / 100f
// Deprecated since 3.15
if (cityInfo.civInfo.hasUnique("-50% food consumption by specialists"))
foodEatenBySpecialists *= 0.5f
//
foodEaten -= 2f * cityInfo.population.getNumberOfSpecialists() - foodEatenBySpecialists
}
}
}

View File

@ -16,7 +16,11 @@ class CivInfoStats(val civInfo: CivilizationInfo) {
private fun getUnitMaintenance(): Int {
val baseUnitCost = 0.5f
val freeUnits = 3
var freeUnits = 3
for (unique in civInfo.getMatchingUniques("[] units cost no maintenance")) {
freeUnits += unique.params[0].toInt()
}
var unitsToPayFor = civInfo.getCivUnits()
if (civInfo.hasUnique("Units in cities cost no Maintenance"))
// Only land military units can truly "garrison"
@ -39,7 +43,15 @@ class CivInfoStats(val civInfo: CivilizationInfo) {
cost = cost.pow(1 + gameProgress / 3) // Why 3? To spread 1 to 1.33
if (!civInfo.isPlayerCivilization())
cost *= civInfo.gameInfo.getDifficulty().aiUnitMaintenanceModifier
if (civInfo.hasUnique("-33% unit upkeep costs")) cost *= 0.66f
for (unique in civInfo.getMatchingUniques("-[]% unit upkeep costs")) {
cost *= 1f - unique.params[0].toFloat() / 100f
}
// Deprecated since 3.15
if (civInfo.hasUnique("-33% unit upkeep costs")) cost *= 0.67f
//
return cost.toInt()
}

View File

@ -286,24 +286,21 @@ open class TileInfo {
if (city != null) {
val cityWideUniques = city.cityConstructions.builtBuildingUniqueMap.getUniques("[] from [] tiles in this city")
val civWideUniques = city.civInfo.getMatchingUniques("[] from every []")
val improvementUniques = improvement.uniqueObjects.filter {
it.placeholderText == "[] on [] tiles once [] is discovered"
&& observingCiv.tech.isResearched(it.params[2])
}
for (unique in cityWideUniques + civWideUniques + improvementUniques) {
if (improvement.name == unique.params[1]
|| unique.params[1] == "Great Improvement" && improvement.isGreatImprovement()
|| unique.params[1] == "Fresh water" && isAdjacentToFreshwater
|| unique.params[1] == "non-fresh water" && !isAdjacentToFreshwater
)
for (unique in cityWideUniques + improvementUniques) {
if (matchesUniqueFilter(unique.params[1]))
stats.add(unique.stats)
}
}
if (containsGreatImprovement()
&& observingCiv.hasUnique("Tile yield from Great Improvements +100%"))
stats.add(improvement) // again, for the double effect
for (unique in city.civInfo.getMatchingUniques("[] from every []")) {
if (improvement.matchesFilter(unique.params[1])) {
stats.add(unique.stats)
}
}
}
for (unique in improvement.uniqueObjects)
if (unique.placeholderText == "[] for each adjacent []") {
@ -315,7 +312,16 @@ open class TileInfo {
}
stats.add(unique.stats.times(numberOfBonuses.toFloat()))
}
for (unique in observingCiv.getMatchingUniques("+[]% yield from []"))
if (improvement.matchesFilter(unique.params[0]))
stats.timesInPlace(1f + unique.params[1].toFloat() / 100f)
// Deprecated since 3.15
if (containsGreatImprovement() && observingCiv.hasUnique("Tile yield from Great Improvements +100%"))
stats.timesInPlace(2f)
//
return stats
}
@ -386,23 +392,28 @@ open class TileInfo {
* Implementation of _`tileFilter`_
* @see <a href="https://github.com/yairm210/Unciv/wiki/uniques#user-content-tilefilter">tileFilter</a>
*/
fun matchesUniqueFilter(filter: String, civInfo: CivilizationInfo? = null): Boolean {
return filter == "All"
|| filter == baseTerrain
|| filter == "River" && isAdjacentToRiver()
|| terrainFeatures.contains(filter)
|| baseTerrainObject.uniques.contains(filter)
|| improvement == filter
|| resource == filter
|| resource != null && getTileResource().resourceType.name + " resource" == filter
|| filter == "Water" && isWater
|| filter == "Land" && isLand
|| filter == "Coastal" && isCoastalTile()
|| filter == naturalWonder
|| terrainFeatures.isNotEmpty() && getTerrainFeatures().last().uniques.contains(filter)
|| civInfo != null && hasViewableResource(civInfo) && resource == filter
|| filter == "Foreign Land" && civInfo != null && !isFriendlyTerritory(civInfo)
|| filter == "Friendly Land" && civInfo != null && isFriendlyTerritory(civInfo)
fun matchesUniqueFilter(filter: String, civInfo: CivilizationInfo? = null): Boolean {
return when (filter) {
"All" -> true
"Water" -> isWater
"Land" -> isLand
"Coastal" -> isCoastalTile()
"River" -> isAdjacentToRiver()
"Fresh water" -> isAdjacentToFreshwater
"non-fresh water" -> !isAdjacentToFreshwater
improvement -> true
naturalWonder -> true
"Foreign Land" -> civInfo != null && !isFriendlyTerritory(civInfo)
"Friendly Land" -> civInfo != null && isFriendlyTerritory(civInfo)
else -> {
if (terrainFeatures.contains(filter)) return true
if (baseTerrainObject.uniques.contains(filter)) return true
if (terrainFeatures.isNotEmpty() && getTerrainFeatures().last().uniques.contains(filter)) return true
if (resource != null && getTileResource().resourceType.name + " resource" == filter) return true
if (civInfo != null && hasViewableResource(civInfo) && resource == filter) return true
return false
}
}
}
fun hasImprovementInProgress() = improvementInProgress != null

View File

@ -94,5 +94,14 @@ class TileImprovement : NamedStats() {
&& it.params[0] == name
}.any()
}
fun matchesFilter(filter: String): Boolean {
return when (filter) {
name -> true
"All" -> true
"Great Improvement" -> isGreatImprovement()
else -> false
}
}
}

View File

@ -64,6 +64,12 @@ open class Stats() {
for (stat in Stat.values()) hashMap[stat] = number * hashMap[stat]!!
return Stats(hashMap)
}
fun timesInPlace(number: Float) {
val hashMap = toHashMap()
for (stat in Stat.values()) hashMap[stat] = number * hashMap[stat]!!
setStats(hashMap)
}
fun isEmpty() = equals(Stats())