mirror of
https://github.com/yairm210/Unciv.git
synced 2025-09-23 03:23:17 -04:00
Add a field for global unit uniques (#12775)
* Add a field for global unit uniques * Whoops * docs * Fix this check only ever being done once * Revert * Add ruleset uniques when adding rulesets together * Add ruleset to unitTypes for tests in case it's relevant * My suggested changes: Implement a separate rulesetMap for units, remove any additional checks to the type where unnecessary * Remove unit type code, update ruleset info by setter rather than by lazy * Type information is needed before we set the ruleset here * So should unique information
This commit is contained in:
parent
19d0fbc050
commit
45347cc928
@ -39,7 +39,6 @@ import com.unciv.models.ruleset.RulesetCache
|
|||||||
import com.unciv.models.ruleset.Speed
|
import com.unciv.models.ruleset.Speed
|
||||||
import com.unciv.models.ruleset.nation.Difficulty
|
import com.unciv.models.ruleset.nation.Difficulty
|
||||||
import com.unciv.models.ruleset.unique.LocalUniqueCache
|
import com.unciv.models.ruleset.unique.LocalUniqueCache
|
||||||
import com.unciv.models.ruleset.unique.StateForConditionals
|
|
||||||
import com.unciv.models.ruleset.unique.UniqueType
|
import com.unciv.models.ruleset.unique.UniqueType
|
||||||
import com.unciv.models.translations.tr
|
import com.unciv.models.translations.tr
|
||||||
import com.unciv.ui.audio.MusicMood
|
import com.unciv.ui.audio.MusicMood
|
||||||
@ -640,7 +639,7 @@ class GameInfo : IsPartOfGameInfoSerialization, HasGameInfoSerializationVersion
|
|||||||
removeMissingModReferences()
|
removeMissingModReferences()
|
||||||
|
|
||||||
for (baseUnit in ruleset.units.values)
|
for (baseUnit in ruleset.units.values)
|
||||||
baseUnit.ruleset = ruleset
|
baseUnit.setRuleset(ruleset)
|
||||||
|
|
||||||
for (building in ruleset.buildings.values)
|
for (building in ruleset.buildings.values)
|
||||||
building.ruleset = ruleset
|
building.ruleset = ruleset
|
||||||
|
@ -594,7 +594,8 @@ object UnitAutomation {
|
|||||||
|
|
||||||
private fun chooseBombardTarget(city: City): ICombatant? {
|
private fun chooseBombardTarget(city: City): ICombatant? {
|
||||||
var targets = TargetHelper.getBombardableTiles(city).map { Battle.getMapCombatantOfTile(it)!! }
|
var targets = TargetHelper.getBombardableTiles(city).map { Battle.getMapCombatantOfTile(it)!! }
|
||||||
.filterNot { it.isCivilian() && !it.getUnitType().hasUnique(UniqueType.Uncapturable) } // Don't bombard capturable civilians
|
.filterNot { it is MapUnitCombatant &&
|
||||||
|
it.isCivilian() && !it.unit.hasUnique(UniqueType.Uncapturable) } // Don't bombard capturable civilians
|
||||||
if (targets.none()) return null
|
if (targets.none()) return null
|
||||||
|
|
||||||
val siegeUnits = targets
|
val siegeUnits = targets
|
||||||
|
@ -673,12 +673,9 @@ class MapUnit : IsPartOfGameInfoSerialization {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun updateUniques() {
|
fun updateUniques() {
|
||||||
val unitUniqueSources =
|
|
||||||
baseUnit.uniqueObjects.asSequence() +
|
|
||||||
type.uniqueObjects
|
|
||||||
val otherUniqueSources = promotions.getPromotions().flatMap { it.uniqueObjects } +
|
val otherUniqueSources = promotions.getPromotions().flatMap { it.uniqueObjects } +
|
||||||
statuses.flatMap { it.uniques }
|
statuses.flatMap { it.uniques }
|
||||||
val uniqueSources = unitUniqueSources + otherUniqueSources
|
val uniqueSources = baseUnit.rulesetUniqueObjects.asSequence() + otherUniqueSources
|
||||||
|
|
||||||
tempUniquesMap = UniqueMap(uniqueSources)
|
tempUniquesMap = UniqueMap(uniqueSources)
|
||||||
nonUnitUniquesMap = UniqueMap(otherUniqueSources)
|
nonUnitUniquesMap = UniqueMap(otherUniqueSources)
|
||||||
|
@ -7,6 +7,7 @@ import com.unciv.models.ruleset.unique.UniqueType
|
|||||||
class GlobalUniques: RulesetObject() {
|
class GlobalUniques: RulesetObject() {
|
||||||
override var name = "GlobalUniques"
|
override var name = "GlobalUniques"
|
||||||
|
|
||||||
|
var unitUniques: ArrayList<String> = ArrayList()
|
||||||
override fun getUniqueTarget() = UniqueTarget.Global
|
override fun getUniqueTarget() = UniqueTarget.Global
|
||||||
override fun makeLink() = "" // No own category on Civilopedia screen
|
override fun makeLink() = "" // No own category on Civilopedia screen
|
||||||
|
|
||||||
|
@ -184,6 +184,8 @@ class Ruleset {
|
|||||||
globalUniques = GlobalUniques().apply {
|
globalUniques = GlobalUniques().apply {
|
||||||
uniques.addAll(globalUniques.uniques)
|
uniques.addAll(globalUniques.uniques)
|
||||||
uniques.addAll(ruleset.globalUniques.uniques)
|
uniques.addAll(ruleset.globalUniques.uniques)
|
||||||
|
unitUniques.addAll(globalUniques.unitUniques)
|
||||||
|
unitUniques.addAll(ruleset.globalUniques.unitUniques)
|
||||||
}
|
}
|
||||||
ruleset.modOptions.nationsToRemove
|
ruleset.modOptions.nationsToRemove
|
||||||
.flatMap { nationToRemove ->
|
.flatMap { nationToRemove ->
|
||||||
@ -214,7 +216,7 @@ class Ruleset {
|
|||||||
cityStateTypes.putAll(ruleset.cityStateTypes)
|
cityStateTypes.putAll(ruleset.cityStateTypes)
|
||||||
ruleset.modOptions.unitsToRemove
|
ruleset.modOptions.unitsToRemove
|
||||||
.flatMap { unitToRemove ->
|
.flatMap { unitToRemove ->
|
||||||
units.filter { it.apply { value.ruleset = this@Ruleset }.value.matchesFilter(unitToRemove) }.keys
|
units.filter { it.apply { value.setRuleset(this@Ruleset) }.value.matchesFilter(unitToRemove) }.keys
|
||||||
}.toSet().forEach {
|
}.toSet().forEach {
|
||||||
units.remove(it)
|
units.remove(it)
|
||||||
}
|
}
|
||||||
|
@ -21,12 +21,18 @@ interface IHasUniques : INamed {
|
|||||||
val uniqueMap: UniqueMap
|
val uniqueMap: UniqueMap
|
||||||
|
|
||||||
fun uniqueObjectsProvider(): List<Unique> {
|
fun uniqueObjectsProvider(): List<Unique> {
|
||||||
|
return uniqueObjectsProvider(uniques)
|
||||||
|
}
|
||||||
|
fun uniqueMapProvider(): UniqueMap {
|
||||||
|
return uniqueMapProvider(uniqueObjects)
|
||||||
|
}
|
||||||
|
fun uniqueObjectsProvider(uniques: List<String>): List<Unique> {
|
||||||
if (uniques.isEmpty()) return emptyList()
|
if (uniques.isEmpty()) return emptyList()
|
||||||
return uniques.map { Unique(it, getUniqueTarget(), name) }
|
return uniques.map { Unique(it, getUniqueTarget(), name) }
|
||||||
}
|
}
|
||||||
fun uniqueMapProvider(): UniqueMap {
|
fun uniqueMapProvider(uniqueObjects: List<Unique>): UniqueMap {
|
||||||
val newUniqueMap = UniqueMap()
|
val newUniqueMap = UniqueMap()
|
||||||
if (uniques.isNotEmpty())
|
if (uniqueObjects.isNotEmpty())
|
||||||
newUniqueMap.addUniques(uniqueObjects)
|
newUniqueMap.addUniques(uniqueObjects)
|
||||||
return newUniqueMap
|
return newUniqueMap
|
||||||
}
|
}
|
||||||
|
@ -266,6 +266,8 @@ open class UniqueMap() {
|
|||||||
addUniques(uniques.asIterable())
|
addUniques(uniques.asIterable())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun isEmpty(): Boolean = innerUniqueMap.isEmpty()
|
||||||
|
|
||||||
/** Adds one [unique] unless it has a ConditionalTimedUnique conditional */
|
/** Adds one [unique] unless it has a ConditionalTimedUnique conditional */
|
||||||
open fun addUnique(unique: Unique) {
|
open fun addUnique(unique: Unique) {
|
||||||
val existingArrayList = innerUniqueMap[unique.placeholderText]
|
val existingArrayList = innerUniqueMap[unique.placeholderText]
|
||||||
|
@ -16,6 +16,7 @@ import com.unciv.models.ruleset.RulesetObject
|
|||||||
import com.unciv.models.ruleset.unique.Conditionals
|
import com.unciv.models.ruleset.unique.Conditionals
|
||||||
import com.unciv.models.ruleset.unique.StateForConditionals
|
import com.unciv.models.ruleset.unique.StateForConditionals
|
||||||
import com.unciv.models.ruleset.unique.Unique
|
import com.unciv.models.ruleset.unique.Unique
|
||||||
|
import com.unciv.models.ruleset.unique.UniqueMap
|
||||||
import com.unciv.models.ruleset.unique.UniqueTarget
|
import com.unciv.models.ruleset.unique.UniqueTarget
|
||||||
import com.unciv.models.ruleset.unique.UniqueType
|
import com.unciv.models.ruleset.unique.UniqueType
|
||||||
import com.unciv.models.stats.Stat
|
import com.unciv.models.stats.Stat
|
||||||
@ -69,7 +70,24 @@ class BaseUnit : RulesetObject(), INonPerpetualConstruction {
|
|||||||
val costFunctions = BaseUnitCost(this)
|
val costFunctions = BaseUnitCost(this)
|
||||||
|
|
||||||
lateinit var ruleset: Ruleset
|
lateinit var ruleset: Ruleset
|
||||||
|
private set
|
||||||
|
|
||||||
|
fun setRuleset(ruleset: Ruleset) {
|
||||||
|
this.ruleset = ruleset
|
||||||
|
val list = ArrayList(uniques)
|
||||||
|
list.addAll(ruleset.globalUniques.unitUniques)
|
||||||
|
list.addAll(type.uniques)
|
||||||
|
rulesetUniqueObjects = uniqueObjectsProvider(list)
|
||||||
|
rulesetUniqueMap = uniqueMapProvider(rulesetUniqueObjects) // Has global uniques by the unique objects already
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transient
|
||||||
|
var rulesetUniqueObjects: List<Unique> = ArrayList()
|
||||||
|
private set
|
||||||
|
|
||||||
|
@Transient
|
||||||
|
var rulesetUniqueMap: UniqueMap = UniqueMap()
|
||||||
|
private set
|
||||||
|
|
||||||
/** Generate short description as comma-separated string for Technology description "Units enabled" and GreatPersonPickerScreen */
|
/** Generate short description as comma-separated string for Technology description "Units enabled" and GreatPersonPickerScreen */
|
||||||
fun getShortDescription(uniqueExclusionFilter: Unique.() -> Boolean = {false}) = BaseUnitDescriptions.getShortDescription(this, uniqueExclusionFilter)
|
fun getShortDescription(uniqueExclusionFilter: Unique.() -> Boolean = {false}) = BaseUnitDescriptions.getShortDescription(this, uniqueExclusionFilter)
|
||||||
@ -116,45 +134,33 @@ class BaseUnit : RulesetObject(), INonPerpetualConstruction {
|
|||||||
return unit
|
return unit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun hasUnique(uniqueType: UniqueType, state: StateForConditionals?): Boolean {
|
override fun hasUnique(uniqueType: UniqueType, state: StateForConditionals?): Boolean {
|
||||||
return super<RulesetObject>.hasUnique(uniqueType, state) || ::ruleset.isInitialized && type.hasUnique(uniqueType, state)
|
val stateForConditionals = state ?: StateForConditionals.EmptyState
|
||||||
|
return if (::ruleset.isInitialized) rulesetUniqueMap.hasUnique(uniqueType, stateForConditionals)
|
||||||
|
else super<RulesetObject>.hasUnique(uniqueType, stateForConditionals)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hasUnique(uniqueTag: String, state: StateForConditionals?): Boolean {
|
override fun hasUnique(uniqueTag: String, state: StateForConditionals?): Boolean {
|
||||||
return super<RulesetObject>.hasUnique(uniqueTag, state) || ::ruleset.isInitialized && type.hasUnique(uniqueTag, state)
|
val stateForConditionals = state ?: StateForConditionals.EmptyState
|
||||||
|
return if (::ruleset.isInitialized) rulesetUniqueMap.hasUnique(uniqueTag, stateForConditionals)
|
||||||
|
else super<RulesetObject>.hasUnique(uniqueTag, stateForConditionals)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hasTagUnique(tagUnique: String): Boolean {
|
override fun hasTagUnique(tagUnique: String): Boolean {
|
||||||
return super<RulesetObject>.hasTagUnique(tagUnique) || ::ruleset.isInitialized && type.hasTagUnique(tagUnique)
|
return if (::ruleset.isInitialized) rulesetUniqueMap.hasTagUnique(tagUnique)
|
||||||
|
else super<RulesetObject>.hasTagUnique(tagUnique)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Allows unique functions (getMatchingUniques, hasUnique) to "see" uniques from the UnitType */
|
/** Allows unique functions (getMatchingUniques, hasUnique) to "see" uniques from the UnitType */
|
||||||
override fun getMatchingUniques(uniqueType: UniqueType, state: StateForConditionals): Sequence<Unique> {
|
override fun getMatchingUniques(uniqueType: UniqueType, state: StateForConditionals): Sequence<Unique> {
|
||||||
val ourUniques = super<RulesetObject>.getMatchingUniques(uniqueType, state)
|
return if (::ruleset.isInitialized) rulesetUniqueMap.getMatchingUniques(uniqueType, state)
|
||||||
if (! ::ruleset.isInitialized) { // Not sure if this will ever actually happen, but better safe than sorry
|
else super<RulesetObject>.getMatchingUniques(uniqueType, state)
|
||||||
return ourUniques
|
|
||||||
}
|
|
||||||
val typeUniques = type.getMatchingUniques(uniqueType, state)
|
|
||||||
// Memory optimization - very rarely do we actually get uniques from both sources,
|
|
||||||
// and sequence addition is expensive relative to the rare case that we'll actually need it
|
|
||||||
if (ourUniques.none()) return typeUniques
|
|
||||||
if (typeUniques.none()) return ourUniques
|
|
||||||
return ourUniques + type.getMatchingUniques(uniqueType, state)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Allows unique functions (getMatchingUniques, hasUnique) to "see" uniques from the UnitType */
|
/** Allows unique functions (getMatchingUniques, hasUnique) to "see" uniques from the UnitType */
|
||||||
override fun getMatchingUniques(uniqueTag: String, state: StateForConditionals): Sequence<Unique> {
|
override fun getMatchingUniques(uniqueTag: String, state: StateForConditionals): Sequence<Unique> {
|
||||||
val ourUniques = super<RulesetObject>.getMatchingUniques(uniqueTag, state)
|
return if (::ruleset.isInitialized) rulesetUniqueMap.getMatchingUniques(uniqueTag, state)
|
||||||
if (! ::ruleset.isInitialized) { // Not sure if this will ever actually happen, but better safe than sorry
|
else super<RulesetObject>.getMatchingUniques(uniqueTag, state)
|
||||||
return ourUniques
|
|
||||||
}
|
|
||||||
val typeUniques = type.getMatchingUniques(uniqueTag, state)
|
|
||||||
// Memory optimization - very rarely do we actually get uniques from both sources,
|
|
||||||
// and sequence addition is expensive relative to the rare case that we'll actually need it
|
|
||||||
if (ourUniques.none()) return typeUniques
|
|
||||||
if (typeUniques.none()) return ourUniques
|
|
||||||
return ourUniques + type.getMatchingUniques(uniqueTag, state)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getProductionCost(civInfo: Civilization, city: City?): Int = costFunctions.getProductionCost(civInfo, city)
|
override fun getProductionCost(civInfo: Civilization, city: City?): Int = costFunctions.getProductionCost(civInfo, city)
|
||||||
@ -519,7 +525,7 @@ class BaseUnit : RulesetObject(), INonPerpetualConstruction {
|
|||||||
power += 4000
|
power += 4000
|
||||||
|
|
||||||
// Uniques
|
// Uniques
|
||||||
val allUniques = uniqueObjects.asSequence() +
|
val allUniques = rulesetUniqueObjects.asSequence() +
|
||||||
promotions.asSequence()
|
promotions.asSequence()
|
||||||
.mapNotNull { ruleset.unitPromotions[it] }
|
.mapNotNull { ruleset.unitPromotions[it] }
|
||||||
.flatMap { it.uniqueObjects }
|
.flatMap { it.uniqueObjects }
|
||||||
|
@ -15,7 +15,6 @@ enum class UnitMovementType { // The types of tiles the unit can by default ente
|
|||||||
class UnitType() : RulesetObject() {
|
class UnitType() : RulesetObject() {
|
||||||
private var movementType: String? = null
|
private var movementType: String? = null
|
||||||
private val unitMovementType: UnitMovementType? by lazy { if (movementType == null) null else UnitMovementType.valueOf(movementType!!) }
|
private val unitMovementType: UnitMovementType? by lazy { if (movementType == null) null else UnitMovementType.valueOf(movementType!!) }
|
||||||
|
|
||||||
override fun getUniqueTarget() = UniqueTarget.UnitType
|
override fun getUniqueTarget() = UniqueTarget.UnitType
|
||||||
override fun makeLink() = "UnitType/$name"
|
override fun makeLink() = "UnitType/$name"
|
||||||
|
|
||||||
|
@ -292,7 +292,15 @@ With `civModifier` being the multiplicative aggregate of ["\[relativeAmount\]% G
|
|||||||
[link to original](https://github.com/yairm210/Unciv/tree/master/android/assets/jsons/GlobalUniques.json)
|
[link to original](https://github.com/yairm210/Unciv/tree/master/android/assets/jsons/GlobalUniques.json)
|
||||||
|
|
||||||
GlobalUniques defines uniques that apply globally. e.g. Vanilla rulesets define the effects of Unhappiness here.
|
GlobalUniques defines uniques that apply globally. e.g. Vanilla rulesets define the effects of Unhappiness here.
|
||||||
Only the `uniques` field is used, but a name must still be set (the Ruleset validator might display it).
|
|
||||||
|
It has the following structure:
|
||||||
|
|
||||||
|
| Attribute | Type | Default | Notes |
|
||||||
|
|-------------|-----------------|-----------------|---------------------------------------------------------------------------------------------|
|
||||||
|
| name | String | "GlobalUniques" | The name field is not used, but still must be set (the Ruleset validator might display it). |
|
||||||
|
| uniques | List of Strings | empty | List of [unique abilities](../../uniques) that apply globally |
|
||||||
|
| unitUniques | List of Strings | empty | List of [unique abilities](../../uniques) that applies to each unit |
|
||||||
|
|
||||||
When extension rulesets define GlobalUniques, all uniques are merged. At the moment there is no way to change/remove uniques set by a base mod.
|
When extension rulesets define GlobalUniques, all uniques are merged. At the moment there is no way to change/remove uniques set by a base mod.
|
||||||
|
|
||||||
## Tutorials.json
|
## Tutorials.json
|
||||||
|
@ -11,7 +11,6 @@ import com.unciv.logic.civilization.diplomacy.DiplomaticStatus
|
|||||||
import com.unciv.logic.map.mapunit.MapUnit
|
import com.unciv.logic.map.mapunit.MapUnit
|
||||||
import com.unciv.logic.map.tile.Tile
|
import com.unciv.logic.map.tile.Tile
|
||||||
import com.unciv.models.ruleset.nation.Nation
|
import com.unciv.models.ruleset.nation.Nation
|
||||||
import com.unciv.models.ruleset.unique.StateForConditionals
|
|
||||||
import com.unciv.models.ruleset.unique.UniqueType
|
import com.unciv.models.ruleset.unique.UniqueType
|
||||||
import com.unciv.models.ruleset.unit.BaseUnit
|
import com.unciv.models.ruleset.unit.BaseUnit
|
||||||
import com.unciv.models.ruleset.unit.UnitType
|
import com.unciv.models.ruleset.unit.UnitType
|
||||||
@ -54,8 +53,8 @@ class UnitMovementTests {
|
|||||||
fun addFakeUnit(unitType: UnitType, uniques: List<String> = listOf()): MapUnit {
|
fun addFakeUnit(unitType: UnitType, uniques: List<String> = listOf()): MapUnit {
|
||||||
val baseUnit = BaseUnit()
|
val baseUnit = BaseUnit()
|
||||||
baseUnit.unitType = unitType.name
|
baseUnit.unitType = unitType.name
|
||||||
baseUnit.ruleset = testGame.ruleset
|
|
||||||
baseUnit.uniques.addAll(uniques)
|
baseUnit.uniques.addAll(uniques)
|
||||||
|
baseUnit.setRuleset(testGame.ruleset)
|
||||||
|
|
||||||
val unit = MapUnit()
|
val unit = MapUnit()
|
||||||
unit.name = baseUnit.name
|
unit.name = baseUnit.name
|
||||||
|
@ -64,7 +64,7 @@ class TestGame {
|
|||||||
tileMap.gameInfo = gameInfo
|
tileMap.gameInfo = gameInfo
|
||||||
|
|
||||||
for (baseUnit in ruleset.units.values)
|
for (baseUnit in ruleset.units.values)
|
||||||
baseUnit.ruleset = ruleset
|
baseUnit.setRuleset(ruleset)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Makes a new rectangular tileMap and sets it in gameInfo. Removes all existing tiles. All new tiles have terrain [baseTerrain] */
|
/** Makes a new rectangular tileMap and sets it in gameInfo. Removes all existing tiles. All new tiles have terrain [baseTerrain] */
|
||||||
@ -183,7 +183,7 @@ class TestGame {
|
|||||||
|
|
||||||
fun addUnit(name: String, civInfo: Civilization, tile: Tile?): MapUnit {
|
fun addUnit(name: String, civInfo: Civilization, tile: Tile?): MapUnit {
|
||||||
val baseUnit = ruleset.units[name]!!
|
val baseUnit = ruleset.units[name]!!
|
||||||
baseUnit.ruleset = ruleset
|
baseUnit.setRuleset(ruleset)
|
||||||
val mapUnit = baseUnit.getMapUnit(civInfo)
|
val mapUnit = baseUnit.getMapUnit(civInfo)
|
||||||
civInfo.units.addUnit(mapUnit)
|
civInfo.units.addUnit(mapUnit)
|
||||||
if (tile!=null) {
|
if (tile!=null) {
|
||||||
@ -238,8 +238,8 @@ class TestGame {
|
|||||||
fun createBaseUnit(unitType: String = createUnitType().name, vararg uniques: String) =
|
fun createBaseUnit(unitType: String = createUnitType().name, vararg uniques: String) =
|
||||||
createRulesetObject(ruleset.units, *uniques) {
|
createRulesetObject(ruleset.units, *uniques) {
|
||||||
val baseUnit = BaseUnit()
|
val baseUnit = BaseUnit()
|
||||||
baseUnit.ruleset = gameInfo.ruleset
|
|
||||||
baseUnit.unitType = unitType
|
baseUnit.unitType = unitType
|
||||||
|
baseUnit.setRuleset(gameInfo.ruleset)
|
||||||
baseUnit
|
baseUnit
|
||||||
}
|
}
|
||||||
fun createBelief(type: BeliefType = BeliefType.Any, vararg uniques: String) =
|
fun createBelief(type: BeliefType = BeliefType.Any, vararg uniques: String) =
|
||||||
|
Loading…
x
Reference in New Issue
Block a user