From 4d3f16c64e0faf0b57ee70b1b06447c85477d8ab Mon Sep 17 00:00:00 2001 From: Yair Morgenstern Date: Mon, 19 Oct 2020 23:07:01 +0300 Subject: [PATCH] Added "locate mod errors" button in the options menu for discovering broken links in base ruleset mods --- core/src/com/unciv/models/ruleset/Ruleset.kt | 137 +++++++++--------- .../unciv/ui/mapeditor/MapEditorMenuPopup.kt | 2 +- .../ui/worldscreen/mainmenu/OptionsPopup.kt | 20 +++ 3 files changed, 91 insertions(+), 68 deletions(-) diff --git a/core/src/com/unciv/models/ruleset/Ruleset.kt b/core/src/com/unciv/models/ruleset/Ruleset.kt index e3208e27d7..a71d57a0ad 100644 --- a/core/src/com/unciv/models/ruleset/Ruleset.kt +++ b/core/src/com/unciv/models/ruleset/Ruleset.kt @@ -105,7 +105,7 @@ class Ruleset { } - fun load(folderHandle: FileHandle, printOutput:Boolean) { + fun load(folderHandle: FileHandle, printOutput: Boolean) { val gameBasicsStartTime = System.currentTimeMillis() val modOptionsFile = folderHandle.child("ModOptions.json") @@ -176,7 +176,7 @@ class Ruleset { if (difficultiesFile.exists()) difficulties += createHashmap(jsonParser.getFromJson(Array::class.java, difficultiesFile)) val gameBasicsLoadTime = System.currentTimeMillis() - gameBasicsStartTime - if(printOutput) println("Loading ruleset - " + gameBasicsLoadTime + "ms") + if (printOutput) println("Loading ruleset - " + gameBasicsLoadTime + "ms") } /** Building costs are unique in that they are dependant on info in the technology part. @@ -211,6 +211,68 @@ class Ruleset { stringList += "" return stringList.joinToString() } + + + fun checkModLinks(): String { + val lines = ArrayList() + + // Checks for all mods + for (unit in units.values) + if (unit.upgradesTo == unit.name) + lines += "${unit.name} upgrades to itself!" + + for (tech in technologies.values) { + for (otherTech in tech.column!!.techs) { + if (tech != otherTech && otherTech.row == tech.row) + lines += "${tech.name} is in the same row as ${otherTech.name}!" + } + } + + if (!modOptions.isBaseRuleset) return lines.joinToString("\n") + + + for (unit in units.values) { + if (unit.requiredTech != null && !technologies.containsKey(unit.requiredTech!!)) + lines += "${unit.name} requires tech ${unit.requiredTech} which does not exist!" + if (unit.obsoleteTech != null && !technologies.containsKey(unit.obsoleteTech!!)) + lines += "${unit.name} obsoletes at tech ${unit.obsoleteTech} which does not exist!" + if (unit.requiredResource != null && !tileResources.containsKey(unit.requiredResource!!)) + lines += "${unit.name} requires resource ${unit.requiredResource} which does not exist!" + if (unit.upgradesTo != null && !units.containsKey(unit.upgradesTo!!)) + lines += "${unit.name} upgrades to unit ${unit.upgradesTo} which does not exist!" + if (unit.replaces != null && !units.containsKey(unit.replaces!!)) + lines += "${unit.replaces} replaces ${unit.replaces} which does not exist!" + } + + for (building in buildings.values) { + if (building.requiredTech != null && !technologies.containsKey(building.requiredTech!!)) + lines += "${building.name} requires tech ${building.requiredTech} which does not exist!" + if (building.requiredResource != null && !tileResources.containsKey(building.requiredResource!!)) + lines += "${building.name} requires resource ${building.requiredResource} which does not exist!" + if (building.replaces != null && !buildings.containsKey(building.replaces!!)) + lines += "${building.name} replaces ${building.replaces} which does not exist!" + } + + for (resource in tileResources.values) { + if (resource.revealedBy != null && !technologies.containsKey(resource.revealedBy!!)) + lines += "${resource.name} revealed by tech ${resource.revealedBy} which does not exist!" + if (resource.improvement != null && !tileImprovements.containsKey(resource.improvement!!)) + lines += "${resource.name} improved by improvement ${resource.improvement} which does not exist!" + } + + for (improvement in tileImprovements.values) { + if (improvement.techRequired != null && !technologies.containsKey(improvement.techRequired!!)) + lines += "${improvement.name} requires tech ${improvement.techRequired} which does not exist!" + } + + for (tech in technologies.values) { + for (prereq in tech.prerequisites) { + if (!technologies.containsKey(prereq)) + lines += "${tech.name} requires tech $prereq which does not exist!" + } + } + return lines.joinToString("\n") + } } /** Loading mods is expensive, so let's only do it once and @@ -226,8 +288,8 @@ object RulesetCache :HashMap() { this[ruleset.fullName] = Ruleset().apply { load(fileHandle, printOutput) } } - val modsHandles = if(consoleMode) FileHandle("mods").list() - else Gdx.files.local("mods").list() + val modsHandles = if (consoleMode) FileHandle("mods").list() + else Gdx.files.local("mods").list() for (modFolder in modsHandles) { if (modFolder.name().startsWith('.')) continue @@ -237,8 +299,10 @@ object RulesetCache :HashMap() { modRuleset.load(modFolder.child("jsons"), printOutput) modRuleset.name = modFolder.name() this[modRuleset.name] = modRuleset - if(printOutput) println("Mod loaded successfully: " + modRuleset.name) - if(printOutput) checkModLinks(modRuleset) + if (printOutput) { + println("Mod loaded successfully: " + modRuleset.name) + println(modRuleset.checkModLinks()) + } } catch (ex: Exception) { if (printOutput) { println("Exception loading mod '${modFolder.name()}':") @@ -249,55 +313,6 @@ object RulesetCache :HashMap() { } } - fun checkModLinks(modRuleset: Ruleset) { - for (unit in modRuleset.units.values) { - if (unit.requiredTech != null && !modRuleset.technologies.containsKey(unit.requiredTech!!)) - println("${unit.name} requires tech ${unit.requiredTech} which does not exist!") - if (unit.obsoleteTech != null && !modRuleset.technologies.containsKey(unit.obsoleteTech!!)) - println("${unit.name} obsoletes at tech ${unit.obsoleteTech} which does not exist!") - if (unit.requiredResource != null && !modRuleset.tileResources.containsKey(unit.requiredResource!!)) - println("${unit.name} requires resource ${unit.requiredResource} which does not exist!") - if (unit.upgradesTo != null && !modRuleset.units.containsKey(unit.upgradesTo!!)) - println("${unit.name} upgrades to unit ${unit.upgradesTo} which does not exist!") - if (unit.upgradesTo == unit.name) - println("${unit.name} upgrades to itself!") - if (unit.replaces != null && !modRuleset.units.containsKey(unit.replaces!!)) - println("${unit.replaces} replaces ${unit.replaces} which does not exist!") - } - - for (building in modRuleset.buildings.values) { - if (building.requiredTech != null && !modRuleset.technologies.containsKey(building.requiredTech!!)) - println("${building.name} requires tech ${building.requiredTech} which does not exist!") - if (building.requiredResource != null && !modRuleset.tileResources.containsKey(building.requiredResource!!)) - println("${building.name} requires resource ${building.requiredResource} which does not exist!") - if (building.replaces != null && !modRuleset.buildings.containsKey(building.replaces!!)) - println("${building.name} replaces ${building.replaces} which does not exist!") - } - - for (resource in modRuleset.tileResources.values) { - if (resource.revealedBy != null && !modRuleset.technologies.containsKey(resource.revealedBy!!)) - println("${resource.name} revealed by tech ${resource.revealedBy} which does not exist!") - if (resource.improvement != null && !modRuleset.tileImprovements.containsKey(resource.improvement!!)) - println("${resource.name} improved by improvement ${resource.improvement} which does not exist!") - } - - for (improvement in modRuleset.tileImprovements.values) { - if (improvement.techRequired != null && !modRuleset.technologies.containsKey(improvement.techRequired!!)) - println("${improvement.name} requires tech ${improvement.techRequired} which does not exist!") - } - - for (tech in modRuleset.technologies.values) { - for (prereq in tech.prerequisites) { - if (!modRuleset.technologies.containsKey(prereq)) - println("${tech.name} requires tech $prereq which does not exist!") - } - for (otherTech in tech.column!!.techs) { - if (tech != otherTech && otherTech.row == tech.row) - println("${tech.name} is in the same row as ${otherTech.name}!") - } - } - - } fun getBaseRuleset() = this[BaseRuleset.Civ_V_Vanilla.fullName]!! @@ -325,18 +340,6 @@ class Specialist: NamedStats() { var greatPersonPoints= Stats() companion object { - - fun convertStatsToSpecialistHashmap(stats: Stats): Counter { - val specialistHashmap = Counter() - for ((stat, amount) in stats.toHashMap()) { - if (amount == 0f) continue - val specialistName = specialistNameByStat(stat) - specialistHashmap[specialistName] = amount.toInt() - } - return specialistHashmap - } - - internal fun specialistNameByStat(stat: Stat) = when (stat) { Stat.Production -> "Engineer" Stat.Gold -> "Merchant" diff --git a/core/src/com/unciv/ui/mapeditor/MapEditorMenuPopup.kt b/core/src/com/unciv/ui/mapeditor/MapEditorMenuPopup.kt index 5c1516e9f5..2f9ffcc22a 100644 --- a/core/src/com/unciv/ui/mapeditor/MapEditorMenuPopup.kt +++ b/core/src/com/unciv/ui/mapeditor/MapEditorMenuPopup.kt @@ -71,7 +71,7 @@ class MapEditorMenuPopup(var mapEditorScreen: MapEditorScreen): Popup(mapEditorS tileGroup.update() } - }, mapEditorScreen).open() + }, mapEditorScreen).open(true) } add(clearCurrentMapButton).row() } diff --git a/core/src/com/unciv/ui/worldscreen/mainmenu/OptionsPopup.kt b/core/src/com/unciv/ui/worldscreen/mainmenu/OptionsPopup.kt index fa27f89fb6..fd49435cfb 100644 --- a/core/src/com/unciv/ui/worldscreen/mainmenu/OptionsPopup.kt +++ b/core/src/com/unciv/ui/worldscreen/mainmenu/OptionsPopup.kt @@ -8,6 +8,7 @@ import com.badlogic.gdx.utils.Array import com.unciv.MainMenuScreen import com.unciv.logic.civilization.PlayerType import com.unciv.models.UncivSound +import com.unciv.models.ruleset.RulesetCache import com.unciv.models.translations.TranslationFileWriter import com.unciv.models.translations.Translations import com.unciv.models.translations.tr @@ -133,6 +134,7 @@ class OptionsPopup(val previousScreen:CameraStageBaseScreen) : Popup(previousScr addSoundEffectsVolumeSlider() addMusicVolumeSlider() addTranslationGeneration() + addModPopup() addSetUserId() innerTable.add("Version".toLabel()).pad(10f) @@ -193,6 +195,24 @@ class OptionsPopup(val previousScreen:CameraStageBaseScreen) : Popup(previousScr } } + private fun addModPopup() { + val generateTranslationsButton = "Locate mod errors".toTextButton() + generateTranslationsButton.onClick { + var text = "" + for (mod in RulesetCache.values) { + val modLinks = mod.checkModLinks() + if (modLinks != "") + text += "\n\n" + mod.name + "\n\n" + modLinks + } + val popup = Popup(screen) + popup.add(ScrollPane(text.toLabel()).apply { setOverscroll(false,false) }) + .maxHeight(screen.stage.height / 2).row() + popup.addCloseButton() + popup.open(true) + } + innerTable.add(generateTranslationsButton).colspan(2).row() + } + private fun addSoundEffectsVolumeSlider() { innerTable.add("Sound effects volume".tr())