Split off properties of era's to another JSON file (#4336)

* Added Era.json and imported the data to the ruleset

* Units at start are now also determined by eras.json

* Research agreement costs now determined by json file

* Gold and Culture provided at the start are now awarded

* Can no longer build wonders that are more than two eras older than the starting era

* Default population and buildings for settlers can now be added

* Added checks for validity of eras.json file in mods

* Colors for the icons of technologies are no also saved in eras.json

* Removed constants for all era's as they have been generalized away by this PR

* Removed spurios println's

* Added compatibility for mods

* Updated the military unit you get from ruines to be the military unit you received at the start of the game
This commit is contained in:
Xander Lenstra
2021-07-02 14:45:18 +02:00
committed by GitHub
parent d94a1d1432
commit b8d79dc23d
14 changed files with 372 additions and 98 deletions

View File

@ -9,7 +9,7 @@
"policyCostModifier": 0.5,
"unhappinessModifier": 0.4,
"barbarianBonus": 0.75,
"startingUnits": ["Settler", "Warrior"],
"playerBonusStartingUnits": [], // Note that the units from Eras.json are added to this pool. This should only contain bonus starting units.
"aiCityGrowthModifier": 1.6, // that is to say it'll take them 1.6 times as long to grow the city
"aiUnitCostModifier": 1.75,
"aiBuildingCostModifier": 1.6,
@ -17,8 +17,8 @@
"aiBuildingMaintenanceModifier": 1,
"aiUnitMaintenanceModifier": 1,
"aiFreeTechs": [],
"aiMajorCivStartingUnits": ["Settler", "Warrior"],
"aiCityStateStartingUnits": ["Settler", "Warrior"],
"aiMajorCivBonusStartingUnits": [],
"aiCityStateBonusStartingUnits": [],
"aiUnhappinessModifier": 1,
"aisExchangeTechs": false,
"turnBarbariansCanEnterPlayerTiles": 10000,
@ -34,7 +34,7 @@
"policyCostModifier": 0.67,
"unhappinessModifier": 0.6,
"barbarianBonus": 0.5,
"startingUnits": ["Settler", "Warrior"],
"playerBonusStartingUnits": [],
"aiCityGrowthModifier": 1.3,
"aiUnitCostModifier": 1.3,
"aiBuildingCostModifier": 1.3,
@ -42,8 +42,8 @@
"aiBuildingMaintenanceModifier": 1,
"aiUnitMaintenanceModifier": 1,
"aiFreeTechs": [],
"aiMajorCivStartingUnits": ["Settler", "Warrior"],
"aiCityStateStartingUnits": ["Settler", "Warrior"],
"aiMajorCivBonusStartingUnits": [],
"aiCityStateBonusStartingUnits": [],
"aiUnhappinessModifier": 1,
"aisExchangeTechs": false,
"turnBarbariansCanEnterPlayerTiles": 60,
@ -59,7 +59,7 @@
"policyCostModifier": 0.85,
"unhappinessModifier": 0.75,
"barbarianBonus": 0.4,
"startingUnits": ["Settler", "Warrior"],
"playerBonusStartingUnits": [],
"aiCityGrowthModifier": 1.1,
"aiUnitCostModifier": 1.1,
"aiBuildingCostModifier": 1.1,
@ -67,8 +67,8 @@
"aiBuildingMaintenanceModifier": 1,
"aiUnitMaintenanceModifier": 1,
"aiFreeTechs": [],
"aiMajorCivStartingUnits": ["Settler", "Warrior"],
"aiCityStateStartingUnits": ["Settler", "Warrior"],
"aiMajorCivBonusStartingUnits": [],
"aiCityStateBonusStartingUnits": [],
"aiUnhappinessModifier": 1,
"aisExchangeTechs": false,
"turnBarbariansCanEnterPlayerTiles": 20,
@ -84,7 +84,7 @@
"policyCostModifier": 1,
"unhappinessModifier": 1,
"barbarianBonus": 0.33,
"startingUnits": ["Settler", "Warrior"],
"playerBonusStartingUnits": [],
"aiCityGrowthModifier": 1,
"aiUnitCostModifier": 1,
"aiBuildingCostModifier": 1,
@ -92,8 +92,8 @@
"aiBuildingMaintenanceModifier": 1,
"aiUnitMaintenanceModifier": 0.85,
"aiFreeTechs": [],
"aiMajorCivStartingUnits": ["Settler", "Warrior"],
"aiCityStateStartingUnits": ["Settler", "Warrior"],
"aiMajorCivBonusStartingUnits": [],
"aiCityStateBonusStartingUnits": [],
"aiUnhappinessModifier": 1,
"aisExchangeTechs": true,
"turnBarbariansCanEnterPlayerTiles": 0,
@ -109,7 +109,7 @@
"policyCostModifier": 1,
"unhappinessModifier": 1,
"barbarianBonus": 0.25,
"startingUnits": ["Settler", "Warrior"],
"playerBonusStartingUnits": [],
"aiCityGrowthModifier": 0.9,
"aiUnitCostModifier": 0.85,
"aiBuildingCostModifier": 0.85,
@ -117,8 +117,8 @@
"aiBuildingMaintenanceModifier": 0.85,
"aiUnitMaintenanceModifier": 0.8,
"aiFreeTechs": ["Pottery"],
"aiMajorCivStartingUnits": ["Settler", "Warrior", "Warrior"],
"aiCityStateStartingUnits": ["Settler", "Warrior"],
"aiMajorCivBonusStartingUnits": ["Era Starting Unit"],
"aiCityStateBonusStartingUnits": [],
"aiUnhappinessModifier": 0.9,
"aisExchangeTechs": true,
"turnBarbariansCanEnterPlayerTiles": 0,
@ -134,7 +134,7 @@
"policyCostModifier": 1,
"unhappinessModifier": 1,
"barbarianBonus": 0.2,
"startingUnits": ["Settler", "Warrior"],
"playerBonusStartingUnits": [],
"aiCityGrowthModifier": 0.85,
"aiUnitCostModifier": 0.8,
"aiBuildingCostModifier": 0.8,
@ -142,8 +142,8 @@
"aiBuildingMaintenanceModifier": 0.8,
"aiUnitMaintenanceModifier": 0.75,
"aiFreeTechs": ["Pottery","Animal Husbandry"],
"aiMajorCivStartingUnits": ["Settler", "Warrior", "Warrior", "Scout"],
"aiCityStateStartingUnits": ["Settler", "Warrior"],
"aiMajorCivBonusStartingUnits": ["Era Starting Unit", "Scout"],
"aiCityStateBonusStartingUnits": [],
"aiUnhappinessModifier": 0.85,
"aisExchangeTechs": true,
"turnBarbariansCanEnterPlayerTiles": 0,
@ -159,7 +159,7 @@
"policyCostModifier": 1,
"unhappinessModifier": 1,
"barbarianBonus": 0.1,
"startingUnits": ["Settler", "Warrior"],
"playerBonusStartingUnits": [],
"aiCityGrowthModifier": 0.75,
"aiUnitCostModifier": 0.65,
"aiBuildingCostModifier": 0.65,
@ -167,8 +167,8 @@
"aiBuildingMaintenanceModifier": 0.65,
"aiUnitMaintenanceModifier": 0.65,
"aiFreeTechs": ["Pottery","Animal Husbandry","Mining"],
"aiMajorCivStartingUnits": ["Settler", "Warrior", "Warrior", "Warrior", "Worker", "Scout"],
"aiCityStateStartingUnits": ["Settler", "Warrior"],
"aiMajorCivBonusStartingUnits": ["Era Starting Unit", "Era Starting Unit", "Worker", "Scout"],
"aiCityStateBonusStartingUnits": [],
"aiUnhappinessModifier": 0.75,
"aisExchangeTechs": true,
"turnBarbariansCanEnterPlayerTiles": 0,
@ -184,7 +184,7 @@
"policyCostModifier": 1,
"unhappinessModifier": 1,
"barbarianBonus": 0,
"startingUnits": ["Settler", "Warrior"],
"playerBonusStartingUnits": [],
"aiCityGrowthModifier": 0.6,
"aiUnitCostModifier": 0.5,
"aiBuildingCostModifier": 0.5,
@ -192,8 +192,8 @@
"aiBuildingMaintenanceModifier": 0.5,
"aiUnitMaintenanceModifier": 0.5,
"aiFreeTechs": ["Pottery","Animal Husbandry","Mining","The Wheel"],
"aiMajorCivStartingUnits": ["Settler", "Warrior", "Settler", "Warrior", "Warrior", "Worker", "Worker", "Scout"],
"aiCityStateStartingUnits": ["Settler", "Warrior"],
"aiMajorCivBonusStartingUnits": ["Settler", "Era Starting Unit", "Era Starting Unit", "Era Starting Unit", "Worker", "Scout"],
"aiCityStateBonusStartingUnits": [],
"aiUnhappinessModifier": 0.6,
"aisExchangeTechs": true,
"turnBarbariansCanEnterPlayerTiles": 0,

