From 8ac3a88cecac7f83de61487bab349256cd517480 Mon Sep 17 00:00:00 2001 From: HadeanLake <69697985+HadeanLake@users.noreply.github.com> Date: Sat, 5 Sep 2020 20:32:27 +0300 Subject: [PATCH] fixed #3066, crash in chooseMilitaryUnit and some great people actions (#3099) * fixed KotlinNullPointerException crash in chooseMilitaryUnit random() is not to be used in predicate * GodmodeCheckbox is not lockable and unchecked by default * no great people actions if no movement points left * unique "Can start an []-turn golden age" now has parameter and 8-turn golden ages will last 8 turns instead of 10 golden age can be started if unit is on own territory (even embarked) * "Golden Age length increased by [50]%" - now has parameter * tweaked changed fort and terrain defence bonuses fort can be built on forest and jungle (vegetation will not be removed) any open flat land gives 10% penalty marsh gives 15% penalty only top terrain counts, improvement bonus will be added to that flatland + fort = 40% hill + fort = 75% hill = 25% forest/jungle on flatland = 25% forest/jungle on hill = 25% forest on flat + fort = 75% forest on hill + fort = 75% forest on hill + citadel = 125% fixed 20% penalty for attacking over river - will be displayed if unit is standing on the other side of river "Amphibious" unique removes this penalty --- .../jsons/Civ V - Vanilla/Buildings.json | 2 +- .../assets/jsons/Civ V - Vanilla/Nations.json | 2 +- .../jsons/Civ V - Vanilla/Policies.json | 2 +- .../jsons/Civ V - Vanilla/Terrains.json | 4 +- .../Civ V - Vanilla/TileImprovements.json | 2 +- .../assets/jsons/Civ V - Vanilla/Units.json | 6 +- .../com/unciv/logic/automation/Automation.kt | 3 +- .../com/unciv/logic/battle/BattleDamage.kt | 6 +- .../logic/civilization/GoldenAgeManager.kt | 11 +- core/src/com/unciv/logic/map/TileInfo.kt | 3 +- .../ui/newgamescreen/GameOptionsTable.kt | 6 +- .../unciv/ui/newgamescreen/MapOptionsTable.kt | 12 +- .../unciv/ui/worldscreen/unit/UnitActions.kt | 134 +++++++++--------- 13 files changed, 103 insertions(+), 90 deletions(-) diff --git a/android/assets/jsons/Civ V - Vanilla/Buildings.json b/android/assets/jsons/Civ V - Vanilla/Buildings.json index 387e431c..d36ec42e 100644 --- a/android/assets/jsons/Civ V - Vanilla/Buildings.json +++ b/android/assets/jsons/Civ V - Vanilla/Buildings.json @@ -396,7 +396,7 @@ "happiness": 4, "greatPersonPoints": {"production": 1}, "isWonder": true, - "uniques": ["Golden Age length increases +50%"], + "uniques": ["Golden Age length increased by [50]%"], "requiredTech": "Civil Service", "quote": "'The katun is established at Chichen Itza. The settlement of the Itza shall take place there. The quetzal shall come, the green bird shall come. Ah Kantenal shall come. It is the word of God. The Itza shall come.' - The Books of Chilam Balam" }, diff --git a/android/assets/jsons/Civ V - Vanilla/Nations.json b/android/assets/jsons/Civ V - Vanilla/Nations.json index 583ce8f5..eec77b78 100644 --- a/android/assets/jsons/Civ V - Vanilla/Nations.json +++ b/android/assets/jsons/Civ V - Vanilla/Nations.json @@ -441,7 +441,7 @@ "outerColor": [153,5,3], "innerColor": [244,232,54], "uniqueName": "Achaemenid Legacy", - "uniques": ["Golden Age length increases +50%", "+1 Movement for all units during Golden Age", "+10% Strength for all units during Golden Age"], + "uniques": ["Golden Age length increased by [50]%", "+1 Movement for all units during Golden Age", "+10% Strength for all units during Golden Age"], "cities": ["Persepolis","Parsagadae","Susa","Ecbatana","Tarsus","Gordium","Bactra","Sardis","Ergili","Dariushkabir", "Ghulaman","Zohak","Istakhr","Jinjan","Borazjan","Herat","Dakyanus","Bampur","Turengtepe","Rey","Shiraz", "Thuspa","Hasanlu","Gabae","Merv","Behistun","Kandahar","Altintepe","Bunyan","Charsadda","Uratyube", diff --git a/android/assets/jsons/Civ V - Vanilla/Policies.json b/android/assets/jsons/Civ V - Vanilla/Policies.json index 8b9ffba4..dc19efc2 100644 --- a/android/assets/jsons/Civ V - Vanilla/Policies.json +++ b/android/assets/jsons/Civ V - Vanilla/Policies.json @@ -439,7 +439,7 @@ { "name": "Freedom Complete", "effect": "Tile yield from great improvement +100%, golden ages increase by 50%", - "uniques": ["Tile yield from Great Improvements +100%", "Golden Age length increases +50%"] + "uniques": ["Tile yield from Great Improvements +100%", "Golden Age length increased by [50]%"] } ] }, diff --git a/android/assets/jsons/Civ V - Vanilla/Terrains.json b/android/assets/jsons/Civ V - Vanilla/Terrains.json index eefb0dd1..9baf7f5f 100644 --- a/android/assets/jsons/Civ V - Vanilla/Terrains.json +++ b/android/assets/jsons/Civ V - Vanilla/Terrains.json @@ -20,6 +20,7 @@ "type": "Land", "food": 2, "movementCost": 1, + "defenceBonus": -0.1, "RGB": [109,139,53] }, { @@ -28,6 +29,7 @@ "food": 1, "production": 1, "movementCost": 1, + "defenceBonus": -0.1, "RGB": [200,208,161] }, { @@ -107,7 +109,7 @@ "food": -1, "movementCost": 3, "unbuildable": true, - "defenceBonus": -0.1, + "defenceBonus": -0.15, "occursOn": ["Grassland"] }, { diff --git a/android/assets/jsons/Civ V - Vanilla/TileImprovements.json b/android/assets/jsons/Civ V - Vanilla/TileImprovements.json index b2b06aa4..0b71b9ed 100644 --- a/android/assets/jsons/Civ V - Vanilla/TileImprovements.json +++ b/android/assets/jsons/Civ V - Vanilla/TileImprovements.json @@ -76,7 +76,7 @@ // Military improvement { "name": "Fort", - "terrainsCanBeBuiltOn": ["Plains","Grassland","Desert","Hill","Tundra","Snow"], + "terrainsCanBeBuiltOn": ["Plains","Grassland","Desert","Hill","Tundra","Snow","Forest","Jungle"], "turnsToBuild": 6, "techRequired": "Engineering", "uniques": ["Gives a defensive bonus of [50]%", "Can be built outside your borders"] diff --git a/android/assets/jsons/Civ V - Vanilla/Units.json b/android/assets/jsons/Civ V - Vanilla/Units.json index cbbd6104..577864b6 100644 --- a/android/assets/jsons/Civ V - Vanilla/Units.json +++ b/android/assets/jsons/Civ V - Vanilla/Units.json @@ -1293,7 +1293,7 @@ { "name": "Great Artist", "unitType": "Civilian", - "uniques": ["Can start an 8-turn golden age", "Can construct [Landmark]", "Great Person - [Culture]", "Unbuildable"], + "uniques": ["Can start an [8]-turn golden age", "Can construct [Landmark]", "Great Person - [Culture]", "Unbuildable"], "movement": 2 }, { @@ -1318,7 +1318,7 @@ { "name": "Great General", "unitType": "Civilian", - "uniques": ["Can start an 8-turn golden age", "Bonus for units in 2 tile radius 15%", "Can construct [Citadel]", + "uniques": ["Can start an [8]-turn golden age", "Bonus for units in 2 tile radius 15%", "Can construct [Citadel]", "Great Person - [War]", "Unbuildable"], "movement": 2 }, @@ -1327,7 +1327,7 @@ "unitType": "Civilian", "uniqueTo": "Mongolia", "replaces": "Great General", - "uniques": ["Can start an 8-turn golden age","Bonus for units in 2 tile radius 15%", + "uniques": ["Can start an [8]-turn golden age","Bonus for units in 2 tile radius 15%", "Heal adjacent units for an additional 15 HP per turn", "Can construct [Citadel]", "Great Person - [War]", "Unbuildable"], "movement": 5 } diff --git a/core/src/com/unciv/logic/automation/Automation.kt b/core/src/com/unciv/logic/automation/Automation.kt index cb4e9ea4..3e34b745 100644 --- a/core/src/com/unciv/logic/automation/Automation.kt +++ b/core/src/com/unciv/logic/automation/Automation.kt @@ -80,7 +80,8 @@ object Automation { else { // randomize type of unit and take the most expensive of its kind val availableTypes = militaryUnits.map { it.unitType }.distinct().filterNot { it == UnitType.Scout }.toList() if (availableTypes.isEmpty()) return null - chosenUnit = militaryUnits.filter { it.unitType == availableTypes.random() }.maxBy { it.cost }!! + val randomType = availableTypes.random() + chosenUnit = militaryUnits.filter { it.unitType == randomType }.maxBy { it.cost }!! } return chosenUnit.name } diff --git a/core/src/com/unciv/logic/battle/BattleDamage.kt b/core/src/com/unciv/logic/battle/BattleDamage.kt index ead849bc..c88d54c2 100644 --- a/core/src/com/unciv/logic/battle/BattleDamage.kt +++ b/core/src/com/unciv/logic/battle/BattleDamage.kt @@ -137,9 +137,9 @@ object BattleDamage { } if (numberOfAttackersSurroundingDefender > 1) modifiers["Flanking"] = 0.1f * (numberOfAttackersSurroundingDefender - 1) //https://www.carlsguides.com/strategy/civilization5/war/combatbonuses.php - - if (tileToAttackFrom != null && tileToAttackFrom.isConnectedByRiver(defender.getTile())) { - if (!tileToAttackFrom.hasConnection(attacker.getCivInfo()) // meaning, the tiles are not road-connected for this civ + if (attacker.getTile().aerialDistanceTo(defender.getTile()) == 1 && attacker.getTile().isConnectedByRiver(defender.getTile()) + && !attacker.unit.hasUnique("Amphibious")) { + if (!attacker.getTile().hasConnection(attacker.getCivInfo()) // meaning, the tiles are not road-connected for this civ || !defender.getTile().hasConnection(attacker.getCivInfo()) || !attacker.getCivInfo().tech.roadsConnectAcrossRivers) { modifiers["Across river"] = -0.2f diff --git a/core/src/com/unciv/logic/civilization/GoldenAgeManager.kt b/core/src/com/unciv/logic/civilization/GoldenAgeManager.kt index 40685b30..7d87e20d 100644 --- a/core/src/com/unciv/logic/civilization/GoldenAgeManager.kt +++ b/core/src/com/unciv/logic/civilization/GoldenAgeManager.kt @@ -24,10 +24,15 @@ class GoldenAgeManager{ return ((500 + numberOfGoldenAges * 250) * (1 + civInfo.cities.size / 100.0)).toInt() //https://forums.civfanatics.com/resources/complete-guide-to-happiness-vanilla.25584/ } - fun enterGoldenAge() { - var turnsToGoldenAge = 10.0 + fun enterGoldenAge(unmodifiedNumberOfTurns: Int = 10) { + var turnsToGoldenAge = unmodifiedNumberOfTurns.toFloat() + + // as of 3.10.7 This is to be deprecated and converted to "Golden Age length increased by []%" - keeping it here to that mods with this can still work for now for(unique in civInfo.getMatchingUniques("Golden Age length increases +50%")) - turnsToGoldenAge *= 1.5 + turnsToGoldenAge *= 1.5f + + for(unique in civInfo.getMatchingUniques("Golden Age length increased by []%")) + turnsToGoldenAge *= (unique.params[0].toFloat()/100 + 1) turnsToGoldenAge *= civInfo.gameInfo.gameParameters.gameSpeed.modifier turnsLeftForCurrentGoldenAge += turnsToGoldenAge.toInt() civInfo.addNotification("You have entered a Golden Age!", null, Color.GOLD) diff --git a/core/src/com/unciv/logic/map/TileInfo.kt b/core/src/com/unciv/logic/map/TileInfo.kt index 67749809..6df6f786 100644 --- a/core/src/com/unciv/logic/map/TileInfo.kt +++ b/core/src/com/unciv/logic/map/TileInfo.kt @@ -330,8 +330,7 @@ open class TileInfo { tileMap.getTilesAtDistance(position, distance) fun getDefensiveBonus(): Float { - var bonus = getBaseTerrain().defenceBonus - if (terrainFeature != null) bonus += getTerrainFeature()!!.defenceBonus + var bonus = getLastTerrain().defenceBonus val tileImprovement = getTileImprovement() if (tileImprovement != null) { for (unique in tileImprovement.uniqueObjects) diff --git a/core/src/com/unciv/ui/newgamescreen/GameOptionsTable.kt b/core/src/com/unciv/ui/newgamescreen/GameOptionsTable.kt index a290155e..dc1ce64f 100644 --- a/core/src/com/unciv/ui/newgamescreen/GameOptionsTable.kt +++ b/core/src/com/unciv/ui/newgamescreen/GameOptionsTable.kt @@ -56,10 +56,10 @@ class GameOptionsTable(val previousScreen: IPreviousScreen, val updatePlayerPick pack() } - private fun Table.addCheckbox(text: String, initialState: Boolean, onChange: (newValue: Boolean) -> Unit) { + private fun Table.addCheckbox(text: String, initialState: Boolean, lockable: Boolean = true, onChange: (newValue: Boolean) -> Unit) { val checkbox = CheckBox(text.tr(), CameraStageBaseScreen.skin) checkbox.isChecked = initialState - checkbox.isDisabled = locked + checkbox.isDisabled = lockable && locked checkbox.onChange { onChange(checkbox.isChecked) } add(checkbox).colspan(2).left().row() } @@ -77,7 +77,7 @@ class GameOptionsTable(val previousScreen: IPreviousScreen, val updatePlayerPick { gameParameters.nuclearWeaponsEnabled = it } private fun Table.addGodmodeCheckbox() = - addCheckbox("Scenario Editor", gameParameters.godMode) + addCheckbox("Scenario Editor", gameParameters.godMode, lockable = false) { gameParameters.godMode = it } diff --git a/core/src/com/unciv/ui/newgamescreen/MapOptionsTable.kt b/core/src/com/unciv/ui/newgamescreen/MapOptionsTable.kt index 56f88ab0..a3c45b9e 100644 --- a/core/src/com/unciv/ui/newgamescreen/MapOptionsTable.kt +++ b/core/src/com/unciv/ui/newgamescreen/MapOptionsTable.kt @@ -95,9 +95,7 @@ class MapOptionsTable(val newGameScreen: NewGameScreen): Table() { mapParameters.type = MapType.custom mapParameters.name = mapFileSelectBox.selected.toString() mapTypeSpecificTable.add(savedMapOptionsTable) - newGameScreen.gameSetupInfo.gameParameters.godMode = false newGameScreen.unlockTables() - newGameScreen.updateTables() } else if (mapTypeSelectBox.selected.value == MapType.scenarioMap) { mapParameters.type = MapType.scenarioMap mapParameters.name = scenarioMapSelectBox.selected.toString() @@ -108,7 +106,6 @@ class MapOptionsTable(val newGameScreen: NewGameScreen): Table() { newGameScreen.updateRuleset() // update PlayerTable and GameOptionsTable newGameScreen.lockTables() - newGameScreen.updateTables() } else if(mapTypeSelectBox.selected.value == MapType.scenario){ selectSavedGameAsScenario(scenarioSelectBox.selected.fileHandle) mapTypeSpecificTable.add(scenarioOptionsTable) @@ -116,11 +113,11 @@ class MapOptionsTable(val newGameScreen: NewGameScreen): Table() { } else { // generated map mapParameters.name = "" mapParameters.type = generatedMapOptionsTable.mapTypeSelectBox.selected.value - newGameScreen.gameSetupInfo.gameParameters.godMode = false mapTypeSpecificTable.add(generatedMapOptionsTable) newGameScreen.unlockTables() - newGameScreen.updateTables() } + newGameScreen.gameSetupInfo.gameParameters.godMode = false + newGameScreen.updateTables() } // activate once, so when we had a file map before we'll have the right things set for another one @@ -145,7 +142,10 @@ class MapOptionsTable(val newGameScreen: NewGameScreen): Table() { } mapFileSelectBox.items = mapFiles val selectedItem = mapFiles.firstOrNull { it.fileHandle.name()==mapParameters.name } - if (selectedItem != null) mapFileSelectBox.selected = selectedItem + if (selectedItem != null) { + mapFileSelectBox.selected = selectedItem + newGameScreen.gameSetupInfo.mapFile = mapFileSelectBox.selected.fileHandle + } else if (!mapFiles.isEmpty) { mapFileSelectBox.selected = mapFiles.first() newGameScreen.gameSetupInfo.mapFile = mapFileSelectBox.selected.fileHandle diff --git a/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt b/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt index a4a32550..c0532485 100644 --- a/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt +++ b/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt @@ -25,8 +25,6 @@ import com.unciv.ui.worldscreen.WorldScreen object UnitActions { - const val CAN_UNDERTAKE = "Can undertake" - fun getUnitActions(unit: MapUnit, worldScreen: WorldScreen): List { val tile = unit.getTile() val unitTable = worldScreen.bottomUnitTable @@ -295,69 +293,77 @@ object UnitActions { private fun addGreatPersonActions(unit: MapUnit, actionList: ArrayList, tile: TileInfo) { - if (unit.hasUnique("Can hurry technology research") && !unit.isEmbarked()) { - actionList += UnitAction( - type = UnitActionType.HurryResearch, - uncivSound = UncivSound.Chimes, - action = { - unit.civInfo.tech.hurryResearch() - addGoldPerGreatPersonUsage(unit.civInfo) - unit.destroy() - }.takeIf { unit.civInfo.tech.currentTechnologyName() != null && unit.currentMovement > 0 }) - } - - if (unit.hasUnique("Can start an 8-turn golden age") && !unit.isEmbarked()) { - actionList += UnitAction( - type = UnitActionType.StartGoldenAge, - uncivSound = UncivSound.Chimes, - action = { - unit.civInfo.goldenAges.enterGoldenAge() - addGoldPerGreatPersonUsage(unit.civInfo) - unit.destroy() - }.takeIf { unit.currentMovement > 0 }) - } - - if (unit.hasUnique("Can speed up construction of a wonder") && !unit.isEmbarked()) { - val canHurryWonder = if (unit.currentMovement == 0f || !tile.isCityCenter()) false - else { - val currentConstruction = tile.getCity()!!.cityConstructions.getCurrentConstruction() - if (currentConstruction !is Building) false - else currentConstruction.isWonder || currentConstruction.isNationalWonder + if (unit.currentMovement > 0) for (unique in unit.getUniques()) when (unique.placeholderText) { + "Can hurry technology research" -> { + actionList += UnitAction( + type = UnitActionType.HurryResearch, + uncivSound = UncivSound.Chimes, + action = { + unit.civInfo.tech.hurryResearch() + addGoldPerGreatPersonUsage(unit.civInfo) + unit.destroy() + }.takeIf { unit.civInfo.tech.currentTechnologyName() != null }) + } + "Can start an []-turn golden age" -> { + val turnsToGoldenAge = unique.params[0].toInt() + actionList += UnitAction( + type = UnitActionType.StartGoldenAge, + uncivSound = UncivSound.Chimes, + action = { + unit.civInfo.goldenAges.enterGoldenAge(turnsToGoldenAge) + addGoldPerGreatPersonUsage(unit.civInfo) + unit.destroy() + }.takeIf { unit.currentTile.getOwner() != null && unit.currentTile.getOwner() == unit.civInfo }) + } + // As of 3.10.7 This is to be deprecated and converted to "Can start an []-turn golden age" - keeping it here to that mods with this can still work for now + "Can start an 8-turn golden age" -> { + actionList += UnitAction( + type = UnitActionType.StartGoldenAge, + uncivSound = UncivSound.Chimes, + action = { + unit.civInfo.goldenAges.enterGoldenAge(8) + addGoldPerGreatPersonUsage(unit.civInfo) + unit.destroy() + }.takeIf { unit.currentTile.getOwner() != null && unit.currentTile.getOwner() == unit.civInfo }) + } + "Can speed up construction of a wonder" -> { + val canHurryWonder = if (!tile.isCityCenter()) false + else { + val currentConstruction = tile.getCity()!!.cityConstructions.getCurrentConstruction() + if (currentConstruction !is Building) false + else currentConstruction.isWonder || currentConstruction.isNationalWonder + } + actionList += UnitAction( + type = UnitActionType.HurryWonder, + uncivSound = UncivSound.Chimes, + action = { + tile.getCity()!!.cityConstructions.apply { + addProductionPoints(300 + 30 * tile.getCity()!!.population.population) //http://civilization.wikia.com/wiki/Great_engineer_(Civ5) + constructIfEnough() + } + addGoldPerGreatPersonUsage(unit.civInfo) + unit.destroy() + }.takeIf { canHurryWonder }) + } + "Can undertake a trade mission with City-State, giving a large sum of gold and [] Influence" -> { + val canConductTradeMission = tile.owningCity?.civInfo?.isCityState() == true + && tile.owningCity?.civInfo?.isAtWarWith(unit.civInfo) == false + val influenceEarned = unique.params[0].toInt() + actionList += UnitAction( + type = UnitActionType.ConductTradeMission, + uncivSound = UncivSound.Chimes, + action = { + // http://civilization.wikia.com/wiki/Great_Merchant_(Civ5) + var goldEarned = ((350 + 50 * unit.civInfo.getEraNumber()) * unit.civInfo.gameInfo.gameParameters.gameSpeed.modifier).toInt() + if (unit.civInfo.hasUnique("Double gold from Great Merchant trade missions")) + goldEarned *= 2 + unit.civInfo.gold += goldEarned + tile.owningCity!!.civInfo.getDiplomacyManager(unit.civInfo).influence += influenceEarned + unit.civInfo.addNotification("Your trade mission to [${tile.owningCity!!.civInfo}] has earned you [${goldEarned}] gold and [$influenceEarned] influence!", null, Color.GOLD) + addGoldPerGreatPersonUsage(unit.civInfo) + unit.destroy() + }.takeIf { canConductTradeMission }) } - actionList += UnitAction( - type = UnitActionType.HurryWonder, - uncivSound = UncivSound.Chimes, - action = { - tile.getCity()!!.cityConstructions.apply { - addProductionPoints(300 + 30 * tile.getCity()!!.population.population) //http://civilization.wikia.com/wiki/Great_engineer_(Civ5) - constructIfEnough() - } - addGoldPerGreatPersonUsage(unit.civInfo) - unit.destroy() - }.takeIf { canHurryWonder }) - } - - if (unit.hasUnique("Can undertake a trade mission with City-State, giving a large sum of gold and [30] Influence") - && !unit.isEmbarked()) { - val canConductTradeMission = tile.owningCity?.civInfo?.isCityState() == true - && tile.owningCity?.civInfo?.isAtWarWith(unit.civInfo) == false - && unit.currentMovement > 0 - actionList += UnitAction( - type = UnitActionType.ConductTradeMission, - uncivSound = UncivSound.Chimes, - action = { - // http://civilization.wikia.com/wiki/Great_Merchant_(Civ5) - var goldEarned = (350 + 50 * unit.civInfo.getEraNumber()) * unit.civInfo.gameInfo.gameParameters.gameSpeed.modifier - if (unit.civInfo.hasUnique("Double gold from Great Merchant trade missions")) - goldEarned *= 2 - unit.civInfo.gold += goldEarned.toInt() - val relevantUnique = unit.getUniques().first { it.text.startsWith(CAN_UNDERTAKE) } - val influenceEarned = Regex("\\d+").find(relevantUnique.text)!!.value.toInt() - tile.owningCity!!.civInfo.getDiplomacyManager(unit.civInfo).influence += influenceEarned - unit.civInfo.addNotification("Your trade mission to [${tile.owningCity!!.civInfo}] has earned you [${goldEarned.toInt()}] gold and [$influenceEarned] influence!", null, Color.GOLD) - addGoldPerGreatPersonUsage(unit.civInfo) - unit.destroy() - }.takeIf { canConductTradeMission }) } }