Allowed specifying custom colors for unit promotions

This commit is contained in:
yairm210 2024-08-19 16:24:45 +03:00
parent 6944d8d5f5
commit 4d39c80c93
7 changed files with 47 additions and 20 deletions

View File

@ -4,6 +4,8 @@
"name": "Heal Instantly",
"uniques": ["Heal this unit by [50] HP", "Doing so will consume this opportunity to choose a Promotion"],
"unitTypes": ["Sword","Gunpowder","Mounted","Scout","Siege","Archery","Ranged Gunpowder","Armored","Melee Water","Ranged Water","Submarine"],
"innerColor": [195,53,43],
"outerColor": [253,236,234],
"row": 0,
"column": 0
},

View File

@ -3,7 +3,9 @@
{
"name": "Heal Instantly",
"uniques": ["Heal this unit by [50] HP", "Doing so will consume this opportunity to choose a Promotion"],
"unitTypes": ["Sword","Gunpowder","Mounted","Scout","Siege","Archery","Ranged Gunpowder","Armored","Melee Water","Ranged Water","Submarine"]
"unitTypes": ["Sword","Gunpowder","Mounted","Scout","Siege","Archery","Ranged Gunpowder","Armored","Melee Water","Ranged Water","Submarine"],
"innerColor": [195,53,43],
"outerColor": [253,236,234]
},
// Ranged+Siege

View File

@ -257,9 +257,7 @@ class Nation : RulesetObject() {
}
}
}
fun getContrastRatio() = getContrastRatio(getInnerColor(), getOuterColor())
fun matchesFilter(filter: String): Boolean {
return MultiFilter.multiFilter(filter, ::matchesSingleFilter)
}

View File

