diff --git a/core/src/com/unciv/models/ruleset/unique/UniqueType.kt b/core/src/com/unciv/models/ruleset/unique/UniqueType.kt index 1a0f650d41..f9042e963d 100644 --- a/core/src/com/unciv/models/ruleset/unique/UniqueType.kt +++ b/core/src/com/unciv/models/ruleset/unique/UniqueType.kt @@ -887,6 +887,8 @@ enum class UniqueType( ModIsAudioVisualOnly("Should only be used as permanent audiovisual mod", UniqueTarget.ModOptions, flags = UniqueFlag.setOfNoConditionals), ModIsAudioVisual("Can be used as permanent audiovisual mod", UniqueTarget.ModOptions, flags = UniqueFlag.setOfNoConditionals), ModIsNotAudioVisual("Cannot be used as permanent audiovisual mod", UniqueTarget.ModOptions, flags = UniqueFlag.setOfNoConditionals), + ModMapPreselection("Mod preselects map [comment]", UniqueTarget.ModOptions, flags = UniqueFlag.setOfNoConditionals, + docDescription = "Only meaningful for Mods containing several maps. When this mod is selected on the new game screen's custom maps mod dropdown, the named map will be selected on the map dropdown. Also disables selection by recently modified. Case insensitive."), // endregion diff --git a/core/src/com/unciv/models/ruleset/validation/RulesetValidator.kt b/core/src/com/unciv/models/ruleset/validation/RulesetValidator.kt index 564e0226fb..6c1d22f769 100644 --- a/core/src/com/unciv/models/ruleset/validation/RulesetValidator.kt +++ b/core/src/com/unciv/models/ruleset/validation/RulesetValidator.kt @@ -123,6 +123,23 @@ class RulesetValidator(val ruleset: Ruleset) { // modOptions is a valid sourceObject, but unnecessary if (ruleset.modOptions.uniqueObjects.count { it.type in audioVisualUniqueTypes } > 1) lines.add("A mod should only specify one of the 'can/should/cannot be used as permanent audiovisual mod' options.", sourceObject = null) + + val mapSelectUniques = ruleset.modOptions.getMatchingUniques(UniqueType.ModMapPreselection).toList() + if (mapSelectUniques.size > 1) + lines.add("Specifying more than one map as preselection makes no sense", RulesetErrorSeverity.WarningOptionsOnly, sourceObject = null) + if (mapSelectUniques.isNotEmpty()) { + val mapsFolder = Gdx.files.local("mods").child(ruleset.name).child("maps") + if (mapsFolder.exists()) { + val maps = mapsFolder.list().map { it.name().lowercase() } + for (unique in mapSelectUniques) { + if (unique.params[0].lowercase() in maps) continue + lines.add("Mod names map '${unique.params[0]}' as preselection, which does not exist.", RulesetErrorSeverity.WarningOptionsOnly, sourceObject = null) + } + } else { + lines.add("Mod option for map preselection exists but Mod has no 'maps' folder.", RulesetErrorSeverity.WarningOptionsOnly, sourceObject = null) + } + } + if (!ruleset.modOptions.isBaseRuleset) return for (unique in ruleset.modOptions.getMatchingUniques(UniqueType.ModRequires)) { lines.add("Mod option '${unique.text}' is invalid for a base ruleset.", sourceObject = null) diff --git a/core/src/com/unciv/ui/screens/newgamescreen/MapFileSelectTable.kt b/core/src/com/unciv/ui/screens/newgamescreen/MapFileSelectTable.kt index 033623628f..bf8b48db95 100644 --- a/core/src/com/unciv/ui/screens/newgamescreen/MapFileSelectTable.kt +++ b/core/src/com/unciv/ui/screens/newgamescreen/MapFileSelectTable.kt @@ -18,6 +18,7 @@ import com.unciv.logic.map.TileMap import com.unciv.models.metadata.Player import com.unciv.models.ruleset.RulesetCache import com.unciv.models.ruleset.nation.Nation +import com.unciv.models.ruleset.unique.UniqueType import com.unciv.models.translations.tr import com.unciv.ui.components.SmallButtonStyle import com.unciv.ui.components.extensions.disable @@ -209,13 +210,22 @@ class MapFileSelectTable( .toGdxArray() fun getPreselect(): MapWrapper? { if (mapFiles.isEmpty) return null - val recent = mapFiles.asSequence() - .filter { it.fileHandle.isRecentlyModified() } - .maxByOrNull { it.fileHandle.lastModified() } - val oldestTimestamp = mapFiles.minOfOrNull { it.fileHandle.lastModified() } ?: 0L - // Do not use most recent if all maps in the category have the same time within a tenth of a second (like a mod unzip does) - if (recent != null && (recent.fileHandle.lastModified() - oldestTimestamp) > 100 || mapFiles.size == 1) - return recent + val modOptionPreselect = RulesetCache[selectedRuleset] + ?.modOptions?.getMatchingUniques(UniqueType.ModMapPreselection) + ?.firstOrNull()?.params?.get(0) + if (modOptionPreselect != null) { + val preselectFile = mapFiles.firstOrNull { it.fileHandle.name() == modOptionPreselect } + if (preselectFile != null) + return preselectFile + } else { + val recent = mapFiles.asSequence() + .filter { it.fileHandle.isRecentlyModified() } + .maxByOrNull { it.fileHandle.lastModified() } + val oldestTimestamp = mapFiles.minOfOrNull { it.fileHandle.lastModified() } ?: 0L + // Do not use most recent if all maps in the category have the same time within a tenth of a second (like a mod unzip does) + if (recent != null && (recent.fileHandle.lastModified() - oldestTimestamp) > 100 || mapFiles.size == 1) + return recent + } val named = mapFiles.firstOrNull { it.fileHandle.name() == preselectedName } if (named != null) return named diff --git a/docs/Modders/uniques.md b/docs/Modders/uniques.md index d1100a7c61..22222b9b5d 100644 --- a/docs/Modders/uniques.md +++ b/docs/Modders/uniques.md @@ -1921,6 +1921,12 @@ Simple unique parameters are explained by mouseover. Complex parameters are expl ??? example "Cannot be used as permanent audiovisual mod" Applicable to: ModOptions +??? example "Mod preselects map [comment]" + Only meaningful for Mods containing several maps. When this mod is selected on the new game screen's custom maps mod dropdown, the named map will be selected on the map dropdown. Also disables selection by recently modified. Case insensitive. + Example: "Mod preselects map [comment]" + + Applicable to: ModOptions + ## Conditional uniques !!! note ""