mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-06 16:28:40 +07:00
Newgame Mod incompatibility toast (#5076)
This commit is contained in:
@ -293,4 +293,3 @@
|
||||
"uniques": ["[+50 Faith] whenever a Great Person is expended"]
|
||||
}
|
||||
]
|
||||
|
||||
|
@ -331,6 +331,10 @@ No human players selected! =
|
||||
Mods: =
|
||||
Base ruleset mods: =
|
||||
Extension mods: =
|
||||
The mod you selected is incorrectly defined! =
|
||||
The mod combination you selected is incorrectly defined! =
|
||||
The mod combination you selected has problems. =
|
||||
You can play it, but don't expect everything to work! =
|
||||
Base Ruleset =
|
||||
[amount] Techs =
|
||||
[amount] Nations =
|
||||
|
@ -17,6 +17,7 @@ import com.unciv.models.ruleset.unit.UnitType
|
||||
import com.unciv.models.stats.INamed
|
||||
import com.unciv.models.stats.NamedStats
|
||||
import com.unciv.models.stats.Stat
|
||||
import com.unciv.models.stats.Stats
|
||||
import com.unciv.models.translations.tr
|
||||
import com.unciv.ui.utils.colorFromRGB
|
||||
import kotlin.collections.set
|
||||
@ -353,7 +354,9 @@ class Ruleset {
|
||||
val improvementName = unique.params[0]
|
||||
if (improvementName !in tileImprovements)
|
||||
lines += "${unit.name} can place improvement $improvementName which does not exist!"
|
||||
else if (tileImprovements[improvementName]!!.firstOrNull() == null && !unit.hasUnique("Bonus for units in 2 tile radius 15%")) {
|
||||
else if ((tileImprovements[improvementName] as Stats).none() &&
|
||||
unit.isCivilian() &&
|
||||
!unit.hasUnique("Bonus for units in 2 tile radius 15%")) {
|
||||
lines += "${unit.name} can place improvement $improvementName which has no stats, preventing unit automation!"
|
||||
warningCount++
|
||||
}
|
||||
@ -498,6 +501,10 @@ object RulesetCache : HashMap<String,Ruleset>() {
|
||||
|
||||
fun getBaseRuleset() = this[BaseRuleset.Civ_V_Vanilla.fullName]!!.clone() // safeguard, so no-one edits the base ruleset by mistake
|
||||
|
||||
/**
|
||||
* Creates a combined [Ruleset] from a list of mods. If no baseRuleset is listed in [mods],
|
||||
* then the vanilla Ruleset is included automatically.
|
||||
*/
|
||||
fun getComplexRuleset(mods: LinkedHashSet<String>): Ruleset {
|
||||
val newRuleset = Ruleset()
|
||||
val loadedMods = mods.filter { containsKey(it) }.map { this[it]!! }
|
||||
@ -528,6 +535,21 @@ object RulesetCache : HashMap<String,Ruleset>() {
|
||||
return newRuleset
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs [Ruleset.checkModLinks] on a temporary [combined Ruleset][getComplexRuleset] for a list of [mods]
|
||||
*/
|
||||
fun checkCombinedModLinks(mods: LinkedHashSet<String>): Ruleset.CheckModLinksResult {
|
||||
return try {
|
||||
val newRuleset = getComplexRuleset(mods)
|
||||
newRuleset.modOptions.isBaseRuleset = true // This is so the checkModLinks finds all connections
|
||||
newRuleset.checkModLinks()
|
||||
} catch (ex: Exception) {
|
||||
// This happens if a building is dependent on a tech not in the base ruleset
|
||||
// because newRuleset.updateBuildingCosts() in getComplexRuleset() throws an error
|
||||
Ruleset.CheckModLinksResult(Ruleset.CheckModLinksStatus.Error, ex.localizedMessage)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Specialist: NamedStats() {
|
||||
|
@ -481,7 +481,7 @@ class CityConstructionsTable(private val cityScreen: CityScreen) {
|
||||
/** This tests whether the buy button should be _shown_ */
|
||||
private fun isConstructionPurchaseShown(construction: INonPerpetualConstruction, stat: Stat): Boolean {
|
||||
val city = cityScreen.city
|
||||
return construction.canBePurchasedWithStat(city, stat) || city.civInfo.gameInfo.gameParameters.godMode
|
||||
return construction.canBePurchasedWithStat(city, stat)
|
||||
}
|
||||
|
||||
/** This tests whether the buy button should be _enabled_ */
|
||||
|
@ -249,7 +249,7 @@ class FormattedLine (
|
||||
}
|
||||
|
||||
val fontSize = when {
|
||||
header in headerSizes.indices -> headerSizes[header]
|
||||
header in 1 until headerSizes.size -> headerSizes[header]
|
||||
size == Int.MIN_VALUE -> defaultSize
|
||||
else -> size
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import com.badlogic.gdx.scenes.scene2d.ui.Table
|
||||
import com.unciv.models.ruleset.Ruleset
|
||||
import com.unciv.models.ruleset.Ruleset.CheckModLinksResult
|
||||
import com.unciv.models.ruleset.RulesetCache
|
||||
import com.unciv.models.translations.tr
|
||||
import com.unciv.ui.utils.*
|
||||
|
||||
class ModCheckboxTable(
|
||||
@ -15,9 +16,10 @@ class ModCheckboxTable(
|
||||
onUpdate: (String) -> Unit
|
||||
): Table(){
|
||||
private val modRulesets = RulesetCache.values.filter { it.name != "" }
|
||||
private val baseRulesetCheckboxes = ArrayList<CheckBox>()
|
||||
private var lastToast: ToastPopup? = null
|
||||
|
||||
init {
|
||||
val baseRulesetCheckboxes = ArrayList<CheckBox>()
|
||||
val extensionRulesetModButtons = ArrayList<CheckBox>()
|
||||
|
||||
for (mod in modRulesets.sortedBy { it.name }) {
|
||||
@ -54,41 +56,48 @@ class ModCheckboxTable(
|
||||
|
||||
private fun checkBoxChanged(checkBox: CheckBox, mod: Ruleset): Boolean {
|
||||
if (checkBox.isChecked) {
|
||||
// First the quick standalone check
|
||||
val modLinkErrors = mod.checkModLinks()
|
||||
if (modLinkErrors.isNotOK()) {
|
||||
ToastPopup("The mod you selected is incorrectly defined!\n\n$modLinkErrors", screen)
|
||||
if (modLinkErrors.isError()) {
|
||||
lastToast?.close()
|
||||
val toastMessage =
|
||||
"The mod you selected is incorrectly defined!".tr() + "\n\n$modLinkErrors"
|
||||
lastToast = ToastPopup(toastMessage, screen, 5000L)
|
||||
if (modLinkErrors.isError()) {
|
||||
checkBox.isChecked = false
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Save selection for a rollback
|
||||
val previousMods = mods.toList()
|
||||
|
||||
if (mod.modOptions.isBaseRuleset)
|
||||
// Ensure only one base can be selected
|
||||
if (mod.modOptions.isBaseRuleset) {
|
||||
for (oldBaseRuleset in previousMods) // so we don't get concurrent modification exceptions
|
||||
if (modRulesets.firstOrNull { it.name == oldBaseRuleset }?.modOptions?.isBaseRuleset == true)
|
||||
if (RulesetCache[oldBaseRuleset]?.modOptions?.isBaseRuleset == true)
|
||||
mods.remove(oldBaseRuleset)
|
||||
baseRulesetCheckboxes.filter { it != checkBox }.forEach { it.isChecked = false }
|
||||
}
|
||||
mods.add(mod.name)
|
||||
|
||||
var complexModLinkCheck: CheckModLinksResult
|
||||
try {
|
||||
val newRuleset = RulesetCache.getComplexRuleset(mods)
|
||||
newRuleset.modOptions.isBaseRuleset = true // This is so the checkModLinks finds all connections
|
||||
complexModLinkCheck = newRuleset.checkModLinks()
|
||||
} catch (ex: Exception) {
|
||||
// This happens if a building is dependent on a tech not in the base ruleset
|
||||
// because newRuleset.updateBuildingCosts() in getComplexRuleset() throws an error
|
||||
complexModLinkCheck = CheckModLinksResult(Ruleset.CheckModLinksStatus.Error, ex.localizedMessage)
|
||||
}
|
||||
// Check over complete combination of selected mods
|
||||
val complexModLinkCheck = RulesetCache.checkCombinedModLinks(mods)
|
||||
if (complexModLinkCheck.isNotOK()) {
|
||||
lastToast?.close()
|
||||
val toastMessage = (
|
||||
if (complexModLinkCheck.isError()) "The mod combination you selected is incorrectly defined!"
|
||||
else "{The mod combination you selected has problems.}\n{You can play it, but don't expect everything to work!}"
|
||||
).tr() + "\n\n$complexModLinkCheck"
|
||||
lastToast = ToastPopup(toastMessage, screen, 5000L)
|
||||
|
||||
if (complexModLinkCheck.isError()) {
|
||||
ToastPopup("{The mod you selected is incompatible with the defined ruleset!}\n\n{$complexModLinkCheck}", screen)
|
||||
checkBox.isChecked = false
|
||||
mods.clear()
|
||||
mods.addAll(previousMods)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
mods.remove(mod.name)
|
||||
|
@ -1,6 +1,7 @@
|
||||
package com.unciv.ui.utils
|
||||
|
||||
import com.badlogic.gdx.Gdx
|
||||
import com.badlogic.gdx.scenes.scene2d.Touchable
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
/**
|
||||
@ -11,6 +12,7 @@ class ToastPopup (message: String, screen: CameraStageBaseScreen, val time: Long
|
||||
init {
|
||||
//Make this popup unobtrusive
|
||||
setFillParent(false)
|
||||
onClick { close() } // or `touchable = Touchable.disabled` so you can operate what's behind
|
||||
|
||||
addGoodSizedLabel(message)
|
||||
open()
|
||||
|
@ -12,7 +12,7 @@ import com.unciv.models.translations.tr
|
||||
/**
|
||||
* A **Replacement** for Gdx [Tooltip], placement does not follow the mouse.
|
||||
*
|
||||
* Usage: [group][Group].addStaticTip([text][String], size) builds a [Label] as tip actor and attaches it to your [Group].
|
||||
* Usage: [group][Group].addTooltip([text][String], size) builds a [Label] as tip actor and attaches it to your [Group].
|
||||
*
|
||||
* @param target The widget the tooltip will be added to - take care this is the same for which addListener is called
|
||||
* @param content The actor to display as Tooltip
|
||||
|
Reference in New Issue
Block a user