@ -5,6 +5,7 @@ import com.unciv.models.ruleset.RulesetObject
import com.unciv.models.ruleset.unique.UniqueTarget
import com.unciv.models.ruleset.unique.UniqueType
import com.unciv.models.translations.tr
import com.unciv.ui.components.extensions.colorFromRGB
import com.unciv.ui.objectdescriptions.uniquesToCivilopediaTextLines
import com.unciv.ui.objectdescriptions.uniquesToDescription
import com.unciv.ui.screens.civilopediascreen.FormattedLine
@ -15,6 +16,11 @@ class Promotion : RulesetObject() {
var unitTypes = listOf<String>() // The json parser wouldn't agree to deserialize this as a list of UnitTypes. =(
var innerColor: List<Int>? = null
val innerColorObject by lazy { if (innerColor == null) null else colorFromRGB(innerColor!!)}
var outerColor: List<Int>? = null
val outerColorObject by lazy { if (outerColor == null) null else colorFromRGB(outerColor!!)}
/** Used as **column** hint in the current [PromotionPickerScreen]
* This is no longer a direct position, it is used to sort before an automatic distribution.
* -1 determines that the modder has not set a position */

View File

@ -23,6 +23,7 @@ import com.unciv.models.tilesets.TileSetCache
import com.unciv.models.tilesets.TileSetConfig
import com.unciv.ui.images.AtlasPreview
import com.unciv.ui.images.Portrait
import com.unciv.ui.images.PortraitPromotion
class RulesetValidator(val ruleset: Ruleset) {
@ -644,7 +645,8 @@ class RulesetValidator(val ruleset: Ruleset) {
) {
for (promotion in ruleset.unitPromotions.values) {
uniqueValidator.checkUniques(promotion, lines, false, tryFixUnknownUniques)
checkContrasts(promotion.innerColorObject ?: PortraitPromotion.defaultInnerColor,
promotion.outerColorObject ?: PortraitPromotion.defaultOuterColor, promotion, lines)
addPromotionErrorRulesetInvariant(promotion, lines)
}
}
@ -673,10 +675,20 @@ class RulesetValidator(val ruleset: Ruleset) {
lines.add("${nation.name} can settle cities, but has no city names!", sourceObject = nation)
}
// https://www.w3.org/TR/WCAG20/#visual-audio-contrast-contrast
val constrastRatio = nation.getContrastRatio()
if (constrastRatio < 3) {
val (newInnerColor, newOuterColor) = getSuggestedColors(nation)
checkContrasts(nation.getInnerColor(), nation.getOuterColor(), nation, lines)
}
private fun checkContrasts(
innerColor: Color,
outerColor: Color,
nation: RulesetObject,
lines: RulesetErrorList
) {
val constrastRatio = getContrastRatio(innerColor, outerColor)
if (constrastRatio < 3) { // https://www.w3.org/TR/WCAG20/#visual-audio-contrast-contrast
val (newInnerColor, newOuterColor) = getSuggestedColors(innerColor, outerColor)
var text = "${nation.name}'s colors do not contrast enough - it is unreadable!"
text += "\nSuggested colors: "
@ -687,11 +699,12 @@ class RulesetValidator(val ruleset: Ruleset) {
}
}
data class SuggestedColors(val innerColor: Color, val outerColor: Color)
private fun getSuggestedColors(nation: Nation): SuggestedColors {
val innerColorLuminance = getRelativeLuminance(nation.getInnerColor())
val outerColorLuminance = getRelativeLuminance(nation.getOuterColor())
private fun getSuggestedColors(innerColor: Color, outerColor: Color): SuggestedColors {
val innerColorLuminance = getRelativeLuminance(innerColor)
val outerColorLuminance = getRelativeLuminance(outerColor)
val innerLerpColor: Color
val outerLerpColor: Color
@ -706,12 +719,12 @@ class RulesetValidator(val ruleset: Ruleset) {
for (i in 1..10) {
val newInnerColor = nation.getInnerColor().cpy().lerp(innerLerpColor, 0.05f * i)
val newOuterColor = nation.getOuterColor().cpy().lerp(outerLerpColor, 0.05f * i)
val newInnerColor = innerColor.cpy().lerp(innerLerpColor, 0.05f * i)
val newOuterColor = outerColor.cpy().lerp(outerLerpColor, 0.05f * i)
if (getContrastRatio(newInnerColor, newOuterColor) > 3) return SuggestedColors(newInnerColor, newOuterColor)
}
throw Exception("Error getting suggested colors for nation "+nation.name)
throw Exception("Error getting suggested colors")
}
private fun addBuildingErrorsRulesetInvariant(

View File

@ -223,7 +223,6 @@ class PortraitImprovement(name: String, size: Float, dim: Boolean = false, isPil
class PortraitNation(name: String, size: Float) : Portrait(Type.Nation, name, size, size*0.1f) {
override fun getDefaultImage(): Image {
val nation = ruleset.nations[imageName]
val isCityState = nation != null && nation.isCityState
val pathCityState = "NationIcons/CityState"
@ -240,7 +239,6 @@ class PortraitNation(name: String, size: Float) : Portrait(Type.Nation, name, si
ruleset.nations[imageName]?.getOuterColor() ?: Color.BLACK
override fun getDefaultOuterBackgroundTint(): Color = getDefaultImageTint()
override fun getDefaultImageTint(): Color = ruleset.nations[imageName]?.getInnerColor() ?: Color.WHITE
}
@ -280,9 +278,15 @@ class PortraitPromotion(name: String, size: Float) : Portrait(Type.Promotion, na
else -> ImageGetter.getImage(pathIconFallback)
}
}
override fun getDefaultImageTint(): Color = colorFromRGB(255, 226, 0)
override fun getDefaultImageTint(): Color = ruleset.unitPromotions[imageName]?.innerColorObject
?: defaultInnerColor
override fun getDefaultOuterBackgroundTint(): Color = getDefaultImageTint()
override fun getDefaultInnerBackgroundTint(): Color = colorFromRGB(0, 12, 49)
override fun getDefaultInnerBackgroundTint(): Color = ruleset.unitPromotions[imageName]?.outerColorObject
?: defaultOuterColor
companion object {
val defaultInnerColor = colorFromRGB(255, 226, 0)
val defaultOuterColor = colorFromRGB(0, 12, 49)
}
}

View File

@ -53,6 +53,8 @@ Each promotion has the following structure:
| unitTypes | List of Strings | empty | The unit types for which this promotion applies as specified in [UnitTypes.json](#unittypesjson) |
| uniques | List of Strings | empty | List of [unique abilities](../../uniques.md) this promotion grants to the units |
| civilopediaText | List | empty | See [civilopediaText chapter](5-Miscellaneous-JSON-files.md#civilopedia-text) |
| innerColor | List | empty | Color of the *icon* |
| outerColor | List | empty | Color of the *background* |
## UnitTypes.json