mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-14 17:59:11 +07: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:
@ -11,6 +11,7 @@ import com.unciv.logic.map.TileInfo
|
||||
import com.unciv.logic.map.TileMap
|
||||
import com.unciv.models.Counter
|
||||
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.ResourceType
|
||||
import com.unciv.models.ruleset.unit.BaseUnit
|
||||
@ -292,10 +293,10 @@ class CityInfo {
|
||||
cityResources.add(
|
||||
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
|
||||
cityResources.add(
|
||||
resource,
|
||||
|
@ -7,6 +7,7 @@ import com.unciv.logic.HexMath
|
||||
import com.unciv.logic.city.CityInfo
|
||||
import com.unciv.logic.civilization.CivilizationInfo
|
||||
import com.unciv.models.ruleset.Ruleset
|
||||
import com.unciv.models.ruleset.UniqueType
|
||||
import com.unciv.models.ruleset.tile.*
|
||||
import com.unciv.models.stats.Stats
|
||||
import com.unciv.models.translations.tr
|
||||
@ -387,7 +388,7 @@ open class TileInfo {
|
||||
matchesTerrainFilter(it.params[0]) && !civInfo.tech.isResearched(it.params[1])
|
||||
} -> false
|
||||
improvement.uniqueObjects.any {
|
||||
it.placeholderText == "Consumes [] []"
|
||||
it.matches(UniqueType.ConsumesResources, ruleset)
|
||||
&& civInfo.getCivResourcesByName()[it.params[1]]!! < it.params[0].toInt()
|
||||
} -> false
|
||||
else -> canImprovementBeBuiltHere(improvement, hasViewableResource(civInfo))
|
||||
|
@ -95,7 +95,7 @@ class Building : NamedStats(), INonPerpetualConstruction, ICivilopediaText {
|
||||
if (!tileBonusHashmap.containsKey(stats)) tileBonusHashmap[stats] = ArrayList()
|
||||
tileBonusHashmap[stats]!!.add(unique.params[1])
|
||||
}
|
||||
unique.placeholderText == "Consumes [] []" -> Unit // skip these,
|
||||
unique.isOfType(UniqueType.ConsumesResources) -> Unit // skip these,
|
||||
else -> yield(unique.text)
|
||||
}
|
||||
for ((key, value) in tileBonusHashmap)
|
||||
@ -706,7 +706,7 @@ class Building : NamedStats(), INonPerpetualConstruction, ICivilopediaText {
|
||||
val resourceRequirements = HashMap<String, Int>()
|
||||
if (requiredResource != null) resourceRequirements[requiredResource!!] = 1
|
||||
for (unique in uniqueObjects)
|
||||
if (unique.placeholderText == "Consumes [] []")
|
||||
if (unique.isOfType(UniqueType.ConsumesResources))
|
||||
resourceRequirements[unique.params[1]] = unique.params[0].toInt()
|
||||
return resourceRequirements
|
||||
}
|
||||
|
@ -14,9 +14,68 @@ import com.unciv.models.translations.hasPlaceholderParameters
|
||||
import com.unciv.ui.worldscreen.unit.UnitActions
|
||||
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 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
|
||||
* - for instance, in the city screen, we call every tile unique for every tile, which can lead to ANRs */
|
||||
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
|
||||
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>>() {
|
||||
@ -116,7 +180,7 @@ object UniqueTriggerActivation {
|
||||
|
||||
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
|
||||
"Free Social Policy" -> {
|
||||
if (civInfo.isSpectator()) return false
|
||||
@ -201,7 +265,7 @@ object UniqueTriggerActivation {
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
"Free Technology" -> {
|
||||
if (civInfo.isSpectator()) return false
|
||||
civInfo.tech.freeTechs += 1
|
||||
@ -457,8 +521,8 @@ object UniqueTriggerActivation {
|
||||
civInfo.addNotification(notification, NotificationIcon.Diplomacy)
|
||||
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" ->
|
||||
civInfo.civConstructions.tryAddFreeBuildings()
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import com.unciv.logic.civilization.CivilizationInfo
|
||||
import com.unciv.logic.map.MapUnit
|
||||
import com.unciv.models.ruleset.Ruleset
|
||||
import com.unciv.models.ruleset.Unique
|
||||
import com.unciv.models.ruleset.UniqueType
|
||||
import com.unciv.models.stats.INamed
|
||||
import com.unciv.models.stats.Stat
|
||||
import com.unciv.models.translations.tr
|
||||
@ -516,7 +517,7 @@ class BaseUnit : INamed, INonPerpetualConstruction, ICivilopediaText {
|
||||
val resourceRequirements = HashMap<String, Int>()
|
||||
if (requiredResource != null) resourceRequirements[requiredResource!!] = 1
|
||||
for (unique in uniqueObjects)
|
||||
if (unique.placeholderText == "Consumes [] []")
|
||||
if (unique.isOfType(UniqueType.ConsumesResources))
|
||||
resourceRequirements[unique.params[1]] = unique.params[0].toInt()
|
||||
return resourceRequirements
|
||||
}
|
||||
|
@ -200,22 +200,7 @@ object TranslationFileWriter {
|
||||
"Buildings",
|
||||
"Building"
|
||||
)) }
|
||||
val unitTypeMap = ruleset.unitTypes.keys.toMutableSet().apply { addAll(sequenceOf(
|
||||
"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 unitTypeMap = ruleset.unitTypes.keys.toMutableSet().apply { addAll(UniqueParameterType.unitTypeStrings) }
|
||||
val cityFilterMap = setOf(
|
||||
"in this city",
|
||||
"in all cities",
|
||||
|
@ -16,6 +16,7 @@ import com.unciv.models.UncivSound
|
||||
import com.unciv.models.UnitAction
|
||||
import com.unciv.models.UnitActionType
|
||||
import com.unciv.models.ruleset.Building
|
||||
import com.unciv.models.ruleset.UniqueType
|
||||
import com.unciv.models.stats.Stat
|
||||
import com.unciv.models.stats.Stats
|
||||
import com.unciv.models.translations.tr
|
||||
@ -594,7 +595,7 @@ object UnitActions {
|
||||
|
||||
var resourcesAvailable = true
|
||||
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
|
||||
|
||||
|
Reference in New Issue
Block a user