diff --git a/core/src/com/unciv/models/ruleset/validation/RulesetValidator.kt b/core/src/com/unciv/models/ruleset/validation/RulesetValidator.kt index 3d2d8be1d4..266582eedf 100644 --- a/core/src/com/unciv/models/ruleset/validation/RulesetValidator.kt +++ b/core/src/com/unciv/models/ruleset/validation/RulesetValidator.kt @@ -496,6 +496,13 @@ class RulesetValidator(val ruleset: Ruleset) { checkUnitRulesetSpecific(unit, lines) uniqueValidator.checkUniques(unit, lines, false, tryFixUnknownUniques) } + + // We start with the units that are further along the tech tree, since they are likely to contain previous units. + // This allows us to minimize the double-checking. + val checkedUnits = HashSet() + for (unit in ruleset.units.values.sortedByDescending { it.techColumn(ruleset)?.columnNumber }) + if (unit !in checkedUnits) + checkedUnits += checkUnitUpgradePath(unit, lines) } private fun addResourceErrorsRulesetInvariant( @@ -723,8 +730,6 @@ class RulesetValidator(val ruleset: Ruleset) { RulesetErrorSeverity.WarningOptionsOnly) } } - - checkUnitUpgradePath(unit, lines) } // for UnitTypes fallback, used if and only if the 'Ruleset Specific Part' runs, @@ -752,6 +757,7 @@ class RulesetValidator(val ruleset: Ruleset) { /** Checks all possible upgrade paths of [unit], reporting to [lines]. * @param path used in recursion collecting the BaseUnits seen so far + * @return units checked in this session - includes all units in this tree * * Note: Since the units down the path will also be checked, this could log the same mistakes * repeatedly, but that is mostly prevented by RulesetErrorList.add(). Each unit involved in a @@ -761,21 +767,23 @@ class RulesetValidator(val ruleset: Ruleset) { unit: BaseUnit, lines: RulesetErrorList, path: Set = emptySet() - ) { + ) : Set { // This is similar to UnitUpgradeManager.getUpgradePath but without the dependency on a Civilization instance // It also branches over all possible nation-unique replacements in one go, since we only look for loops. if (unit in path) { lines += "Circular or self-referencing upgrade path for ${unit.name}" - return + return setOf(unit) } - val upgrade = ruleset.units[unit.upgradesTo] ?: return + val upgrade = ruleset.units[unit.upgradesTo] ?: return setOf(unit) val newPath = path + unit // All Set additions are new Sets - we're recursing! val newPathWithReplacements = unitReplacesMap[unit.name]?.let { newPath + it } ?: newPath checkUnitUpgradePath(upgrade, lines, newPathWithReplacements) - val replacements = unitReplacesMap[upgrade.name] ?: return + val replacements = unitReplacesMap[upgrade.name] ?: return setOf(unit) + val checkedUnits = HashSet() for (toCheck in replacements) { - checkUnitUpgradePath(toCheck, lines, newPath) + checkedUnits += checkUnitUpgradePath(toCheck, lines, newPath) } + return checkedUnits } private fun checkTilesetSanity(lines: RulesetErrorList) {