mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-04 15:27:50 +07:00
Unique docs writer makeover (#6516)
* Comments to 'link' implementation (matchFilter) to UniqueParameterType * Comments to 'link' UniqueParameterType to implementation (matchFilter) * Fix two mistakes in UniqueParameterType * Make "and" filter logic for MapUnit and BaseUnit modular, fix mistake * Make UnitMovementMemory immutable and without lateinit * Keep UniqueType comment promise that instances can override parameter types * Reorg UniqueDocsWriter so UniqueParameterType-specific text is moved to the enum Also some minor improvements - e.g. abbreviations sorted & consistent punctuation, less memory allocations, a/b/c type params get an example,... * New abilities for UniqueDocsWriter concerning UniqueTarget and inheritsFrom (initially deactivated) * Actually implement the wish for distinction between absolute and relative amounts * Change UniqueParameterType.UnitName.docExample to Xander's choice
This commit is contained in:
@ -1,103 +1,92 @@
|
||||
package com.unciv.app.desktop
|
||||
|
||||
import com.unciv.models.ruleset.Ruleset
|
||||
import com.unciv.models.ruleset.unique.UniqueParameterType
|
||||
import com.unciv.models.ruleset.unique.UniqueTarget
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import com.unciv.models.translations.fillPlaceholders
|
||||
import java.io.File
|
||||
|
||||
class UniqueDocsWriter {
|
||||
companion object {
|
||||
/**
|
||||
* Switch from each Unique shown once under one UniqueTarget heading chosen from targetTypes (`true`)
|
||||
* to showing each Unique repeatedly under each UniqueTarget heading it applies to (`false`).
|
||||
*/
|
||||
private const val showUniqueOnOneTarget = true
|
||||
|
||||
/** Switch **on** the display of _inherited_ UniqueTargets in "Applicable to:" */
|
||||
private const val showInheritedTargets = false
|
||||
|
||||
private fun UniqueTarget.allTargets(): Sequence<UniqueTarget> = sequence {
|
||||
if (showInheritedTargets && inheritsFrom != null) yieldAll(inheritsFrom!!.allTargets())
|
||||
yield(this@allTargets)
|
||||
}
|
||||
private fun UniqueType.allTargets(): Sequence<UniqueTarget> =
|
||||
targetTypes.asSequence().flatMap { it.allTargets() }.distinct()
|
||||
private fun UniqueTarget.allUniqueTypes(): Sequence<UniqueType> =
|
||||
UniqueType.values().asSequence().filter {
|
||||
this in it.targetTypes
|
||||
}
|
||||
}
|
||||
fun toLink(string: String): String {
|
||||
return "#" + string.split(' ').joinToString("-") { it.lowercase() }
|
||||
}
|
||||
|
||||
fun write() {
|
||||
val lines = ArrayList<String>()
|
||||
val targetTypesToUniques = UniqueType.values().groupBy { it.targetTypes.minOrNull()!! }
|
||||
.toSortedMap()
|
||||
// This will output each unique only once, even if it has several targets.
|
||||
// Each is grouped under the UniqueTarget is is allowed for with the lowest enum ordinal.
|
||||
// UniqueTarget.inheritsFrom is _not_ resolved for this.
|
||||
// The UniqueType are shown in enum order within their group, and groups are ordered
|
||||
// by their UniqueTarget.ordinal as well - source code order.
|
||||
val targetTypesToUniques: Map<UniqueTarget, List<UniqueType>> =
|
||||
if(showUniqueOnOneTarget)
|
||||
UniqueType.values().asSequence()
|
||||
.groupBy { it.targetTypes.minOrNull()!! }
|
||||
.toSortedMap()
|
||||
else
|
||||
// if, on the other hand, we wish to list every UniqueType with multiple targets under
|
||||
// _each_ of the groups it is applicable to, then this might do:
|
||||
UniqueTarget.values().asSequence().associateWith { target ->
|
||||
target.allTargets().flatMap { inheritedTarget ->
|
||||
inheritedTarget.allUniqueTypes()
|
||||
}.distinct().toList()
|
||||
}
|
||||
|
||||
fun replaceExamples(text:String):String {
|
||||
return text
|
||||
.replace("[amount]", "[20]")
|
||||
.replace("[combatantFilter]", "[City]")
|
||||
.replace("[mapUnitFilter]", "[Wounded]")
|
||||
.replace("[baseUnitFilter]", "[Melee]")
|
||||
.replace("[unit]","[Musketman]")
|
||||
.replace("[great person]", "[Great Scientist]")
|
||||
.replace("[stats]", "[+1 Gold, +2 Production]")
|
||||
.replace("[stat]", "[Culture]")
|
||||
.replace("[plunderableStat]", "[Gold]")
|
||||
.replace("[cityFilter]", "[in all cities]")
|
||||
.replace("[buildingName]", "[Library]")
|
||||
.replace("[buildingFilter]", "[Culture]")
|
||||
.replace("[constructionFilter]", "[Spaceship Part]")
|
||||
.replace("[terrainFilter]", "[Forest]")
|
||||
.replace("[tileFilter]", "[Farm]")
|
||||
.replace("[simpleTerrain]", "[Elevated]")
|
||||
.replace("[baseTerrain]", "[Grassland]")
|
||||
.replace("[regionType]", "[Hybrid]")
|
||||
.replace("[terrainQuality]","[Undesirable]")
|
||||
.replace("[promotion]","[Shock I]")
|
||||
.replace("[era]", "[Ancient era]")
|
||||
.replace("[improvementName]", "[Trading Post]")
|
||||
.replace("[improvementFilter]", "[All Road]")
|
||||
.replace("[resource]", "[Iron]")
|
||||
.replace("[beliefType]", "[Follower]")
|
||||
.replace("[belief]","[God of War]")
|
||||
.replace("[foundingOrEnhancing]", "[founding]")
|
||||
.replace("[tech]", "[Agriculture]")
|
||||
.replace("[specialist]","[Merchant]")
|
||||
.replace("[policy]", "[Oligarchy]")
|
||||
.replace("[victoryType]", "[Domination]")
|
||||
.replace("[costOrStrength]", "[Cost]")
|
||||
}
|
||||
|
||||
lines += "# Uniques\n" +
|
||||
"Simple unique parameters are explained by mouseover. Complex parameters are explained in [Unique parameter types](../unique parameters)"
|
||||
val capacity = 25 + targetTypesToUniques.size + UniqueType.values().size * (if (showUniqueOnOneTarget) 3 else 16)
|
||||
val lines = ArrayList<String>(capacity)
|
||||
lines += "# Uniques"
|
||||
lines += "Simple unique parameters are explained by mouseover. Complex parameters are explained in [Unique parameter types](../unique parameters)"
|
||||
|
||||
|
||||
val deprecatedUniques = ArrayList<UniqueType>()
|
||||
for (targetType in targetTypesToUniques) {
|
||||
lines += "## " + targetType.key.name + " uniques"
|
||||
for (uniqueType in targetType.value) {
|
||||
for ((targetType, uniqueTypes) in targetTypesToUniques) {
|
||||
if (uniqueTypes.isEmpty()) continue
|
||||
lines += "## " + targetType.name + " uniques"
|
||||
for (uniqueType in uniqueTypes) {
|
||||
if (uniqueType.getDeprecationAnnotation() != null) continue
|
||||
|
||||
val uniqueText = if (targetType.key == UniqueTarget.Conditional) "<${uniqueType.text}>"
|
||||
val uniqueText = if (targetType == UniqueTarget.Conditional) "<${uniqueType.text}>"
|
||||
else uniqueType.text
|
||||
lines += "??? example \"$uniqueText\"" // collapsable material mkdocs block, see https://squidfunk.github.io/mkdocs-material/reference/admonitions/?h=%3F%3F%3F#collapsible-blocks
|
||||
if (uniqueType.text.contains('['))
|
||||
lines += "\tExample: \"${replaceExamples(uniqueText)}\"\n"
|
||||
lines += "\tApplicable to: " + uniqueType.targetTypes.sorted().joinToString()
|
||||
if (uniqueType.parameterTypeMap.isNotEmpty()) {
|
||||
// This one will give examples for _each_ filter in a "tileFilter/specialist/buildingFilter" kind of parameter e.g. "Farm/Merchant/Library":
|
||||
// `val paramExamples = uniqueType.parameterTypeMap.map { it.joinToString("/") { pt -> pt.docExample } }.toTypedArray()`
|
||||
// Might confuse modders to think "/" can go into the _actual_ unique and mean "or", so better show just one ("Farm" in the example above):
|
||||
val paramExamples = uniqueType.parameterTypeMap.map { it.first().docExample }.toTypedArray()
|
||||
lines += "\tExample: \"${uniqueText.fillPlaceholders(*paramExamples)}\"\n"
|
||||
}
|
||||
lines += "\tApplicable to: " + uniqueType.allTargets().sorted().joinToString()
|
||||
lines += ""
|
||||
}
|
||||
}
|
||||
|
||||
// Abbreviations, for adding short unique parameter help - see https://squidfunk.github.io/mkdocs-material/reference/abbreviations/
|
||||
|
||||
// Abbreviations, for adding short unique parameter help - see https://squidfunk.github.io/mkdocs-material/reference/abbreviations/
|
||||
lines += ""
|
||||
lines += "*[amount]: This indicates a whole number, possibly with a + or - sign, such as `2`, `+13`, or `-3`."
|
||||
lines += "*[baseTerrain]: The name of any terrain that is a base terrain according to the json file."
|
||||
lines += "*[action]: An action that a unit can preform. Currently, there are only two actions part of this: 'Spread Religion' and 'Remove Foreign religions from your own cities'"
|
||||
lines += "*[belief]: The name of any belief"
|
||||
lines += "*[beliefType]: 'Pantheon', 'Follower', 'Founder' or 'Enhancer'."
|
||||
lines += "*[victoryType]: The name of any victory type: 'Neutral', 'Cultural', 'Diplomatic', 'Domination', 'Scientific', 'Time'"
|
||||
lines += "*[tech]: The name of any tech"
|
||||
lines += "*[resource]: The name of any resource"
|
||||
lines += "*[specialist]: The name of any specialist"
|
||||
lines += "*[promotion]: The name of any promotion"
|
||||
lines += "*[policy]: The name of any policy"
|
||||
lines += "*[improvementName]: The name of any improvement"
|
||||
lines += "*[buildingName]: The name of any building"
|
||||
lines += "*[era]: The name of any era"
|
||||
lines += "*[constructionFilter]: A filter for used when testing the current construction of a city. All values of `baseUnitFilter` and `buildingFilter` are allowed."
|
||||
lines += "*[foundingOrEnhancing]: `founding` or `enhancing`"
|
||||
lines += "*[costOrStrength]: `Cost` or `Strength`"
|
||||
lines += "*[combatantFilter]: This indicates a combatant, which can either be a unit or a city (when bombarding). Must either be `City` or a `mapUnitFilter`."
|
||||
lines += "*[plunderableStat]: All the following stats can be plundered: `Gold`, `Science`, `Culture`, `Faith`"
|
||||
lines += "*[tileFilter]: Anything that can be used either in an improvementFilter or in a tileFilter can be used here"
|
||||
lines += "*[stat]: This is one of the 7 major stats in the game - `Gold`, `Science`, `Production`, `Food`, `Happiness`, `Culture` and `Faith`. Note that the stat names need to be capitalized!"
|
||||
lines += "*[stats]: For example: `+2 Production, +3 Food`. Note that the stat names need to be capitalized!"
|
||||
|
||||
|
||||
// order irrelevant for rendered wiki, but could potentially reduce source control differences
|
||||
for (paramType in UniqueParameterType.values().asSequence().sortedBy { it.parameterName }) {
|
||||
if (paramType.docDescription == null) continue
|
||||
val punctuation = if (paramType.docDescription!!.last().category == '.'.category) "" else "."
|
||||
lines += "*[${paramType.parameterName}]: ${paramType.docDescription}$punctuation"
|
||||
}
|
||||
|
||||
File("../../docs/Modders/uniques.md").writeText(lines.joinToString("\n"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user