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
This commit is contained in:
HadeanLake 2020-09-05 20:32:27 +03:00 committed by GitHub
parent ca36084e8b
commit 8ac3a88cec
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 103 additions and 90 deletions

View file

@ -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"
},

View file

@ -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",

View file

@ -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]%"]
}
]
},

View file

@ -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"]
},
{

View file

@ -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"]

View file

@ -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
}

View file

@ -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
}

View file

@ -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

View file

@ -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)

View file

@ -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)

View file

@ -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 }

View file

@ -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

View file

@ -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<UnitAction> {
val tile = unit.getTile()
val unitTable = worldScreen.bottomUnitTable
@ -295,69 +293,77 @@ object UnitActions {
private fun addGreatPersonActions(unit: MapUnit, actionList: ArrayList<UnitAction>, 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 })
}
}