mirror of
https://github.com/yairm210/Unciv.git
synced 2025-09-27 13:55:54 -04:00
Local mod folder names preserved. (#9844)
* Reduce conversion of local mod folder names to treat only ending spaces/dashes * Wiki entry for GlobalUniques * Missing originRuleset coverage * Nicer RulesetValidator messages when name or originRuleset are missing
This commit is contained in:
parent
b992144ecd
commit
443bf3afdb
@ -581,7 +581,7 @@ class GameInfo : IsPartOfGameInfoSerialization, HasGameInfoSerializationVersion
|
|||||||
|
|
||||||
// Cater for the mad modder using trailing '-' in their repo name - convert the mods list so
|
// Cater for the mad modder using trailing '-' in their repo name - convert the mods list so
|
||||||
// it requires our new, Windows-safe local name (no trailing blanks)
|
// it requires our new, Windows-safe local name (no trailing blanks)
|
||||||
for ((oldName, newName) in gameParameters.mods.map { it to it.repoNameToFolderName() }) {
|
for ((oldName, newName) in gameParameters.mods.map { it to it.repoNameToFolderName(onlyOuterBlanks = true) }) {
|
||||||
if (newName == oldName) continue
|
if (newName == oldName) continue
|
||||||
gameParameters.mods.remove(oldName)
|
gameParameters.mods.remove(oldName)
|
||||||
gameParameters.mods.add(newName)
|
gameParameters.mods.add(newName)
|
||||||
|
@ -230,6 +230,8 @@ class Ruleset {
|
|||||||
allRulesetObjects() + sequenceOf(modOptions)
|
allRulesetObjects() + sequenceOf(modOptions)
|
||||||
|
|
||||||
fun load(folderHandle: FileHandle) {
|
fun load(folderHandle: FileHandle) {
|
||||||
|
// Note: Most files are loaded using createHashmap, which sets originRuleset automatically.
|
||||||
|
// For other files containing IRulesetObject's we'll have to remember to do so manually - e.g. Tech.
|
||||||
val modOptionsFile = folderHandle.child("ModOptions.json")
|
val modOptionsFile = folderHandle.child("ModOptions.json")
|
||||||
if (modOptionsFile.exists()) {
|
if (modOptionsFile.exists()) {
|
||||||
try {
|
try {
|
||||||
@ -250,6 +252,7 @@ class Ruleset {
|
|||||||
for (tech in techColumn.techs) {
|
for (tech in techColumn.techs) {
|
||||||
if (tech.cost == 0) tech.cost = techColumn.techCost
|
if (tech.cost == 0) tech.cost = techColumn.techCost
|
||||||
tech.column = techColumn
|
tech.column = techColumn
|
||||||
|
tech.originRuleset = name
|
||||||
technologies[tech.name] = tech
|
technologies[tech.name] = tech
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -261,7 +264,10 @@ class Ruleset {
|
|||||||
val terrainsFile = folderHandle.child("Terrains.json")
|
val terrainsFile = folderHandle.child("Terrains.json")
|
||||||
if (terrainsFile.exists()) {
|
if (terrainsFile.exists()) {
|
||||||
terrains += createHashmap(json().fromJsonFile(Array<Terrain>::class.java, terrainsFile))
|
terrains += createHashmap(json().fromJsonFile(Array<Terrain>::class.java, terrainsFile))
|
||||||
for (terrain in terrains.values) terrain.setTransients()
|
for (terrain in terrains.values) {
|
||||||
|
terrain.originRuleset = name
|
||||||
|
terrain.setTransients()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val resourcesFile = folderHandle.child("TileResources.json")
|
val resourcesFile = folderHandle.child("TileResources.json")
|
||||||
@ -316,6 +322,7 @@ class Ruleset {
|
|||||||
// Append child policies of this branch
|
// Append child policies of this branch
|
||||||
for (policy in branch.policies) {
|
for (policy in branch.policies) {
|
||||||
policy.branch = branch
|
policy.branch = branch
|
||||||
|
policy.originRuleset = name
|
||||||
if (policy.requires == null) {
|
if (policy.requires == null) {
|
||||||
policy.requires = arrayListOf(branch.name)
|
policy.requires = arrayListOf(branch.name)
|
||||||
}
|
}
|
||||||
@ -366,6 +373,7 @@ class Ruleset {
|
|||||||
val globalUniquesFile = folderHandle.child("GlobalUniques.json")
|
val globalUniquesFile = folderHandle.child("GlobalUniques.json")
|
||||||
if (globalUniquesFile.exists()) {
|
if (globalUniquesFile.exists()) {
|
||||||
globalUniques = json().fromJsonFile(GlobalUniques::class.java, globalUniquesFile)
|
globalUniques = json().fromJsonFile(GlobalUniques::class.java, globalUniquesFile)
|
||||||
|
globalUniques.originRuleset = name
|
||||||
}
|
}
|
||||||
|
|
||||||
val victoryTypesFile = folderHandle.child("VictoryTypes.json")
|
val victoryTypesFile = folderHandle.child("VictoryTypes.json")
|
||||||
|
@ -475,8 +475,8 @@ class RulesetValidator(val ruleset: Ruleset) {
|
|||||||
namedObj: INamed?,
|
namedObj: INamed?,
|
||||||
severityToReport: UniqueType.UniqueComplianceErrorSeverity
|
severityToReport: UniqueType.UniqueComplianceErrorSeverity
|
||||||
): List<RulesetError> {
|
): List<RulesetError> {
|
||||||
var name = namedObj?.name ?: ""
|
val prefix = (if (namedObj is IRulesetObject) "${namedObj.originRuleset}: " else "") +
|
||||||
if (namedObj != null && namedObj is IRulesetObject) name = "${namedObj.originRuleset}: $name"
|
(if (namedObj == null) "The" else "${namedObj.name}'s")
|
||||||
if (unique.type == null) {
|
if (unique.type == null) {
|
||||||
if (!tryFixUnknownUniques) return emptyList()
|
if (!tryFixUnknownUniques) return emptyList()
|
||||||
val similarUniques = UniqueType.values().filter {
|
val similarUniques = UniqueType.values().filter {
|
||||||
@ -490,17 +490,17 @@ class RulesetValidator(val ruleset: Ruleset) {
|
|||||||
return when {
|
return when {
|
||||||
// Malformed conditional
|
// Malformed conditional
|
||||||
unique.text.count { it=='<' } != unique.text.count { it=='>' } ->listOf(
|
unique.text.count { it=='<' } != unique.text.count { it=='>' } ->listOf(
|
||||||
RulesetError("$name's unique \"${unique.text}\" contains mismatched conditional braces!",
|
RulesetError("$prefix unique \"${unique.text}\" contains mismatched conditional braces!",
|
||||||
RulesetErrorSeverity.Warning))
|
RulesetErrorSeverity.Warning))
|
||||||
|
|
||||||
// This should only ever happen if a bug is or has been introduced that prevents Unique.type from being set for a valid UniqueType, I think.\
|
// This should only ever happen if a bug is or has been introduced that prevents Unique.type from being set for a valid UniqueType, I think.\
|
||||||
equalUniques.isNotEmpty() -> listOf(RulesetError(
|
equalUniques.isNotEmpty() -> listOf(RulesetError(
|
||||||
"$name's unique \"${unique.text}\" looks like it should be fine, but for some reason isn't recognized.",
|
"$prefix unique \"${unique.text}\" looks like it should be fine, but for some reason isn't recognized.",
|
||||||
RulesetErrorSeverity.OK))
|
RulesetErrorSeverity.OK))
|
||||||
|
|
||||||
similarUniques.isNotEmpty() -> {
|
similarUniques.isNotEmpty() -> {
|
||||||
val text =
|
val text =
|
||||||
"$name's unique \"${unique.text}\" looks like it may be a misspelling of:\n" +
|
"$prefix unique \"${unique.text}\" looks like it may be a misspelling of:\n" +
|
||||||
similarUniques.joinToString("\n") { uniqueType ->
|
similarUniques.joinToString("\n") { uniqueType ->
|
||||||
var text = "\"${uniqueType.text}"
|
var text = "\"${uniqueType.text}"
|
||||||
if (unique.conditionals.isNotEmpty())
|
if (unique.conditionals.isNotEmpty())
|
||||||
@ -513,7 +513,7 @@ class RulesetValidator(val ruleset: Ruleset) {
|
|||||||
}
|
}
|
||||||
RulesetCache.modCheckerAllowUntypedUniques -> emptyList()
|
RulesetCache.modCheckerAllowUntypedUniques -> emptyList()
|
||||||
else -> listOf(RulesetError(
|
else -> listOf(RulesetError(
|
||||||
"$name's unique \"${unique.text}\" not found in Unciv's unique types.",
|
"$prefix unique \"${unique.text}\" not found in Unciv's unique types.",
|
||||||
RulesetErrorSeverity.OK))
|
RulesetErrorSeverity.OK))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -521,12 +521,12 @@ class RulesetValidator(val ruleset: Ruleset) {
|
|||||||
val rulesetErrors = RulesetErrorList()
|
val rulesetErrors = RulesetErrorList()
|
||||||
|
|
||||||
if (namedObj is IHasUniques && !unique.type.canAcceptUniqueTarget(namedObj.getUniqueTarget()))
|
if (namedObj is IHasUniques && !unique.type.canAcceptUniqueTarget(namedObj.getUniqueTarget()))
|
||||||
rulesetErrors.add(RulesetError("$name's unique \"${unique.text}\" is not allowed on its target type", RulesetErrorSeverity.Warning))
|
rulesetErrors.add(RulesetError("$prefix unique \"${unique.text}\" is not allowed on its target type", RulesetErrorSeverity.Warning))
|
||||||
|
|
||||||
val typeComplianceErrors = unique.type.getComplianceErrors(unique, ruleset)
|
val typeComplianceErrors = unique.type.getComplianceErrors(unique, ruleset)
|
||||||
for (complianceError in typeComplianceErrors) {
|
for (complianceError in typeComplianceErrors) {
|
||||||
if (complianceError.errorSeverity <= severityToReport)
|
if (complianceError.errorSeverity <= severityToReport)
|
||||||
rulesetErrors.add(RulesetError("$name's unique \"${unique.text}\" contains parameter ${complianceError.parameterName}," +
|
rulesetErrors.add(RulesetError("$prefix unique \"${unique.text}\" contains parameter ${complianceError.parameterName}," +
|
||||||
" which does not fit parameter type" +
|
" which does not fit parameter type" +
|
||||||
" ${complianceError.acceptableParameterTypes.joinToString(" or ") { it.parameterName }} !",
|
" ${complianceError.acceptableParameterTypes.joinToString(" or ") { it.parameterName }} !",
|
||||||
complianceError.errorSeverity.getRulesetErrorSeverity(severityToReport)
|
complianceError.errorSeverity.getRulesetErrorSeverity(severityToReport)
|
||||||
@ -536,13 +536,13 @@ class RulesetValidator(val ruleset: Ruleset) {
|
|||||||
for (conditional in unique.conditionals) {
|
for (conditional in unique.conditionals) {
|
||||||
if (conditional.type == null) {
|
if (conditional.type == null) {
|
||||||
rulesetErrors.add(
|
rulesetErrors.add(
|
||||||
"$name's unique \"${unique.text}\" contains the conditional \"${conditional.text}\"," +
|
"$prefix unique \"${unique.text}\" contains the conditional \"${conditional.text}\"," +
|
||||||
" which is of an unknown type!",
|
" which is of an unknown type!",
|
||||||
RulesetErrorSeverity.Warning
|
RulesetErrorSeverity.Warning
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
if (conditional.type.targetTypes.none { it.modifierType != UniqueTarget.ModifierType.None })
|
if (conditional.type.targetTypes.none { it.modifierType != UniqueTarget.ModifierType.None })
|
||||||
rulesetErrors.add("$name's unique \"${unique.text}\" contains the conditional \"${conditional.text}\"," +
|
rulesetErrors.add("$prefix unique \"${unique.text}\" contains the conditional \"${conditional.text}\"," +
|
||||||
" which is a Unique type not allowed as conditional or trigger.",
|
" which is a Unique type not allowed as conditional or trigger.",
|
||||||
RulesetErrorSeverity.Warning)
|
RulesetErrorSeverity.Warning)
|
||||||
|
|
||||||
@ -550,7 +550,7 @@ class RulesetValidator(val ruleset: Ruleset) {
|
|||||||
conditional.type.getComplianceErrors(conditional, ruleset)
|
conditional.type.getComplianceErrors(conditional, ruleset)
|
||||||
for (complianceError in conditionalComplianceErrors) {
|
for (complianceError in conditionalComplianceErrors) {
|
||||||
if (complianceError.errorSeverity == severityToReport)
|
if (complianceError.errorSeverity == severityToReport)
|
||||||
rulesetErrors.add(RulesetError( "$name's unique \"${unique.text}\" contains the conditional \"${conditional.text}\"." +
|
rulesetErrors.add(RulesetError( "$prefix unique \"${unique.text}\" contains the conditional \"${conditional.text}\"." +
|
||||||
" This contains the parameter ${complianceError.parameterName} which does not fit parameter type" +
|
" This contains the parameter ${complianceError.parameterName} which does not fit parameter type" +
|
||||||
" ${complianceError.acceptableParameterTypes.joinToString(" or ") { it.parameterName }} !",
|
" ${complianceError.acceptableParameterTypes.joinToString(" or ") { it.parameterName }} !",
|
||||||
complianceError.errorSeverity.getRulesetErrorSeverity(severityToReport)
|
complianceError.errorSeverity.getRulesetErrorSeverity(severityToReport)
|
||||||
@ -570,7 +570,7 @@ class RulesetValidator(val ruleset: Ruleset) {
|
|||||||
if (deprecationAnnotation != null) {
|
if (deprecationAnnotation != null) {
|
||||||
val replacementUniqueText = unique.getReplacementText(ruleset)
|
val replacementUniqueText = unique.getReplacementText(ruleset)
|
||||||
val deprecationText =
|
val deprecationText =
|
||||||
"$name's unique \"${unique.text}\" is deprecated ${deprecationAnnotation.message}," +
|
"$prefix unique \"${unique.text}\" is deprecated ${deprecationAnnotation.message}," +
|
||||||
if (deprecationAnnotation.replaceWith.expression != "") " replace with \"${replacementUniqueText}\"" else ""
|
if (deprecationAnnotation.replaceWith.expression != "") " replace with \"${replacementUniqueText}\"" else ""
|
||||||
val severity = if (deprecationAnnotation.level == DeprecationLevel.WARNING)
|
val severity = if (deprecationAnnotation.level == DeprecationLevel.WARNING)
|
||||||
RulesetErrorSeverity.WarningOptionsOnly // Not user-visible
|
RulesetErrorSeverity.WarningOptionsOnly // Not user-visible
|
||||||
|
@ -463,9 +463,10 @@ object Github {
|
|||||||
* Replaces '-' with blanks but ensures no leading or trailing blanks.
|
* Replaces '-' with blanks but ensures no leading or trailing blanks.
|
||||||
* As mad modders know no limits, trailing "-" did indeed happen, causing things to break due to trailing blanks on a folder name.
|
* As mad modders know no limits, trailing "-" did indeed happen, causing things to break due to trailing blanks on a folder name.
|
||||||
* As "test-" and "test" are different allowed repository names, trimmed blanks are replaced with one overscore per side.
|
* As "test-" and "test" are different allowed repository names, trimmed blanks are replaced with one overscore per side.
|
||||||
|
* @param onlyOuterBlanks If `true` ignores inner dashes - only start and end are treated. Useful when modders have manually creted local folder names using dashes.
|
||||||
*/
|
*/
|
||||||
fun String.repoNameToFolderName(): String {
|
fun String.repoNameToFolderName(onlyOuterBlanks: Boolean = false): String {
|
||||||
var result = replace('-', ' ')
|
var result = if (onlyOuterBlanks) this else replace('-', ' ')
|
||||||
if (result.endsWith(' ')) result = result.trimEnd() + outerBlankReplacement
|
if (result.endsWith(' ')) result = result.trimEnd() + outerBlankReplacement
|
||||||
if (result.startsWith(' ')) result = outerBlankReplacement + result.trimStart()
|
if (result.startsWith(' ')) result = outerBlankReplacement + result.trimStart()
|
||||||
return result
|
return result
|
||||||
|
@ -30,6 +30,7 @@ The JSON files that make up mods can have many different fields, and as not all
|
|||||||
- [Difficulties.json](5-Miscellaneous-JSON-files.md#difficultiesjson)
|
- [Difficulties.json](5-Miscellaneous-JSON-files.md#difficultiesjson)
|
||||||
- [Eras.json](5-Miscellaneous-JSON-files.md#erasjson)
|
- [Eras.json](5-Miscellaneous-JSON-files.md#erasjson)
|
||||||
- [ModOptions.json](5-Miscellaneous-JSON-files.md#modoptionsjson)
|
- [ModOptions.json](5-Miscellaneous-JSON-files.md#modoptionsjson)
|
||||||
|
- [GlobalUniques.json](5-Miscellaneous-JSON-files.md#globaluniquesjson)
|
||||||
- [Tutorials.json](5-Miscellaneous-JSON-files.md#tutorialsjson)
|
- [Tutorials.json](5-Miscellaneous-JSON-files.md#tutorialsjson)
|
||||||
- [Stats](3-Map-related-JSON-files.md#stats)
|
- [Stats](3-Map-related-JSON-files.md#stats)
|
||||||
- [Sounds](../Images-and-Audio.md#sounds)
|
- [Sounds](../Images-and-Audio.md#sounds)
|
||||||
|
@ -203,6 +203,14 @@ The formula for the gold cost of a unit upgrade is (rounded down to a multiple o
|
|||||||
) ^ `exponent`
|
) ^ `exponent`
|
||||||
With `civModifier` being the multiplicative aggregate of ["\[relativeAmount\]% Gold cost of upgrading"](../uniques.md#global-uniques) uniques that apply.
|
With `civModifier` being the multiplicative aggregate of ["\[relativeAmount\]% Gold cost of upgrading"](../uniques.md#global-uniques) uniques that apply.
|
||||||
|
|
||||||
|
## GlobalUniques.json
|
||||||
|
|
||||||
|
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).
|
||||||
|
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.
|
||||||
|
|
||||||
|
[link to original](https://github.com/yairm210/Unciv/tree/master/android/assets/jsons/GlobalUniques.json)
|
||||||
|
|
||||||
## Tutorials.json
|
## Tutorials.json
|
||||||
|
|
||||||
[link to original](https://github.com/yairm210/Unciv/tree/master/android/assets/jsons/Tutorials.json)
|
[link to original](https://github.com/yairm210/Unciv/tree/master/android/assets/jsons/Tutorials.json)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user