View File

@ -0,0 +1,150 @@
[
// Source: the fandom, so take these with a grain of salt
// I also did some testing in the official version, but by far not all
{
"name": "Ancient era",
"researchAgreementCost": 150,
"startingSettlerCount": 1, // These values should not include the values given in Difficulties.json
"startingWorkerCount": 0,
"startingMilitaryUnitCount": 1,
"startingMilitaryUnit": "Warrior",
"settlerPopulation": 1,
"iconRGB": [255, 87, 35]
},
{
"name": "Classical era",
"researchAgreementCost": 150,
"startingSettlerCount": 1,
"startingWorkerCount": 0,
"startingMilitaryUnitCount": 2,
"startingMilitaryUnit": "Spearman",
"startingGold": 10,
"startingCulture": 100,
"settlerPopulation": 1,
"iconRGB": [233, 31, 99]
},
{
"name": "Medieval era",
"researchAgreementCost": 250,
"startingSettlerCount": 2,
"startingWorkerCount": 1,
"startingMilitaryUnitCount": 3,
"startingMilitaryUnit": "Spearman",
"startingGold": 25,
"startingCulture": 200,
"settlerPopulation": 1,
"settlerBuildings": ["Shrine","Monument"],
"startingObsoleteWonders": ["Temple of Artemis", "Stonehenge", "The Great Library", "Mausoleum of Halicarnassus", "The Pyramids", "Statue of Zeus"],
"iconRGB": [157, 39, 176]
},
{
"name": "Renaissance era",
"researchAgreementCost": 250,
"startingSettlerCount": 2,
"startingWorkerCount": 1,
"startingMilitaryUnitCount": 3,
"startingMilitaryUnit": "Pikeman",
"startingGold": 50,
"startingCulture": 300,
"settlerPopulation": 2,
"settlerBuildings": ["Shrine","Monument","Granary","Lighthouse"],
"startingObsoleteWonders": ["Temple of Artemis", "Stonehenge", "The Great Library", "Mausoleum of Halicarnassus", "The Pyramids", "Statue of Zeus",
"The Great Lighthouse", "Hanging Gardens", "Terracotta Army", "The Oracle", "Petra", "Great Wall", "Colossus"],
"iconRGB": [104, 58, 183]
},
{
"name": "Industrial era",
"researchAgreementCost": 300,
"startingSettlerCount": 3,
"startingWorkerCount": 2,
"startingMilitaryUnitCount": 5,
"startingMilitaryUnit": "Musketman",
"startingGold": 100,
"startingCulture": 400,
"settlerPopulation": 3,
"settlerBuildings": ["Monument","Granary","Lighthouse","Market","Workshop","Amphitheater","Barracks","Library","Colosseum"],
"startingObsoleteWonders": ["Temple of Artemis", "Stonehenge", "The Great Library", "Mausoleum of Halicarnassus", "The Pyramids", "Statue of Zeus",
"The Great Lighthouse", "Hanging Gardens", "Terracotta Army", "The Oracle", "Petra", "Great Wall", "Colossus",
"Hagia Sophia", "Chichen Itza", "Machu Picchu", "Angkor Wat", "Alhambra", "Notre Dame"],
"iconRGB": [63, 81, 182]
},
{
"name": "Modern era",
"researchAgreementCost": 350,
"startingSettlerCount": 3,
"startingWorkerCount": 2,
"startingMilitaryUnitCount": 5,
"startingMilitaryUnit": "Rifleman",
"startingGold": 200,
"startingCulture": 500,
"settlerPopulation": 3,
"settlerBuildings": ["Monument","Granary","Lighthouse","Market","Workshop","Amphitheater","Barracks","Library","Colosseum"],
"startingObsoleteWonders": ["Temple of Artemis", "Stonehenge", "The Great Library", "Mausoleum of Halicarnassus", "The Pyramids", "Statue of Zeus",
"The Great Lighthouse", "Hanging Gardens", "Terracotta Army", "The Oracle", "Petra", "Great Wall", "Colossus",
"Hagia Sophia", "Chichen Itza", "Machu Picchu", "Angkor Wat", "Alhambra", "Notre Dame",
"Sistine Chapel", "Forbidden Palace", "Leaning Tower of Pisa", "Himeji Castle", "Taj Mahal", "Porcelain Tower", "Kremlin"],
"iconRGB": [33, 150, 243]
},
{
"name": "Atomic era",
"researchAgreementCost": 400,
"startingSettlerCount": 3,
"startingWorkerCount": 2,
"startingMilitaryUnitCount": 5,
"startingMilitaryUnit": "Infantry",
"startingGold": 200,
"startingCulture": 500,
"settlerPopulation": 4,
"settlerBuildings": ["Monument","Granary","Lighthouse","Market","Workshop","Amphitheater","Barracks","Library","Colosseum"],
"startingObsoleteWonders": ["Temple of Artemis", "Stonehenge", "The Great Library", "Mausoleum of Halicarnassus", "The Pyramids", "Statue of Zeus",
"The Great Lighthouse", "Hanging Gardens", "Terracotta Army", "The Oracle", "Petra", "Great Wall", "Colossus",
"Hagia Sophia", "Chichen Itza", "Machu Picchu", "Angkor Wat", "Alhambra", "Notre Dame",
"Sistine Chapel", "Forbidden Palace", "Leaning Tower of Pisa", "Himeji Castle", "Taj Mahal", "Porcelain Tower", "Kremlin",
"The Louvre", "Big Ben", "Brandenburg Gate"],
"iconRGB": [0, 150, 136]
},
{
"name": "Information era",
"researchAgreementCost": 400,
"startingSettlerCount": 3,
"startingWorkerCount": 3,
"startingMilitaryUnitCount": 5,
"startingMilitaryUnit": "Marine",
"startingGold": 400,
"startingCulture": 600,
"settlerPopulation": 5,
"settlerBuildings": ["Monument","Granary","Lighthouse","Market","Workshop","Amphitheater","Barracks","Library","Colosseum","Theatre","Bank"],
"startingObsoleteWonders": ["Temple of Artemis", "Stonehenge", "The Great Library", "Mausoleum of Halicarnassus", "The Pyramids", "Statue of Zeus",
"The Great Lighthouse", "Hanging Gardens", "Terracotta Army", "The Oracle", "Petra", "Great Wall", "Colossus",
"Hagia Sophia", "Chichen Itza", "Machu Picchu", "Angkor Wat", "Alhambra", "Notre Dame",
"Sistine Chapel", "Forbidden Palace", "Leaning Tower of Pisa", "Himeji Castle", "Taj Mahal", "Porcelain Tower", "Kremlin",
"The Louvre", "Big Ben", "Brandenburg Gate",
"Eiffel Tower", "Statue of Liberty", "Neuschwanstein", "Cristo Redentor"],
// So theoretically this is always just all the wonders at least 2 eras old. So we could just use that.
// But where is the modularity? The excluding of very specific wonders? That is no fun.
// So we just write down the entire long list (sorted by era!) instead.
"iconRGB": [76, 176, 81]
},
{ // Technically, this Era doesn't exist in the original game.
// But as it is _really_ usefull to have for testing, I'd like to keep it.
// The stats are just copy-pasted from the information era.
"name": "Future era",
"researchAgreementCost": 400,
"startingSettlerCount": 3,
"startingWorkerCount": 3,
"startingMilitaryUnitCount": 5,
"startingMilitaryUnit": "Marine",
"startingGold": 400,
"startingCulture": 600,
"settlerPopulation": 5,
"settlerBuildings": ["Monument","Granary","Lighthouse","Market","Workshop","Amphitheater","Barracks","Library","Colosseum","Theatre","Bank"],
"startingObsoleteWonders": ["Temple of Artemis", "Stonehenge", "The Great Library", "Mausoleum of Halicarnassus", "The Pyramids", "Statue of Zeus",
"The Great Lighthouse", "Hanging Gardens", "Terracotta Army", "The Oracle", "Petra", "Great Wall", "Colossus",
"Hagia Sophia", "Chichen Itza", "Machu Picchu", "Angkor Wat", "Alhambra", "Notre Dame",
"Sistine Chapel", "Forbidden Palace", "Leaning Tower of Pisa", "Himeji Castle", "Taj Mahal", "Porcelain Tower", "Kremlin",
"The Louvre", "Big Ben", "Brandenburg Gate",
"Eiffel Tower", "Statue of Liberty", "Neuschwanstein", "Cristo Redentor"],
"iconRGB": [76, 176, 81]
}
]

