mirror of
https://github.com/yairm210/Unciv.git
synced 2025-01-07 05:51:12 +07:00
Allow for replacement improvements (#11369)
* Allow for replacement improvements * imports * Forgot the most important change, lol * Docs * Replacement description, validation, and filter * Move more into ImprovementDescriptions * Whoops, forgot to yield * Fix some copy-paste artifacts * New translations * Fix double see also * Add space for translation engine
This commit is contained in:
parent
0caf0cb4fa
commit
3ea1e4a539
@ -1538,6 +1538,7 @@ Unique to [civName], replaces [unitName] =
|
||||
Unique to [civName] =
|
||||
Tutorials =
|
||||
Cost =
|
||||
Turns to build =
|
||||
May contain [listOfResources] =
|
||||
May contain: =
|
||||
Can upgrade from [unit] =
|
||||
@ -1557,6 +1558,7 @@ Improvements that provide this resource =
|
||||
Buildings that require this resource worked near the city =
|
||||
Units that consume this resource =
|
||||
Can be built on =
|
||||
Cannot be built on =
|
||||
or [terrainType] =
|
||||
Can be constructed by =
|
||||
Can be created instantly by =
|
||||
|
@ -275,7 +275,7 @@ object Automation {
|
||||
): Boolean {
|
||||
if (construction !is Building) return true
|
||||
if (!construction.hasCreateOneImprovementUnique()) return true // redundant but faster???
|
||||
val improvement = construction.getImprovementToCreate(city.getRuleset()) ?: return true
|
||||
val improvement = construction.getImprovementToCreate(city.getRuleset(), civInfo) ?: return true
|
||||
return city.getTiles().any {
|
||||
it.improvementFunctions.canBuildImprovement(improvement, civInfo)
|
||||
}
|
||||
|
@ -655,7 +655,7 @@ class CityConstructions : IsPartOfGameInfoSerialization {
|
||||
tile: Tile? = null
|
||||
): Boolean {
|
||||
// Support UniqueType.CreatesOneImprovement: it is active when getImprovementToCreate returns an improvement
|
||||
val improvementToPlace = (construction as? Building)?.getImprovementToCreate(city.getRuleset())
|
||||
val improvementToPlace = (construction as? Building)?.getImprovementToCreate(city.getRuleset(), city.civ)
|
||||
if (improvementToPlace != null) {
|
||||
// If active without a predetermined tile to place the improvement on, automate a tile
|
||||
val finalTile = tile
|
||||
@ -716,7 +716,7 @@ class CityConstructions : IsPartOfGameInfoSerialization {
|
||||
|
||||
/** Support for [UniqueType.CreatesOneImprovement] - if an Improvement-creating Building was auto-queued, auto-choose a tile: */
|
||||
val building = getCurrentConstruction() as? Building ?: return
|
||||
val improvement = building.getImprovementToCreate(city.getRuleset()) ?: return
|
||||
val improvement = building.getImprovementToCreate(city.getRuleset(), city.civ) ?: return
|
||||
if (getTileForImprovement(improvement.name) != null) return
|
||||
val newTile = Automation.getTileForConstructionImprovement(city, improvement) ?: return
|
||||
newTile.improvementFunctions.markForCreatesOneImprovement(improvement.name)
|
||||
@ -778,7 +778,7 @@ class CityConstructions : IsPartOfGameInfoSerialization {
|
||||
// UniqueType.CreatesOneImprovement support
|
||||
val construction = getConstruction(constructionName)
|
||||
if (construction is Building) {
|
||||
val improvement = construction.getImprovementToCreate(city.getRuleset())
|
||||
val improvement = construction.getImprovementToCreate(city.getRuleset(), city.civ)
|
||||
if (improvement != null) {
|
||||
getTileForImprovement(improvement.name)?.stopWorkingOnImprovement()
|
||||
}
|
||||
@ -846,7 +846,7 @@ class CityConstructions : IsPartOfGameInfoSerialization {
|
||||
* (skip if none found), then un-mark the tile and place the improvement unless [removeOnly] is set.
|
||||
*/
|
||||
private fun applyCreateOneImprovement(building: Building, removeOnly: Boolean = false) {
|
||||
val improvement = building.getImprovementToCreate(city.getRuleset())
|
||||
val improvement = building.getImprovementToCreate(city.getRuleset(), city.civ)
|
||||
?: return
|
||||
val tileForImprovement = getTileForImprovement(improvement.name) ?: return
|
||||
tileForImprovement.stopWorkingOnImprovement() // clears mark
|
||||
@ -866,7 +866,7 @@ class CityConstructions : IsPartOfGameInfoSerialization {
|
||||
val ruleset = city.getRuleset()
|
||||
val indexToRemove = constructionQueue.withIndex().firstNotNullOfOrNull {
|
||||
val construction = getConstruction(it.value)
|
||||
val buildingImprovement = (construction as? Building)?.getImprovementToCreate(ruleset)?.name
|
||||
val buildingImprovement = (construction as? Building)?.getImprovementToCreate(ruleset, city.civ)?.name
|
||||
it.index.takeIf { buildingImprovement == improvement }
|
||||
} ?: return
|
||||
|
||||
|
@ -45,6 +45,7 @@ import com.unciv.models.ruleset.nation.Personality
|
||||
import com.unciv.models.ruleset.tech.Era
|
||||
import com.unciv.models.ruleset.tile.ResourceSupplyList
|
||||
import com.unciv.models.ruleset.tile.ResourceType
|
||||
import com.unciv.models.ruleset.tile.TileImprovement
|
||||
import com.unciv.models.ruleset.tile.TileResource
|
||||
import com.unciv.models.ruleset.unique.StateForConditionals
|
||||
import com.unciv.models.ruleset.unique.TemporaryUnique
|
||||
@ -544,6 +545,22 @@ class Civilization : IsPartOfGameInfoSerialization {
|
||||
return baseBuilding
|
||||
}
|
||||
|
||||
fun getEquivalentTileImprovement(tileImprovementName: String): TileImprovement {
|
||||
val tileImprovement = gameInfo.ruleset.tileImprovements[tileImprovementName]
|
||||
?: throw UncivShowableException("Improvement $tileImprovementName doesn't seem to exist!")
|
||||
return getEquivalentTileImprovement(tileImprovement)
|
||||
}
|
||||
|
||||
fun getEquivalentTileImprovement(tileImprovement: TileImprovement): TileImprovement {
|
||||
if (tileImprovement.replaces != null)
|
||||
return getEquivalentTileImprovement(tileImprovement.replaces!!)
|
||||
|
||||
for (improvement in cache.uniqueImprovements)
|
||||
if (improvement.replaces == tileImprovement.name)
|
||||
return improvement
|
||||
return tileImprovement
|
||||
}
|
||||
|
||||
fun getEquivalentUnit(baseUnitName: String): BaseUnit {
|
||||
val baseUnit = gameInfo.ruleset.units[baseUnitName]
|
||||
?: throw UncivShowableException("Unit $baseUnitName doesn't seem to exist!")
|
||||
|
@ -14,6 +14,7 @@ import com.unciv.logic.map.tile.Tile
|
||||
import com.unciv.models.ruleset.Building
|
||||
import com.unciv.models.ruleset.tile.ResourceSupplyList
|
||||
import com.unciv.models.ruleset.tile.ResourceType
|
||||
import com.unciv.models.ruleset.tile.TileImprovement
|
||||
import com.unciv.models.ruleset.unique.StateForConditionals
|
||||
import com.unciv.models.ruleset.unique.UniqueTarget
|
||||
import com.unciv.models.ruleset.unique.UniqueTriggerActivation
|
||||
@ -35,6 +36,9 @@ class CivInfoTransientCache(val civInfo: Civilization) {
|
||||
@Transient
|
||||
val uniqueUnits = hashSetOf<BaseUnit>()
|
||||
|
||||
@Transient
|
||||
val uniqueImprovements = hashSetOf<TileImprovement>()
|
||||
|
||||
@Transient
|
||||
val uniqueBuildings = hashSetOf<Building>()
|
||||
|
||||
@ -64,6 +68,10 @@ class CivInfoTransientCache(val civInfo: Civilization) {
|
||||
}
|
||||
}
|
||||
|
||||
for (improvement in ruleset.tileImprovements.values)
|
||||
if (improvement.uniqueTo == civInfo.civName)
|
||||
uniqueImprovements.add(improvement)
|
||||
|
||||
for (unit in ruleset.units.values) {
|
||||
if (unit.uniqueTo == civInfo.civName) {
|
||||
uniqueUnits.add(unit)
|
||||
|
@ -20,6 +20,7 @@ enum class ImprovementBuildingProblem(
|
||||
/** `true` if the ImprovementPicker should report this problem */
|
||||
val reportable: Boolean = false
|
||||
) {
|
||||
Replaced(permanent = true),
|
||||
WrongCiv(permanent = true),
|
||||
MissingTech(reportable = true),
|
||||
Unbuildable(permanent = true),
|
||||
@ -45,6 +46,8 @@ class TileInfoImprovementFunctions(val tile: Tile) {
|
||||
|
||||
if (improvement.uniqueTo != null && improvement.uniqueTo != civInfo.civName)
|
||||
yield(ImprovementBuildingProblem.WrongCiv)
|
||||
if (civInfo.cache.uniqueImprovements.any { it.replaces == improvement.name })
|
||||
yield(ImprovementBuildingProblem.Replaced)
|
||||
if (improvement.techRequired != null && !civInfo.tech.isResearched(improvement.techRequired!!))
|
||||
yield(ImprovementBuildingProblem.MissingTech)
|
||||
if (improvement.getMatchingUniques(UniqueType.Unbuildable, StateForConditionals.IgnoreConditionals)
|
||||
|
@ -551,6 +551,10 @@ class Building : RulesetStatsObject(), INonPerpetualConstruction {
|
||||
}
|
||||
return _getImprovementToCreate
|
||||
}
|
||||
fun getImprovementToCreate(ruleset: Ruleset, civInfo: Civilization): TileImprovement? {
|
||||
val improvement = getImprovementToCreate(ruleset) ?: return null
|
||||
return civInfo.getEquivalentTileImprovement(improvement)
|
||||
}
|
||||
|
||||
fun isSellable() = !isAnyWonder() && !hasUnique(UniqueType.Unsellable)
|
||||
|
||||
|
@ -13,6 +13,7 @@ import com.unciv.models.translations.tr
|
||||
import com.unciv.ui.components.extensions.colorFromRGB
|
||||
import com.unciv.ui.objectdescriptions.BaseUnitDescriptions
|
||||
import com.unciv.ui.objectdescriptions.BuildingDescriptions
|
||||
import com.unciv.ui.objectdescriptions.ImprovementDescriptions
|
||||
import com.unciv.ui.objectdescriptions.uniquesToCivilopediaTextLines
|
||||
import com.unciv.ui.screens.civilopediascreen.CivilopediaScreen.Companion.showReligionInCivilopedia
|
||||
import com.unciv.ui.screens.civilopediascreen.FormattedLine
|
||||
@ -246,17 +247,15 @@ class Nation : RulesetObject() {
|
||||
yield(FormattedLine(separator = true))
|
||||
yield(FormattedLine(improvement.name, link = "Improvement/${improvement.name}"))
|
||||
yield(FormattedLine(improvement.cloneStats().toString(), indent = 1)) // = (improvement as Stats).toString minus import plus copy overhead
|
||||
if (improvement.terrainsCanBeBuiltOn.isNotEmpty()) {
|
||||
improvement.terrainsCanBeBuiltOn.withIndex().forEach {
|
||||
yield(
|
||||
FormattedLine(if (it.index == 0) "{Can be built on} {${it.value}}" else "or [${it.value}]",
|
||||
link = "Terrain/${it.value}", indent = if (it.index == 0) 1 else 2)
|
||||
)
|
||||
}
|
||||
}
|
||||
for (unique in improvement.uniqueObjects) {
|
||||
if (unique.isHiddenToUsers()) continue
|
||||
yield(FormattedLine(unique, indent = 1))
|
||||
if (improvement.replaces != null && ruleset.tileImprovements.containsKey(improvement.replaces!!)) {
|
||||
val originalImprovement = ruleset.tileImprovements[improvement.replaces!!]!!
|
||||
yield(FormattedLine("Replaces [${originalImprovement.name}]", link = originalImprovement.makeLink(), indent=1))
|
||||
yieldAll(ImprovementDescriptions.getDifferences(ruleset, originalImprovement, improvement))
|
||||
yield(FormattedLine())
|
||||
} else if (improvement.replaces != null) {
|
||||
yield(FormattedLine("Replaces [${improvement.replaces}], which is not found in the ruleset!", indent=1))
|
||||
} else {
|
||||
yieldAll(improvement.getShortDecription())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,28 +1,24 @@
|
||||
package com.unciv.models.ruleset.tile
|
||||
|
||||
import com.unciv.Constants
|
||||
import com.unciv.UncivGame
|
||||
import com.unciv.logic.MultiFilter
|
||||
import com.unciv.logic.civilization.Civilization
|
||||
import com.unciv.logic.map.mapunit.MapUnit
|
||||
import com.unciv.logic.map.tile.RoadStatus
|
||||
import com.unciv.models.ruleset.Belief
|
||||
import com.unciv.models.ruleset.Ruleset
|
||||
import com.unciv.models.ruleset.RulesetStatsObject
|
||||
import com.unciv.models.ruleset.unique.StateForConditionals
|
||||
import com.unciv.models.ruleset.unique.UniqueTarget
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import com.unciv.models.ruleset.unit.BaseUnit
|
||||
import com.unciv.models.translations.tr
|
||||
import com.unciv.ui.components.extensions.toPercent
|
||||
import com.unciv.ui.objectdescriptions.uniquesToCivilopediaTextLines
|
||||
import com.unciv.ui.objectdescriptions.uniquesToDescription
|
||||
import com.unciv.ui.screens.civilopediascreen.CivilopediaScreen.Companion.showReligionInCivilopedia
|
||||
import com.unciv.ui.objectdescriptions.ImprovementDescriptions
|
||||
import com.unciv.ui.screens.civilopediascreen.FormattedLine
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class TileImprovement : RulesetStatsObject() {
|
||||
|
||||
var replaces: String? = null
|
||||
var terrainsCanBeBuiltOn: Collection<String> = ArrayList()
|
||||
var techRequired: String? = null
|
||||
var uniqueTo: String? = null
|
||||
@ -44,29 +40,8 @@ class TileImprovement : RulesetStatsObject() {
|
||||
// In some weird cases it was possible for something to take 0 turns, leading to it instead never finishing
|
||||
}
|
||||
|
||||
fun getDescription(ruleset: Ruleset): String {
|
||||
val lines = ArrayList<String>()
|
||||
|
||||
val statsDesc = cloneStats().toString()
|
||||
if (statsDesc.isNotEmpty()) lines += statsDesc
|
||||
if (!terrainsCanBeBuiltOn.isEmpty()) {
|
||||
val terrainsCanBeBuiltOnString: ArrayList<String> = arrayListOf()
|
||||
for (i in terrainsCanBeBuiltOn) {
|
||||
terrainsCanBeBuiltOnString.add(i.tr())
|
||||
}
|
||||
lines += "Can be built on".tr() + terrainsCanBeBuiltOnString.joinToString(", ", " ") //language can be changed when setting changes.
|
||||
}
|
||||
for (resource: TileResource in ruleset.tileResources.values.filter { it.isImprovedBy(name) }) {
|
||||
if (resource.improvementStats == null) continue
|
||||
val statsString = resource.improvementStats.toString()
|
||||
lines += "[${statsString}] <in [${resource.name}] tiles>".tr()
|
||||
}
|
||||
if (techRequired != null) lines += "Required tech: [$techRequired]".tr()
|
||||
|
||||
uniquesToDescription(lines)
|
||||
|
||||
return lines.joinToString("\n")
|
||||
}
|
||||
fun getDescription(ruleset: Ruleset): String = ImprovementDescriptions.getDescription(this, ruleset)
|
||||
fun getShortDecription() = ImprovementDescriptions.getShortDescription(this)
|
||||
|
||||
fun isGreatImprovement() = hasUnique(UniqueType.GreatImprovement)
|
||||
fun isRoad() = RoadStatus.values().any { it != RoadStatus.None && it.name == this.name }
|
||||
@ -100,6 +75,7 @@ class TileImprovement : RulesetStatsObject() {
|
||||
private fun matchesSingleFilter(filter: String): Boolean {
|
||||
return when (filter) {
|
||||
name -> true
|
||||
replaces -> true
|
||||
in Constants.all -> true
|
||||
"Improvement" -> true // For situations involving tileFilter
|
||||
"All Road" -> isRoad()
|
||||
@ -111,95 +87,10 @@ class TileImprovement : RulesetStatsObject() {
|
||||
|
||||
override fun makeLink() = "Improvement/$name"
|
||||
|
||||
override fun getCivilopediaTextLines(ruleset: Ruleset): List<FormattedLine> {
|
||||
val textList = ArrayList<FormattedLine>()
|
||||
override fun getCivilopediaTextLines(ruleset: Ruleset): List<FormattedLine> =
|
||||
ImprovementDescriptions.getCivilopediaTextLines(this, ruleset)
|
||||
|
||||
val statsDesc = cloneStats().toString()
|
||||
if (statsDesc.isNotEmpty()) textList += FormattedLine(statsDesc)
|
||||
|
||||
if (uniqueTo != null) {
|
||||
textList += FormattedLine()
|
||||
textList += FormattedLine("Unique to [$uniqueTo]", link="Nation/$uniqueTo")
|
||||
}
|
||||
|
||||
val constructorUnits = getConstructorUnits(ruleset)
|
||||
val creatingUnits = getCreatingUnits(ruleset)
|
||||
val creatorExists = constructorUnits.isNotEmpty() || creatingUnits.isNotEmpty()
|
||||
|
||||
if (creatorExists && terrainsCanBeBuiltOn.isNotEmpty()) {
|
||||
textList += FormattedLine()
|
||||
if (terrainsCanBeBuiltOn.size == 1) {
|
||||
with (terrainsCanBeBuiltOn.first()) {
|
||||
textList += FormattedLine("{Can be built on} {$this}", link="Terrain/$this")
|
||||
}
|
||||
} else {
|
||||
textList += FormattedLine("{Can be built on}:")
|
||||
terrainsCanBeBuiltOn.forEach {
|
||||
textList += FormattedLine(it, link="Terrain/$it", indent=1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var addedLineBeforeResourceBonus = false
|
||||
for (resource in ruleset.tileResources.values) {
|
||||
if (resource.improvementStats == null || !resource.isImprovedBy(name)) continue
|
||||
if (!addedLineBeforeResourceBonus) {
|
||||
addedLineBeforeResourceBonus = true
|
||||
textList += FormattedLine()
|
||||
}
|
||||
val statsString = resource.improvementStats.toString()
|
||||
// Line intentionally modeled as UniqueType.Stats + ConditionalInTiles
|
||||
textList += FormattedLine("[${statsString}] <in [${resource.name}] tiles>", link = resource.makeLink())
|
||||
}
|
||||
|
||||
if (techRequired != null) {
|
||||
textList += FormattedLine()
|
||||
textList += FormattedLine("Required tech: [$techRequired]", link="Technology/$techRequired")
|
||||
}
|
||||
|
||||
uniquesToCivilopediaTextLines(textList)
|
||||
|
||||
// Be clearer when one needs to chop down a Forest first... A "Can be built on Plains" is clear enough,
|
||||
// but a "Can be built on Land" is not - how is the user to know Forest is _not_ Land?
|
||||
if (creatorExists &&
|
||||
!isEmpty() && // Has any Stats
|
||||
!hasUnique(UniqueType.NoFeatureRemovalNeeded) &&
|
||||
!hasUnique(UniqueType.RemovesFeaturesIfBuilt) &&
|
||||
terrainsCanBeBuiltOn.none { it in ruleset.terrains }
|
||||
)
|
||||
textList += FormattedLine("Needs removal of terrain features to be built")
|
||||
|
||||
if (isAncientRuinsEquivalent() && ruleset.ruinRewards.isNotEmpty()) {
|
||||
val difficulty = if (!UncivGame.isCurrentInitialized() || UncivGame.Current.gameInfo == null)
|
||||
"Prince" // most factors == 1
|
||||
else UncivGame.Current.gameInfo!!.gameParameters.difficulty
|
||||
val religionEnabled = showReligionInCivilopedia(ruleset)
|
||||
textList += FormattedLine()
|
||||
textList += FormattedLine("The possible rewards are:")
|
||||
ruleset.ruinRewards.values.asSequence()
|
||||
.filter { reward ->
|
||||
difficulty !in reward.excludedDifficulties &&
|
||||
(religionEnabled || !reward.hasUnique(UniqueType.HiddenWithoutReligion))
|
||||
}
|
||||
.forEach { reward ->
|
||||
textList += FormattedLine(reward.name, starred = true, color = reward.color)
|
||||
textList += reward.civilopediaText
|
||||
}
|
||||
}
|
||||
|
||||
if (creatorExists)
|
||||
textList += FormattedLine()
|
||||
for (unit in constructorUnits)
|
||||
textList += FormattedLine("{Can be constructed by} {$unit}", unit.makeLink())
|
||||
for (unit in creatingUnits)
|
||||
textList += FormattedLine("{Can be created instantly by} {$unit}", unit.makeLink())
|
||||
|
||||
textList += Belief.getCivilopediaTextMatching(name, ruleset)
|
||||
|
||||
return textList
|
||||
}
|
||||
|
||||
private fun getConstructorUnits(ruleset: Ruleset): List<BaseUnit> {
|
||||
fun getConstructorUnits(ruleset: Ruleset): List<BaseUnit> {
|
||||
//todo Why does this have to be so complicated? A unit's "Can build [Land] improvements on tiles"
|
||||
// creates the _justified_ expectation that an improvement it can build _will_ have
|
||||
// `matchesFilter("Land")==true` - but that's not the case.
|
||||
@ -251,7 +142,7 @@ class TileImprovement : RulesetStatsObject() {
|
||||
}.toList()
|
||||
}
|
||||
|
||||
private fun getCreatingUnits(ruleset: Ruleset): List<BaseUnit> {
|
||||
fun getCreatingUnits(ruleset: Ruleset): List<BaseUnit> {
|
||||
return ruleset.units.values.asSequence()
|
||||
.filter { unit ->
|
||||
unit.getMatchingUniques(UniqueType.ConstructImprovementInstantly, StateForConditionals.IgnoreConditionals)
|
||||
|
@ -401,13 +401,15 @@ class RulesetValidator(val ruleset: Ruleset) {
|
||||
for (improvement in ruleset.tileImprovements.values) {
|
||||
if (improvement.techRequired != null && !ruleset.technologies.containsKey(improvement.techRequired!!))
|
||||
lines.add("${improvement.name} requires tech ${improvement.techRequired} which does not exist!", sourceObject = improvement)
|
||||
if (improvement.replaces != null && !ruleset.tileImprovements.containsKey(improvement.replaces))
|
||||
lines.add("${improvement.name} replaces ${improvement.replaces} which does not exist!", sourceObject = improvement)
|
||||
for (terrain in improvement.terrainsCanBeBuiltOn)
|
||||
if (!ruleset.terrains.containsKey(terrain) && terrain != "Land" && terrain != "Water")
|
||||
lines.add("${improvement.name} can be built on terrain $terrain which does not exist!", sourceObject = improvement)
|
||||
if (improvement.terrainsCanBeBuiltOn.isEmpty()
|
||||
&& !improvement.hasUnique(UniqueType.CanOnlyImproveResource)
|
||||
&& !improvement.hasUnique(UniqueType.Unbuildable)
|
||||
&& !improvement.name.startsWith(Constants.remove)
|
||||
&& improvement !in ruleset.tileRemovals
|
||||
&& improvement.name !in RoadStatus.values().map { it.removeAction }
|
||||
&& improvement.name != Constants.cancelImprovementOrder
|
||||
) {
|
||||
|
@ -0,0 +1,197 @@
|
||||
package com.unciv.ui.objectdescriptions
|
||||
|
||||
import com.unciv.UncivGame
|
||||
import com.unciv.models.ruleset.Belief
|
||||
import com.unciv.models.ruleset.Ruleset
|
||||
import com.unciv.models.ruleset.tile.TileImprovement
|
||||
import com.unciv.models.ruleset.tile.TileResource
|
||||
import com.unciv.models.ruleset.unique.Unique
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import com.unciv.models.translations.tr
|
||||
import com.unciv.ui.screens.civilopediascreen.CivilopediaScreen
|
||||
import com.unciv.ui.screens.civilopediascreen.FormattedLine
|
||||
|
||||
object ImprovementDescriptions {
|
||||
/**
|
||||
* Lists differences: how a nation-unique Improvement compares to its replacement.
|
||||
*
|
||||
* Result as indented, non-linking [FormattedLine]s
|
||||
*
|
||||
* @param originalImprovement The "standard" Improvement
|
||||
* @param replacementImprovement The "uniqueTo" Improvement
|
||||
*/
|
||||
fun getDifferences(
|
||||
ruleset: Ruleset, originalImprovement: TileImprovement, replacementImprovement: TileImprovement
|
||||
): Sequence<FormattedLine> = sequence {
|
||||
for ((key, value) in replacementImprovement)
|
||||
if (value != originalImprovement[key])
|
||||
yield(FormattedLine( key.name.tr() + " " +"[${value.toInt()}] vs [${originalImprovement[key].toInt()}]".tr(), indent=1))
|
||||
|
||||
for (terrain in replacementImprovement.terrainsCanBeBuiltOn)
|
||||
if (terrain !in originalImprovement.terrainsCanBeBuiltOn)
|
||||
yield(FormattedLine("Can be built on [${terrain}]", link = ruleset.terrains[terrain]?.makeLink() ?: "", indent = 1))
|
||||
for (terrain in originalImprovement.terrainsCanBeBuiltOn)
|
||||
if (terrain !in replacementImprovement.terrainsCanBeBuiltOn)
|
||||
yield(FormattedLine("Cannot be built on [${terrain}]", link = ruleset.terrains[terrain]?.makeLink() ?: "", indent = 1))
|
||||
|
||||
if (replacementImprovement.turnsToBuild != originalImprovement.turnsToBuild)
|
||||
yield(FormattedLine("{Turns to build} ".tr() + "[${replacementImprovement.turnsToBuild}] vs [${originalImprovement.turnsToBuild}]".tr(), indent=1))
|
||||
|
||||
val newAbilityPredicate: (Unique)->Boolean = { it.text in originalImprovement.uniques || it.isHiddenToUsers() }
|
||||
for (unique in replacementImprovement.uniqueObjects.filterNot(newAbilityPredicate))
|
||||
yield(FormattedLine(unique.text, indent=1)) // FormattedLine(unique) would look worse - no indent and autolinking could distract
|
||||
|
||||
val lostAbilityPredicate: (Unique)->Boolean = { it.text in replacementImprovement.uniques || it.isHiddenToUsers() }
|
||||
for (unique in originalImprovement.uniqueObjects.filterNot(lostAbilityPredicate)) {
|
||||
// Need double translation of the "ability" here - unique texts may contain square brackets
|
||||
yield(FormattedLine("Lost ability (vs [${originalImprovement.name}]): [${unique.text.tr()}]", indent=1))
|
||||
}
|
||||
}
|
||||
|
||||
fun getCivilopediaTextLines(improvement: TileImprovement, ruleset: Ruleset): List<FormattedLine> {
|
||||
val textList = ArrayList<FormattedLine>()
|
||||
|
||||
val statsDesc = improvement.cloneStats().toString()
|
||||
if (statsDesc.isNotEmpty()) textList += FormattedLine(statsDesc)
|
||||
|
||||
if (improvement.uniqueTo != null) {
|
||||
textList += FormattedLine()
|
||||
textList += FormattedLine("Unique to [${improvement.uniqueTo}]", link="Nation/${improvement.uniqueTo}")
|
||||
}
|
||||
if (improvement.replaces != null) {
|
||||
val replaceImprovement = ruleset.tileImprovements[improvement.replaces]
|
||||
textList += FormattedLine("Replaces [${improvement.replaces}]", link=replaceImprovement?.makeLink() ?: "", indent = 1)
|
||||
}
|
||||
|
||||
val constructorUnits = improvement.getConstructorUnits(ruleset)
|
||||
val creatingUnits = improvement.getCreatingUnits(ruleset)
|
||||
val creatorExists = constructorUnits.isNotEmpty() || creatingUnits.isNotEmpty()
|
||||
|
||||
if (creatorExists && improvement.terrainsCanBeBuiltOn.isNotEmpty()) {
|
||||
textList += FormattedLine()
|
||||
if (improvement.terrainsCanBeBuiltOn.size == 1) {
|
||||
with (improvement.terrainsCanBeBuiltOn.first()) {
|
||||
textList += FormattedLine("{Can be built on} {$this}", link="Terrain/$this")
|
||||
}
|
||||
} else {
|
||||
textList += FormattedLine("{Can be built on}:")
|
||||
improvement.terrainsCanBeBuiltOn.forEach {
|
||||
textList += FormattedLine(it, link="Terrain/$it", indent=1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var addedLineBeforeResourceBonus = false
|
||||
for (resource in ruleset.tileResources.values) {
|
||||
if (resource.improvementStats == null || !resource.isImprovedBy(improvement.name)) continue
|
||||
if (!addedLineBeforeResourceBonus) {
|
||||
addedLineBeforeResourceBonus = true
|
||||
textList += FormattedLine()
|
||||
}
|
||||
val statsString = resource.improvementStats.toString()
|
||||
// Line intentionally modeled as UniqueType.Stats + ConditionalInTiles
|
||||
textList += FormattedLine("[${statsString}] <in [${resource.name}] tiles>", link = resource.makeLink())
|
||||
}
|
||||
|
||||
if (improvement.techRequired != null) {
|
||||
textList += FormattedLine()
|
||||
textList += FormattedLine("Required tech: [${improvement.techRequired}]", link="Technology/${improvement.techRequired}")
|
||||
}
|
||||
|
||||
improvement.uniquesToCivilopediaTextLines(textList)
|
||||
|
||||
// Be clearer when one needs to chop down a Forest first... A "Can be built on Plains" is clear enough,
|
||||
// but a "Can be built on Land" is not - how is the user to know Forest is _not_ Land?
|
||||
if (creatorExists &&
|
||||
!improvement.isEmpty() && // Has any Stats
|
||||
!improvement.hasUnique(UniqueType.NoFeatureRemovalNeeded) &&
|
||||
!improvement.hasUnique(UniqueType.RemovesFeaturesIfBuilt) &&
|
||||
improvement.terrainsCanBeBuiltOn.none { it in ruleset.terrains }
|
||||
)
|
||||
textList += FormattedLine("Needs removal of terrain features to be built")
|
||||
|
||||
if (improvement.isAncientRuinsEquivalent() && ruleset.ruinRewards.isNotEmpty()) {
|
||||
val difficulty = if (!UncivGame.isCurrentInitialized() || UncivGame.Current.gameInfo == null)
|
||||
"Prince" // most factors == 1
|
||||
else UncivGame.Current.gameInfo!!.gameParameters.difficulty
|
||||
val religionEnabled = CivilopediaScreen.showReligionInCivilopedia(ruleset)
|
||||
textList += FormattedLine()
|
||||
textList += FormattedLine("The possible rewards are:")
|
||||
ruleset.ruinRewards.values.asSequence()
|
||||
.filter { reward ->
|
||||
difficulty !in reward.excludedDifficulties &&
|
||||
(religionEnabled || !reward.hasUnique(UniqueType.HiddenWithoutReligion))
|
||||
}
|
||||
.forEach { reward ->
|
||||
textList += FormattedLine(reward.name, starred = true, color = reward.color)
|
||||
textList += reward.civilopediaText
|
||||
}
|
||||
}
|
||||
|
||||
if (creatorExists)
|
||||
textList += FormattedLine()
|
||||
for (unit in constructorUnits)
|
||||
textList += FormattedLine("{Can be constructed by} {$unit}", unit.makeLink())
|
||||
for (unit in creatingUnits)
|
||||
textList += FormattedLine("{Can be created instantly by} {$unit}", unit.makeLink())
|
||||
|
||||
val seeAlso = ArrayList<FormattedLine>()
|
||||
for (alsoImprovement in ruleset.tileImprovements.values) {
|
||||
if (alsoImprovement.replaces == improvement.name)
|
||||
seeAlso += FormattedLine(alsoImprovement.name, link = alsoImprovement.makeLink(), indent = 1)
|
||||
}
|
||||
|
||||
seeAlso += Belief.getCivilopediaTextMatching(improvement.name, ruleset, false)
|
||||
|
||||
if (seeAlso.isNotEmpty()) {
|
||||
textList += FormattedLine()
|
||||
textList += FormattedLine("{See also}:")
|
||||
textList += seeAlso
|
||||
}
|
||||
|
||||
return textList
|
||||
}
|
||||
|
||||
fun getDescription(improvement: TileImprovement, ruleset: Ruleset): String {
|
||||
val lines = ArrayList<String>()
|
||||
|
||||
val statsDesc = improvement.cloneStats().toString()
|
||||
if (statsDesc.isNotEmpty()) lines += statsDesc
|
||||
if (improvement.uniqueTo != null) lines += "Unique to [${improvement.uniqueTo}]".tr()
|
||||
if (improvement.replaces != null) lines += "Replaces [${improvement.replaces}]".tr()
|
||||
if (!improvement.terrainsCanBeBuiltOn.isEmpty()) {
|
||||
val terrainsCanBeBuiltOnString: ArrayList<String> = arrayListOf()
|
||||
for (i in improvement.terrainsCanBeBuiltOn) {
|
||||
terrainsCanBeBuiltOnString.add(i.tr())
|
||||
}
|
||||
lines += "Can be built on".tr() + terrainsCanBeBuiltOnString.joinToString(", ", " ") //language can be changed when setting changes.
|
||||
}
|
||||
for (resource: TileResource in ruleset.tileResources.values.filter { it.isImprovedBy(improvement.name) }) {
|
||||
if (resource.improvementStats == null) continue
|
||||
val statsString = resource.improvementStats.toString()
|
||||
lines += "[${statsString}] <in [${resource.name}] tiles>".tr()
|
||||
}
|
||||
if (improvement.techRequired != null) lines += "Required tech: [${improvement.techRequired}]".tr()
|
||||
|
||||
improvement.uniquesToDescription(lines)
|
||||
|
||||
return lines.joinToString("\n")
|
||||
}
|
||||
|
||||
fun getShortDescription(improvement: TileImprovement) = sequence {
|
||||
if (improvement.terrainsCanBeBuiltOn.isNotEmpty()) {
|
||||
improvement.terrainsCanBeBuiltOn.withIndex().forEach {
|
||||
yield(
|
||||
FormattedLine(
|
||||
if (it.index == 0) "{Can be built on} {${it.value}}" else "or [${it.value}]",
|
||||
link = "Terrain/${it.value}", indent = if (it.index == 0) 1 else 2
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
for (unique in improvement.uniqueObjects) {
|
||||
if (unique.isHiddenToUsers()) continue
|
||||
yield(FormattedLine(unique, indent = 1))
|
||||
}
|
||||
}
|
||||
}
|
@ -660,7 +660,8 @@ class CityConstructionsTable(private val cityScreen: CityScreen) {
|
||||
return cityScreen.startPickTileForCreatesOneImprovement(construction, stat, true)
|
||||
// Buying a UniqueType.CreatesOneImprovement building from queue must pass down
|
||||
// the already selected tile, otherwise a new one is chosen from Automation code.
|
||||
val improvement = construction.getImprovementToCreate(cityScreen.city.getRuleset())!!
|
||||
val improvement = construction.getImprovementToCreate(
|
||||
cityScreen.city.getRuleset(), cityScreen.city.civ)!!
|
||||
val tileForImprovement = cityScreen.city.cityConstructions.getTileForImprovement(improvement.name)
|
||||
askToBuyConstruction(construction, stat, tileForImprovement)
|
||||
}
|
||||
|
@ -454,7 +454,7 @@ class CityScreen(
|
||||
fun selectConstruction(newConstruction: IConstruction) {
|
||||
selectedConstruction = newConstruction
|
||||
if (newConstruction is Building && newConstruction.hasCreateOneImprovementUnique()) {
|
||||
val improvement = newConstruction.getImprovementToCreate(city.getRuleset())
|
||||
val improvement = newConstruction.getImprovementToCreate(city.getRuleset(), city.civ)
|
||||
selectedQueueEntryTargetTile = if (improvement == null) null
|
||||
else city.cityConstructions.getTileForImprovement(improvement.name)
|
||||
} else {
|
||||
@ -472,7 +472,7 @@ class CityScreen(
|
||||
fun clearSelection() = selectTile(null)
|
||||
|
||||
fun startPickTileForCreatesOneImprovement(construction: Building, stat: Stat, isBuying: Boolean) {
|
||||
val improvement = construction.getImprovementToCreate(city.getRuleset()) ?: return
|
||||
val improvement = construction.getImprovementToCreate(city.getRuleset(), city.civ) ?: return
|
||||
pickTileData = PickTileForImprovementData(construction, improvement, isBuying, stat)
|
||||
updateTileGroups()
|
||||
ToastPopup("Please select a tile for this building's [${improvement.name}]", this)
|
||||
|
@ -45,6 +45,7 @@ Each improvement has the following structure:
|
||||
| name | String | Required | [^A] |
|
||||
| terrainsCanBeBuiltOn | List of Strings | empty | Terrains that this improvement can be built on [^B]. Removable terrain features will need to be removed before building an improvement [^C]. Must be in [Terrains.json](#terrainsjson) |
|
||||
| techRequired | String | none | The name of the technology required to build this improvement |
|
||||
| replaces | String | none | The name of a improvement that should be replaced by this improvement. Must be in [TileImprovements.json](#TileImprovementsjson) |
|
||||
| uniqueTo | String | none | The name of the nation this improvement is unique for |
|
||||
| [`<stats>`](#stats) | Integer | 0 | Per-turn bonus yield for the tile |
|
||||
| turnsToBuild | Integer | -1 | Number of turns a worker spends building this. If -1, the improvement is unbuildable [^D]. If 0, the improvement is always built in one turn |
|
||||
|
Loading…
Reference in New Issue
Block a user