From 3d684e815757c8ac2b7b758a4b48f22c34bce80b Mon Sep 17 00:00:00 2001 From: NoMathExpectation <85624722+NoMathExpectation@users.noreply.github.com> Date: Thu, 12 Mar 2026 03:06:58 +0800 Subject: [PATCH] Added SetThemeEvent. --- .../cn/rdlevel/rdkt/core/data/Easing.kt | 100 +++++ .../core/data/SelectedRoomsAndTopLayer.kt | 4 + .../cn/rdlevel/rdkt/core/data/action/Theme.kt | 397 ++++++++++++++++++ .../rdlevel/rdkt/core/events/SetThemeEvent.kt | 24 ++ .../FieldFlatteningSerializer.kt | 14 +- .../rdkt/core/test/SetThemeEventTest.kt | 38 ++ 6 files changed, 571 insertions(+), 6 deletions(-) create mode 100644 core/src/commonMain/kotlin/cn/rdlevel/rdkt/core/data/Easing.kt create mode 100644 core/src/commonMain/kotlin/cn/rdlevel/rdkt/core/data/action/Theme.kt create mode 100644 core/src/commonMain/kotlin/cn/rdlevel/rdkt/core/events/SetThemeEvent.kt create mode 100644 core/src/commonTest/kotlin/cn/rdlevel/rdkt/core/test/SetThemeEventTest.kt diff --git a/core/src/commonMain/kotlin/cn/rdlevel/rdkt/core/data/Easing.kt b/core/src/commonMain/kotlin/cn/rdlevel/rdkt/core/data/Easing.kt new file mode 100644 index 0000000..1374f0f --- /dev/null +++ b/core/src/commonMain/kotlin/cn/rdlevel/rdkt/core/data/Easing.kt @@ -0,0 +1,100 @@ +package cn.rdlevel.rdkt.core.data + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +public enum class Easing { + @SerialName("Linear") + LINEAR, + + @SerialName("InSine") + IN_SINE, + + @SerialName("OutSine") + OUT_SINE, + + @SerialName("InOutSine") + IN_OUT_SINE, + + @SerialName("InQuad") + IN_QUAD, + + @SerialName("OutQuad") + OUT_QUAD, + + @SerialName("InOutQuad") + IN_OUT_QUAD, + + @SerialName("InCubic") + IN_CUBIC, + + @SerialName("OutCubic") + OUT_CUBIC, + + @SerialName("InOutCubic") + IN_OUT_CUBIC, + + @SerialName("InQuart") + IN_QUART, + + @SerialName("OutQuart") + OUT_QUART, + + @SerialName("InOutQuart") + IN_OUT_QUART, + + @SerialName("InQuint") + IN_QUINT, + + @SerialName("OutQuint") + OUT_QUINT, + + @SerialName("InOutQuint") + IN_OUT_QUINT, + + @SerialName("InExpo") + IN_EXPO, + + @SerialName("OutExpo") + OUT_EXPO, + + @SerialName("InOutExpo") + IN_OUT_EXPO, + + @SerialName("InCirc") + IN_CIRC, + + @SerialName("OutCirc") + OUT_CIRC, + + @SerialName("InOutCirc") + IN_OUT_CIRC, + + @SerialName("InElastic") + IN_ELASTIC, + + @SerialName("OutElastic") + OUT_ELASTIC, + + @SerialName("InOutElastic") + IN_OUT_ELASTIC, + + @SerialName("InBack") + IN_BACK, + + @SerialName("OutBack") + OUT_BACK, + + @SerialName("InOutBack") + IN_OUT_BACK, + + @SerialName("InBounce") + IN_BOUNCE, + + @SerialName("OutBounce") + OUT_BOUNCE, + + @SerialName("InOutBounce") + IN_OUT_BOUNCE, +} \ No newline at end of file diff --git a/core/src/commonMain/kotlin/cn/rdlevel/rdkt/core/data/SelectedRoomsAndTopLayer.kt b/core/src/commonMain/kotlin/cn/rdlevel/rdkt/core/data/SelectedRoomsAndTopLayer.kt index d033134..306d40b 100644 --- a/core/src/commonMain/kotlin/cn/rdlevel/rdkt/core/data/SelectedRoomsAndTopLayer.kt +++ b/core/src/commonMain/kotlin/cn/rdlevel/rdkt/core/data/SelectedRoomsAndTopLayer.kt @@ -123,6 +123,10 @@ private sealed class AbstractSelectedRoomsAndTopLayer : SelectedRoomsAndTopLayer override fun hashCode(): Int { return rooms.hashCode() } + + override fun toString(): String { + return rooms.toString() + } } /** diff --git a/core/src/commonMain/kotlin/cn/rdlevel/rdkt/core/data/action/Theme.kt b/core/src/commonMain/kotlin/cn/rdlevel/rdkt/core/data/action/Theme.kt new file mode 100644 index 0000000..a48f6d0 --- /dev/null +++ b/core/src/commonMain/kotlin/cn/rdlevel/rdkt/core/data/action/Theme.kt @@ -0,0 +1,397 @@ +@file:OptIn(RDKTInternalAPI::class) + +package cn.rdlevel.rdkt.core.data.action + +import cn.rdlevel.rdkt.core.annotations.RDKTInternalAPI +import cn.rdlevel.rdkt.core.data.Easing +import cn.rdlevel.rdkt.core.serialization.EnumIndexSerializer +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.KSerializer +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.JsonClassDiscriminator +import kotlinx.serialization.json.JsonIgnoreUnknownKeys + +@OptIn(ExperimentalSerializationApi::class) +@Serializable +@JsonClassDiscriminator("preset") +@JsonIgnoreUnknownKeys +public sealed interface Theme { + /** + * Whether to skip row painting effects when applying the theme. + */ + public var skipPaintEffects: Boolean + + @Serializable + @SerialName("None") + public class None : AbstractTheme() + + @Serializable + @SerialName("OrientalTechno") + public class OrientalTechno : AbstractTheme(), VariedTheme { + override var variant: Variant = Variant.OPEN + + @Serializable(Variant.Serializer::class) + public enum class Variant { + OPEN, CLOSED; + + public object Serializer : KSerializer by EnumIndexSerializer() + } + } + + @Serializable + @SerialName("BoyWard") + public class BoyWard : AbstractTheme() + + @Serializable + @SerialName("GirlWard") + public class GirlWard : AbstractTheme() + + @Serializable + @SerialName("Skyline") + public class Skyline : AbstractTheme(), VariedTheme { + override var variant: Variant = Variant.PINK + + @Serializable(Variant.Serializer::class) + public enum class Variant { + PINK, BLUE; + + public object Serializer : KSerializer by EnumIndexSerializer() + } + } + + @Serializable + @SerialName("InsomniacDay") + public class InsomniacDay : AbstractTheme(), VariedTheme { + override var variant: Variant = Variant.DAY + + @Serializable(Variant.Serializer::class) + public enum class Variant { + DAY, NIGHT; + + public object Serializer : KSerializer by EnumIndexSerializer() + } + } + + @Serializable + @SerialName("ColeWardNight") + public class ColeWardNight : AbstractTheme(), VariedTheme { + override var variant: Variant = Variant.NIGHT + + @Serializable(Variant.Serializer::class) + public enum class Variant { + NIGHT, SUNRISE; + + public object Serializer : KSerializer by EnumIndexSerializer() + } + } + + @Serializable + @SerialName("Kaleidoscope") + public class Kaleidoscope : AbstractTheme() + + @Serializable + @SerialName("CoffeeShop") + public class CoffeeShop : AbstractTheme(), VariedTheme { + override var variant: Variant = Variant.DAY + + @Serializable(Variant.Serializer::class) + public enum class Variant { + DAY, NIGHT; + + public object Serializer : KSerializer by EnumIndexSerializer() + } + } + + @Serializable + @SerialName("PoliticiansRally") + public class PoliticiansRally : AbstractTheme() + + @Serializable + @SerialName("TrainDay") + public class TrainDay : AbstractPositionedTheme(), VariedTheme { + override val positionXRange: ClosedFloatingPointRange get() = -528.0..176.0 + + override var variant: Variant = Variant.DAY + + @Serializable(Variant.Serializer::class) + public enum class Variant { + DAY, NIGHT; + + public object Serializer : KSerializer by EnumIndexSerializer() + } + } + + @Serializable + @SerialName("DesertDay") + public class DesertDay : AbstractPositionedTheme(), VariedTheme { + override val positionXRange: ClosedFloatingPointRange get() = -528.0..176.0 + + override var variant: Variant = Variant.DAY + + @Serializable(Variant.Serializer::class) + public enum class Variant { + DAY, NIGHT; + + public object Serializer : KSerializer by EnumIndexSerializer() + } + } + + @Serializable + @SerialName("Stadium") + public class Stadium : AbstractTheme(), VariedTheme { + override var variant: Variant = Variant.SUNNY + + @Serializable(Variant.Serializer::class) + public enum class Variant { + SUNNY, STORMY; + + public object Serializer : KSerializer by EnumIndexSerializer() + } + } + + @Serializable + @SerialName("HospitalWard") + public class HospitalWard : AbstractPositionedTheme(), VariedTheme { + override val positionXRange: ClosedFloatingPointRange get() = -490.0..1172.0 + + override var variant: Variant = Variant.DAY + + @Serializable(Variant.Serializer::class) + public enum class Variant { + DAY, NIGHT; + + public object Serializer : KSerializer by EnumIndexSerializer() + } + } + + @Serializable + @SerialName("PaigeOffice") + public class PaigeOffice : AbstractPositionedTheme() { + override val positionXRange: ClosedFloatingPointRange get() = -358.0..42.0 + } + + @Serializable + @SerialName("Basement") + public class Basement : AbstractPositionedTheme() { + override val positionXRange: ClosedFloatingPointRange get() = -32.0..320.0 + } + + @Serializable + @SerialName("Garden") + public class Garden : AbstractTheme(), VariedTheme { + override var variant: Variant = Variant.DAY + + @Serializable(Variant.Serializer::class) + public enum class Variant { + DAY, NIGHT; + + public object Serializer : KSerializer by EnumIndexSerializer() + } + } + + @Serializable + @SerialName("Rooftop") + public class Rooftop : AbstractTheme(), VariedTheme { + override var variant: Variant = Variant.STANDARD + + @Serializable(Variant.Serializer::class) + public enum class Variant { + STANDARD, SUMMER, AUTUMN; + + public object Serializer : KSerializer by EnumIndexSerializer() + } + } + + @Serializable + @SerialName("AthleteWard") + public class AthleteWard : AbstractPositionedTheme(), VariedTheme { + override val positionXRange: ClosedFloatingPointRange get() = 0.0..732.0 + + override var variant: Variant = Variant.DAY + + @Serializable(Variant.Serializer::class) + public enum class Variant { + DAY, NIGHT; + + public object Serializer : KSerializer by EnumIndexSerializer() + } + } + + @Serializable + @SerialName("RecordsRoom") + public class RecordsRoom : AbstractPositionedTheme() { + override val positionXRange: ClosedFloatingPointRange get() = -475.0..0.0 + } + + @Serializable + @SerialName("AbandonedWard") + public class AbandonedWard : AbstractPositionedTheme() { + override val positionXRange: ClosedFloatingPointRange get() = -42.0..706.0 + } + + @Serializable + @SerialName("Intimate") + public class Intimate : AbstractTheme(), VariedTheme { + override var variant: Variant = Variant.STANDARD + + @Serializable(Variant.Serializer::class) + public enum class Variant { + STANDARD, SIMPLE; + + public object Serializer : KSerializer by EnumIndexSerializer() + } + } + + @Serializable + @SerialName("Sky") + public class Sky : AbstractTheme(), VariedTheme { + override var variant: Variant = Variant.DAY + + @Serializable(Variant.Serializer::class) + public enum class Variant { + DAY, NIGHT; + + public object Serializer : KSerializer by EnumIndexSerializer() + } + } + + @Serializable + @SerialName("Space") + public class Space : AbstractTheme() + + @Serializable + @SerialName("NeonMuseum") + public class NeonMuseum : AbstractTheme() + + @Serializable + @SerialName("BackAlley") + public class BackAlley : AbstractTheme() + + @Serializable + @SerialName("Airport") + public class Airport : AbstractPositionedTheme() { + override val positionXRange: ClosedFloatingPointRange get() = 0.0..248.0 + } + + @Serializable + @SerialName("RollerDisco") + public class RollerDisco : AbstractPositionedTheme() { + override val positionXRange: ClosedFloatingPointRange get() = -1000.0..1000.0 + } + + @Serializable + @SerialName("Matrix") + public class Matrix : AbstractTheme() + + @Serializable + @SerialName("CrossesStraight") + public class CrossesStraight : AbstractTheme(), VariedTheme { + override var variant: Variant = Variant.STRAIGHT + + @Serializable(Variant.Serializer::class) + public enum class Variant { + STRAIGHT, FALLING; + + public object Serializer : KSerializer by EnumIndexSerializer() + } + } + + @Serializable + @SerialName("CubesFalling") + public class CubesFalling : AbstractTheme(), VariedTheme { + override var variant: Variant = Variant.STANDARD + + @Serializable(Variant.Serializer::class) + public enum class Variant { + STANDARD, NICE_BLUE; + + public object Serializer : KSerializer by EnumIndexSerializer() + } + } + + @Serializable + @SerialName("HallOfMirrors") + public class HallOfMirrors : AbstractTheme() + + @Serializable + @SerialName("FloatingHeart") + public class FloatingHeart : AbstractTheme(), VariedTheme { + override var variant: Variant = Variant.STANDARD + + @Serializable(Variant.Serializer::class) + public enum class Variant { + STANDARD, WITH_CUBES; + + public object Serializer : KSerializer by EnumIndexSerializer() + } + } + + @Serializable + @SerialName("FloatingHeartBroken") + public class FloatingHeartBroken : AbstractTheme(), VariedTheme { + override var variant: Variant = Variant.STANDARD + + @Serializable(Variant.Serializer::class) + public enum class Variant { + STANDARD, WITH_CUBES; + + public object Serializer : KSerializer by EnumIndexSerializer() + } + } + + @Serializable + @SerialName("ZenGarden") + public class ZenGarden : AbstractTheme() + + @Serializable + @SerialName("Vaporwave") + public class Vaporwave : AbstractTheme() + + @Serializable + @SerialName("ProceduralTree") + public class ProceduralTree : AbstractTheme() +} + +public sealed interface VariedTheme> : Theme { + public var variant: E +} + +@Serializable +public data class ThemePosition( + var x: Int = 0, + var duration: Double = 0.0, + var ease: Easing = Easing.LINEAR, +) + +public sealed interface PositionedTheme : Theme { + public var enablePosition: Boolean + + public val positionXRange: ClosedFloatingPointRange + + public var positionX: Double + + public var positionDuration: Double + + public var positionEase: Easing +} + +@RDKTInternalAPI +public sealed class AbstractTheme : Theme { + override var skipPaintEffects: Boolean = false +} + +@RDKTInternalAPI +public sealed class AbstractPositionedTheme : AbstractTheme(), PositionedTheme { + override var enablePosition: Boolean = false + + override var positionX: Double = 0.0 + set(value) { + require(value in positionXRange) { "positionX must be within the range of ${positionXRange.start} to ${positionXRange.endInclusive}." } + field = value + } + + override var positionDuration: Double = 0.0 + + override var positionEase: Easing = Easing.LINEAR +} \ No newline at end of file diff --git a/core/src/commonMain/kotlin/cn/rdlevel/rdkt/core/events/SetThemeEvent.kt b/core/src/commonMain/kotlin/cn/rdlevel/rdkt/core/events/SetThemeEvent.kt new file mode 100644 index 0000000..493605a --- /dev/null +++ b/core/src/commonMain/kotlin/cn/rdlevel/rdkt/core/events/SetThemeEvent.kt @@ -0,0 +1,24 @@ +@file:OptIn(RDKTInternalAPI::class) + +package cn.rdlevel.rdkt.core.events + +import cn.rdlevel.rdkt.core.annotations.RDKTInternalAPI +import cn.rdlevel.rdkt.core.data.ROOM1 +import cn.rdlevel.rdkt.core.data.SelectedRooms +import cn.rdlevel.rdkt.core.data.action.Theme +import cn.rdlevel.rdkt.core.data.roomsOf +import cn.rdlevel.rdkt.core.serialization.Flatten +import cn.rdlevel.rdkt.core.serialization.flatten +import kotlinx.serialization.* + +@OptIn(ExperimentalSerializationApi::class) +@Serializable(SetThemeEvent.Serializer::class) +@KeepGeneratedSerializer +@SerialName("SetTheme") +public data class SetThemeEvent( + @Flatten + var theme: Theme, + override var rooms: SelectedRooms = roomsOf(ROOM1), +) : ActionEvent(), MutableRoomsSpecificEvent { + public object Serializer : KSerializer by generatedSerializer().flatten() +} \ No newline at end of file diff --git a/core/src/commonMain/kotlin/cn/rdlevel/rdkt/core/serialization/FieldFlatteningSerializer.kt b/core/src/commonMain/kotlin/cn/rdlevel/rdkt/core/serialization/FieldFlatteningSerializer.kt index 6eee9fe..eab0753 100644 --- a/core/src/commonMain/kotlin/cn/rdlevel/rdkt/core/serialization/FieldFlatteningSerializer.kt +++ b/core/src/commonMain/kotlin/cn/rdlevel/rdkt/core/serialization/FieldFlatteningSerializer.kt @@ -4,13 +4,12 @@ import cn.rdlevel.rdkt.core.annotations.RDKTInternalAPI import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.KSerializer import kotlinx.serialization.SerialInfo -import kotlinx.serialization.json.JsonElement -import kotlinx.serialization.json.JsonTransformingSerializer -import kotlinx.serialization.json.buildJsonObject -import kotlinx.serialization.json.jsonObject +import kotlinx.serialization.json.* /** * Annotation to indicate that a field should be flattened when serialized. + * + * @see FieldFlatteningSerializer */ @OptIn(ExperimentalSerializationApi::class) @SerialInfo @@ -34,12 +33,15 @@ public annotation class Flatten * * @param tSerializer The serializer, usually the [generated one][kotlinx.serialization.KeepGeneratedSerializer], for the type T that this serializer will delegate to. */ +@OptIn(ExperimentalSerializationApi::class) @RDKTInternalAPI public class FieldFlatteningSerializer( - tSerializer: KSerializer + private val tSerializer: KSerializer ) : JsonTransformingSerializer(tSerializer) { private val flattenFieldNames: MutableList = mutableListOf() private val notFlattenFieldNames: MutableList = mutableListOf() + private val classDiscriminator: String = + descriptor.annotations.filterIsInstance().firstOrNull()?.discriminator ?: "type" init { (0..( element.jsonObject.forEach { (k, v) -> if (k in notFlattenFieldNames) { this@base.put(k, v) - } else { + } else if (k != classDiscriminator) { put(k, v) } } diff --git a/core/src/commonTest/kotlin/cn/rdlevel/rdkt/core/test/SetThemeEventTest.kt b/core/src/commonTest/kotlin/cn/rdlevel/rdkt/core/test/SetThemeEventTest.kt new file mode 100644 index 0000000..9197e8d --- /dev/null +++ b/core/src/commonTest/kotlin/cn/rdlevel/rdkt/core/test/SetThemeEventTest.kt @@ -0,0 +1,38 @@ +package cn.rdlevel.rdkt.core.test + +import cn.rdlevel.rdkt.core.data.Easing +import cn.rdlevel.rdkt.core.data.ROOM1 +import cn.rdlevel.rdkt.core.data.ROOM3 +import cn.rdlevel.rdkt.core.data.action.Theme +import cn.rdlevel.rdkt.core.data.roomsOf +import cn.rdlevel.rdkt.core.events.Event +import cn.rdlevel.rdkt.core.events.SetThemeEvent +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.json.Json +import kotlin.test.Test + +class SetThemeEventTest { + @OptIn(ExperimentalSerializationApi::class) + @Test + fun test() { + val json = Json { + isLenient = true + allowTrailingComma = true + encodeDefaults = true + } + val event: Event = SetThemeEvent(Theme.TrainDay().apply { + skipPaintEffects = true + variant = Theme.TrainDay.Variant.NIGHT + enablePosition = true + positionX = 100.0 + positionDuration = 2.5 + positionEase = Easing.OUT_EXPO + }).apply { + rooms = roomsOf(ROOM1, ROOM3) + } + val text = json.encodeToString(event) + println(text) + val event2: Event = json.decodeFromString(text) + println(event2) + } +} \ No newline at end of file