From 06c68302ce7b2395ff14b04f160f5b4361d79e06 Mon Sep 17 00:00:00 2001 From: Yair Morgenstern Date: Wed, 25 Jul 2018 22:56:25 +0300 Subject: [PATCH] Added difficulty settings! --- android/assets/jsons/Difficulties.json | 82 +++++++++++++++++++ android/build.gradle | 4 +- core/src/com/unciv/GameStarter.kt | 10 ++- core/src/com/unciv/UnCivGame.kt | 2 +- .../com/unciv/logic/city/CityConstructions.kt | 3 + core/src/com/unciv/logic/city/CityStats.kt | 7 +- .../com/unciv/logic/city/PopulationManager.kt | 7 +- .../logic/civilization/CivilizationInfo.kt | 30 ++++--- .../logic/civilization/DiplomacyManager.kt | 1 + .../unciv/logic/civilization/TechManager.kt | 13 ++- .../com/unciv/models/gamebasics/Difficulty.kt | 15 ++++ .../com/unciv/models/gamebasics/GameBasics.kt | 2 + core/src/com/unciv/ui/NewGameScreen.kt | 14 +++- .../com/unciv/ui/worldscreen/WorldScreen.kt | 2 +- 14 files changed, 160 insertions(+), 32 deletions(-) create mode 100644 android/assets/jsons/Difficulties.json create mode 100644 core/src/com/unciv/models/gamebasics/Difficulty.kt diff --git a/android/assets/jsons/Difficulties.json b/android/assets/jsons/Difficulties.json new file mode 100644 index 0000000000..8a5a7fa9d3 --- /dev/null +++ b/android/assets/jsons/Difficulties.json @@ -0,0 +1,82 @@ +[ + { + name:"Settler", + baseHappiness:15, + researchCostModifier:0.9, + unhappinessModifier:0.4, + aiCityGrowthModifier:1.6, // that is to say it'll take them 1.6 times as long to grow the city + aiUnitMaintainanceModifier:1, + aiConstructionModifier:0.6, // Replaces "Construction rate" and "Create rate" in original config + aiFreeTechs:[] + }, + { + name:"Chieftain", + baseHappiness:12, + researchCostModifier:0.95, + unhappinessModifier:0.6, + aiCityGrowthModifier:1.3, + aiUnitMaintainanceModifier:1, + aiConstructionModifier:0.75, + aiFreeTechs:[] + }, + { + name:"Warlord", + baseHappiness:12, + researchCostModifier:1, + unhappinessModifier:0.75, + aiCityGrowthModifier:1.1, + aiUnitMaintainanceModifier:1, + aiConstructionModifier:0.9, + aiFreeTechs:[] + }, + { + name:"Prince", + baseHappiness:9, + researchCostModifier:1, + unhappinessModifier:1 + aiCityGrowthModifier:1, + aiUnitMaintainanceModifier:0.85, + aiConstructionModifier:1, + aiFreeTechs:[] + }, + { + name:"King", + baseHappiness:9, + researchCostModifier:1, + unhappinessModifier:1 + aiCityGrowthModifier:0.9, + aiUnitMaintainanceModifier:0.8, + aiConstructionModifier:1.15, + aiFreeTechs:["Pottery"] + }, + { + name:"Emperor", + baseHappiness:9, + researchCostModifier:1, + unhappinessModifier:1 + aiCityGrowthModifier:0.85, + aiUnitMaintainanceModifier:0.75, + aiConstructionModifier:1.25, + aiFreeTechs:["Pottery","Animal Husbandry"] + }, + { + name:"Immortal", + baseHappiness:9, + researchCostModifier:1, + unhappinessModifier:1 + aiCityGrowthModifier:0.75, + aiUnitMaintainanceModifier:0.65, + aiConstructionModifier:1.5, + aiFreeTechs:["Pottery","Animal Husbandry","Mining"] + }, + { + name:"Deity", + baseHappiness:9, + researchCostModifier:1, + unhappinessModifier:1 + aiCityGrowthModifier:0.6, + aiUnitMaintainanceModifier:0.5, + aiConstructionModifier:2, + aiFreeTechs:["Pottery","Animal Husbandry","Mining","The Wheel"] + } +] \ No newline at end of file diff --git a/android/build.gradle b/android/build.gradle index f0cca50a63..68bfbff788 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -21,8 +21,8 @@ android { applicationId "com.unciv.game" minSdkVersion 14 targetSdkVersion 26 - versionCode 105 - versionName "2.6.7" + versionCode 106 + versionName "2.6.8" } buildTypes { release { diff --git a/core/src/com/unciv/GameStarter.kt b/core/src/com/unciv/GameStarter.kt index ecdcc8b4b3..26cab312d3 100644 --- a/core/src/com/unciv/GameStarter.kt +++ b/core/src/com/unciv/GameStarter.kt @@ -8,7 +8,7 @@ import com.unciv.models.gamebasics.GameBasics import com.unciv.ui.utils.getRandom class GameStarter(){ - fun startNewGame(mapRadius: Int, numberOfCivs: Int, civilization: String): GameInfo { + fun startNewGame(mapRadius: Int, numberOfCivs: Int, civilization: String, difficulty:String): GameInfo { val gameInfo = GameInfo() gameInfo.tileMap = TileMap(mapRadius) @@ -24,7 +24,9 @@ class GameStarter(){ val freeTiles = gameInfo.tileMap.values.toMutableList().filter { vectorIsWithinNTilesOfEdge(it.position,3)}.toMutableList() val playerPosition = freeTiles.getRandom().position - gameInfo.civilizations.add(CivilizationInfo(civilization, playerPosition, gameInfo)) // first one is player civ + val playerCiv = CivilizationInfo(civilization, playerPosition, gameInfo) + playerCiv.difficulty=difficulty + gameInfo.civilizations.add(playerCiv) // first one is player civ freeTiles.removeAll(gameInfo.tileMap.getTilesInDistance(playerPosition,6)) @@ -33,7 +35,9 @@ class GameStarter(){ for (civname in GameBasics.Civilizations.keys.filterNot { it=="Barbarians" || it==civilization }.take(numberOfCivs)) { val startingLocation = freeTiles.toList().getRandom().position - gameInfo.civilizations.add(CivilizationInfo(civname, startingLocation, gameInfo)) + val civ = CivilizationInfo(civname, startingLocation, gameInfo) + civ.tech.techsResearched.addAll(playerCiv.getDifficulty().aiFreeTechs) + gameInfo.civilizations.add(civ) freeTiles.removeAll(gameInfo.tileMap.getTilesInDistance(startingLocation, 6)) } diff --git a/core/src/com/unciv/UnCivGame.kt b/core/src/com/unciv/UnCivGame.kt index 97f33c7701..fc18156463 100644 --- a/core/src/com/unciv/UnCivGame.kt +++ b/core/src/com/unciv/UnCivGame.kt @@ -45,7 +45,7 @@ class UnCivGame : Game() { } fun startNewGame(saveTutorialState:Boolean = false) { - val newGame = GameStarter().startNewGame(20, 3, "Babylon") + val newGame = GameStarter().startNewGame(20, 3, "Babylon","Chieftain") if(saveTutorialState) { newGame.tutorial = gameInfo.tutorial } diff --git a/core/src/com/unciv/logic/city/CityConstructions.kt b/core/src/com/unciv/logic/city/CityConstructions.kt index bbd719ab13..b09ce9e4ff 100644 --- a/core/src/com/unciv/logic/city/CityConstructions.kt +++ b/core/src/com/unciv/logic/city/CityConstructions.kt @@ -102,6 +102,9 @@ class CityConstructions { } else currentConstruction = saveCurrentConstruction + var productionToAdd = cityStats.production + if(!cityInfo.civInfo.isPlayerCivilization()) + productionToAdd *= cityInfo.civInfo.gameInfo.getPlayerCivilization().getDifficulty().aiConstructionModifier addConstruction(Math.round(cityStats.production)) val productionCost = construction.getProductionCost(cityInfo.civInfo.policies.adoptedPolicies) if (inProgressConstructions[currentConstruction]!! >= productionCost) { diff --git a/core/src/com/unciv/logic/city/CityStats.kt b/core/src/com/unciv/logic/city/CityStats.kt index bc58600a92..5dc8cf9579 100644 --- a/core/src/com/unciv/logic/city/CityStats.kt +++ b/core/src/com/unciv/logic/city/CityStats.kt @@ -99,8 +99,11 @@ class CityStats { // in order to determine how much food is produced in a city! // -3 happiness per city fun getCityHappiness(): LinkedHashMap { - happinessList["Cities"] = -3f val civInfo = cityInfo.civInfo + var unhappinessModifier = civInfo.getDifficulty().unhappinessModifier + + happinessList["Cities"] = -3f * unhappinessModifier + var unhappinessFromCitizens = cityInfo.population.population.toFloat() if (civInfo.policies.isAdopted("Democracy")) unhappinessFromCitizens -= cityInfo.population.getNumberOfSpecialists() * 0.5f @@ -109,7 +112,7 @@ class CityStats { if (civInfo.policies.isAdopted("Meritocracy")) unhappinessFromCitizens *= 0.95f - happinessList["Population"] = -unhappinessFromCitizens + happinessList["Population"] = -unhappinessFromCitizens * unhappinessModifier var happinessFromPolicies = 0f if (civInfo.policies.isAdopted("Aristocracy")) diff --git a/core/src/com/unciv/logic/city/PopulationManager.kt b/core/src/com/unciv/logic/city/PopulationManager.kt index 4aaeceeae3..11af49b124 100644 --- a/core/src/com/unciv/logic/city/PopulationManager.kt +++ b/core/src/com/unciv/logic/city/PopulationManager.kt @@ -36,8 +36,11 @@ class PopulationManager { fun getFoodToNextPopulation(): Int { - // civ v math,civilization.wikia - return 15 + 6 * (population - 1) + Math.floor(Math.pow((population - 1).toDouble(), 1.8)).toInt() + // civ v math, civilization.wikia + var foodRequired = 15 + 6 * (population - 1) + Math.floor(Math.pow((population - 1).toDouble(), 1.8)) + if(!cityInfo.civInfo.isPlayerCivilization()) + foodRequired *= cityInfo.civInfo.gameInfo.getPlayerCivilization().getDifficulty().aiCityGrowthModifier + return foodRequired.toInt() } diff --git a/core/src/com/unciv/logic/civilization/CivilizationInfo.kt b/core/src/com/unciv/logic/civilization/CivilizationInfo.kt index ef8fa41755..b1cc33e7ee 100644 --- a/core/src/com/unciv/logic/civilization/CivilizationInfo.kt +++ b/core/src/com/unciv/logic/civilization/CivilizationInfo.kt @@ -8,7 +8,6 @@ import com.unciv.logic.map.MapUnit import com.unciv.logic.map.RoadStatus import com.unciv.logic.map.TileInfo import com.unciv.models.Counter -import com.unciv.models.gamebasics.Civilization import com.unciv.models.gamebasics.GameBasics import com.unciv.models.gamebasics.tech.TechEra import com.unciv.models.gamebasics.tile.ResourceType @@ -19,6 +18,7 @@ import com.unciv.ui.utils.getRandom import com.unciv.ui.utils.tr import kotlin.math.max import kotlin.math.pow +import kotlin.math.roundToInt class CivilizationInfo { @@ -28,6 +28,7 @@ class CivilizationInfo { var gold = 0 var happiness = 15 + var difficulty = "Chieftain" var civName = "Babylon" var tech = TechManager() @@ -40,7 +41,8 @@ class CivilizationInfo { var cities = ArrayList() var exploredTiles = HashSet() - fun getCivilization(): Civilization {return GameBasics.Civilizations[civName]!!} + fun getDifficulty() = GameBasics.Difficulties[difficulty]!! + fun getCivilization() = GameBasics.Civilizations[civName]!! fun getCapital()=cities.first { it.isCapital() } @@ -97,9 +99,11 @@ class CivilizationInfo { if(policies.isAdopted("Oligarchy")) unitsToPayFor = unitsToPayFor.filterNot { it.getTile().isCityCenter() } val totalPaidUnits = max(0,unitsToPayFor.count()-freeUnits) val gameProgress = gameInfo.turns/400f // as game progresses maintainance cost rises - val cost = baseUnitCost*totalPaidUnits*(1+gameProgress) - val finalCost = cost.pow(1+gameProgress/3) // Why 3? No reason. - return finalCost.toInt() + var cost = baseUnitCost*totalPaidUnits*(1+gameProgress) + cost = cost.pow(1+gameProgress/3) // Why 3? To spread 1 to 1.33 + if(!isPlayerCivilization()) + cost *= gameInfo.getPlayerCivilization().getDifficulty().aiUnitMaintainanceModifier + return cost.toInt() } private fun getTransportationUpkeep(): Int { @@ -115,11 +119,11 @@ class CivilizationInfo { } // base happiness - fun getHappinessForNextTurn(): HashMap { - val statMap = HashMap() - statMap["Base happiness"] = 15 + fun getHappinessForNextTurn(): HashMap { + val statMap = HashMap() + statMap["Base happiness"] = getDifficulty().baseHappiness.toFloat() - var happinessPerUniqueLuxury = 5 + var happinessPerUniqueLuxury = 5f if (policies.isAdopted("Protectionism")) happinessPerUniqueLuxury += 1 statMap["Luxury resources"]= getCivResources().keys .count { it.resourceType === ResourceType.Luxury } * happinessPerUniqueLuxury @@ -127,13 +131,13 @@ class CivilizationInfo { for(city in cities){ for(keyvalue in city.cityStats.getCityHappiness()){ if(statMap.containsKey(keyvalue.key)) - statMap[keyvalue.key] = statMap[keyvalue.key]!!+keyvalue.value.toInt() - else statMap[keyvalue.key] = keyvalue.value.toInt() + statMap[keyvalue.key] = statMap[keyvalue.key]!!+keyvalue.value + else statMap[keyvalue.key] = keyvalue.value } } if (buildingUniques.contains("Provides 1 happiness per social policy")) - statMap["Policies"] = policies.getAdoptedPolicies().count { !it.endsWith("Complete") } + statMap["Policies"] = policies.getAdoptedPolicies().count { !it.endsWith("Complete") }.toFloat() return statMap } @@ -227,7 +231,7 @@ class CivilizationInfo { getViewableTiles() // adds explored tiles so that the units will be able to perform automated actions better for (city in cities) city.cityStats.update() - happiness = getHappinessForNextTurn().values.sum() + happiness = getHappinessForNextTurn().values.sum().roundToInt() getCivUnits().forEach { it.startTurn() } } diff --git a/core/src/com/unciv/logic/civilization/DiplomacyManager.kt b/core/src/com/unciv/logic/civilization/DiplomacyManager.kt index 4b5818d69c..d86ff20789 100644 --- a/core/src/com/unciv/logic/civilization/DiplomacyManager.kt +++ b/core/src/com/unciv/logic/civilization/DiplomacyManager.kt @@ -21,6 +21,7 @@ class DiplomacyManager() { civInfo=civilizationInfo otherCivName=OtherCivName } + // var status:DiplomaticStatus = DiplomaticStatus.War var trades = ArrayList() diff --git a/core/src/com/unciv/logic/civilization/TechManager.kt b/core/src/com/unciv/logic/civilization/TechManager.kt index ca8fa2b257..81c1e4236f 100644 --- a/core/src/com/unciv/logic/civilization/TechManager.kt +++ b/core/src/com/unciv/logic/civilization/TechManager.kt @@ -20,10 +20,9 @@ class TechManager { private fun getCurrentTechnology(): Technology = GameBasics.Technologies[currentTechnology()]!! - fun getAmountResearchedText(): String = - if (currentTechnology() == null) "" - else "(" + researchOfTech(currentTechnology()!!) + "/" + getCurrentTechnology().cost + ")" - + fun costOfTech(techName: String): Int { + return (GameBasics.Technologies[techName]!!.cost * civInfo.getDifficulty().researchCostModifier).toInt() + } fun currentTechnology(): String? { if (techsToResearch.isEmpty()) return null @@ -35,8 +34,8 @@ class TechManager { else return 0 } - fun turnsToTech(TechName: String): Int { - val remainingScience =GameBasics.Technologies[TechName]!!.cost - researchOfTech(TechName) + fun turnsToTech(techName: String): Int { + val remainingScience = costOfTech(techName) - researchOfTech(techName) return Math.ceil( remainingScience.toDouble() / civInfo.getStatsForNextTurn().science).toInt() } @@ -51,7 +50,7 @@ class TechManager { val currentTechnology = currentTechnology() if (currentTechnology == null) return techsInProgress[currentTechnology] = researchOfTech(currentTechnology) + scienceForNewTurn - if (techsInProgress[currentTechnology]!! < getCurrentTechnology().cost) + if (techsInProgress[currentTechnology]!! < costOfTech(currentTechnology)) return val previousEra = civInfo.getEra() diff --git a/core/src/com/unciv/models/gamebasics/Difficulty.kt b/core/src/com/unciv/models/gamebasics/Difficulty.kt new file mode 100644 index 0000000000..19074eeb91 --- /dev/null +++ b/core/src/com/unciv/models/gamebasics/Difficulty.kt @@ -0,0 +1,15 @@ +package com.unciv.models.gamebasics + +import com.unciv.models.stats.INamed +import java.util.* + +class Difficulty: INamed { + override lateinit var name: String + var baseHappiness: Int = 0 + var researchCostModifier:Float = 1f + var unhappinessModifier = 1f + var aiCityGrowthModifier = 1f + var aiUnitMaintainanceModifier = 1f + var aiConstructionModifier = 1f + var aiFreeTechs = ArrayList() +} \ No newline at end of file diff --git a/core/src/com/unciv/models/gamebasics/GameBasics.kt b/core/src/com/unciv/models/gamebasics/GameBasics.kt index 68c64c777d..09e7a84119 100644 --- a/core/src/com/unciv/models/gamebasics/GameBasics.kt +++ b/core/src/com/unciv/models/gamebasics/GameBasics.kt @@ -23,6 +23,7 @@ object GameBasics { val UnitPromotions = LinkedHashMap() val Civilizations = LinkedHashMap() val PolicyBranches = LinkedHashMap() + val Difficulties = LinkedHashMap() val Tutorials = LinkedHashMap>() val Translations = Translations(Gdx.files.internal("jsons/Translations.json").readString()) @@ -48,6 +49,7 @@ object GameBasics { UnitPromotions += createHashmap(getFromJson(Array::class.java, "UnitPromotions")) PolicyBranches += createHashmap(getFromJson(Array::class.java, "Policies")) Civilizations += createHashmap(getFromJson(Array::class.java, "Civilizations")) + Difficulties += createHashmap(getFromJson(Array::class.java, "Difficulties")) // ...Yes. Total Voodoo. I wish I didn't have to do this. val x = LinkedHashMap>>() diff --git a/core/src/com/unciv/ui/NewGameScreen.kt b/core/src/com/unciv/ui/NewGameScreen.kt index 5f362ece2f..35020c6e3b 100644 --- a/core/src/com/unciv/ui/NewGameScreen.kt +++ b/core/src/com/unciv/ui/NewGameScreen.kt @@ -19,6 +19,7 @@ class NewGameScreen: PickerScreen(){ val table = Table() table.skin= skin + table.add("Civilization:".tr()) val civSelectBox = SelectBox(skin) val civArray = Array() @@ -48,6 +49,16 @@ class NewGameScreen: PickerScreen(){ enemiesSelectBox.selected=3 table.add(enemiesSelectBox).pad(10f).row() + + table.add("Difficulty:".tr()) + val difficultySelectBox = SelectBox(skin) + val difficultyArray = Array() + GameBasics.Difficulties.keys.forEach{difficultyArray.add(it)} + difficultySelectBox.items = difficultyArray + difficultySelectBox.selected = "Chieftain" + table.add(difficultySelectBox).pad(10f).row() + + rightSideButton.enable() rightSideButton.setText("Start game!".tr()) rightSideButton.addClickListener { @@ -57,7 +68,8 @@ class NewGameScreen: PickerScreen(){ kotlin.concurrent.thread { // Creating a new game can tke a while and we don't want ANRs newGame = GameStarter().startNewGame( - worldSizeToRadius[worldSizeSelectBox.selected]!!, enemiesSelectBox.selected, civSelectBox.selected ) + worldSizeToRadius[worldSizeSelectBox.selected]!!, enemiesSelectBox.selected, + civSelectBox.selected, difficultySelectBox.selected ) .apply { tutorial=game.gameInfo.tutorial } } } diff --git a/core/src/com/unciv/ui/worldscreen/WorldScreen.kt b/core/src/com/unciv/ui/worldscreen/WorldScreen.kt index f56d86d2ca..ebf5e0aacb 100644 --- a/core/src/com/unciv/ui/worldscreen/WorldScreen.kt +++ b/core/src/com/unciv/ui/worldscreen/WorldScreen.kt @@ -83,7 +83,7 @@ class WorldScreen : CameraStageBaseScreen() { fun update() { - kotlin.concurrent.thread { civInfo.happiness = civInfo.getHappinessForNextTurn().values.sum() } + kotlin.concurrent.thread { civInfo.happiness = civInfo.getHappinessForNextTurn().values.sum().toInt() } if (game.gameInfo.tutorial.contains("CityEntered")) { displayTutorials("AfterCityEntered")