diff --git a/android/assets/jsons/Civ V - Gods & Kings/Techs.json b/android/assets/jsons/Civ V - Gods & Kings/Techs.json index 311bf687c7..18b589f573 100644 --- a/android/assets/jsons/Civ V - Gods & Kings/Techs.json +++ b/android/assets/jsons/Civ V - Gods & Kings/Techs.json @@ -38,6 +38,7 @@ "name": "Archery", "row": 7, "prerequisites": ["Agriculture"], + "uniques": ["[-50]% weight to this choice for AI decisions"], "quote": "'The haft of the arrow has been feathered with one of the eagle's own plumes, we often give our enemies the means of our own destruction' - Aesop" }, { @@ -101,6 +102,7 @@ "name": "Bronze Working", "row": 10, "prerequisites": ["Mining"], + "uniques": ["[+50]% weight to this choice for AI decisions"], "quote": "'Here Hector entered, with a spear eleven cubits long in his hand; the bronze point gleamed in front of him, and was fastened to the shaft of the spear by a ring of gold.' - Homer" } ] @@ -124,6 +126,7 @@ "name": "Horseback Riding", "row": 5, "prerequisites": ["Trapping","The Wheel"], + "uniques": ["[+50]% weight to this choice for AI decisions"], "quote": "'A Horse! A Horse! My kingdom for a horse!' - Shakespeare (Richard III)" }, { @@ -178,6 +181,7 @@ "name": "Iron Working", "row": 10, "prerequisites": ["Bronze Working"], + "uniques": ["[-50]% weight to this choice for AI decisions"], "quote": "'Do not wait to strike til the iron is hot, but make it hot by striking.' - William Butler Yeats" } ] @@ -213,6 +217,7 @@ "name": "Metal Casting", "row": 9, "prerequisites": ["Engineering","Iron Working"], + "uniques": ["[+100]% weight to this choice for AI decisions"], "quote": "'When pieces of bronze or gold or iron break, the metal-smith welds them together again in the fire, and the bond is established.' - Sri Guru Granth Sahib" } ] @@ -229,6 +234,7 @@ "cost": 375, "row": 1, "prerequisites": ["Optics","Theology"], + "uniques": ["[-50]% weight to this choice for AI decisions"], "quote": "'I find the great thing in this world is not so much where we stand, as in what direction we are moving.' - Oliver Wendell Holmes" }, { @@ -262,6 +268,7 @@ "name": "Steel", "row": 10, "prerequisites": ["Metal Casting"], + "uniques": ["[-50]% weight to this choice for AI decisions"], "quote": "'John Henry said to his Captain, / 'A man ain't nothin' but a man, / And before I'll let your steam drill beat me down, / I'll die with the hammer in my hand.'' - Anonymous: The Ballad of John Henry, the Steel-Drivin' Man" } ] @@ -276,7 +283,7 @@ { "name": "Astronomy", "row": 2, - "uniques": ["[+1] Movement ","Enables [Embarked] units to enter ocean tiles"], + "uniques": ["[+1] Movement ","Enables [Embarked] units to enter ocean tiles","[+50]% weight to this choice for AI decisions"], "prerequisites": ["Compass","Education"], "quote": "'Joyfully to the breeze royal Odysseus spread his sail, and with his rudder skillfully he steered.' - Homer" }, @@ -305,6 +312,7 @@ "name": "Gunpowder", "row": 10, "prerequisites": ["Physics","Steel"], + "uniques": ["[-50]% weight to this choice for AI decisions"], "quote": "'The day when two army corps can annihilate each other in one second, all civilized nations, it is to be hoped, will recoil from war and discharge their troops.' - Alfred Nobel" } ] @@ -332,18 +340,21 @@ "name": "Economics", "row": 6, "prerequisites": ["Banking","Printing Press"], + "uniques": ["[+50]% weight to this choice for AI decisions"], "quote": "'Compound interest is the most powerful force in the universe.' - Albert Einstein" }, { "name": "Metallurgy", "row": 9, "prerequisites": ["Printing Press","Gunpowder"], + "uniques": ["[-50]% weight to this choice for AI decisions"], "quote": "'There never was a good knife made of bad steel.' - Benjamin Franklin" }, { "name": "Chemistry", "row": 10, "prerequisites": ["Gunpowder"], + "uniques": ["[+100]% weight to this choice for AI decisions"], "quote": "'Wherever we look, the work of the chemist has raised the level of our civilization and has increased the productive capacity of the nation.' - Calvin Coolidge" } ] @@ -359,6 +370,7 @@ "name": "Archaeology", "row": 2, "prerequisites": ["Navigation","Architecture"], + "uniques": ["[+50]% weight to this choice for AI decisions"], "quote": "'Those who cannot remember the past are condemned to repeat it.' - George Santayana" }, { @@ -372,12 +384,14 @@ "name": "Industrialization", "row": 6, "prerequisites": ["Economics"], + "uniques": ["[+50]% weight to this choice for AI decisions"], "quote": "'Industrialization based on machinery, already referred to as a characteristic of our age, is but one aspect of the revolution that is being wrought by technology.' - Emily Greene Balch" }, { "name": "Rifling", "row": 7, "prerequisites": ["Economics","Metallurgy"], + "uniques": ["[-50]% weight to this choice for AI decisions"], "quote": "'It is well that war is so terrible, or we should grow too fond of it.' - Robert E. Lee" }, { @@ -442,19 +456,21 @@ "name": "Refrigeration", "row": 2, "prerequisites": ["Biology", "Electricity"], //todo: offshore platform should need this - "uniques": ["[-50]% weight to this choice for AI decisions"], + "uniques": ["[-75]% weight to this choice for AI decisions"], "quote": "'And homeless near a thousand homes I stood, and near a thousand tables pined and wanted food.' - William Wordsworth" }, { "name": "Radio", "row": 3, "prerequisites": ["Electricity"], + "uniques": ["[+100]% weight to this choice for AI decisions"], "quote": "'The whole country was tied together by radio. We all experienced the same heroes and comedians and singers. They were giants.' - Woody Allen" }, { "name": "Replaceable Parts", "row": 4, "prerequisites": ["Electricity","Steam Power"], + "uniques": ["[+100]% weight to this choice for AI decisions"], "quote": "'Nothing is particularly hard if you divide it into small jobs.' - Henry Ford" }, { @@ -483,7 +499,7 @@ "name": "Plastics", "row": 3, "prerequisites": ["Radio","Replaceable Parts"], - "uniques": ["[+100]% weight to this choice for AI decisions"], + "uniques": ["[+200]% weight to this choice for AI decisions"], "quote": "'Ben, I want to say one word to you, just one word: plastics.' - Buck Henry and Calder Willingham, The Graduate" }, { @@ -531,6 +547,7 @@ "name": "Radar", "row": 6, "prerequisites": ["Ballistics","Electronics"], + "uniques": ["[+100]% weight to this choice for AI decisions"], "quote": "'Vision is the art of seeing things invisible.' - Jonathan Swift" }, { @@ -604,7 +621,7 @@ "name": "Satellites", "row": 6, "prerequisites": ["Rocketry"], - "uniques": ["Reveals the entire map","[+100]% weight to this choice for AI decisions"], + "uniques": ["Reveals the entire map","[+200]% weight to this choice for AI decisions"], "quote": "'Now, somehow, in some new way, the sky seemed almost alien.' - Lyndon B. Johnson" }, { diff --git a/core/src/com/unciv/logic/automation/city/ConstructionAutomation.kt b/core/src/com/unciv/logic/automation/city/ConstructionAutomation.kt index 0f97611f99..3ba44cd111 100644 --- a/core/src/com/unciv/logic/automation/city/ConstructionAutomation.kt +++ b/core/src/com/unciv/logic/automation/city/ConstructionAutomation.kt @@ -259,7 +259,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions) { if (!civInfo.hasUnique(UniqueType.EnablesConstructionOfSpaceshipParts)) return val spaceshipPart = (nonWonders + units).filter { it.name in spaceshipParts }.filterBuildable().firstOrNull() ?: return - val modifier = 3f * personality.modifierFocus(PersonalityValue.Science, .4f) + val modifier = 20f //We're weighing Apollo program according to personality. If we decided to invest in that, we might as well commit to it. addChoice(relativeCostEffectiveness, spaceshipPart.name, modifier) } diff --git a/core/src/com/unciv/logic/automation/civilization/NextTurnAutomation.kt b/core/src/com/unciv/logic/automation/civilization/NextTurnAutomation.kt index 1bfa622144..d1e175c642 100644 --- a/core/src/com/unciv/logic/automation/civilization/NextTurnAutomation.kt +++ b/core/src/com/unciv/logic/automation/civilization/NextTurnAutomation.kt @@ -367,17 +367,9 @@ object NextTurnAutomation { if (greatPeople.isEmpty()) return var greatPerson = greatPeople.random() - - if (civInfo.wantsToFocusOn(Victory.Focus.Culture)) { - val culturalGP = - greatPeople.firstOrNull { it.uniques.contains("Great Person - [Culture]") } - if (culturalGP != null) greatPerson = culturalGP - } - if (civInfo.wantsToFocusOn(Victory.Focus.Science)) { - val scientificGP = - greatPeople.firstOrNull { it.uniques.contains("Great Person - [Science]") } - if (scientificGP != null) greatPerson = scientificGP - } + val scienceGP = greatPeople.firstOrNull { it.uniques.contains("Great Person - [Science]") } + if (scienceGP != null) greatPerson = scienceGP + // Humans would pick a prophet or engineer, but it'd require more sophistication on part of the AI - a scientist is the safest option for now civInfo.units.addUnit(greatPerson, civInfo.cities.firstOrNull { it.isCapital() }) diff --git a/core/src/com/unciv/logic/automation/civilization/ReligionAutomation.kt b/core/src/com/unciv/logic/automation/civilization/ReligionAutomation.kt index 6adef6d0d1..ae3642445c 100644 --- a/core/src/com/unciv/logic/automation/civilization/ReligionAutomation.kt +++ b/core/src/com/unciv/logic/automation/civilization/ReligionAutomation.kt @@ -33,16 +33,12 @@ object ReligionAutomation { if (civInfo.religionManager.remainingFoundableReligions() == 0 ) { buyGreatPerson(civInfo) - return - } - - // We don't have a religion and no more change of getting it :( - if (civInfo.religionManager.religionState <= ReligionState.Pantheon) { tryBuyAnyReligiousBuilding(civInfo) return } // If we don't have majority in all our own cities, build missionaries and inquisitors to solve this + //TODO: in late game, this may not be worth it val citiesWithoutOurReligion = civInfo.cities.filter { it.religion.getMajorityReligion() != civInfo.religionManager.religion!! } // The original had a cap at 4 missionaries total, but 1/4 * the number of cities should be more appropriate imo if (citiesWithoutOurReligion.count() > @@ -59,7 +55,6 @@ object ReligionAutomation { return } - // Get an inquisitor to defend our holy city val holyCity = civInfo.religionManager.getHolyCity() if (holyCity != null @@ -70,23 +65,13 @@ object ReligionAutomation { buyInquisitorNear(civInfo, holyCity) return } - - // Buy religious buildings in cities if possible - val citiesWithMissingReligiousBuildings = civInfo.cities.filter { city -> - city.religion.getMajorityReligion() != null - && !city.cityConstructions.isAllBuilt(city.religion.getMajorityReligion()!!.buildingsPurchasableByBeliefs) - } - if (citiesWithMissingReligiousBuildings.any()) { - tryBuyAnyReligiousBuilding(civInfo) - return - } - + // Just buy missionaries to spread our religion outside of our civ if (civInfo.units.getCivUnits().count { it.hasUnique(UniqueType.CanSpreadReligion) } < 4) { buyMissionaryInAnyCity(civInfo) return } - // Todo: buy inquisitors for defence of other cities + // Todo: declare war if enemy missionaries enter our civ without permission } private fun tryBuyAnyReligiousBuilding(civInfo: Civilization) { @@ -289,17 +274,17 @@ object ReligionAutomation { UniqueType.StatsFromObject -> when { ruleSet.buildings.containsKey(unique.params[1]) -> { - unique.stats.values.sum() / - if (ruleSet.buildings[unique.params[1]]!!.isWonder) 2f - else 1f + unique.stats.values.sum() * + if (ruleSet.buildings[unique.params[1]]!!.isNationalWonder) 0.5f //there's at most 1 copy of each of these in our empire + else 1.5f //yields for buildings are usually more desireable than faith-purchased buildings, as we might not need an upfront investment } ruleSet.specialists.containsKey(unique.params[1]) -> { unique.stats.values.sum() * - if (city.population.population > 8f) 3f + if (city.population.population > 8f) 2f else 1f } - else -> 0f + else -> unique.stats.values.sum() * 1f //should account for most edge cases here } UniqueType.StatsFromTradeRoute -> unique.stats.values.sum() * @@ -309,7 +294,7 @@ object ReligionAutomation { min(unique.params[0].toFloat() * city.population.population, unique.params[2].toFloat()) UniqueType.StatsPerCity -> if (city.matchesFilter(unique.params[1])) - unique.stats.values.sum() + unique.stats.values.sum() * 2f //free yields else 0f else -> 0f } @@ -359,7 +344,7 @@ object ReligionAutomation { // Some city-filters are modified by personality (non-enemy foreign cities) score += modifier * when (unique.type) { UniqueType.KillUnitPlunderNearCity -> - unique.params[0].toFloat() * 4f * + unique.params[0].toFloat() * 0.5f * //can be very strong, but a low weight for now as the AI currently isn't farming barb camp if (civInfo.wantsToFocusOn(Victory.Focus.Military)) 2f else 1f UniqueType.BuyUnitsForAmountStat, UniqueType.BuyBuildingsForAmountStat -> @@ -368,39 +353,39 @@ object ReligionAutomation { ) 0f // This is something completely different from the original, but I have no idea // what happens over there - else civInfo.stats.statsForNextTurn[Stat.valueOf(unique.params[2])] * 5f / unique.params[1].toFloat() + else civInfo.stats.statsForNextTurn[Stat.valueOf(unique.params[2])] * 200f / unique.params[1].toFloat() //the costs of these are probably similar to the baseUnitBuyCost UniqueType.BuyUnitsWithStat, UniqueType.BuyBuildingsWithStat -> if (civInfo.religionManager.religion != null && civInfo.religionManager.religion!!.followerBeliefUniqueMap.getUniques(unique.type).any() ) 0f // This is something completely different from the original, but I have no idea // what happens over there - else civInfo.stats.statsForNextTurn[Stat.valueOf(unique.params[1])] * 10f / civInfo.getEra().baseUnitBuyCost + else civInfo.stats.statsForNextTurn[Stat.valueOf(unique.params[1])] * 200f / civInfo.getEra().baseUnitBuyCost //baseUnitBuyCost is 200 in Standard speed pre-Renaissance UniqueType.BuyUnitsByProductionCost -> - 15f * if (civInfo.wantsToFocusOn(Victory.Focus.Military)) 2f else 1f + 0f //Holy Warriors is a waste if we don't buy units with it, and if we buy units with it'll cost us great persons UniqueType.StatsWhenSpreading -> - unique.params[0].toFloat() / 5f + unique.params[0].toFloat() / 10f UniqueType.StatsWhenAdoptingReligion -> unique.stats.values.sum() / 50f UniqueType.RestingPointOfCityStatesFollowingReligionChange -> if (civInfo.wantsToFocusOn(Victory.Focus.CityStates)) - unique.params[0].toFloat() / 3.5f + unique.params[0].toFloat() / 4f else - unique.params[0].toFloat() / 7f + unique.params[0].toFloat() / 8f UniqueType.StatsFromGlobalCitiesFollowingReligion -> - unique.stats.values.sum() + unique.stats.values.sum() * 2f //free yields that are potentially more than our own number of cities would allow UniqueType.StatsFromGlobalFollowers -> - 4f * (unique.stats.values.sum() / unique.params[1].toFloat()) + 10f * (unique.stats.values.sum() / unique.params[1].toFloat()) UniqueType.Strength -> - unique.params[0].toFloat() / 4f + unique.params[0].toFloat() * 2f//combat strength from beliefs is very strong UniqueType.ReligionSpreadDistance -> (10f + unique.params[0].toFloat()) * goodEarlyModifier UniqueType.NaturalReligionSpreadStrength -> - unique.params[0].toFloat() * goodEarlyModifier / 5f + unique.params[0].toFloat() * goodEarlyModifier / 5f //We should weigh this according to cityFilter; Religious Texts is way stronger than Religious Unity UniqueType.SpreadReligionStrength -> unique.params[0].toFloat() * goodLateModifier / 5f UniqueType.FaithCostOfGreatProphetChange -> - -unique.params[0].toFloat() * goodLateModifier / 2f + -unique.params[0].toFloat() * goodLateModifier / 10f //It's only about 1 more prophet, due to the increasing costs. UniqueType.BuyBuildingsDiscount, UniqueType.BuyUnitsDiscount -> -unique.params[2].toFloat() * goodLateModifier / 5f UniqueType.BuyItemsDiscount -> diff --git a/core/src/com/unciv/logic/automation/unit/CityLocationTileRanker.kt b/core/src/com/unciv/logic/automation/unit/CityLocationTileRanker.kt index 9f95a9ee83..1c771c5c27 100644 --- a/core/src/com/unciv/logic/automation/unit/CityLocationTileRanker.kt +++ b/core/src/com/unciv/logic/automation/unit/CityLocationTileRanker.kt @@ -96,25 +96,30 @@ object CityLocationTileRanker { if (onCoast) tileValue += 3 // Hills are free production and defence if (onHill) tileValue += 7 - // Observatories are good, but current implementation no mod-friendly + // Observatories are good, but current implementation not mod-friendly if (isNextToMountain) tileValue += 5 - if (newCityTile.isAdjacentToRiver()) tileValue += 14 - if (newCityTile.terrainHasUnique(UniqueType.FreshWater)) tileValue += 5 + // This bonus for settling on river is a bit outsized for the importance, but otherwise they have a habit of settling 1 tile away + if (newCityTile.isAdjacentToRiver()) tileValue += 20 // We want to found the city on an oasis because it can't be improved otherwise if (newCityTile.terrainHasUnique(UniqueType.Unbuildable)) tileValue += 3 // If we build the city on a resource tile, then we can't build any special improvements on it if (newCityTile.resource != null) tileValue -= 4 + if (newCityTile.resource != null && newCityTile.tileResource.resourceType == ResourceType.Bonus) tileValue -= 8 + // Settling on bonus resources tends to waste a food + // Settling on luxuries generally speeds up our game, and settling on strategics as well, as the AI cheats and can see them. var tiles = 0 for (i in 0..3) { + //Ideally, we shouldn't really count the center tile, as it's converted into 1 production 2 food anyways with special cases treated above, but doing so can lead to AI moving settler back and forth until forever for (nearbyTile in newCityTile.getTilesAtDistance(i)) { tiles++ - tileValue += rankTile(nearbyTile, civ, onCoast, newUniqueLuxuryResources, baseTileMap, uniqueCache) + tileValue += rankTile(nearbyTile, civ, onCoast, newUniqueLuxuryResources, baseTileMap, uniqueCache) * (3 / (i + 1)) + //Tiles close to the city can be worked more quickly, and thus should gain higher weight. } } // Placing cities on the edge of the map is bad, we can't even build improvements on them! - tileValue -= (HexMath.getNumberOfTilesInHexagon(3) - tiles) * 3 + tileValue -= (HexMath.getNumberOfTilesInHexagon(3) - tiles) * 2.4f return tileValue } @@ -150,7 +155,7 @@ object CityLocationTileRanker { // Don't settle near but not on the coast if (rankTile.isCoastalTile() && !onCoast) locationSpecificTileValue -= 2 // Apply the effect of having a lighthouse, since we can probably assume that we will build it - if (onCoast && rankTile.isOcean) locationSpecificTileValue += 1 + if (onCoast && rankTile.isOcean) locationSpecificTileValue += 1.2f //food is ranked at 1.2 points // Check if there are any new unique luxury resources if (rankTile.resource != null && rankTile.tileResource.resourceType == ResourceType.Luxury && !(civ.hasResource(rankTile.resource!!) || newUniqueLuxuryResources.contains(rankTile.resource))) { @@ -170,9 +175,13 @@ object CityLocationTileRanker { rankTileValue += when (rankTile.tileResource.resourceType) { ResourceType.Bonus -> 2f ResourceType.Strategic -> 1.2f * rankTile.resourceAmount - ResourceType.Luxury -> 5f * rankTile.resourceAmount + ResourceType.Luxury -> 10f * rankTile.resourceAmount //very important for humans who might want to conquer the AI } } + if (rankTile.terrainHasUnique(UniqueType.FreshWater)) rankTileValue += 0.5f + //Taking into account freshwater farm food, maybe less important in baseruleset mods + if (rankTile.terrainFeatures.isNotEmpty() && rankTile.lastTerrain.hasUnique(UniqueType.ProductionBonusWhenRemoved)) rankTileValue += 0.5f + //Taking into account yields from forest chopping if (rankTile.isNaturalWonder()) rankTileValue += 10 diff --git a/core/src/com/unciv/logic/automation/unit/CivilianUnitAutomation.kt b/core/src/com/unciv/logic/automation/unit/CivilianUnitAutomation.kt index da53636294..96f8e08253 100644 --- a/core/src/com/unciv/logic/automation/unit/CivilianUnitAutomation.kt +++ b/core/src/com/unciv/logic/automation/unit/CivilianUnitAutomation.kt @@ -85,6 +85,7 @@ object CivilianUnitAutomation { val hurriedPolicy = UnitActions.invokeUnitAction(unit, UnitActionType.HurryPolicy) if (hurriedPolicy) return + //TODO: save up great scientists/writers for late game (8 turns after research labs/broadcast towers resp.) } // Great merchant -> Conduct trade mission if late game and if not at war. @@ -145,7 +146,7 @@ object CivilianUnitAutomation { private fun isLateGame(civ: Civilization): Boolean { val researchCompletePercent = (civ.tech.researchedTechnologies.size * 1.0f) / civ.gameInfo.ruleset.technologies.size - return researchCompletePercent >= 0.7f + return researchCompletePercent >= 0.6f } /** Returns whether the civilian spends its turn hiding and not moving */