Compare commits

...
Author SHA1 Message Date
Guilherme Pereira 89a1da273e remove hsl colour mixing 2025-11-25 21:30:54 -03:00
Paul 64c7b7da67 Changelog 2025-11-24 19:28:56 +10:00
Paul 5209a77be2 Merge remote-tracking branch 'origin/main' into attempted-jvm-fix 2025-11-24 19:25:49 +10:00
Paul 567a5ff617 Try to fix crazy JVM issue 2025-11-24 15:28:49 +10:00
3 changed files with 50 additions and 124 deletions
+7 -8
View File
@@ -6,23 +6,22 @@
- Added brewing recipe for Throat Spray.
### Changes
- Convert the riding freelook button to a configurable keybinding.
- Reduced cost of the Vivichoke Seed trade with the Wandering Trader.
- Converted the riding freelook button to a configurable keybinding.
- Reduced the cost of the Vivichoke Seed trade with the Wandering Trader.
### Fixes
- Prevent displaying ride controls overlay to passengers.
- Fixed Poké Snack spawning sound positioning.
- Fixed Poké Snack spawning sounds not coming from the block.
- Fix player suffocation on vanilla mounts experienced on some worlds.
- Stop passengers from hearing the shiny noise of a ridden shiny Pokémon.
- Ponigiri can no longer be eaten at full hunger.
- Medicinal Brew's Campfire Pot recipe now correctly displays that it can be made using an empty glass bottle or a filled bottle,
- Fix incorrect camera pivot on Bird, Jet, and Dolphin mounts.
- Fixed Combees not depositing honey upon leaving a hive if they entered with nectar.
- Safer reading of brain memories in hive mixins.
- Fixed crash when opening a PC box with certain wallpapers when using *VulkanMod*.
- Fix player suffocation on vanilla mounts experienced on some worlds.
- Stop passengers from hearing the shiny noise of a ridden shiny Pokémon.
- Fix a glitch where friendship would reset to the default value when evolving a Pokémon.
- Fixed a crash involving Combees and hives.
- Fixed a crash when opening a PC box with certain wallpapers while using *VulkanMod*.
- Fixed friendship resetting to the default value when evolving a Pokémon.
- Fixed a graphics crash that could occur on some machines when campfires were nearby.
### Developer
@@ -9,13 +9,8 @@
package com.cobblemon.mod.common.api.cooking
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.util.math.geometry.toDegrees
import com.cobblemon.mod.common.util.math.geometry.toRadians
import net.minecraft.util.FastColor
import net.minecraft.world.item.ItemStack
import kotlin.math.atan2
import kotlin.math.cos
import kotlin.math.sin
private val colourMap = mapOf(
Flavour.SPICY to 0xFEB37D,
@@ -34,27 +29,30 @@ private val bubbleColourMap = mapOf(
)
fun getColourMixFromSeasonings(seasonings: List<ItemStack>, forBubbles: Boolean = false): Int? {
val flavors = seasonings
.mapNotNull { Seasonings.getFlavoursFromItemStack(it) }
.flatMap { it.entries }
.groupingBy { it.key }
.fold(0) { acc, entry -> acc + entry.value }
val flavourValues = mutableMapOf<Flavour, Int>()
for (seasoning in seasonings) {
val flavours = Seasonings.getFlavoursFromItemStack(seasoning) ?: continue
for ((flavour, value) in flavours.entries) {
flavourValues[flavour] = (flavourValues[flavour] ?: 0) + value
}
}
if (flavors.isEmpty()) return null
if (flavourValues.isEmpty()) {
return null
}
val maxFlavorValue = flavors.values.maxOrNull()
val dominantFlavors = flavors.filter { it.value == maxFlavorValue }.map { it.key }
val maxFlavorValue = flavourValues.values.maxOrNull()
val dominantFlavours = flavourValues.filter { it.value == maxFlavorValue }.map { it.key }
return getColourMixFromFlavours(dominantFlavors, forBubbles)
return getColourMixFromFlavours(dominantFlavours, forBubbles)
}
fun getColourMixFromFlavours(dominantFlavours: List<Flavour>, forBubbles: Boolean = false): Int? {
val colors =
dominantFlavours.mapNotNull { if (forBubbles) bubbleColourMap[it] else colourMap[it] }
dominantFlavours
.mapNotNull { if (forBubbles) bubbleColourMap[it] else colourMap[it] }
.map { FastColor.ARGB32.opaque(it) }
if (colors.isEmpty()) return null
return getColourMixFromColors(colors)
}
@@ -62,104 +60,25 @@ fun getColourMixFromRideStatBoosts(dominantBoosts: Iterable<RidingStat>, forBubb
return getColourMixFromFlavours(dominantBoosts.map { it.flavour }, forBubbles)
}
// Mixing RGB colors directly often gives poor results
// Converting them to HSL and averaging hues circularly produces more natural blends
fun getColourMixFromColors(colors: List<Int>): Int? {
if (colors.isEmpty()) return null
if (colors.size == 1) return colors[0]
val hslColors = colors.map {
val red = FastColor.ARGB32.red(it)
val green = FastColor.ARGB32.green(it)
val blue = FastColor.ARGB32.blue(it)
rgbToHsl(red, green, blue)
var alphaSum = 0
var redSum = 0
var greenSum = 0
var blueSum = 0
for (color in colors) {
alphaSum += FastColor.ARGB32.alpha(color)
redSum += FastColor.ARGB32.red(color)
greenSum += FastColor.ARGB32.green(color)
blueSum += FastColor.ARGB32.blue(color)
}
val hues = hslColors.map { it.h.toDouble() }
val huesAverage = hueAverage(hues).toFloat()
val saturations = hslColors.map { it.s }
val saturationsAverage = average(saturations)
val lightness = hslColors.map { it.l }
val lightnessAverage = average(lightness)
val alpha = alphaSum / colors.size
val red = redSum / colors.size
val green = greenSum / colors.size
val blue = blueSum / colors.size
val rgbColor = hslToRgb(huesAverage, saturationsAverage, lightnessAverage)
return FastColor.ARGB32.color(rgbColor.r, rgbColor.g, rgbColor.b)
}
// Based on https://iter.ca/post/hue-avg
fun hueAverage(hues: List<Double>): Float {
val points = hues.map { hue ->
val rad = hue.toRadians()
Pair(cos(rad), sin(rad))
}
val avgX = points.sumOf { it.first.toDouble() } / points.size
val avgY = points.sumOf { it.second.toDouble() } / points.size
var degrees = atan2(avgY, avgX).toDegrees()
if (degrees < 0) {
degrees += 360f
} else if (degrees > 360) {
degrees -= 360f
}
return degrees
}
fun average(values: List<Float>): Float {
if (values.isEmpty()) return 0f
return values.sum() / values.size
}
data class HSL(val h: Float, val s: Float, val l: Float)
fun rgbToHsl(r: Int, g: Int, b: Int): HSL {
val rf = r / 255f
val gf = g / 255f
val bf = b / 255f
val max = maxOf(rf, gf, bf)
val min = minOf(rf, gf, bf)
val delta = max - min
var h = 0f
val l = (max + min) / 2f
val s = if (delta == 0f) 0f else delta / (1f - kotlin.math.abs(2f * l - 1f))
if (delta != 0f) {
h = when (max) {
rf -> ((gf - bf) / delta) % 6f
gf -> ((bf - rf) / delta) + 2f
else -> ((rf - gf) / delta) + 4f
}
h *= 60f
if (h < 0) h += 360f
}
return HSL(h, s, l)
}
data class RGB(val r: Int, val g: Int, val b: Int)
fun hslToRgb(h: Float, s: Float, l: Float): RGB {
val c = (1f - kotlin.math.abs(2f * l - 1f)) * s
val x = c * (1f - kotlin.math.abs((h / 60f) % 2f - 1f))
val m = l - c / 2f
val (rf, gf, bf) = when {
h < 60f -> Triple(c, x, 0f)
h < 120f -> Triple(x, c, 0f)
h < 180f -> Triple(0f, c, x)
h < 240f -> Triple(0f, x, c)
h < 300f -> Triple(x, 0f, c)
else -> Triple(c, 0f, x)
}
return RGB(
((rf + m) * 255).toInt().coerceIn(0, 255),
((gf + m) * 255).toInt().coerceIn(0, 255),
((bf + m) * 255).toInt().coerceIn(0, 255)
)
return FastColor.ARGB32.color(alpha, red, green, blue)
}
@@ -111,13 +111,18 @@ class CampfireBlockEntity(pos: BlockPos, state: BlockState) : BaseContainerBlock
BlockEntitySoundTracker.stop(pos, campfireBlockEntity.ambientSound.location)
}
campfireBlockEntity.brothColor =
getColourMixFromSeasonings(campfireBlockEntity.getSeasonings())
?: BASE_BROTH_COLOR
val seasonings = campfireBlockEntity.getSeasonings()
if (seasonings.size != campfireBlockEntity.lastSeasoningStacks.size || seasonings.any { it !in campfireBlockEntity.lastSeasoningStacks }) {
campfireBlockEntity.lastSeasoningStacks = seasonings
campfireBlockEntity.bubbleColor =
getColourMixFromSeasonings(campfireBlockEntity.getSeasonings(), true)
?: BASE_BROTH_BUBBLE_COLOR
campfireBlockEntity.brothColor =
getColourMixFromSeasonings(seasonings)
?: BASE_BROTH_COLOR
campfireBlockEntity.bubbleColor =
getColourMixFromSeasonings(seasonings, true)
?: BASE_BROTH_BUBBLE_COLOR
}
if (campfireBlockEntity.particleCooldown > 0) {
campfireBlockEntity.particleCooldown--
@@ -241,6 +246,9 @@ class CampfireBlockEntity(pos: BlockPos, state: BlockState) : BaseContainerBlock
var bubbleColor: Int = BASE_BROTH_BUBBLE_COLOR
var time: Int = 0
/** Used to know if we should be changing the colour of the broth or if we can go with the previous colour. */
var lastSeasoningStacks = listOf<ItemStack>()
var dataAccess: ContainerData = object : ContainerData {
override fun get(index: Int): Int {
return when (index) {