View File

@ -8,6 +8,7 @@ object Constants {
//
const val settler = "Settler"
const val settlerUnique = "Founds a new city"
const val eraSpecificUnit = "Era Starting Unit"
const val impassable = "Impassable"
const val ocean = "Ocean"
@ -71,14 +72,6 @@ object Constants {
const val disabled = "disabled"
const val enabled = "enabled"
const val ancientEra = "Ancient era"
const val classicalEra = "Classical era"
const val medievalEra = "Medieval era"
const val renaissanceEra = "Renaissance era"
const val industrialEra = "Industrial era"
const val modernEra = "Modern era"
const val informationEra = "Information era"
const val futureEra = "Future era"
const val barbarians = "Barbarians"
const val spectator = "Spectator"
const val custom = "Custom"

View File

@ -8,10 +8,12 @@ import com.unciv.logic.map.TileInfo
import com.unciv.logic.map.TileMap
import com.unciv.logic.map.mapgenerator.MapGenerator
import com.unciv.models.metadata.GameParameters
import com.unciv.models.ruleset.Era
import com.unciv.models.ruleset.Ruleset
import com.unciv.models.ruleset.RulesetCache
import com.unciv.ui.newgamescreen.GameSetupInfo
import java.util.*
import kotlin.NoSuchElementException
import kotlin.collections.ArrayList
import kotlin.math.max
@ -53,6 +55,8 @@ object GameStarter {
addCivTechs(gameInfo, ruleset, gameSetupInfo)
addCivStats(gameInfo)
// and only now do we add units for everyone, because otherwise both the gameInfo.setTransients() and the placeUnit will both add the unit to the civ's unit list!
addCivStartingUnits(gameInfo)
@ -111,6 +115,21 @@ object GameStarter {
}
}
private fun addCivStats(gameInfo: GameInfo) {
val ruleSet = gameInfo.ruleSet
val startingEra = gameInfo.gameParameters.startingEra
val era =
if (startingEra in ruleSet.eras.keys) {
ruleSet.eras[startingEra]!!
} else {
Era()
}
for (civInfo in gameInfo.civilizations.filter { !it.isBarbarian() }) {
civInfo.addGold((era.startingGold * gameInfo.gameParameters.gameSpeed.modifier).toInt())
civInfo.policies.addCulture((era.startingCulture * gameInfo.gameParameters.gameSpeed.modifier).toInt())
}
}
private fun addCivilizations(newGameParameters: GameParameters, gameInfo: GameInfo, ruleset: Ruleset) {
val availableCivNames = Stack<String>()
// CityState or Spectator civs are not available for Random pick
@ -161,15 +180,7 @@ object GameStarter {
gameInfo.civilizations.filter { !it.isBarbarian() },
gameInfo.tileMap)
// For later starting eras, or for civs like Polynesia with a different Warrior, we need different starting units
fun getWarriorEquivalent(civ: CivilizationInfo): String? {
val availableMilitaryUnits = gameInfo.ruleSet.units.values.filter {
it.isBuildable(civ)
&& it.unitType.isLandUnit()
&& !it.unitType.isCivilian()
}
return availableMilitaryUnits.maxByOrNull { max(it.strength, it.rangedStrength) }?.name
}
// no starting units for Barbarians and Spectators
for (civ in gameInfo.civilizations.filter { !it.isBarbarian() && !it.isSpectator() }) {
val startingLocation = startingLocations[civ]!!
@ -181,15 +192,75 @@ object GameStarter {
civ.placeUnitNearTile(startingLocation.position, unitName)
}
val warriorEquivalent = getWarriorEquivalent(civ)
val startingUnits = when {
civ.isPlayerCivilization() -> gameInfo.getDifficulty().startingUnits
civ.isMajorCiv() -> gameInfo.getDifficulty().aiMajorCivStartingUnits
else -> gameInfo.getDifficulty().aiCityStateStartingUnits
// Determine starting units based on starting era
val ruleSet = gameInfo.ruleSet
val startingEra = gameInfo.gameParameters.startingEra
var startingUnits: MutableList<String>
var eraUnitReplacement: String
if (ruleSet.eras.isEmpty()) { // We are using an older mod, so we only look at the difficulty file
startingUnits = (when {
civ.isPlayerCivilization() -> gameInfo.getDifficulty().startingUnits
civ.isMajorCiv() -> gameInfo.getDifficulty().aiMajorCivStartingUnits
else -> gameInfo.getDifficulty().aiCityStateStartingUnits
}).toMutableList()
val warriorEquivalent = ruleSet.units
.filter { it.value.unitType.isLandUnit() && it.value.unitType.isMilitary() && it.value.isBuildable(civ) }
.maxByOrNull {max(it.value.strength, it.value.rangedStrength)}
?.key
for (unit in startingUnits) {
val unitToAdd = if (unit == "Warrior") warriorEquivalent else unit
if (unitToAdd != null) placeNearStartingPosition(unitToAdd)
}
continue
}
if (startingEra in ruleSet.eras.keys) {
startingUnits = ruleSet.eras[startingEra]!!.getStartingUnits().toMutableList()
eraUnitReplacement = ruleSet.eras[startingEra]!!.startingMilitaryUnit
} else {
startingUnits = Era().getStartingUnits().toMutableList()
eraUnitReplacement = Era().startingMilitaryUnit
}
// Add extra units granted by difficulty
startingUnits.addAll(when {
civ.isPlayerCivilization() -> gameInfo.getDifficulty().playerBonusStartingUnits
civ.isMajorCiv() -> gameInfo.getDifficulty().aiMajorCivBonusStartingUnits
else -> gameInfo.getDifficulty().aiCityStateBonusStartingUnits
})
fun getEquivalentUnit(civ: CivilizationInfo, unitParam: String): String? {
var unit = unitParam // We want to change it and this is the easiest way to do so
if (unit == Constants.eraSpecificUnit) unit = eraUnitReplacement
if (unit == "Settler" && "Settler" !in ruleSet.units) {
val settlerLikeUnits = ruleSet.units.filter {
it.value.uniqueObjects.any { it.placeholderText == Constants.settlerUnique }
&& it.value.isBuildable(civ)
&& it.value.unitType.isCivilian()
}
if (settlerLikeUnits.isEmpty()) return null // No settlers in this mod
return civ.getEquivalentUnit(settlerLikeUnits.keys.random()).name
}
if (unit == "Worker" && "Worker" !in ruleSet.units) {
val workerLikeUnits = ruleSet.units.filter {
it.value.uniqueObjects.any { it.placeholderText == Constants.canBuildImprovements }
&& it.value.isBuildable(civ)
&& it.value.unitType.isCivilian()
}
if (workerLikeUnits.isEmpty()) return null // No workers in this mod
return civ.getEquivalentUnit(workerLikeUnits.keys.random()).name
}
return civ.getEquivalentUnit(unit).name
}
for (unit in startingUnits) {
val unitToAdd = if (unit == "Warrior") warriorEquivalent else unit
val unitToAdd = getEquivalentUnit(civ, unit)
if (unitToAdd != null) placeNearStartingPosition(unitToAdd)
}
}

View File

@ -97,6 +97,18 @@ class CityInfo {
}
}
// Add buildings and pop we get from starting in this era
val ruleset = civInfo.gameInfo.ruleSet
val startingEra = civInfo.gameInfo.gameParameters.startingEra
if (startingEra in ruleset.eras) {
population.setPopulation(ruleset.eras[startingEra]!!.settlerPopulation)
for (building in ruleset.eras[startingEra]!!.settlerBuildings) {
if (ruleset.buildings[building]!!.isBuildable(cityConstructions)) {
cityConstructions.addBuilding(building)
}
}
}
expansion.reset()

View File

@ -717,14 +717,8 @@ class CivilizationInfo {
fun getResearchAgreementCost(): Int {
// https://forums.civfanatics.com/resources/research-agreements-bnw.25568/
val basicGoldCostOfSignResearchAgreement = when (getEra()) {
Constants.medievalEra, Constants.renaissanceEra -> 250
Constants.industrialEra -> 300
Constants.modernEra -> 350
Constants.informationEra, Constants.futureEra -> 400
else -> 0
}
return (basicGoldCostOfSignResearchAgreement * gameInfo.gameParameters.gameSpeed.modifier).toInt()
val era = if (getEra() in gameInfo.ruleSet.eras) gameInfo.ruleSet.eras[getEra()]!! else Era()
return (era.researchAgreementCost * gameInfo.gameParameters.gameSpeed.modifier).toInt()
}
fun gainMilitaryUnitFromCityState(otherCiv: CivilizationInfo) {

View File

@ -749,15 +749,15 @@ class MapUnit {
)
}
val researchableAncientEraTechs = tile.tileMap.gameInfo.ruleSet.technologies.values
val researchableFirstEraTechs = tile.tileMap.gameInfo.ruleSet.technologies.values
.filter {
!civInfo.tech.isResearched(it.name)
&& civInfo.tech.canBeResearched(it.name)
&& it.era() == Constants.ancientEra
&& civInfo.gameInfo.ruleSet.getEraNumber(it.era()) == 1
}
if (researchableAncientEraTechs.isNotEmpty())
if (researchableFirstEraTechs.isNotEmpty())
actions.add {
val tech = researchableAncientEraTechs.random(tileBasedRandom).name
val tech = researchableFirstEraTechs.random(tileBasedRandom).name
civInfo.tech.addTechnology(tech)
civInfo.addNotification(
"We have discovered the lost technology of [$tech] in the ruins!",
@ -767,10 +767,13 @@ class MapUnit {
)
}
val militaryUnit =
if (civInfo.gameInfo.gameParameters.startingEra !in civInfo.gameInfo.ruleSet.eras) "Warrior"
else civInfo.gameInfo.ruleSet.eras[civInfo.gameInfo.gameParameters.startingEra]!!.startingMilitaryUnit
val possibleUnits = (
//City-States and OCC don't get settler from ruins
listOf(Constants.settler).filterNot { civInfo.isCityState() || civInfo.isOneCityChallenger() }
+ listOf(Constants.worker, "Warrior")
+ listOf(Constants.worker, militaryUnit)
).filter { civInfo.gameInfo.ruleSet.units.containsKey(it) }
if (possibleUnits.isNotEmpty())
actions.add {

View File

@ -24,7 +24,7 @@ class GameParameters { // Default values are the default new game
var religionEnabled = false
var victoryTypes: ArrayList<VictoryType> = arrayListOf(VictoryType.Cultural, VictoryType.Domination, VictoryType.Scientific) // By default, all victory types
var startingEra = Constants.ancientEra
var startingEra = "Ancient Era"
var isOnlineMultiplayer = false
var baseRuleset: BaseRuleset = BaseRuleset.Civ_V_Vanilla

View File

@ -309,6 +309,11 @@ class Building : NamedStats(), IConstruction {
if (civInfo.isCityState())
return "No world wonders for city-states"
val ruleSet = civInfo.gameInfo.ruleSet
val startingEra = civInfo.gameInfo.gameParameters.startingEra
if (startingEra in ruleSet.eras && name in ruleSet.eras[startingEra]!!.startingObsoleteWonders)
return "Wonder is disabled when starting in this era"
}

View File

@ -14,7 +14,8 @@ class Difficulty: INamed {
var policyCostModifier:Float = 1f
var unhappinessModifier:Float = 1f
var barbarianBonus:Float = 0f
var startingUnits = ArrayList<String>()
var startingUnits = ArrayList<String>() // Deprecated since 3.15.8
var playerBonusStartingUnits = ArrayList<String>()
var aiCityGrowthModifier:Float = 1f
var aiUnitCostModifier:Float = 1f
@ -23,22 +24,14 @@ class Difficulty: INamed {
var aiBuildingMaintenanceModifier:Float = 1f
var aiUnitMaintenanceModifier = 1f
var aiFreeTechs = ArrayList<String>()
var aiMajorCivStartingUnits = ArrayList<String>()
var aiCityStateStartingUnits = ArrayList<String>()
var aiMajorCivStartingUnits = ArrayList<String>() // Deprecated since 3.15.8
var aiMajorCivBonusStartingUnits = ArrayList<String>()
var aiCityStateStartingUnits = ArrayList<String>() // Deprecated since 3.15.8
var aiCityStateBonusStartingUnits = ArrayList<String>()
var aiUnhappinessModifier = 1f
var turnBarbariansCanEnterPlayerTiles = 0
var clearBarbarianCampReward = 25
init {
// For compatibility with old mods that use deprecated var aiFreeUnits and do not have startingUnits, aiCityStateStartingUnits, aiMajorCivStartingUnits
if (startingUnits.isEmpty()) {
startingUnits.add(Constants.settler)
startingUnits.add("Warrior")
aiCityStateStartingUnits.addAll(startingUnits)
aiMajorCivStartingUnits.addAll(startingUnits)
}
}
fun getDescription(): String {
val lines = ArrayList<String>()
lines += "Player settings"

View File

@ -0,0 +1,35 @@
package com.unciv.models.ruleset
import com.badlogic.gdx.graphics.Color
import com.unciv.models.stats.INamed
import com.unciv.ui.utils.colorFromRGB
class Era : INamed {
override var name: String = ""
var researchAgreementCost = 300
var startingSettlerCount = 1
var startingSettlerUnit = "Settler" // For mods which have differently named settlers
var startingWorkerCount = 0
var startingWorkerUnit = "Worker"
var startingMilitaryUnitCount = 1
var startingMilitaryUnit = "Warrior"
var startingGold = 0
var startingCulture = 0
var settlerPopulation = 1
var settlerBuildings = ArrayList<String>()
var startingObsoleteWonders = ArrayList<String>()
var iconRGB: List<Int>? = null
fun getStartingUnits(): List<String> {
val startingUnits = mutableListOf<String>()
repeat(startingSettlerCount) {startingUnits.add(startingSettlerUnit)}
repeat(startingWorkerCount) {startingUnits.add(startingWorkerUnit)}
repeat(startingMilitaryUnitCount) {startingUnits.add(startingMilitaryUnit)}
return startingUnits
}
fun getColor(): Color {
if (iconRGB == null) return Color.WHITE.cpy()
return colorFromRGB(iconRGB!![0], iconRGB!![1], iconRGB!![2])
}
}

View File

@ -50,6 +50,7 @@ class Ruleset {
val tileResources = LinkedHashMap<String, TileResource>()
val tileImprovements = LinkedHashMap<String, TileImprovement>()
val technologies = LinkedHashMap<String, Technology>()
val eras = LinkedHashMap<String, Era>()
val units = LinkedHashMap<String, BaseUnit>()
val unitPromotions = LinkedHashMap<String, Promotion>()
val nations = LinkedHashMap<String, Nation>()
@ -79,6 +80,7 @@ class Ruleset {
buildings.putAll(ruleset.buildings)
for (buildingToRemove in ruleset.modOptions.buildingsToRemove) buildings.remove(buildingToRemove)
difficulties.putAll(ruleset.difficulties)
eras.putAll(ruleset.eras)
nations.putAll(ruleset.nations)
policyBranches.putAll(ruleset.policyBranches)
policies.putAll(ruleset.policies)
@ -99,22 +101,22 @@ class Ruleset {
}
fun clear() {
beliefs.clear()
buildings.clear()
difficulties.clear()
nations.clear()
eras.clear()
policyBranches.clear()
specialists.clear()
mods.clear()
nations.clear()
policies.clear()
beliefs.clear()
quests.clear()
technologies.clear()
buildings.clear()
terrains.clear()
tileImprovements.clear()
tileResources.clear()
unitPromotions.clear()
specialists.clear()
units.clear()
mods.clear()
modWithReligionLoaded = false
}
@ -153,6 +155,8 @@ class Ruleset {
val improvementsFile = folderHandle.child("TileImprovements.json")
if (improvementsFile.exists()) tileImprovements += createHashmap(jsonParser.getFromJson(Array<TileImprovement>::class.java, improvementsFile))
val erasFile = folderHandle.child("Eras.json")
if (erasFile.exists()) eras += createHashmap(jsonParser.getFromJson(Array<Era>::class.java, erasFile))
val unitsFile = folderHandle.child("Units.json")
if (unitsFile.exists()) units += createHashmap(jsonParser.getFromJson(Array<BaseUnit>::class.java, unitsFile))
@ -371,8 +375,27 @@ class Ruleset {
warningCount++
}
}
// eras.isNotEmpty() is only for mod compatibility, it should be removed at some point.
if (eras.isNotEmpty() && tech.era() !in eras)
lines += "Unknown era ${tech.era()} referenced in column of tech ${tech.name}"
}
for (era in eras) {
for (wonder in era.value.startingObsoleteWonders)
if (wonder !in buildings)
lines += "Nonexistent wonder ${wonder} obsoleted when starting in ${era.key}!"
for (building in era.value.settlerBuildings)
if (building !in buildings)
lines += "Nonexistent building ${building} built by settlers when starting in ${era.key}"
if (era.value.startingMilitaryUnit !in units)
lines += "Nonexistent unit ${era.value.startingMilitaryUnit} marked as starting unit when starting in ${era.key}"
if (era.value.researchAgreementCost < 0 || era.value.startingSettlerCount < 0 || era.value.startingWorkerCount < 0 || era.value.startingMilitaryUnitCount < 0 || era.value.startingGold < 0 || era.value.startingCulture < 0)
lines += "Unexpected negative number found while parsing era ${era.key}"
if (era.value.settlerPopulation <= 0)
lines += "Population in cities from settlers must be strictly positive! Found value ${era.value.settlerPopulation} for era ${era.key}"
}
return CheckModLinksResult(warningCount, lines)
}
}

View File

@ -137,6 +137,7 @@ class GameOptionsTable(val previousScreen: IPreviousScreen, val updatePlayerPick
private fun Table.addEraSelectBox() {
if (ruleset.technologies.isEmpty()) return // mod with no techs
// Should eventually be changed to use eras.json, but we'll keep it like this for now for mod compatibility
val eras = ruleset.technologies.values.filter { !it.uniques.contains("Starting tech") }.map { it.era() }.distinct()
addSelectBox("{Starting Era}:", eras, gameParameters.startingEra)
{ gameParameters.startingEra = it }

View File

@ -16,6 +16,7 @@ import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable
import com.badlogic.gdx.utils.Align
import com.unciv.Constants
import com.unciv.UncivGame
import com.unciv.models.ruleset.Era
import com.unciv.models.ruleset.Nation
import com.unciv.models.ruleset.Ruleset
import com.unciv.models.ruleset.tile.ResourceType
@ -319,17 +320,10 @@ object ImageGetter {
fun getTechIconGroup(techName: String, circleSize: Float) = getTechIcon(techName).surroundWithCircle(circleSize)
fun getTechIcon(techName: String): Image {
val techIconColor = when (ruleset.technologies[techName]!!.era()) {
Constants.ancientEra -> colorFromRGB(255, 87, 35)
Constants.classicalEra -> colorFromRGB(233, 31, 99)
Constants.medievalEra -> colorFromRGB(157, 39, 176)
Constants.renaissanceEra -> colorFromRGB(104, 58, 183)
Constants.industrialEra -> colorFromRGB(63, 81, 182)
Constants.modernEra -> colorFromRGB(33, 150, 243)
Constants.informationEra -> colorFromRGB(0, 150, 136)
Constants.futureEra -> colorFromRGB(76, 176, 81)
else -> Color.WHITE.cpy()
}
val era = ruleset.technologies[techName]!!.era()
val techIconColor =
if (era !in ruleset.eras) Era().getColor()
else ruleset.eras[ruleset.technologies[techName]!!.era()]!!.getColor()
return getImage("TechIcons/$techName").apply { color = techIconColor.lerp(Color.BLACK, 0.6f) }
}