mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-05 07:49:17 +07:00
Refactor UncivShowableException (#6899)
This commit is contained in:
@ -1437,6 +1437,12 @@ Invisible to others =
|
|||||||
Bison =
|
Bison =
|
||||||
Cocoa =
|
Cocoa =
|
||||||
|
|
||||||
|
# Exceptions that _may_ be shown to the user
|
||||||
|
|
||||||
|
Building '[buildingName]' is buildable and therefore must either have an explicit cost or reference an existing tech. =
|
||||||
|
Nation [nationName] is not found! =
|
||||||
|
Unit [unitName] doesn't seem to exist! =
|
||||||
|
|
||||||
|
|
||||||
# In English we just paste all these conditionals at the end of each unique, but in your language that
|
# In English we just paste all these conditionals at the end of each unique, but in your language that
|
||||||
# may not turn into valid sentences. Therefore we have the following two translations to determine
|
# may not turn into valid sentences. Therefore we have the following two translations to determine
|
||||||
@ -1467,5 +1473,3 @@ ConditionalsPlacement =
|
|||||||
|
|
||||||
|
|
||||||
########################### AUTOMATICALLY GENERATED TRANSLATABLE STRINGS ###########################
|
########################### AUTOMATICALLY GENERATED TRANSLATABLE STRINGS ###########################
|
||||||
|
|
||||||
|
|
||||||
|
@ -22,9 +22,6 @@ import com.unciv.ui.audio.MusicTrackChooserFlags
|
|||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
|
||||||
class MissingModsException(val missingMods: String) : UncivShowableException("Missing mods: [$missingMods]")
|
|
||||||
open class UncivShowableException(errorText: String) : Exception(errorText)
|
|
||||||
|
|
||||||
class GameInfo {
|
class GameInfo {
|
||||||
//region Fields - Serialized
|
//region Fields - Serialized
|
||||||
var civilizations = mutableListOf<CivilizationInfo>()
|
var civilizations = mutableListOf<CivilizationInfo>()
|
||||||
|
24
core/src/com/unciv/logic/UncivExceptions.kt
Normal file
24
core/src/com/unciv/logic/UncivExceptions.kt
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package com.unciv.logic
|
||||||
|
|
||||||
|
import com.unciv.models.translations.tr
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An [Exception] wrapper marking an Exception as suitable to be shown to the user.
|
||||||
|
*
|
||||||
|
* @param [errorText] should be the _**untranslated**_ error message.
|
||||||
|
* Use [getLocalizedMessage] to get the translated [message], _**or**_ use auto-translating helpers like .toLabel() or FormattedLine().
|
||||||
|
* Usual formatting (`[] or {}`) applies, as does the need to include the text in templates.properties.
|
||||||
|
*/
|
||||||
|
open class UncivShowableException(
|
||||||
|
errorText: String,
|
||||||
|
override val cause: Throwable? = null
|
||||||
|
) : Exception(errorText) {
|
||||||
|
// override because we _definitely_ have a non-null message from [errorText]
|
||||||
|
override val message: String
|
||||||
|
get() = super.message!!
|
||||||
|
override fun getLocalizedMessage() = message.tr()
|
||||||
|
}
|
||||||
|
|
||||||
|
class MissingModsException(
|
||||||
|
val missingMods: String
|
||||||
|
) : UncivShowableException("Missing mods: [$missingMods]")
|
@ -56,13 +56,13 @@ class ModOptions : IHasUniques {
|
|||||||
var modUrl = ""
|
var modUrl = ""
|
||||||
var author = ""
|
var author = ""
|
||||||
var modSize = 0
|
var modSize = 0
|
||||||
|
|
||||||
@Deprecated("As of 3.18.15")
|
@Deprecated("As of 3.18.15")
|
||||||
var maxXPfromBarbarians = 30
|
var maxXPfromBarbarians = 30
|
||||||
|
|
||||||
override var uniques = ArrayList<String>()
|
override var uniques = ArrayList<String>()
|
||||||
|
|
||||||
// If these two are delegated with "by lazy", the mod download process crashes and burns
|
// If these two are delegated with "by lazy", the mod download process crashes and burns
|
||||||
// Instead, Ruleset.load sets them, which is preferable in this case anyway
|
// Instead, Ruleset.load sets them, which is preferable in this case anyway
|
||||||
override var uniqueObjects: List<Unique> = listOf()
|
override var uniqueObjects: List<Unique> = listOf()
|
||||||
override var uniqueMap: Map<String, List<Unique>> = mapOf()
|
override var uniqueMap: Map<String, List<Unique>> = mapOf()
|
||||||
@ -307,21 +307,21 @@ class Ruleset {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val difficultiesFile = folderHandle.child("Difficulties.json")
|
val difficultiesFile = folderHandle.child("Difficulties.json")
|
||||||
if (difficultiesFile.exists())
|
if (difficultiesFile.exists())
|
||||||
difficulties += createHashmap(json().fromJsonFile(Array<Difficulty>::class.java, difficultiesFile))
|
difficulties += createHashmap(json().fromJsonFile(Array<Difficulty>::class.java, difficultiesFile))
|
||||||
|
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
val victoryTypesFiles = folderHandle.child("VictoryTypes.json")
|
val victoryTypesFiles = folderHandle.child("VictoryTypes.json")
|
||||||
if (victoryTypesFiles.exists()) {
|
if (victoryTypesFiles.exists()) {
|
||||||
victories += createHashmap(json().fromJsonFile(Array<Victory>::class.java, victoryTypesFiles))
|
victories += createHashmap(json().fromJsonFile(Array<Victory>::class.java, victoryTypesFiles))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Add objects that might not be present in base ruleset mods, but are required
|
// Add objects that might not be present in base ruleset mods, but are required
|
||||||
if (modOptions.isBaseRuleset) {
|
if (modOptions.isBaseRuleset) {
|
||||||
// This one should be temporary
|
// This one should be temporary
|
||||||
@ -355,7 +355,7 @@ class Ruleset {
|
|||||||
for (building in buildings.values) {
|
for (building in buildings.values) {
|
||||||
if (building.cost == 0 && !building.hasUnique(UniqueType.Unbuildable)) {
|
if (building.cost == 0 && !building.hasUnique(UniqueType.Unbuildable)) {
|
||||||
val column = technologies[building.requiredTech]?.column
|
val column = technologies[building.requiredTech]?.column
|
||||||
?: throw UncivShowableException("Building (${building.name}) is buildable and therefore must either have an explicit cost or reference an existing tech")
|
?: throw UncivShowableException("Building '[${building.name}]' is buildable and therefore must either have an explicit cost or reference an existing tech.")
|
||||||
building.cost = if (building.isAnyWonder()) column.wonderCost else column.buildingCost
|
building.cost = if (building.isAnyWonder()) column.wonderCost else column.buildingCost
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -389,7 +389,7 @@ class Ruleset {
|
|||||||
forOptionsPopup: Boolean
|
forOptionsPopup: Boolean
|
||||||
) {
|
) {
|
||||||
val name = if (uniqueContainer is INamed) uniqueContainer.name else ""
|
val name = if (uniqueContainer is INamed) uniqueContainer.name else ""
|
||||||
|
|
||||||
for (unique in uniqueContainer.uniqueObjects) {
|
for (unique in uniqueContainer.uniqueObjects) {
|
||||||
val errors = checkUnique(
|
val errors = checkUnique(
|
||||||
unique,
|
unique,
|
||||||
@ -424,12 +424,12 @@ class Ruleset {
|
|||||||
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("$name's 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.",
|
"$name's 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" +
|
"$name's unique \"${unique.text}\" looks like it may be a misspelling of:\n" +
|
||||||
@ -503,7 +503,7 @@ class Ruleset {
|
|||||||
if (unique.type.targetTypes.none { uniqueTarget.canAcceptUniqueTarget(it) }
|
if (unique.type.targetTypes.none { uniqueTarget.canAcceptUniqueTarget(it) }
|
||||||
// the 'consume unit' conditional causes a triggerable unique to become a unit action
|
// the 'consume unit' conditional causes a triggerable unique to become a unit action
|
||||||
&& !(uniqueTarget==UniqueTarget.Unit
|
&& !(uniqueTarget==UniqueTarget.Unit
|
||||||
&& unique.isTriggerable
|
&& unique.isTriggerable
|
||||||
&& unique.conditionals.any { it.type == UniqueType.ConditionalConsumeUnit }))
|
&& unique.conditionals.any { it.type == UniqueType.ConditionalConsumeUnit }))
|
||||||
rulesetErrors.add(
|
rulesetErrors.add(
|
||||||
"$name's unique \"${unique.text}\" cannot be put on this type of object!",
|
"$name's unique \"${unique.text}\" cannot be put on this type of object!",
|
||||||
@ -681,10 +681,10 @@ class Ruleset {
|
|||||||
for (terrain in improvement.terrainsCanBeBuiltOn)
|
for (terrain in improvement.terrainsCanBeBuiltOn)
|
||||||
if (!terrains.containsKey(terrain) && terrain != "Land" && terrain != "Water")
|
if (!terrains.containsKey(terrain) && terrain != "Land" && terrain != "Water")
|
||||||
lines += "${improvement.name} can be built on terrain $terrain which does not exist!"
|
lines += "${improvement.name} can be built on terrain $terrain which does not exist!"
|
||||||
if (improvement.terrainsCanBeBuiltOn.isEmpty()
|
if (improvement.terrainsCanBeBuiltOn.isEmpty()
|
||||||
&& !improvement.hasUnique(UniqueType.CanOnlyImproveResource)
|
&& !improvement.hasUnique(UniqueType.CanOnlyImproveResource)
|
||||||
&& !improvement.hasUnique(UniqueType.Unbuildable)
|
&& !improvement.hasUnique(UniqueType.Unbuildable)
|
||||||
&& !improvement.name.startsWith(Constants.remove)
|
&& !improvement.name.startsWith(Constants.remove)
|
||||||
&& improvement.name !in RoadStatus.values().map { it.removeAction }
|
&& improvement.name !in RoadStatus.values().map { it.removeAction }
|
||||||
&& improvement.name != Constants.cancelImprovementOrder
|
&& improvement.name != Constants.cancelImprovementOrder
|
||||||
) {
|
) {
|
||||||
@ -792,7 +792,7 @@ class Ruleset {
|
|||||||
lines += "${policy.name} requires policy $prereq which does not exist!"
|
lines += "${policy.name} requires policy $prereq which does not exist!"
|
||||||
checkUniques(policy, lines, rulesetSpecific, forOptionsPopup)
|
checkUniques(policy, lines, rulesetSpecific, forOptionsPopup)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (policy in policyBranches.values.flatMap { it.policies + it })
|
for (policy in policyBranches.values.flatMap { it.policies + it })
|
||||||
if (policy != policies[policy.name])
|
if (policy != policies[policy.name])
|
||||||
lines += "More than one policy with the name ${policy.name} exists!"
|
lines += "More than one policy with the name ${policy.name} exists!"
|
||||||
@ -818,13 +818,13 @@ class Ruleset {
|
|||||||
for (unitType in unitTypes.values) {
|
for (unitType in unitTypes.values) {
|
||||||
checkUniques(unitType, lines, rulesetSpecific, forOptionsPopup)
|
checkUniques(unitType, lines, rulesetSpecific, forOptionsPopup)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (victoryType in victories.values) {
|
for (victoryType in victories.values) {
|
||||||
for (requiredUnit in victoryType.requiredSpaceshipParts)
|
for (requiredUnit in victoryType.requiredSpaceshipParts)
|
||||||
if (!units.contains(requiredUnit))
|
if (!units.contains(requiredUnit))
|
||||||
lines.add("Victory type ${victoryType.name} requires adding the non-existant unit $requiredUnit to the capital to win!", RulesetErrorSeverity.Warning)
|
lines.add("Victory type ${victoryType.name} requires adding the non-existant unit $requiredUnit to the capital to win!", RulesetErrorSeverity.Warning)
|
||||||
for (milestone in victoryType.milestoneObjects)
|
for (milestone in victoryType.milestoneObjects)
|
||||||
if (milestone.type == null)
|
if (milestone.type == null)
|
||||||
lines.add("Victory type ${victoryType.name} has milestone ${milestone.uniqueDescription} that is of an unknown type!", RulesetErrorSeverity.Error)
|
lines.add("Victory type ${victoryType.name} has milestone ${milestone.uniqueDescription} that is of an unknown type!", RulesetErrorSeverity.Error)
|
||||||
for (victory in victories.values)
|
for (victory in victories.values)
|
||||||
if (victory.name != victoryType.name && victory.milestones == victoryType.milestones)
|
if (victory.name != victoryType.name && victory.milestones == victoryType.milestones)
|
||||||
@ -863,10 +863,10 @@ object RulesetCache : HashMap<String,Ruleset>() {
|
|||||||
clear()
|
clear()
|
||||||
for (ruleset in BaseRuleset.values()) {
|
for (ruleset in BaseRuleset.values()) {
|
||||||
val fileName = "jsons/${ruleset.fullName}"
|
val fileName = "jsons/${ruleset.fullName}"
|
||||||
val fileHandle =
|
val fileHandle =
|
||||||
if (consoleMode) FileHandle(fileName)
|
if (consoleMode) FileHandle(fileName)
|
||||||
else Gdx.files.internal(fileName)
|
else Gdx.files.internal(fileName)
|
||||||
this[ruleset.fullName] = Ruleset().apply {
|
this[ruleset.fullName] = Ruleset().apply {
|
||||||
load(fileHandle, printOutput)
|
load(fileHandle, printOutput)
|
||||||
name = ruleset.fullName
|
name = ruleset.fullName
|
||||||
}
|
}
|
||||||
@ -949,7 +949,7 @@ object RulesetCache : HashMap<String,Ruleset>() {
|
|||||||
val loadedMods = mods.asSequence()
|
val loadedMods = mods.asSequence()
|
||||||
.filter { containsKey(it) }
|
.filter { containsKey(it) }
|
||||||
.map { this[it]!! }
|
.map { this[it]!! }
|
||||||
.filter { !it.modOptions.isBaseRuleset } +
|
.filter { !it.modOptions.isBaseRuleset } +
|
||||||
baseRuleset
|
baseRuleset
|
||||||
|
|
||||||
for (mod in loadedMods.sortedByDescending { it.modOptions.isBaseRuleset }) {
|
for (mod in loadedMods.sortedByDescending { it.modOptions.isBaseRuleset }) {
|
||||||
@ -982,7 +982,7 @@ object RulesetCache : HashMap<String,Ruleset>() {
|
|||||||
// This happens if a building is dependent on a tech not in the base ruleset
|
// This happens if a building is dependent on a tech not in the base ruleset
|
||||||
// because newRuleset.updateBuildingCosts() in getComplexRuleset() throws an error
|
// because newRuleset.updateBuildingCosts() in getComplexRuleset() throws an error
|
||||||
Ruleset.RulesetErrorList()
|
Ruleset.RulesetErrorList()
|
||||||
.apply { add(ex.message!!.tr(), Ruleset.RulesetErrorSeverity.Error) }
|
.apply { add(ex.message, Ruleset.RulesetErrorSeverity.Error) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,7 +122,7 @@ class MapEditorLoadTab(
|
|||||||
val rulesetIncompatibilities = map.getRulesetIncompatibility(ruleset)
|
val rulesetIncompatibilities = map.getRulesetIncompatibility(ruleset)
|
||||||
if (rulesetIncompatibilities.isNotEmpty()) {
|
if (rulesetIncompatibilities.isNotEmpty()) {
|
||||||
map.removeMissingTerrainModReferences(ruleset)
|
map.removeMissingTerrainModReferences(ruleset)
|
||||||
val message = "{This map has errors:}\n\n".tr() +
|
val message = "{This map has errors:}\n\n" +
|
||||||
rulesetIncompatibilities.sorted().joinToString("\n") { it.tr() } +
|
rulesetIncompatibilities.sorted().joinToString("\n") { it.tr() } +
|
||||||
"\n\n{The incompatible elements have been removed.}"
|
"\n\n{The incompatible elements have been removed.}"
|
||||||
ToastPopup(message, editorScreen, 4000L)
|
ToastPopup(message, editorScreen, 4000L)
|
||||||
@ -134,7 +134,7 @@ class MapEditorLoadTab(
|
|||||||
} catch (ex: Throwable) {
|
} catch (ex: Throwable) {
|
||||||
needPopup = false
|
needPopup = false
|
||||||
popup?.close()
|
popup?.close()
|
||||||
println("Error displaying map \"$chosenMap\": ${ex.localizedMessage}")
|
println("Error displaying map \"$chosenMap\": ${ex.message}")
|
||||||
Gdx.input.inputProcessor = editorScreen.stage
|
Gdx.input.inputProcessor = editorScreen.stage
|
||||||
ToastPopup("Error loading map!", editorScreen)
|
ToastPopup("Error loading map!", editorScreen)
|
||||||
}
|
}
|
||||||
@ -143,9 +143,9 @@ class MapEditorLoadTab(
|
|||||||
needPopup = false
|
needPopup = false
|
||||||
Gdx.app.postRunnable {
|
Gdx.app.postRunnable {
|
||||||
popup?.close()
|
popup?.close()
|
||||||
println("Error loading map \"$chosenMap\": ${ex.localizedMessage}")
|
println("Error loading map \"$chosenMap\": ${ex.message}")
|
||||||
ToastPopup("Error loading map!".tr() +
|
ToastPopup("{Error loading map!}" +
|
||||||
(if (ex is UncivShowableException) "\n" + ex.message else ""), editorScreen)
|
(if (ex is UncivShowableException) "\n{${ex.message}}" else ""), editorScreen)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user