mirror of
https://github.com/yairm210/Unciv.git
synced 2025-09-27 13:55:54 -04:00
Added basic functionality for uniques enum (#5222)
* Added basic functionality for uniques enum * Added unique type to Unique class for faster enum comparisons * And Elvis operator for unknown parameter type * Resolved #5162 - AI much less motivated to attack city-states * Whoops, wrong branch
This commit is contained in:
parent
51bfd927c1
commit
6d26a28619
@ -11,6 +11,7 @@ import com.unciv.logic.map.TileInfo
|
|||||||
import com.unciv.logic.map.TileMap
|
import com.unciv.logic.map.TileMap
|
||||||
import com.unciv.models.Counter
|
import com.unciv.models.Counter
|
||||||
import com.unciv.models.ruleset.Unique
|
import com.unciv.models.ruleset.Unique
|
||||||
|
import com.unciv.models.ruleset.UniqueType
|
||||||
import com.unciv.models.ruleset.tile.ResourceSupplyList
|
import com.unciv.models.ruleset.tile.ResourceSupplyList
|
||||||
import com.unciv.models.ruleset.tile.ResourceType
|
import com.unciv.models.ruleset.tile.ResourceType
|
||||||
import com.unciv.models.ruleset.unit.BaseUnit
|
import com.unciv.models.ruleset.unit.BaseUnit
|
||||||
@ -292,10 +293,10 @@ class CityInfo {
|
|||||||
cityResources.add(
|
cityResources.add(
|
||||||
resource,
|
resource,
|
||||||
unique.params[0].toInt() * civInfo.getResourceModifier(resource),
|
unique.params[0].toInt() * civInfo.getResourceModifier(resource),
|
||||||
"Tiles"
|
"Improvements"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (unique.placeholderText == "Consumes [] []") {
|
if (unique.matches(UniqueType.ConsumesResources, getRuleset())) {
|
||||||
val resource = getRuleset().tileResources[unique.params[1]] ?: continue
|
val resource = getRuleset().tileResources[unique.params[1]] ?: continue
|
||||||
cityResources.add(
|
cityResources.add(
|
||||||
resource,
|
resource,
|
||||||
|
@ -7,6 +7,7 @@ import com.unciv.logic.HexMath
|
|||||||
import com.unciv.logic.city.CityInfo
|
import com.unciv.logic.city.CityInfo
|
||||||
import com.unciv.logic.civilization.CivilizationInfo
|
import com.unciv.logic.civilization.CivilizationInfo
|
||||||
import com.unciv.models.ruleset.Ruleset
|
import com.unciv.models.ruleset.Ruleset
|
||||||
|
import com.unciv.models.ruleset.UniqueType
|
||||||
import com.unciv.models.ruleset.tile.*
|
import com.unciv.models.ruleset.tile.*
|
||||||
import com.unciv.models.stats.Stats
|
import com.unciv.models.stats.Stats
|
||||||
import com.unciv.models.translations.tr
|
import com.unciv.models.translations.tr
|
||||||
@ -387,7 +388,7 @@ open class TileInfo {
|
|||||||
matchesTerrainFilter(it.params[0]) && !civInfo.tech.isResearched(it.params[1])
|
matchesTerrainFilter(it.params[0]) && !civInfo.tech.isResearched(it.params[1])
|
||||||
} -> false
|
} -> false
|
||||||
improvement.uniqueObjects.any {
|
improvement.uniqueObjects.any {
|
||||||
it.placeholderText == "Consumes [] []"
|
it.matches(UniqueType.ConsumesResources, ruleset)
|
||||||
&& civInfo.getCivResourcesByName()[it.params[1]]!! < it.params[0].toInt()
|
&& civInfo.getCivResourcesByName()[it.params[1]]!! < it.params[0].toInt()
|
||||||
} -> false
|
} -> false
|
||||||
else -> canImprovementBeBuiltHere(improvement, hasViewableResource(civInfo))
|
else -> canImprovementBeBuiltHere(improvement, hasViewableResource(civInfo))
|
||||||
|
@ -95,7 +95,7 @@ class Building : NamedStats(), INonPerpetualConstruction, ICivilopediaText {
|
|||||||
if (!tileBonusHashmap.containsKey(stats)) tileBonusHashmap[stats] = ArrayList()
|
if (!tileBonusHashmap.containsKey(stats)) tileBonusHashmap[stats] = ArrayList()
|
||||||
tileBonusHashmap[stats]!!.add(unique.params[1])
|
tileBonusHashmap[stats]!!.add(unique.params[1])
|
||||||
}
|
}
|
||||||
unique.placeholderText == "Consumes [] []" -> Unit // skip these,
|
unique.isOfType(UniqueType.ConsumesResources) -> Unit // skip these,
|
||||||
else -> yield(unique.text)
|
else -> yield(unique.text)
|
||||||
}
|
}
|
||||||
for ((key, value) in tileBonusHashmap)
|
for ((key, value) in tileBonusHashmap)
|
||||||
@ -706,7 +706,7 @@ class Building : NamedStats(), INonPerpetualConstruction, ICivilopediaText {
|
|||||||
val resourceRequirements = HashMap<String, Int>()
|
val resourceRequirements = HashMap<String, Int>()
|
||||||
if (requiredResource != null) resourceRequirements[requiredResource!!] = 1
|
if (requiredResource != null) resourceRequirements[requiredResource!!] = 1
|
||||||
for (unique in uniqueObjects)
|
for (unique in uniqueObjects)
|
||||||
if (unique.placeholderText == "Consumes [] []")
|
if (unique.isOfType(UniqueType.ConsumesResources))
|
||||||
resourceRequirements[unique.params[1]] = unique.params[0].toInt()
|
resourceRequirements[unique.params[1]] = unique.params[0].toInt()
|
||||||
return resourceRequirements
|
return resourceRequirements
|
||||||
}
|
}
|
||||||
|
@ -14,9 +14,68 @@ import com.unciv.models.translations.hasPlaceholderParameters
|
|||||||
import com.unciv.ui.worldscreen.unit.UnitActions
|
import com.unciv.ui.worldscreen.unit.UnitActions
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
|
||||||
class Unique(val text:String){
|
|
||||||
|
// parameterName values should be compliant with autogenerated values in TranslationFileWriter.generateStringsFromJSONs
|
||||||
|
// Eventually we'll merge the translation generation to take this as the source of that
|
||||||
|
enum class UniqueParameterType(val parameterName:String, val complianceCheck:(String, Ruleset)->Boolean) {
|
||||||
|
Number("amount", { s, r -> s.toIntOrNull() != null }),
|
||||||
|
UnitFilter("unitType", { s, r -> r.unitTypes.containsKey(s) || unitTypeStrings.contains(s) }),
|
||||||
|
Unknown("",{s,r -> true});
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val unitTypeStrings = hashSetOf(
|
||||||
|
"Military",
|
||||||
|
"Civilian",
|
||||||
|
"non-air",
|
||||||
|
"relevant",
|
||||||
|
"Nuclear Weapon",
|
||||||
|
"City",
|
||||||
|
// These are up for debate
|
||||||
|
"Air",
|
||||||
|
"land units",
|
||||||
|
"water units",
|
||||||
|
"air units",
|
||||||
|
"military units",
|
||||||
|
"submarine units",
|
||||||
|
// Note: this can't handle combinations of parameters (e.g. [{Military} {Water}])
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class UniqueType(val text:String) {
|
||||||
|
|
||||||
|
ConsumesResources("Consumes [amount] [resource]");
|
||||||
|
|
||||||
|
/** For uniques that have "special" parameters that can accept multiple types, we can override them manually
|
||||||
|
* For 95% of cases, auto-matching is fine. */
|
||||||
|
private val parameterTypeMap = ArrayList<List<UniqueParameterType>>()
|
||||||
|
|
||||||
|
init {
|
||||||
|
for (placeholder in text.getPlaceholderParameters()) {
|
||||||
|
val matchingParameterType =
|
||||||
|
UniqueParameterType.values().firstOrNull { it.parameterName == placeholder }
|
||||||
|
?: UniqueParameterType.Unknown
|
||||||
|
parameterTypeMap.add(listOf(matchingParameterType))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val placeholderText = text.getPlaceholderText()
|
||||||
|
|
||||||
|
fun checkCompliance(unique: Unique, ruleset: Ruleset): Boolean {
|
||||||
|
for ((index, param) in unique.params.withIndex())
|
||||||
|
if (parameterTypeMap[index].none { it.complianceCheck(param, ruleset) })
|
||||||
|
return false
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Unique(val text:String) {
|
||||||
val placeholderText = text.getPlaceholderText()
|
val placeholderText = text.getPlaceholderText()
|
||||||
val params = text.getPlaceholderParameters()
|
val params = text.getPlaceholderParameters()
|
||||||
|
val type = UniqueType.values().firstOrNull { it.placeholderText == placeholderText }
|
||||||
|
|
||||||
/** This is so the heavy regex-based parsing is only activated once per unique, instead of every time it's called
|
/** This is so the heavy regex-based parsing is only activated once per unique, instead of every time it's called
|
||||||
* - for instance, in the city screen, we call every tile unique for every tile, which can lead to ANRs */
|
* - for instance, in the city screen, we call every tile unique for every tile, which can lead to ANRs */
|
||||||
val stats: Stats by lazy {
|
val stats: Stats by lazy {
|
||||||
@ -24,6 +83,11 @@ class Unique(val text:String){
|
|||||||
if (firstStatParam == null) Stats() // So badly-defined stats don't crash the entire game
|
if (firstStatParam == null) Stats() // So badly-defined stats don't crash the entire game
|
||||||
else Stats.parse(firstStatParam)
|
else Stats.parse(firstStatParam)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun isOfType(uniqueType: UniqueType) = uniqueType == type
|
||||||
|
fun matches(uniqueType: UniqueType, ruleset: Ruleset) = isOfType(uniqueType)
|
||||||
|
&& uniqueType.checkCompliance(this, ruleset)
|
||||||
}
|
}
|
||||||
|
|
||||||
class UniqueMap:HashMap<String, ArrayList<Unique>>() {
|
class UniqueMap:HashMap<String, ArrayList<Unique>>() {
|
||||||
@ -116,7 +180,7 @@ object UniqueTriggerActivation {
|
|||||||
|
|
||||||
return placedUnit != null
|
return placedUnit != null
|
||||||
}
|
}
|
||||||
|
|
||||||
// spectators get all techs at start of game, and if (in a mod) a tech gives a free policy, the game gets stuck on the policy picker screen
|
// spectators get all techs at start of game, and if (in a mod) a tech gives a free policy, the game gets stuck on the policy picker screen
|
||||||
"Free Social Policy" -> {
|
"Free Social Policy" -> {
|
||||||
if (civInfo.isSpectator()) return false
|
if (civInfo.isSpectator()) return false
|
||||||
@ -201,7 +265,7 @@ object UniqueTriggerActivation {
|
|||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
"Free Technology" -> {
|
"Free Technology" -> {
|
||||||
if (civInfo.isSpectator()) return false
|
if (civInfo.isSpectator()) return false
|
||||||
civInfo.tech.freeTechs += 1
|
civInfo.tech.freeTechs += 1
|
||||||
@ -457,8 +521,8 @@ object UniqueTriggerActivation {
|
|||||||
civInfo.addNotification(notification, NotificationIcon.Diplomacy)
|
civInfo.addNotification(notification, NotificationIcon.Diplomacy)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
"Provides the cheapest [] building in your first [] cities for free",
|
"Provides the cheapest [] building in your first [] cities for free",
|
||||||
"Provides a [] in your first [] cities for free" ->
|
"Provides a [] in your first [] cities for free" ->
|
||||||
civInfo.civConstructions.tryAddFreeBuildings()
|
civInfo.civConstructions.tryAddFreeBuildings()
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ import com.unciv.logic.civilization.CivilizationInfo
|
|||||||
import com.unciv.logic.map.MapUnit
|
import com.unciv.logic.map.MapUnit
|
||||||
import com.unciv.models.ruleset.Ruleset
|
import com.unciv.models.ruleset.Ruleset
|
||||||
import com.unciv.models.ruleset.Unique
|
import com.unciv.models.ruleset.Unique
|
||||||
|
import com.unciv.models.ruleset.UniqueType
|
||||||
import com.unciv.models.stats.INamed
|
import com.unciv.models.stats.INamed
|
||||||
import com.unciv.models.stats.Stat
|
import com.unciv.models.stats.Stat
|
||||||
import com.unciv.models.translations.tr
|
import com.unciv.models.translations.tr
|
||||||
@ -516,7 +517,7 @@ class BaseUnit : INamed, INonPerpetualConstruction, ICivilopediaText {
|
|||||||
val resourceRequirements = HashMap<String, Int>()
|
val resourceRequirements = HashMap<String, Int>()
|
||||||
if (requiredResource != null) resourceRequirements[requiredResource!!] = 1
|
if (requiredResource != null) resourceRequirements[requiredResource!!] = 1
|
||||||
for (unique in uniqueObjects)
|
for (unique in uniqueObjects)
|
||||||
if (unique.placeholderText == "Consumes [] []")
|
if (unique.isOfType(UniqueType.ConsumesResources))
|
||||||
resourceRequirements[unique.params[1]] = unique.params[0].toInt()
|
resourceRequirements[unique.params[1]] = unique.params[0].toInt()
|
||||||
return resourceRequirements
|
return resourceRequirements
|
||||||
}
|
}
|
||||||
|
@ -200,22 +200,7 @@ object TranslationFileWriter {
|
|||||||
"Buildings",
|
"Buildings",
|
||||||
"Building"
|
"Building"
|
||||||
)) }
|
)) }
|
||||||
val unitTypeMap = ruleset.unitTypes.keys.toMutableSet().apply { addAll(sequenceOf(
|
val unitTypeMap = ruleset.unitTypes.keys.toMutableSet().apply { addAll(UniqueParameterType.unitTypeStrings) }
|
||||||
"Military",
|
|
||||||
"Civilian",
|
|
||||||
"non-air",
|
|
||||||
"relevant",
|
|
||||||
"Nuclear Weapon",
|
|
||||||
"City",
|
|
||||||
// These are up for debate
|
|
||||||
"Air",
|
|
||||||
"land units",
|
|
||||||
"water units",
|
|
||||||
"air units",
|
|
||||||
"military units",
|
|
||||||
"submarine units",
|
|
||||||
// Note: this can't handle combinations of parameters (e.g. [{Military} {Water}])
|
|
||||||
)) }
|
|
||||||
val cityFilterMap = setOf(
|
val cityFilterMap = setOf(
|
||||||
"in this city",
|
"in this city",
|
||||||
"in all cities",
|
"in all cities",
|
||||||
|
@ -16,6 +16,7 @@ import com.unciv.models.UncivSound
|
|||||||
import com.unciv.models.UnitAction
|
import com.unciv.models.UnitAction
|
||||||
import com.unciv.models.UnitActionType
|
import com.unciv.models.UnitActionType
|
||||||
import com.unciv.models.ruleset.Building
|
import com.unciv.models.ruleset.Building
|
||||||
|
import com.unciv.models.ruleset.UniqueType
|
||||||
import com.unciv.models.stats.Stat
|
import com.unciv.models.stats.Stat
|
||||||
import com.unciv.models.stats.Stats
|
import com.unciv.models.stats.Stats
|
||||||
import com.unciv.models.translations.tr
|
import com.unciv.models.translations.tr
|
||||||
@ -594,7 +595,7 @@ object UnitActions {
|
|||||||
|
|
||||||
var resourcesAvailable = true
|
var resourcesAvailable = true
|
||||||
if (improvement.uniqueObjects.any {
|
if (improvement.uniqueObjects.any {
|
||||||
it.placeholderText == "Consumes [] []" && civResources[unique.params[1]] ?: 0 < unique.params[0].toInt()
|
it.matches(UniqueType.ConsumesResources, tile.ruleset) && civResources[unique.params[1]] ?: 0 < unique.params[0].toInt()
|
||||||
})
|
})
|
||||||
resourcesAvailable = false
|
resourcesAvailable = false
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user