mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-13 09:18:43 +07:00
Fix a few now flagged but working Unique targets (#9845)
* Allow a few working UniqueType-UniqueTarget combos in RulesetValidator * RulesetValidator checks GlobalUniques * Alternate Settlers and Workers recognized via Unique - same rules in RulesetValidator and GameStarter, wiki clarification
This commit is contained in:
@ -435,7 +435,7 @@ object GameStarter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun getStartingUnitsForEraAndDifficulty(civ: Civilization, gameInfo: GameInfo, ruleset: Ruleset, startingEra: String): MutableList<String> {
|
private fun getStartingUnitsForEraAndDifficulty(civ: Civilization, gameInfo: GameInfo, ruleset: Ruleset, startingEra: String): MutableList<String> {
|
||||||
val startingUnits = ruleset.eras[startingEra]!!.getStartingUnits().toMutableList()
|
val startingUnits = ruleset.eras[startingEra]!!.getStartingUnits(ruleset)
|
||||||
|
|
||||||
// Add extra units granted by difficulty
|
// Add extra units granted by difficulty
|
||||||
startingUnits.addAll(when {
|
startingUnits.addAll(when {
|
||||||
|
@ -40,6 +40,8 @@ class RulesetValidator(val ruleset: Ruleset) {
|
|||||||
val rulesetInvariant = UniqueType.UniqueComplianceErrorSeverity.RulesetInvariant
|
val rulesetInvariant = UniqueType.UniqueComplianceErrorSeverity.RulesetInvariant
|
||||||
val rulesetSpecific = UniqueType.UniqueComplianceErrorSeverity.RulesetSpecific
|
val rulesetSpecific = UniqueType.UniqueComplianceErrorSeverity.RulesetSpecific
|
||||||
|
|
||||||
|
checkUniques(ruleset.globalUniques, lines, rulesetInvariant, tryFixUnknownUniques)
|
||||||
|
|
||||||
for (unit in ruleset.units.values) {
|
for (unit in ruleset.units.values) {
|
||||||
if (unit.upgradesTo == unit.name || (unit.upgradesTo != null && unit.upgradesTo == unit.replaces))
|
if (unit.upgradesTo == unit.name || (unit.upgradesTo != null && unit.upgradesTo == unit.replaces))
|
||||||
lines += "${unit.name} upgrades to itself!"
|
lines += "${unit.name} upgrades to itself!"
|
||||||
@ -145,6 +147,7 @@ class RulesetValidator(val ruleset: Ruleset) {
|
|||||||
|
|
||||||
val vanillaRuleset = RulesetCache.getVanillaRuleset() // for UnitTypes fallback
|
val vanillaRuleset = RulesetCache.getVanillaRuleset() // for UnitTypes fallback
|
||||||
|
|
||||||
|
checkUniques(ruleset.globalUniques, lines, rulesetSpecific, tryFixUnknownUniques)
|
||||||
|
|
||||||
if (ruleset.units.values.none { it.hasUnique(UniqueType.FoundCity, StateForConditionals.IgnoreConditionals) })
|
if (ruleset.units.values.none { it.hasUnique(UniqueType.FoundCity, StateForConditionals.IgnoreConditionals) })
|
||||||
lines += "No city-founding units in ruleset!"
|
lines += "No city-founding units in ruleset!"
|
||||||
@ -313,13 +316,14 @@ class RulesetValidator(val ruleset: Ruleset) {
|
|||||||
if (building !in ruleset.buildings)
|
if (building !in ruleset.buildings)
|
||||||
lines += "Nonexistent building $building built by settlers when starting in ${era.name}"
|
lines += "Nonexistent building $building built by settlers when starting in ${era.name}"
|
||||||
// todo the whole 'starting unit' thing needs to be redone, there's no reason we can't have a single list containing all the starting units.
|
// todo the whole 'starting unit' thing needs to be redone, there's no reason we can't have a single list containing all the starting units.
|
||||||
if (era.startingSettlerUnit !in ruleset.units && (era.startingSettlerUnit!= Constants.settler || ruleset.units.values.none { it.hasUnique(
|
if (era.startingSettlerUnit !in ruleset.units
|
||||||
UniqueType.FoundCity) }))
|
&& ruleset.units.values.none { it.hasUnique(UniqueType.FoundCity) })
|
||||||
lines += "Nonexistent unit ${era.startingSettlerUnit} marked as starting unit when starting in ${era.name}"
|
lines += "Nonexistent unit ${era.startingSettlerUnit} marked as starting unit when starting in ${era.name}"
|
||||||
if (era.startingWorkerCount!=0 && era.startingWorkerUnit !in ruleset.units)
|
if (era.startingWorkerCount != 0 && era.startingWorkerUnit !in ruleset.units
|
||||||
|
&& ruleset.units.values.none { it.hasUnique(UniqueType.BuildImprovements) })
|
||||||
lines += "Nonexistent unit ${era.startingWorkerUnit} marked as starting unit when starting in ${era.name}"
|
lines += "Nonexistent unit ${era.startingWorkerUnit} marked as starting unit when starting in ${era.name}"
|
||||||
|
|
||||||
if ((era.startingMilitaryUnitCount !=0 || allDifficultiesStartingUnits.contains(
|
if ((era.startingMilitaryUnitCount != 0 || allDifficultiesStartingUnits.contains(
|
||||||
Constants.eraSpecificUnit)) && era.startingMilitaryUnit !in ruleset.units)
|
Constants.eraSpecificUnit)) && era.startingMilitaryUnit !in ruleset.units)
|
||||||
lines += "Nonexistent unit ${era.startingMilitaryUnit} marked as starting unit when starting in ${era.name}"
|
lines += "Nonexistent unit ${era.startingMilitaryUnit} marked as starting unit when starting in ${era.name}"
|
||||||
if (era.researchAgreementCost < 0 || era.startingSettlerCount < 0 || era.startingWorkerCount < 0 || era.startingMilitaryUnitCount < 0 || era.startingGold < 0 || era.startingCulture < 0)
|
if (era.researchAgreementCost < 0 || era.startingSettlerCount < 0 || era.startingWorkerCount < 0 || era.startingMilitaryUnitCount < 0 || era.startingGold < 0 || era.startingCulture < 0)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.unciv.models.ruleset.tech
|
package com.unciv.models.ruleset.tech
|
||||||
|
|
||||||
import com.badlogic.gdx.graphics.Color
|
import com.badlogic.gdx.graphics.Color
|
||||||
|
import com.unciv.logic.UncivShowableException
|
||||||
import com.unciv.models.ruleset.IRulesetObject
|
import com.unciv.models.ruleset.IRulesetObject
|
||||||
import com.unciv.models.ruleset.Ruleset
|
import com.unciv.models.ruleset.Ruleset
|
||||||
import com.unciv.models.ruleset.RulesetObject
|
import com.unciv.models.ruleset.RulesetObject
|
||||||
@ -82,10 +83,22 @@ class Era : RulesetObject() {
|
|||||||
}.map { it.first }.distinct()
|
}.map { it.first }.distinct()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getStartingUnits(): List<String> {
|
fun getStartingUnits(ruleset: Ruleset): MutableList<String> {
|
||||||
val startingUnits = mutableListOf<String>()
|
val startingUnits = mutableListOf<String>()
|
||||||
repeat(startingSettlerCount) { startingUnits.add(startingSettlerUnit) }
|
val startingSettlerName: String =
|
||||||
repeat(startingWorkerCount) { startingUnits.add(startingWorkerUnit) }
|
if (startingSettlerUnit in ruleset.units) startingSettlerUnit
|
||||||
|
else ruleset.units.values
|
||||||
|
.firstOrNull { it.hasUnique(UniqueType.FoundCity) }
|
||||||
|
?.name
|
||||||
|
?: throw UncivShowableException("No Settler unit found for era $name")
|
||||||
|
val startingWorkerName: String =
|
||||||
|
if (startingWorkerCount == 0 || startingWorkerUnit in ruleset.units) startingWorkerUnit
|
||||||
|
else ruleset.units.values
|
||||||
|
.firstOrNull { it.hasUnique(UniqueType.BuildImprovements) }
|
||||||
|
?.name
|
||||||
|
?: throw UncivShowableException("No Worker unit found for era $name")
|
||||||
|
repeat(startingSettlerCount) { startingUnits.add(startingSettlerName) }
|
||||||
|
repeat(startingWorkerCount) { startingUnits.add(startingWorkerName) }
|
||||||
repeat(startingMilitaryUnitCount) { startingUnits.add(startingMilitaryUnit) }
|
repeat(startingMilitaryUnitCount) { startingUnits.add(startingMilitaryUnit) }
|
||||||
return startingUnits
|
return startingUnits
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ enum class UniqueTarget(
|
|||||||
Tech(inheritsFrom = Global),
|
Tech(inheritsFrom = Global),
|
||||||
Policy(inheritsFrom = Global),
|
Policy(inheritsFrom = Global),
|
||||||
FounderBelief("Uniques for Founder and Enhancer type Beliefs, that will apply to the founder of this religion", inheritsFrom = Global),
|
FounderBelief("Uniques for Founder and Enhancer type Beliefs, that will apply to the founder of this religion", inheritsFrom = Global),
|
||||||
FollowerBelief("Uniques for Pantheon and Follower type beliefs, that will apply to each city where the religion is the majority religion"),
|
FollowerBelief("Uniques for Pantheon and Follower type beliefs, that will apply to each city where the religion is the majority religion", inheritsFrom = Triggerable),
|
||||||
|
|
||||||
// City-specific
|
// City-specific
|
||||||
Building(inheritsFrom = Global),
|
Building(inheritsFrom = Global),
|
||||||
|
@ -184,7 +184,7 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags:
|
|||||||
|
|
||||||
/// Resource production & consumption
|
/// Resource production & consumption
|
||||||
ConsumesResources("Consumes [amount] [resource]", UniqueTarget.Improvement, UniqueTarget.Building, UniqueTarget.Unit),
|
ConsumesResources("Consumes [amount] [resource]", UniqueTarget.Improvement, UniqueTarget.Building, UniqueTarget.Unit),
|
||||||
ProvidesResources("Provides [amount] [resource]", UniqueTarget.Improvement, UniqueTarget.Global),
|
ProvidesResources("Provides [amount] [resource]", UniqueTarget.Global, UniqueTarget.Improvement, UniqueTarget.FollowerBelief),
|
||||||
CostsResources("Costs [amount] [stockpiledResource]", UniqueTarget.Improvement, UniqueTarget.Building, UniqueTarget.Unit),
|
CostsResources("Costs [amount] [stockpiledResource]", UniqueTarget.Improvement, UniqueTarget.Building, UniqueTarget.Unit),
|
||||||
// Todo: Get rid of forced sign (+[relativeAmount]) and unify these two, e.g.: "[relativeAmount]% [resource/resourceType] production"
|
// Todo: Get rid of forced sign (+[relativeAmount]) and unify these two, e.g.: "[relativeAmount]% [resource/resourceType] production"
|
||||||
// Note that the parameter type 'resourceType' (strategic, luxury, bonus) currently doesn't exist and should then be added as well
|
// Note that the parameter type 'resourceType' (strategic, luxury, bonus) currently doesn't exist and should then be added as well
|
||||||
@ -497,7 +497,7 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags:
|
|||||||
TerrainGrantsPromotion("Grants [promotion] ([comment]) to adjacent [mapUnitFilter] units for the rest of the game", UniqueTarget.Terrain),
|
TerrainGrantsPromotion("Grants [promotion] ([comment]) to adjacent [mapUnitFilter] units for the rest of the game", UniqueTarget.Terrain),
|
||||||
GrantsCityStrength("[amount] Strength for cities built on this terrain", UniqueTarget.Terrain),
|
GrantsCityStrength("[amount] Strength for cities built on this terrain", UniqueTarget.Terrain),
|
||||||
ProductionBonusWhenRemoved("Provides a one-time Production bonus to the closest city when cut down", UniqueTarget.Terrain),
|
ProductionBonusWhenRemoved("Provides a one-time Production bonus to the closest city when cut down", UniqueTarget.Terrain),
|
||||||
Vegetation("Vegetation", UniqueTarget.Terrain, flags = UniqueFlag.setOfHiddenToUsers),
|
Vegetation("Vegetation", UniqueTarget.Terrain, UniqueTarget.Improvement, flags = UniqueFlag.setOfHiddenToUsers), // Improvement included because use as tileFilter works
|
||||||
|
|
||||||
|
|
||||||
TileProvidesYieldWithoutPopulation("Tile provides yield without assigned population", UniqueTarget.Terrain, UniqueTarget.Improvement),
|
TileProvidesYieldWithoutPopulation("Tile provides yield without assigned population", UniqueTarget.Terrain, UniqueTarget.Improvement),
|
||||||
|
@ -8,9 +8,9 @@ This file defines the difficulty levels a player can choose when starting a new
|
|||||||
|
|
||||||
Each difficulty level can have the following attributes:
|
Each difficulty level can have the following attributes:
|
||||||
|
|
||||||
| Attribute | Type | Optional | Notes |
|
| Attribute | Type | Optional | Notes |
|
||||||
| --------- | ---- | -------- | ----- |
|
| --------- | ---- | -------- |--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| name | String | Required | Name of the difficulty level |
|
| name | String | Required | Name of the difficulty level |
|
||||||
| baseHappiness | Integer | Default 0 |
|
| baseHappiness | Integer | Default 0 |
|
||||||
| extraHappinessPerLuxury | Float | Default 0 |
|
| extraHappinessPerLuxury | Float | Default 0 |
|
||||||
| researchCostModifier | Float | Default 1 |
|
| researchCostModifier | Float | Default 1 |
|
||||||
@ -19,7 +19,7 @@ Each difficulty level can have the following attributes:
|
|||||||
| policyCostModifier | Float | Default 1 |
|
| policyCostModifier | Float | Default 1 |
|
||||||
| unhappinessModifier | Float | Default 1 |
|
| unhappinessModifier | Float | Default 1 |
|
||||||
| barbarianBonus | Float | Default 0 |
|
| barbarianBonus | Float | Default 0 |
|
||||||
| playerBonusStartingUnits | List of Units | Default empty | Can also be 'Era Starting Unit', maps to `startingMilitaryUnit` of the Eras file. All other units must be in [Units.json](4-Unit-related-JSON-files.md#Units.json)] |
|
| playerBonusStartingUnits | List of Units | Default empty | Can also be 'Era Starting Unit', maps to `startingMilitaryUnit` of the Eras file. All other units must be in [Units.json](4-Unit-related-JSON-files.md#Units.json)]. Applies only to human player civs |
|
||||||
| aiCityGrowthModifier | Float | Default 1 |
|
| aiCityGrowthModifier | Float | Default 1 |
|
||||||
| aiUnitCostModifier | Float | Default 1 |
|
| aiUnitCostModifier | Float | Default 1 |
|
||||||
| aiBuildingCostModifier | Float | Default 1 |
|
| aiBuildingCostModifier | Float | Default 1 |
|
||||||
@ -27,10 +27,10 @@ Each difficulty level can have the following attributes:
|
|||||||
| aiBuildingMaintenanceModifier | Float | Default 1 |
|
| aiBuildingMaintenanceModifier | Float | Default 1 |
|
||||||
| aiUnitMaintenanceModifier | Float | Default 1 |
|
| aiUnitMaintenanceModifier | Float | Default 1 |
|
||||||
| aiFreeTechs | List of Techs | Default empty |
|
| aiFreeTechs | List of Techs | Default empty |
|
||||||
| aiMajorCivBonusStartingUnits | List of Units | Default empty | See above |
|
| aiMajorCivBonusStartingUnits | List of Units | Default empty | Same rules as playerBonusStartingUnits, See above. Applies only to AI major civs |
|
||||||
| aiCityStateBonusStartingUnits | List of Units | Default empty | See above |
|
| aiCityStateBonusStartingUnits | List of Units | Default empty | Same rules as playerBonusStartingUnits, See above. Applies only to city-state civs |
|
||||||
| aiUnhappinessModifier | Float | Default 1 |
|
| aiUnhappinessModifier | Float | Default 1 |
|
||||||
| aisExchangeTechs | Boolean | | Unimplemented |
|
| aisExchangeTechs | Boolean | | Unimplemented |
|
||||||
| turnBarbariansCanEnterPlayerTiles | Integer | Default 0 |
|
| turnBarbariansCanEnterPlayerTiles | Integer | Default 0 |
|
||||||
| clearBarbarianCampReward | Integer | Default 25 |
|
| clearBarbarianCampReward | Integer | Default 25 |
|
||||||
|
|
||||||
@ -42,23 +42,25 @@ This file should contain all the era's you want to use in your mod.
|
|||||||
|
|
||||||
Each era can have the following attributes:
|
Each era can have the following attributes:
|
||||||
|
|
||||||
| Attribute | Type | Optional | Notes |
|
| Attribute | Type | Optional | Notes |
|
||||||
| --------- | ---- | -------- | ----- |
|
| --------- | ---- | -------- |----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| name | String | required | Name of the era |
|
| name | String | required | Name of the era |
|
||||||
| researchAgreementCost | Integer (≥0) | defaults to 300 | Cost of research agreements were the most technologically advanced civ is in this era |
|
| researchAgreementCost | Integer (≥0) | defaults to 300 | Cost of research agreements were the most technologically advanced civ is in this era |
|
||||||
| iconRGB | List of 3 Integers | defaults to [255, 255, 255] | RGB color that icons for technologies of this era should have in the Tech screen |
|
| iconRGB | List of 3 Integers | defaults to [255, 255, 255] | RGB color that icons for technologies of this era should have in the Tech screen |
|
||||||
| unitBaseBuyCost | Integer (≥0) | defaults to 200 | Base cost of buying units with Faith, Food, Science or Culture when no other cost is provided |
|
| unitBaseBuyCost | Integer (≥0) | defaults to 200 | Base cost of buying units with Faith, Food, Science or Culture when no other cost is provided |
|
||||||
| startingSettlerCount | Integer (≥0) | defaults to 1 | Amount of settler units that should be spawned when starting a game in this era |
|
| startingSettlerCount | Integer (≥0) | defaults to 1 | Amount of settler units that should be spawned when starting a game in this era (setting this to zero is discouraged [^1]) |
|
||||||
| startingSettlerUnit | String | defaults to "Settler" | Name of the unit that should be used for the previous field. Must be in [Units.json](4-Unit-related-JSON-files.md#unitsjson) |
|
| startingSettlerUnit | String | defaults to "Settler" | Name of the unit that should be used for the previous field. Must be in [Units.json](4-Unit-related-JSON-files.md#unitsjson), or a unit with the "Founds a new city" unique must exist |
|
||||||
| startingWorkerCount | Integer (≥0) | defaults to 0 | Amount of worker units that should be spawned when starting a game in this era |
|
| startingWorkerCount | Integer (≥0) | defaults to 0 | Amount of worker units that should be spawned when starting a game in this era |
|
||||||
| startingWorkerUnit | String | defaults to "Worker" | Name of the unit that should be used for the previous field. Must be in [Units.json](4-Unit-related-JSON-files.md#unitsjson) |
|
| startingWorkerUnit | String | defaults to "Worker" | Name of the unit that should be used for the previous field. If startingWorkerCount>0, then it must exist in [Units.json](4-Unit-related-JSON-files.md#unitsjson), or a unit with the "Can build [filter] improvements on tiles" unique must exist |
|
||||||
| startingMilitaryUnitCount | Integer (≥0) | defaults to 1 | Amount of military units that should be spawned when starting a game in this era |
|
| startingMilitaryUnitCount | Integer (≥0) | defaults to 1 | Amount of military units that should be spawned when starting a game in this era |
|
||||||
| startingMilitaryUnit | String | defaults to "Warrior" | Name of the unit that should be used for the previous field. Must be in [Units.json](4-Unit-related-JSON-files.md#unitsjson)|
|
| startingMilitaryUnit | String | defaults to "Warrior" | Name of the unit that should be used for the previous field. Must be in [Units.json](4-Unit-related-JSON-files.md#unitsjson) |
|
||||||
| startingGold | Integer (≥0) | defaults to 0 | Amount of gold each civ should receive when starting a game in this era |
|
| startingGold | Integer (≥0) | defaults to 0 | Amount of gold each civ should receive when starting a game in this era |
|
||||||
| startingCulture | Integer (≥0) | defaults to 0 | Amount of culture each civ should receive when starting a game in this era |
|
| startingCulture | Integer (≥0) | defaults to 0 | Amount of culture each civ should receive when starting a game in this era |
|
||||||
| settlerPopulation | Integer (>0) | defaults to 1 | Default amount of population each city should have when settled when starting a game in this era |
|
| settlerPopulation | Integer (>0) | defaults to 1 | Default amount of population each city should have when settled when starting a game in this era |
|
||||||
| settlerBuildings | List of Strings | defaults to none | Buildings that should automatically be built whenever a city is settled when starting a game in this era |
|
| settlerBuildings | List of Strings | defaults to none | Buildings that should automatically be built whenever a city is settled when starting a game in this era |
|
||||||
| startingObsoleteWonders | List of Strings | defaults to none | Wonders (and technically buildings) that should be impossible to built when starting a game in this era. Used in the base game to remove all wonders older than 2 era's |
|
| startingObsoleteWonders | List of Strings | defaults to none | Wonders (and technically buildings) that should be impossible to built when starting a game in this era. Used in the base game to remove all wonders older than 2 era's |
|
||||||
|
|
||||||
|
[^1]: Successfully setting startingSettlerCount to zero in a mod (idea: conquer or die) is not easy. Some player-controlled settings require at least one Settler, through any source (see difficulties for other possible settler sources), or you won't be able to start a game: Once City Challenge requires one for all players, and allowing any city-states requires one for those. Would also affect defeat rules.
|
||||||
|
|
||||||
## Speeds.json
|
## Speeds.json
|
||||||
|
|
||||||
|
@ -606,7 +606,7 @@ Simple unique parameters are explained by mouseover. Complex parameters are expl
|
|||||||
??? example "Provides [amount] [resource]"
|
??? example "Provides [amount] [resource]"
|
||||||
Example: "Provides [3] [Iron]"
|
Example: "Provides [3] [Iron]"
|
||||||
|
|
||||||
Applicable to: Global, Improvement
|
Applicable to: Global, FollowerBelief, Improvement
|
||||||
|
|
||||||
??? example "Quantity of strategic resources produced by the empire +[relativeAmount]%"
|
??? example "Quantity of strategic resources produced by the empire +[relativeAmount]%"
|
||||||
Example: "Quantity of strategic resources produced by the empire +[+20]%"
|
Example: "Quantity of strategic resources produced by the empire +[+20]%"
|
||||||
@ -1450,7 +1450,7 @@ Simple unique parameters are explained by mouseover. Complex parameters are expl
|
|||||||
Applicable to: Terrain
|
Applicable to: Terrain
|
||||||
|
|
||||||
??? example "Vegetation"
|
??? example "Vegetation"
|
||||||
Applicable to: Terrain
|
Applicable to: Terrain, Improvement
|
||||||
|
|
||||||
??? example "Tile provides yield without assigned population"
|
??? example "Tile provides yield without assigned population"
|
||||||
Applicable to: Terrain, Improvement
|
Applicable to: Terrain, Improvement
|
||||||
|
Reference in New Issue
Block a user