mirror of
https://github.com/RDCN-Community-Developers/rdkt.git
synced 2026-09-05 01:35:31 +08:00
Implemented sound events.
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
package cn.rdlevel.rdkt.core.data
|
||||
|
||||
import cn.rdlevel.rdkt.core.annotations.RDKTInternalAPI
|
||||
import cn.rdlevel.rdkt.core.data.RowPattern.Companion.ALLOWED_CHARS
|
||||
import cn.rdlevel.rdkt.core.serialization.TransformSerializer
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.builtins.serializer
|
||||
|
||||
/**
|
||||
* Represents the pattern of pulses of a row.
|
||||
*/
|
||||
@Serializable(RowPattern.Serializer::class)
|
||||
public class RowPattern {
|
||||
/**
|
||||
* The pattern string representing the pulse pattern of the row. It must be exactly 6 characters long, and each character must be one of the allowed characters defined in [ALLOWED_CHARS].
|
||||
*/
|
||||
public var pattern: String = "------"
|
||||
set(value) {
|
||||
require(value.length == 6) { "Pulse pattern must be 6 characters long." }
|
||||
require(value.all { it in ALLOWED_CHARS }) {
|
||||
"Pulse pattern can only contain '$ALLOWED_CHARS', but incorrect pattern found: '$value'."
|
||||
}
|
||||
field = value
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the pulse pattern using a list of [Pulse]s. The list must contain exactly 6 pulses.
|
||||
*/
|
||||
public fun setPattern(
|
||||
pulses: List<Pulse>,
|
||||
) {
|
||||
pattern = pulses.joinToString(separator = "") { it.char.toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the pulse pattern using individual [Pulse] parameters.
|
||||
*/
|
||||
public fun setPattern(
|
||||
pulse1: Pulse,
|
||||
pulse2: Pulse,
|
||||
pulse3: Pulse,
|
||||
pulse4: Pulse,
|
||||
pulse5: Pulse,
|
||||
pulse6: Pulse,
|
||||
) {
|
||||
setPattern(listOf(pulse1, pulse2, pulse3, pulse4, pulse5, pulse6))
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a [RowPattern] with the specified pattern string. The pattern string must be exactly 6 characters long, and each character must be one of the allowed characters defined in [ALLOWED_CHARS].
|
||||
*/
|
||||
public constructor(
|
||||
pattern: String = "------",
|
||||
) {
|
||||
this.pattern = pattern
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a [RowPattern] with the specified list of [Pulse]s. The list must contain exactly 6 pulses.
|
||||
*/
|
||||
public constructor(
|
||||
pulses: List<Pulse>,
|
||||
) {
|
||||
setPattern(pulses)
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a [RowPattern] with the specified individual [Pulse] parameters.
|
||||
*/
|
||||
public constructor(
|
||||
pulse1: Pulse,
|
||||
pulse2: Pulse,
|
||||
pulse3: Pulse,
|
||||
pulse4: Pulse,
|
||||
pulse5: Pulse,
|
||||
pulse6: Pulse,
|
||||
) {
|
||||
setPattern(listOf(pulse1, pulse2, pulse3, pulse4, pulse5, pulse6))
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the pattern of a pulse.
|
||||
*/
|
||||
public enum class Pulse(public val char: Char) {
|
||||
/**
|
||||
* The pulse is normal.
|
||||
*/
|
||||
NORMAL('-'),
|
||||
|
||||
/**
|
||||
* The pulse is covered by x, indicating not having pulse sound.
|
||||
*/
|
||||
X('x'),
|
||||
|
||||
/**
|
||||
* The pulse is up-shaped.
|
||||
*/
|
||||
UP('u'),
|
||||
|
||||
/**
|
||||
* The pulse is down-shaped.
|
||||
*/
|
||||
DOWN('d'),
|
||||
|
||||
/**
|
||||
* The pulse is blocked, indicating a longer pulse duration.
|
||||
*/
|
||||
BLOCKED('b'),
|
||||
|
||||
/**
|
||||
* The pulse is resumed to normal, indicating the end of a blocked pulse.
|
||||
*/
|
||||
RESUME('r'),
|
||||
}
|
||||
|
||||
public companion object {
|
||||
/**
|
||||
* Characters allowed in the pattern string, corresponding to the defined pulse types.
|
||||
*/
|
||||
public const val ALLOWED_CHARS: String = "-xudbr"
|
||||
}
|
||||
|
||||
@OptIn(RDKTInternalAPI::class)
|
||||
public object Serializer : TransformSerializer<RowPattern, String>(String.serializer()) {
|
||||
override fun toData(value: RowPattern): String {
|
||||
return value.pattern
|
||||
}
|
||||
|
||||
override fun fromData(data: String): RowPattern {
|
||||
return RowPattern(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
@file:OptIn(RDKTInternalAPI::class)
|
||||
@file:JvmName("SelectedRoomsUtils")
|
||||
@file:JvmName("SelectedRoomUtil")
|
||||
|
||||
package cn.rdlevel.rdkt.core.data
|
||||
|
||||
import cn.rdlevel.rdkt.core.annotations.RDKTInternalAPI
|
||||
import cn.rdlevel.rdkt.core.serializers.TransformSerializer
|
||||
import cn.rdlevel.rdkt.core.serialization.TransformSerializer
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.builtins.SetSerializer
|
||||
import kotlinx.serialization.builtins.serializer
|
||||
@@ -79,7 +79,7 @@ public sealed interface SelectedRoomsAndTopLayer {
|
||||
}
|
||||
|
||||
public object Serializer :
|
||||
TransformSerializer<SelectedRoomsAndTopLayer, Set<Int>>(SetSerializer(Int.serializer())) {
|
||||
TransformSerializer<SelectedRoomsAndTopLayer, Set<Int>>(kotlinx.serialization.serializer()) {
|
||||
override fun toData(value: SelectedRoomsAndTopLayer): Set<Int> {
|
||||
return value.rooms
|
||||
}
|
||||
@@ -403,6 +403,7 @@ public fun <T : SingleSelectedRoomOrTopLayer> singleRoomOf(kClass: KClass<T>, ro
|
||||
* @param rooms The set of room IDs to include in the selection.
|
||||
* @return An instance of [SelectedRoomsAndTopLayer] of the specified type.
|
||||
*/
|
||||
@JvmName("of")
|
||||
public inline fun <reified T : SelectedRoomsAndTopLayer> roomsOf(rooms: Set<Int>): T {
|
||||
return roomsOf(T::class, rooms)
|
||||
}
|
||||
@@ -415,6 +416,7 @@ public inline fun <reified T : SelectedRoomsAndTopLayer> roomsOf(rooms: Set<Int>
|
||||
* @param rooms Additional room IDs to include in the selection.
|
||||
* @return An instance of [SelectedRoomsAndTopLayer] of the specified type.
|
||||
*/
|
||||
@JvmName("of")
|
||||
public inline fun <reified T : SelectedRoomsAndTopLayer> roomsOf(room: Int, vararg rooms: Int): T {
|
||||
return roomsOf(T::class, room, *rooms)
|
||||
}
|
||||
@@ -426,6 +428,7 @@ public inline fun <reified T : SelectedRoomsAndTopLayer> roomsOf(room: Int, vara
|
||||
* @param room The ID of the selected room.
|
||||
* @return An instance of [SingleSelectedRoomOrTopLayer] of the specified type.
|
||||
*/
|
||||
@JvmName("ofSingle")
|
||||
public inline fun <reified T : SingleSelectedRoomOrTopLayer> singleRoomOf(room: Int): T {
|
||||
return singleRoomOf(T::class, room)
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
@file:JvmName("AudioDataUtil")
|
||||
|
||||
package cn.rdlevel.rdkt.core.data.sound
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlin.jvm.JvmName
|
||||
import kotlin.jvm.JvmOverloads
|
||||
|
||||
/**
|
||||
* Represents an abstraction of audio and its configuration.
|
||||
*/
|
||||
public sealed class AbstractAudioData {
|
||||
/**
|
||||
* The file name or built-in name of the audio.
|
||||
*/
|
||||
@SerialName("filename")
|
||||
public abstract var fileName: String
|
||||
|
||||
/**
|
||||
* The volume of the audio, from 0 to 300.
|
||||
*/
|
||||
public var volume: Int = 100
|
||||
set(value) {
|
||||
require(value in 0..300) { "Volume must be between 0 and 300." }
|
||||
field = value
|
||||
}
|
||||
|
||||
/**
|
||||
* The pitch of the audio, from 0 to 300.
|
||||
*/
|
||||
public var pitch: Int = 100
|
||||
set(value) {
|
||||
require(value in 0..300) { "Pitch must be between 0 and 300." }
|
||||
field = value
|
||||
}
|
||||
|
||||
/**
|
||||
* The pan of the audio, from -100 to 100.
|
||||
* Negative values pan to the left, positive values pan to the right.
|
||||
*/
|
||||
public var pan: Int = 0
|
||||
set(value) {
|
||||
require(value in -100..100) { "Pan must be between -100 and 100." }
|
||||
field = value
|
||||
}
|
||||
|
||||
/**
|
||||
* The offset in milliseconds to start the audio from.
|
||||
* When the [fileName] is built-in, this property is ignored.
|
||||
*/
|
||||
public var offset: Int = 0
|
||||
|
||||
override fun toString(): String {
|
||||
return "AbstractAudioData(fileName='$fileName', volume=$volume, pitch=$pitch, pan=$pan, offset=$offset)"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an audio and its configuration.
|
||||
*/
|
||||
@Serializable
|
||||
public data class AudioData(
|
||||
@SerialName("filename")
|
||||
override var fileName: String,
|
||||
) : AbstractAudioData()
|
||||
|
||||
|
||||
/**
|
||||
* Creates an [AudioData] instance with the specified [fileName], [volume], [pitch], [pan], and [offset].
|
||||
*
|
||||
* @param fileName The file name or built-in name of the audio.
|
||||
* @param volume The volume of the audio, from 0 to 300. Default is 100.
|
||||
* @param pitch The pitch of the audio, from 0 to 300. Default is 100.
|
||||
* @param pan The pan of the audio, from -100 to 100. Default is 0.
|
||||
* @param offset The offset in milliseconds to start the audio from. Default is 0.
|
||||
*
|
||||
* @return An [AudioData] instance with the specified properties.
|
||||
*/
|
||||
@JvmOverloads
|
||||
@JvmName("ofAudio")
|
||||
public fun audioDataOf(
|
||||
fileName: String,
|
||||
volume: Int = 100,
|
||||
pitch: Int = 100,
|
||||
pan: Int = 0,
|
||||
offset: Int = 0,
|
||||
): AudioData {
|
||||
return AudioData(fileName).apply {
|
||||
this.volume = volume
|
||||
this.pitch = pitch
|
||||
this.pan = pan
|
||||
this.offset = offset
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an [AbstractAudioData] to an [AudioData].
|
||||
*
|
||||
* @param audioData The audio data to convert.
|
||||
* @return An [AudioData] instance with the same properties as the given [audioData].
|
||||
*/
|
||||
@JvmName("ofAudio")
|
||||
public fun audioDataOf(audioData: AbstractAudioData): AudioData {
|
||||
return AudioData(audioData.fileName).apply {
|
||||
this.volume = audioData.volume
|
||||
this.pitch = audioData.pitch
|
||||
this.pan = audioData.pan
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a [AudioData] instance with the specified [fileName], [offset], [volume], [pitch], and [pan].
|
||||
* This is a convenient function to create a [AudioData] for music quickly.
|
||||
*
|
||||
* @param fileName The file name or built-in name of the audio.
|
||||
* @param offset The offset in milliseconds to start the audio from. Default is 0.
|
||||
* @param volume The volume of the audio, from 0 to 300. Default is 100.
|
||||
* @param pitch The pitch of the audio, from 0 to 300. Default is 100.
|
||||
* @param pan The pan of the audio, from -100 to 100. Default is 0.
|
||||
*
|
||||
* @return A [AudioData] instance with the specified properties.
|
||||
*/
|
||||
@JvmOverloads
|
||||
@JvmName("ofMusic")
|
||||
public fun audioDataOfMusic(
|
||||
fileName: String,
|
||||
offset: Int = 0,
|
||||
volume: Int = 100,
|
||||
pitch: Int = 100,
|
||||
pan: Int = 0
|
||||
): AudioData {
|
||||
return AudioData(fileName).apply {
|
||||
this.volume = volume
|
||||
this.pitch = pitch
|
||||
this.pan = pan
|
||||
this.offset = offset
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a subgroup audio and its configuration.
|
||||
*
|
||||
* This is used for [SetGameSoundEvent][cn.rdlevel.rdkt.core.events.SetGameSoundEvent].
|
||||
*/
|
||||
@Serializable
|
||||
public open class SubGroupAudioData @JvmOverloads constructor(
|
||||
/**
|
||||
* The file name or built-in name of the audio.
|
||||
* Leave blank to keep the audio unchanged.
|
||||
*/
|
||||
@SerialName("filename")
|
||||
override var fileName: String = "",
|
||||
/**
|
||||
* Indicates whether this subgroup audio setting is enabled in the editor.
|
||||
*/
|
||||
public var used: Boolean = true
|
||||
) : AbstractAudioData() {
|
||||
override fun toString(): String {
|
||||
return "SubGroupAudioData(fileName='$fileName', used=$used, volume=$volume, pitch=$pitch, pan=$pan, offset=$offset)"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a [SubGroupAudioData] instance with the specified [fileName], [used], [volume], [pitch], and [pan].
|
||||
*
|
||||
* @param fileName The file name or built-in name of the audio. Default is an empty string.
|
||||
* @param used Indicates whether this subgroup audio setting is enabled in the editor. Default is true.
|
||||
* @param volume The volume of the audio, from 0 to 300. Default is 100.
|
||||
* @param pitch The pitch of the audio, from 0 to 300. Default is 100.
|
||||
* @param pan The pan of the audio, from -100 to 100. Default is 0.
|
||||
*
|
||||
* @return A [SubGroupAudioData] instance with the specified properties.
|
||||
*/
|
||||
@JvmOverloads
|
||||
@JvmName("ofSubGroup")
|
||||
public fun subGroupAudioDataOf(
|
||||
fileName: String = "",
|
||||
used: Boolean = true,
|
||||
volume: Int = 100,
|
||||
pitch: Int = 100,
|
||||
pan: Int = 0
|
||||
): SubGroupAudioData {
|
||||
return SubGroupAudioData(fileName, used).apply {
|
||||
this.volume = volume
|
||||
this.pitch = pitch
|
||||
this.pan = pan
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an [AbstractAudioData] to a [SubGroupAudioData].
|
||||
*
|
||||
* @param audioData The audio data to convert.
|
||||
* @return A [SubGroupAudioData] instance with the same properties as the given [audioData].
|
||||
*/
|
||||
@JvmName("ofSubGroup")
|
||||
public fun subGroupAudioDataOf(audioData: AbstractAudioData): SubGroupAudioData {
|
||||
return SubGroupAudioData(audioData.fileName).apply {
|
||||
this.volume = audioData.volume
|
||||
this.pitch = audioData.pitch
|
||||
this.pan = audioData.pan
|
||||
|
||||
if (audioData is SubGroupAudioData) {
|
||||
this.used = audioData.used
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies a [SubGroupAudioData].
|
||||
*
|
||||
* @param audioData The audio data to copy.
|
||||
* @return A [SubGroupAudioData] instance with the same properties as the given [audioData].
|
||||
*/
|
||||
@JvmName("ofSubGroup")
|
||||
public fun subGroupAudioDataOf(audioData: SubGroupAudioData): SubGroupAudioData {
|
||||
return SubGroupAudioData(audioData.fileName, audioData.used).apply {
|
||||
this.volume = audioData.volume
|
||||
this.pitch = audioData.pitch
|
||||
this.pan = audioData.pan
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A predefined instance of [SubGroupAudioData] that is marked as unused.
|
||||
*/
|
||||
public val unusedSubGroupAudioData: SubGroupAudioData = subGroupAudioDataOf(used = false)
|
||||
@@ -0,0 +1,205 @@
|
||||
@file:OptIn(RDKTInternalAPI::class)
|
||||
|
||||
package cn.rdlevel.rdkt.core.data.sound
|
||||
|
||||
import cn.rdlevel.rdkt.core.annotations.RDKTInternalAPI
|
||||
import kotlin.jvm.JvmStatic
|
||||
|
||||
/**
|
||||
* A voice source that provides counting sounds.
|
||||
* Used in [cn.rdlevel.rdkt.core.events.SetCountingSoundEvent] to specify the source of counting sounds.
|
||||
*/
|
||||
public sealed interface VoiceSource {
|
||||
public val type: String
|
||||
|
||||
/**
|
||||
* A classic voice source that counts classic rows.
|
||||
*/
|
||||
public sealed interface Classic : VoiceSource {
|
||||
@RDKTInternalAPI
|
||||
public sealed class AbstractClassic(override val type: String) : Classic
|
||||
|
||||
public object JyiCount : AbstractClassic("JyiCount")
|
||||
|
||||
public object JyiCountFast : AbstractClassic("JyiCountFast")
|
||||
|
||||
public object JyiCountCalm : AbstractClassic("JyiCountCalm")
|
||||
|
||||
public object JyiCountTired : AbstractClassic("JyiCountTired")
|
||||
|
||||
public object JyiCountVeryTired : AbstractClassic("JyiCountVeryTired")
|
||||
|
||||
public object JyiCountJapanese : AbstractClassic("JyiCountJapanese")
|
||||
|
||||
public object JyiCountLegacy : AbstractClassic("JyiCountLegacy")
|
||||
|
||||
public object IanCount : AbstractClassic("IanCount")
|
||||
|
||||
public object IanCountFast : AbstractClassic("IanCountFast")
|
||||
|
||||
public object IanCountCalm : AbstractClassic("IanCountCalm")
|
||||
|
||||
public object IanCountSlow : AbstractClassic("IanCountSlow")
|
||||
|
||||
public object IanCountSlower : AbstractClassic("IanCountSlower")
|
||||
|
||||
public object WhistleCount : AbstractClassic("WhistleCount")
|
||||
|
||||
public object BirdCount : AbstractClassic("BirdCount")
|
||||
|
||||
public object ParrotCount : AbstractClassic("ParrotCount")
|
||||
|
||||
public object OwlCount : AbstractClassic("OwlCount")
|
||||
|
||||
public object OrioleCount : AbstractClassic("OrioleCount")
|
||||
|
||||
public object WrenCount : AbstractClassic("WrenCount")
|
||||
|
||||
public object CanaryCount : AbstractClassic("CanaryCount")
|
||||
|
||||
public object SpearCount : AbstractClassic("SpearCount")
|
||||
}
|
||||
|
||||
/**
|
||||
* A oneshot voice source that counts oneshot rows.
|
||||
*/
|
||||
public sealed interface Oneshot : VoiceSource {
|
||||
@RDKTInternalAPI
|
||||
public sealed class AbstractOneshot(override val type: String) : Oneshot
|
||||
|
||||
/**
|
||||
* Nurse oneshot voice source.
|
||||
*/
|
||||
public object JyiCountEnglish : AbstractOneshot("JyiCountEnglish")
|
||||
|
||||
/**
|
||||
* Ian oneshot voice source.
|
||||
*/
|
||||
public object IanCountEnglish : AbstractOneshot("IanCountEnglish")
|
||||
|
||||
/**
|
||||
* Ian calm oneshot voice source.
|
||||
*/
|
||||
public object IanCountEnglishCalm : AbstractOneshot("IanCountEnglishCalm")
|
||||
|
||||
/**
|
||||
* Ian slow oneshot voice source.
|
||||
*/
|
||||
public object IanCountEnglishSlow : AbstractOneshot("IanCountEnglishSlow")
|
||||
}
|
||||
|
||||
/**
|
||||
* A custom voice source that allows custom sounds.
|
||||
*
|
||||
* @property sounds The array of custom sounds. For classic rows, it should have 7 sounds corresponding to the 7 pulses. For oneshot rows, it should have 10 sounds corresponding to the 10 subdivisions.
|
||||
*/
|
||||
public class Custom internal constructor(
|
||||
public val sounds: Array<AudioData>,
|
||||
) : Classic, Oneshot {
|
||||
override val type: String = TYPE
|
||||
|
||||
public companion object {
|
||||
/**
|
||||
* The type of custom voice sources.
|
||||
*/
|
||||
public const val TYPE: String = "Custom"
|
||||
|
||||
/**
|
||||
* Creates a custom voice source with the given sounds for classic rows.
|
||||
* The sounds array must have exactly 7 sounds, corresponding to the 7 pulses.
|
||||
*/
|
||||
@JvmStatic
|
||||
public fun ofClassic(sounds: Array<AudioData>): Classic {
|
||||
require(sounds.size == 7) { "Classic voice source must have exactly 7 sounds." }
|
||||
return Custom(sounds)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a custom voice source with the given sounds for classic rows.
|
||||
* The sounds must correspond to the 7 pulses in order.
|
||||
*/
|
||||
public fun ofClassic(
|
||||
pulse1: AudioData,
|
||||
pulse2: AudioData,
|
||||
pulse3: AudioData,
|
||||
pulse4: AudioData,
|
||||
pulse5: AudioData,
|
||||
pulse6: AudioData,
|
||||
pulse7: AudioData,
|
||||
): Classic = ofClassic(arrayOf(pulse1, pulse2, pulse3, pulse4, pulse5, pulse6, pulse7))
|
||||
|
||||
/**
|
||||
* Creates a custom voice source with the given sounds for oneshot rows.
|
||||
* The sounds array must have exactly 10 sounds, corresponding to the 10 subdivisions.
|
||||
*/
|
||||
@JvmStatic
|
||||
public fun ofOneshot(sounds: Array<AudioData>): Oneshot {
|
||||
require(sounds.size == 10) { "Oneshot voice source must have exactly 10 sounds." }
|
||||
return Custom(sounds)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a custom voice source with the given sounds for oneshot rows.
|
||||
* The sounds must correspond to the 10 subdivisions in order.
|
||||
*/
|
||||
public fun ofOneshot(
|
||||
subdivision1: AudioData,
|
||||
subdivision2: AudioData,
|
||||
subdivision3: AudioData,
|
||||
subdivision4: AudioData,
|
||||
subdivision5: AudioData,
|
||||
subdivision6: AudioData,
|
||||
subdivision7: AudioData,
|
||||
subdivision8: AudioData,
|
||||
subdivision9: AudioData,
|
||||
subdivision10: AudioData,
|
||||
): Oneshot = ofOneshot(
|
||||
arrayOf(
|
||||
subdivision1,
|
||||
subdivision2,
|
||||
subdivision3,
|
||||
subdivision4,
|
||||
subdivision5,
|
||||
subdivision6,
|
||||
subdivision7,
|
||||
subdivision8,
|
||||
subdivision9,
|
||||
subdivision10
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
public companion object {
|
||||
private val typeToVoiceSourceMap: Map<String, VoiceSource> = listOf(
|
||||
Classic.JyiCount,
|
||||
Classic.JyiCountFast,
|
||||
Classic.JyiCountCalm,
|
||||
Classic.JyiCountTired,
|
||||
Classic.JyiCountVeryTired,
|
||||
Classic.JyiCountJapanese,
|
||||
Classic.JyiCountLegacy,
|
||||
Classic.IanCount,
|
||||
Classic.IanCountFast,
|
||||
Classic.IanCountCalm,
|
||||
Classic.IanCountSlow,
|
||||
Classic.IanCountSlower,
|
||||
Classic.WhistleCount,
|
||||
Classic.BirdCount,
|
||||
Classic.ParrotCount,
|
||||
Classic.OwlCount,
|
||||
Classic.OrioleCount,
|
||||
Classic.WrenCount,
|
||||
Classic.CanaryCount,
|
||||
Classic.SpearCount,
|
||||
Oneshot.JyiCountEnglish,
|
||||
Oneshot.IanCountEnglish,
|
||||
Oneshot.IanCountEnglishCalm,
|
||||
Oneshot.IanCountEnglishSlow
|
||||
).associateBy { it.type }
|
||||
|
||||
internal fun fromType(type: String): VoiceSource? {
|
||||
return typeToVoiceSourceMap[type]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
@file:OptIn(RDKTInternalAPI::class)
|
||||
@file:JvmName("AudioGroups")
|
||||
|
||||
package cn.rdlevel.rdkt.core.data.sound.audioGroup
|
||||
|
||||
import cn.rdlevel.rdkt.core.annotations.RDKTInternalAPI
|
||||
import cn.rdlevel.rdkt.core.data.sound.SubGroupAudioData
|
||||
import cn.rdlevel.rdkt.core.serialization.PolymorphicDelegatedSerializer
|
||||
import cn.rdlevel.rdkt.core.serialization.TransformSerializer
|
||||
import cn.rdlevel.rdkt.core.util.smallCamelToBigCamel
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Transient
|
||||
import kotlin.jvm.JvmName
|
||||
import kotlin.jvm.JvmOverloads
|
||||
import kotlin.jvm.JvmStatic
|
||||
import kotlin.jvm.JvmSynthetic
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
/**
|
||||
* Represents a group of audio subtypes and their configurations.
|
||||
* This is used in [SetGameSoundEvent][cn.rdlevel.rdkt.core.events.SetGameSoundEvent].
|
||||
*
|
||||
* @property subTypes A map of subtype identifiers to their corresponding [SubGroupAudioData].
|
||||
*/
|
||||
@Serializable(AudioGroup.Serializer::class)
|
||||
public abstract class AudioGroup(
|
||||
protected val subTypes: MutableMap<String, SubGroupAudioData> = mutableMapOf(),
|
||||
) : Map<String, SubGroupAudioData> by subTypes {
|
||||
/**
|
||||
* The type of sound group.
|
||||
*/
|
||||
public abstract val soundType: String
|
||||
|
||||
protected operator fun getValue(thisRef: AudioGroup, property: KProperty<*>): SubGroupAudioData {
|
||||
return subTypes[property.name.smallCamelToBigCamel()] ?: error("Subtype '${property.name}' not found!")
|
||||
}
|
||||
|
||||
protected operator fun setValue(thisRef: AudioGroup, property: KProperty<*>, value: SubGroupAudioData) {
|
||||
subTypes[property.name.smallCamelToBigCamel()] = value
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "AudioGroup(soundType='$soundType', subTypes=$subTypes)"
|
||||
}
|
||||
|
||||
public companion object {
|
||||
private val conversionMap: MutableMap<String, (Map<String, SubGroupAudioData>) -> AudioGroup> = mutableMapOf()
|
||||
|
||||
/**
|
||||
* Tries to convert the [AudioGroup] to the appropriate subclass based on the provided [type].
|
||||
*
|
||||
* @param group the [AudioGroup] instance to convert.
|
||||
* @param type The type identifier used to determine the target subclass.
|
||||
* @return The converted [AudioGroup] if a matching converter is found; otherwise, returns the instance itself.
|
||||
*/
|
||||
@JvmStatic
|
||||
public fun convertGroupTo(group: AudioGroup, type: String): AudioGroup {
|
||||
val converter = conversionMap[type]
|
||||
?: return group
|
||||
return converter(group)
|
||||
}
|
||||
|
||||
internal fun registerConversion(
|
||||
type: String,
|
||||
converter: (Map<String, SubGroupAudioData>) -> AudioGroup
|
||||
) {
|
||||
require(type !in conversionMap) {
|
||||
"Conversion for type '$type' is already registered."
|
||||
}
|
||||
|
||||
conversionMap[type] = converter
|
||||
}
|
||||
|
||||
init {
|
||||
Serializer.register()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A common serializer for [AudioGroup] subclasses.
|
||||
* Subclass implementations should be singleton and call [register] to register their conversion function before use.
|
||||
*
|
||||
* @param T The type of [AudioGroup].
|
||||
* @param soundType The type of sound group.
|
||||
*/
|
||||
public abstract class BaseSerializer<T : AudioGroup>(protected val soundType: String) :
|
||||
TransformSerializer<T, List<RawSubGroupAudioData>>(kotlinx.serialization.serializer()) {
|
||||
/**
|
||||
* Creates an instance of [T] from a map of subtype identifiers to [SubGroupAudioData].
|
||||
*/
|
||||
protected abstract fun toAudioGroup(map: Map<String, SubGroupAudioData>): T
|
||||
|
||||
/**
|
||||
* Registers the conversion function for this [AudioGroup] subclass.
|
||||
*/
|
||||
public fun register() {
|
||||
registerConversion(soundType, ::toAudioGroup)
|
||||
}
|
||||
|
||||
override fun toData(value: T): List<RawSubGroupAudioData> {
|
||||
return value.map { (subType, data) ->
|
||||
data.toRaw(subType)
|
||||
}
|
||||
}
|
||||
|
||||
override fun fromData(data: List<RawSubGroupAudioData>): T {
|
||||
val map = data.associateBy { it.groupSubtype }
|
||||
return toAudioGroup(map)
|
||||
}
|
||||
}
|
||||
|
||||
public object Serializer : BaseSerializer<AudioGroup>("Custom") {
|
||||
override fun toAudioGroup(map: Map<String, SubGroupAudioData>): AudioGroup {
|
||||
return CustomAudioGroup(map.toMutableMap())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to convert the [AudioGroup] to the appropriate subclass based on the provided [type].
|
||||
*
|
||||
* @param type The type identifier used to determine the target subclass.
|
||||
* @return The converted [AudioGroup] if a matching converter is found; otherwise, returns the instance itself.
|
||||
*/
|
||||
@JvmSynthetic
|
||||
public fun AudioGroup.tryConvertTo(type: String): AudioGroup {
|
||||
return AudioGroup.convertGroupTo(this, type)
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the data form of audio data for a specific subgroup within an [AudioGroup].
|
||||
*
|
||||
* @property groupSubtype The identifier for the subgroup type.
|
||||
*/
|
||||
@Serializable
|
||||
@RDKTInternalAPI
|
||||
public data class RawSubGroupAudioData(
|
||||
public val groupSubtype: String,
|
||||
) : SubGroupAudioData() {
|
||||
override fun toString(): String {
|
||||
return super.toString()
|
||||
}
|
||||
}
|
||||
|
||||
private fun SubGroupAudioData.toRaw(groupSubtype: String): RawSubGroupAudioData {
|
||||
val data = this
|
||||
return RawSubGroupAudioData(groupSubtype).apply {
|
||||
fileName = data.fileName
|
||||
used = data.used
|
||||
volume = data.volume
|
||||
pitch = data.pitch
|
||||
pan = data.pan
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A customizable audio group that can hold any number of subtypes.
|
||||
*
|
||||
* @param subTypes A map of subtype identifiers to their corresponding [SubGroupAudioData].
|
||||
* @param soundType The type of sound group. Default is "Custom".
|
||||
*/
|
||||
@Suppress("DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE")
|
||||
@Serializable(CustomAudioGroup.Serializer::class)
|
||||
public class CustomAudioGroup @JvmOverloads constructor(
|
||||
subTypes: MutableMap<String, SubGroupAudioData> = mutableMapOf(),
|
||||
@Transient
|
||||
override var soundType: String = "Custom",
|
||||
) : AudioGroup(subTypes), MutableMap<String, SubGroupAudioData> by subTypes {
|
||||
public object Serializer :
|
||||
PolymorphicDelegatedSerializer<CustomAudioGroup, AudioGroup>(kotlinx.serialization.serializer())
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
package cn.rdlevel.rdkt.core.data.sound.audioGroup
|
||||
|
||||
import cn.rdlevel.rdkt.core.data.sound.SubGroupAudioData
|
||||
import cn.rdlevel.rdkt.core.data.sound.subGroupAudioDataOf
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlin.jvm.JvmOverloads
|
||||
|
||||
/**
|
||||
* An audio group representing big mistake sound.
|
||||
*
|
||||
* @param subTypes A map of subtype identifiers to their corresponding [SubGroupAudioData].
|
||||
*/
|
||||
@Serializable(BigMistakeAudioGroup.Serializer::class)
|
||||
public class BigMistakeAudioGroup(
|
||||
subTypes: Map<String, SubGroupAudioData> = mapOf(),
|
||||
) : AudioGroup(subTypes.toMutableMap()) {
|
||||
override val soundType: String = "BigMistake"
|
||||
|
||||
/**
|
||||
* The audio data of the sound.
|
||||
*/
|
||||
public var bigMistake: SubGroupAudioData by this
|
||||
|
||||
init {
|
||||
bigMistake // check existence
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a [BigMistakeAudioGroup] with the specified [bigMistake] audio data.
|
||||
*
|
||||
* @param bigMistake The audio data for the big mistake subtype.
|
||||
*/
|
||||
@JvmOverloads
|
||||
public constructor(bigMistake: SubGroupAudioData = subGroupAudioDataOf()) : this(
|
||||
mapOf("BigMistake" to bigMistake)
|
||||
)
|
||||
|
||||
public companion object Serializer : BaseSerializer<BigMistakeAudioGroup>("BigMistake") {
|
||||
override fun toAudioGroup(map: Map<String, SubGroupAudioData>): BigMistakeAudioGroup {
|
||||
return BigMistakeAudioGroup(map)
|
||||
}
|
||||
|
||||
init {
|
||||
register()
|
||||
}
|
||||
}
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
package cn.rdlevel.rdkt.core.data.sound.audioGroup
|
||||
|
||||
import cn.rdlevel.rdkt.core.data.sound.SubGroupAudioData
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* An audio group representing the sounds associated with a burnshot.
|
||||
*
|
||||
* @param subTypes A map of subtype identifiers to their corresponding [SubGroupAudioData].
|
||||
*/
|
||||
@Serializable(BurnshotSoundAudioGroup.Serializer::class)
|
||||
public class BurnshotSoundAudioGroup(
|
||||
subTypes: Map<String, SubGroupAudioData> = mapOf(),
|
||||
) : AudioGroup(subTypes.toMutableMap()) {
|
||||
override val soundType: String = "BurnshotSound"
|
||||
|
||||
/**
|
||||
* The audio data of the first burnshot sound cue.
|
||||
*/
|
||||
public var burnshotSoundCueLow: SubGroupAudioData by this
|
||||
|
||||
/**
|
||||
* The audio data of the second burnshot sound cue.
|
||||
*/
|
||||
public var burnshotSoundCueHigh: SubGroupAudioData by this
|
||||
|
||||
/**
|
||||
* The audio data of the incoming burnshot sound.
|
||||
*/
|
||||
public var burnshotSoundRiser: SubGroupAudioData by this
|
||||
|
||||
/**
|
||||
* The audio data of the burnshot sound cymbal when hitting a burnshot.
|
||||
*/
|
||||
public var burnshotSoundCymbal: SubGroupAudioData by this
|
||||
|
||||
init {
|
||||
// check existence
|
||||
burnshotSoundCueLow
|
||||
burnshotSoundCueHigh
|
||||
burnshotSoundRiser
|
||||
burnshotSoundCymbal
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a [BurnshotSoundAudioGroup] with the specified audio data.
|
||||
*/
|
||||
public constructor(
|
||||
burnshotSoundCueLow: SubGroupAudioData,
|
||||
burnshotSoundCueHigh: SubGroupAudioData,
|
||||
burnshotSoundRiser: SubGroupAudioData,
|
||||
burnshotSoundCymbal: SubGroupAudioData,
|
||||
) : this(
|
||||
mapOf(
|
||||
"BurnshotSoundCueLow" to burnshotSoundCueLow,
|
||||
"BurnshotSoundCueHigh" to burnshotSoundCueHigh,
|
||||
"BurnshotSoundRiser" to burnshotSoundRiser,
|
||||
"BurnshotSoundCymbal" to burnshotSoundCymbal,
|
||||
),
|
||||
)
|
||||
|
||||
public companion object Serializer : BaseSerializer<BurnshotSoundAudioGroup>("BurnshotSound") {
|
||||
override fun toAudioGroup(map: Map<String, SubGroupAudioData>): BurnshotSoundAudioGroup {
|
||||
return BurnshotSoundAudioGroup(map)
|
||||
}
|
||||
|
||||
init {
|
||||
register()
|
||||
}
|
||||
}
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
package cn.rdlevel.rdkt.core.data.sound.audioGroup
|
||||
|
||||
import cn.rdlevel.rdkt.core.data.sound.SubGroupAudioData
|
||||
import cn.rdlevel.rdkt.core.data.sound.subGroupAudioDataOf
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* An audio group representing the sound when the player is hitting with a hold.
|
||||
*
|
||||
* @param subTypes A map of subtype identifiers to their corresponding [SubGroupAudioData].
|
||||
*/
|
||||
@Serializable(ClapSoundHoldAudioGroup.Serializer::class)
|
||||
public class ClapSoundHoldAudioGroup(
|
||||
subTypes: Map<String, SubGroupAudioData> = mapOf(),
|
||||
) : AudioGroup(subTypes.toMutableMap()) {
|
||||
override val soundType: String = "ClapSoundHold"
|
||||
|
||||
/**
|
||||
* The audio data of a short hold.
|
||||
*/
|
||||
public var clapSoundHoldShortStart: SubGroupAudioData by this
|
||||
|
||||
/**
|
||||
* The audio data of the end of a short hold.
|
||||
*/
|
||||
public var clapSoundHoldShortEnd: SubGroupAudioData by this
|
||||
|
||||
/**
|
||||
* The audio data of a long hold.
|
||||
*/
|
||||
public var clapSoundHoldLongStart: SubGroupAudioData by this
|
||||
|
||||
/**
|
||||
* The audio data of the end of a long hold.
|
||||
*/
|
||||
public var clapSoundHoldLongEnd: SubGroupAudioData by this
|
||||
|
||||
init {
|
||||
// check existence
|
||||
clapSoundHoldShortStart
|
||||
clapSoundHoldShortEnd
|
||||
clapSoundHoldLongStart
|
||||
clapSoundHoldLongEnd
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a [ClapSoundHoldAudioGroup] with the specified audio data.
|
||||
*/
|
||||
public constructor(
|
||||
clapSoundHoldShortStart: SubGroupAudioData = subGroupAudioDataOf(),
|
||||
clapSoundHoldShortEnd: SubGroupAudioData = subGroupAudioDataOf(),
|
||||
clapSoundHoldLongStart: SubGroupAudioData = subGroupAudioDataOf(),
|
||||
clapSoundHoldLongEnd: SubGroupAudioData = subGroupAudioDataOf(),
|
||||
) : this(
|
||||
mapOf(
|
||||
"ClapSoundHoldShortStart" to clapSoundHoldShortStart,
|
||||
"ClapSoundHoldShortEnd" to clapSoundHoldShortEnd,
|
||||
"ClapSoundHoldLongStart" to clapSoundHoldLongStart,
|
||||
"ClapSoundHoldLongEnd" to clapSoundHoldLongEnd,
|
||||
)
|
||||
)
|
||||
|
||||
public companion object Serializer : BaseSerializer<ClapSoundHoldAudioGroup>("ClapSoundHold") {
|
||||
override fun toAudioGroup(map: Map<String, SubGroupAudioData>): ClapSoundHoldAudioGroup {
|
||||
return ClapSoundHoldAudioGroup(map)
|
||||
}
|
||||
|
||||
init {
|
||||
register()
|
||||
}
|
||||
}
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
package cn.rdlevel.rdkt.core.data.sound.audioGroup
|
||||
|
||||
import cn.rdlevel.rdkt.core.data.sound.SubGroupAudioData
|
||||
import cn.rdlevel.rdkt.core.data.sound.subGroupAudioDataOf
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* An audio group representing the sound when the player 2 is hitting with a hold.
|
||||
*
|
||||
* @param subTypes A map of subtype identifiers to their corresponding [SubGroupAudioData].
|
||||
*/
|
||||
@Serializable(ClapSoundHoldP2AudioGroup.Serializer::class)
|
||||
public class ClapSoundHoldP2AudioGroup(
|
||||
subTypes: Map<String, SubGroupAudioData> = mapOf(),
|
||||
) : AudioGroup(subTypes.toMutableMap()) {
|
||||
override val soundType: String = "ClapSoundHoldP2"
|
||||
|
||||
/**
|
||||
* The audio data of a short hold.
|
||||
*/
|
||||
public var clapSoundHoldShortStartP2: SubGroupAudioData by this
|
||||
|
||||
/**
|
||||
* The audio data of the end of a short hold.
|
||||
*/
|
||||
public var clapSoundHoldShortEndP2: SubGroupAudioData by this
|
||||
|
||||
/**
|
||||
* The audio data of a long hold.
|
||||
*/
|
||||
public var clapSoundHoldLongStartP2: SubGroupAudioData by this
|
||||
|
||||
/**
|
||||
* The audio data of the end of a long hold.
|
||||
*/
|
||||
public var clapSoundHoldLongEndP2: SubGroupAudioData by this
|
||||
|
||||
init {
|
||||
// check existence
|
||||
clapSoundHoldShortStartP2
|
||||
clapSoundHoldShortEndP2
|
||||
clapSoundHoldLongStartP2
|
||||
clapSoundHoldLongEndP2
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a [ClapSoundHoldP2AudioGroup] with the specified audio data.
|
||||
*/
|
||||
public constructor(
|
||||
clapSoundHoldShortStartP2: SubGroupAudioData = subGroupAudioDataOf(),
|
||||
clapSoundHoldShortEndP2: SubGroupAudioData = subGroupAudioDataOf(),
|
||||
clapSoundHoldLongStartP2: SubGroupAudioData = subGroupAudioDataOf(),
|
||||
clapSoundHoldLongEndP2: SubGroupAudioData = subGroupAudioDataOf(),
|
||||
) : this(
|
||||
mapOf(
|
||||
"ClapSoundHoldShortStartP2" to clapSoundHoldShortStartP2,
|
||||
"ClapSoundHoldShortEndP2" to clapSoundHoldShortEndP2,
|
||||
"ClapSoundHoldLongStartP2" to clapSoundHoldLongStartP2,
|
||||
"ClapSoundHoldLongEndP2" to clapSoundHoldLongEndP2,
|
||||
)
|
||||
)
|
||||
|
||||
public companion object Serializer : BaseSerializer<ClapSoundHoldP2AudioGroup>("ClapSoundHoldP2") {
|
||||
override fun toAudioGroup(map: Map<String, SubGroupAudioData>): ClapSoundHoldP2AudioGroup {
|
||||
return ClapSoundHoldP2AudioGroup(map)
|
||||
}
|
||||
|
||||
init {
|
||||
register()
|
||||
}
|
||||
}
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
package cn.rdlevel.rdkt.core.data.sound.audioGroup
|
||||
|
||||
import cn.rdlevel.rdkt.core.data.sound.SubGroupAudioData
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* An audio group representing the sounds associated with a freezeshot.
|
||||
*
|
||||
* @param subTypes A map of subtype identifiers to their corresponding [SubGroupAudioData].
|
||||
*/
|
||||
@Serializable(FreezeshotSoundAudioGroup.Serializer::class)
|
||||
public class FreezeshotSoundAudioGroup(
|
||||
subTypes: Map<String, SubGroupAudioData> = mapOf(),
|
||||
) : AudioGroup(subTypes.toMutableMap()) {
|
||||
override val soundType: String = "FreezeshotSound"
|
||||
|
||||
/**
|
||||
* The audio data of the first freezeshot sound cue.
|
||||
*/
|
||||
public var freezeshotSoundCueLow: SubGroupAudioData by this
|
||||
|
||||
/**
|
||||
* The audio data of the second freezeshot sound cue.
|
||||
*/
|
||||
public var freezeshotSoundCueHigh: SubGroupAudioData by this
|
||||
|
||||
/**
|
||||
* The audio data of the incoming freezeshot sound.
|
||||
*/
|
||||
public var freezeshotSoundRiser: SubGroupAudioData by this
|
||||
|
||||
/**
|
||||
* The audio data of the freezeshot sound cymbal when hitting a freezeshot.
|
||||
*/
|
||||
public var freezeshotSoundCymbal: SubGroupAudioData by this
|
||||
|
||||
init {
|
||||
// check existence
|
||||
freezeshotSoundCueLow
|
||||
freezeshotSoundCueHigh
|
||||
freezeshotSoundRiser
|
||||
freezeshotSoundCymbal
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a [FreezeshotSoundAudioGroup] with the specified audio data.
|
||||
*/
|
||||
public constructor(
|
||||
freezeshotSoundCueLow: SubGroupAudioData,
|
||||
freezeshotSoundCueHigh: SubGroupAudioData,
|
||||
freezeshotSoundRiser: SubGroupAudioData,
|
||||
freezeshotSoundCymbal: SubGroupAudioData,
|
||||
) : this(
|
||||
mapOf(
|
||||
"FreezeshotSoundCueLow" to freezeshotSoundCueLow,
|
||||
"FreezeshotSoundCueHigh" to freezeshotSoundCueHigh,
|
||||
"FreezeshotSoundRiser" to freezeshotSoundRiser,
|
||||
"FreezeshotSoundCymbal" to freezeshotSoundCymbal,
|
||||
),
|
||||
)
|
||||
|
||||
public companion object Serializer : BaseSerializer<FreezeshotSoundAudioGroup>("FreezeshotSound") {
|
||||
override fun toAudioGroup(map: Map<String, SubGroupAudioData>): FreezeshotSoundAudioGroup {
|
||||
return FreezeshotSoundAudioGroup(map)
|
||||
}
|
||||
|
||||
init {
|
||||
register()
|
||||
}
|
||||
}
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
package cn.rdlevel.rdkt.core.data.sound.audioGroup
|
||||
|
||||
import cn.rdlevel.rdkt.core.data.sound.SubGroupAudioData
|
||||
import cn.rdlevel.rdkt.core.data.sound.subGroupAudioDataOf
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlin.jvm.JvmOverloads
|
||||
|
||||
/**
|
||||
* An audio group representing the sound when the hand 1 presses the button on empty hit.
|
||||
*
|
||||
* @param subTypes A map of subtype identifiers to their corresponding [SubGroupAudioData].
|
||||
*/
|
||||
@Serializable(Hand1PopSoundAudioGroup.Serializer::class)
|
||||
public class Hand1PopSoundAudioGroup(
|
||||
subTypes: Map<String, SubGroupAudioData> = mapOf(),
|
||||
) : AudioGroup(subTypes.toMutableMap()) {
|
||||
override val soundType: String = "Hand1PopSound"
|
||||
|
||||
/**
|
||||
* The audio data of the sound.
|
||||
*/
|
||||
public var hand1PopSound: SubGroupAudioData by this
|
||||
|
||||
init {
|
||||
hand1PopSound // check existence
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a [Hand1PopSoundAudioGroup] with the specified [hand1PopSound] audio data.
|
||||
*
|
||||
* @param hand1PopSound The audio data for the Hand1PopSound subtype.
|
||||
*/
|
||||
@JvmOverloads
|
||||
public constructor(hand1PopSound: SubGroupAudioData = subGroupAudioDataOf()) : this(
|
||||
mapOf("Hand1PopSound" to hand1PopSound)
|
||||
)
|
||||
|
||||
public companion object Serializer : BaseSerializer<Hand1PopSoundAudioGroup>("Hand1PopSound") {
|
||||
override fun toAudioGroup(map: Map<String, SubGroupAudioData>): Hand1PopSoundAudioGroup {
|
||||
return Hand1PopSoundAudioGroup(map)
|
||||
}
|
||||
|
||||
init {
|
||||
register()
|
||||
}
|
||||
}
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
package cn.rdlevel.rdkt.core.data.sound.audioGroup
|
||||
|
||||
import cn.rdlevel.rdkt.core.data.sound.SubGroupAudioData
|
||||
import cn.rdlevel.rdkt.core.data.sound.subGroupAudioDataOf
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlin.jvm.JvmOverloads
|
||||
|
||||
/**
|
||||
* An audio group representing the sound when the hand 2 presses the button on empty hit.
|
||||
*
|
||||
* @param subTypes A map of subtype identifiers to their corresponding [SubGroupAudioData].
|
||||
*/
|
||||
@Serializable(Hand2PopSoundAudioGroup.Serializer::class)
|
||||
public class Hand2PopSoundAudioGroup(
|
||||
subTypes: Map<String, SubGroupAudioData> = mapOf(),
|
||||
) : AudioGroup(subTypes.toMutableMap()) {
|
||||
override val soundType: String = "Hand2PopSound"
|
||||
|
||||
/**
|
||||
* The audio data of the sound.
|
||||
*/
|
||||
public var hand2PopSound: SubGroupAudioData by this
|
||||
|
||||
init {
|
||||
hand2PopSound // check existence
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a [Hand2PopSoundAudioGroup] with the specified [hand2PopSound] audio data.
|
||||
*
|
||||
* @param hand2PopSound The audio data for the Hand2PopSound subtype.
|
||||
*/
|
||||
@JvmOverloads
|
||||
public constructor(hand2PopSound: SubGroupAudioData = subGroupAudioDataOf()) : this(
|
||||
mapOf("Hand2PopSound" to hand2PopSound)
|
||||
)
|
||||
|
||||
public companion object Serializer : BaseSerializer<Hand2PopSoundAudioGroup>("Hand2PopSound") {
|
||||
override fun toAudioGroup(map: Map<String, SubGroupAudioData>): Hand2PopSoundAudioGroup {
|
||||
return Hand2PopSoundAudioGroup(map)
|
||||
}
|
||||
|
||||
init {
|
||||
register()
|
||||
}
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
package cn.rdlevel.rdkt.core.data.sound.audioGroup
|
||||
|
||||
import cn.rdlevel.rdkt.core.data.sound.SubGroupAudioData
|
||||
import cn.rdlevel.rdkt.core.data.sound.subGroupAudioDataOf
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlin.jvm.JvmOverloads
|
||||
|
||||
/**
|
||||
* An audio group representing the heart explosion sound after hitting 2 hits in a row.
|
||||
*
|
||||
* @param subTypes A map of subtype identifiers to their corresponding [SubGroupAudioData].
|
||||
*/
|
||||
@Serializable(HeartExplosion2AudioGroup.Serializer::class)
|
||||
public class HeartExplosion2AudioGroup(
|
||||
subTypes: Map<String, SubGroupAudioData> = mapOf(),
|
||||
) : AudioGroup(subTypes.toMutableMap()) {
|
||||
override val soundType: String = "HeartExplosion2"
|
||||
|
||||
/**
|
||||
* The audio data of the sound.
|
||||
*/
|
||||
public var heartExplosion2: SubGroupAudioData by this
|
||||
|
||||
init {
|
||||
heartExplosion2 // check existence
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a [HeartExplosion2AudioGroup] with the specified [heartExplosion2] audio data.
|
||||
*/
|
||||
@JvmOverloads
|
||||
public constructor(heartExplosion2: SubGroupAudioData = subGroupAudioDataOf()) : this(
|
||||
mapOf("HeartExplosion2" to heartExplosion2)
|
||||
)
|
||||
|
||||
public companion object Serializer : BaseSerializer<HeartExplosion2AudioGroup>("HeartExplosion2") {
|
||||
override fun toAudioGroup(map: Map<String, SubGroupAudioData>): HeartExplosion2AudioGroup {
|
||||
return HeartExplosion2AudioGroup(map)
|
||||
}
|
||||
|
||||
init {
|
||||
register()
|
||||
}
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
package cn.rdlevel.rdkt.core.data.sound.audioGroup
|
||||
|
||||
import cn.rdlevel.rdkt.core.data.sound.SubGroupAudioData
|
||||
import cn.rdlevel.rdkt.core.data.sound.subGroupAudioDataOf
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlin.jvm.JvmOverloads
|
||||
|
||||
/**
|
||||
* An audio group representing the heart explosion sound after hitting 3 or more hits in a row.
|
||||
*
|
||||
* @param subTypes A map of subtype identifiers to their corresponding [SubGroupAudioData].
|
||||
*/
|
||||
@Serializable(HeartExplosion3AudioGroup.Serializer::class)
|
||||
public class HeartExplosion3AudioGroup(
|
||||
subTypes: Map<String, SubGroupAudioData> = mapOf(),
|
||||
) : AudioGroup(subTypes.toMutableMap()) {
|
||||
override val soundType: String = "HeartExplosion3"
|
||||
|
||||
/**
|
||||
* The audio data of the sound.
|
||||
*/
|
||||
public var heartExplosion3: SubGroupAudioData by this
|
||||
|
||||
init {
|
||||
heartExplosion3 // check existence
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a [HeartExplosion3AudioGroup] with the specified [heartExplosion3] audio data.
|
||||
*/
|
||||
@JvmOverloads
|
||||
public constructor(heartExplosion3: SubGroupAudioData = subGroupAudioDataOf()) : this(
|
||||
mapOf("HeartExplosion3" to heartExplosion3)
|
||||
)
|
||||
|
||||
public companion object Serializer : BaseSerializer<HeartExplosion3AudioGroup>("HeartExplosion3") {
|
||||
override fun toAudioGroup(map: Map<String, SubGroupAudioData>): HeartExplosion3AudioGroup {
|
||||
return HeartExplosion3AudioGroup(map)
|
||||
}
|
||||
|
||||
init {
|
||||
register()
|
||||
}
|
||||
}
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
package cn.rdlevel.rdkt.core.data.sound.audioGroup
|
||||
|
||||
import cn.rdlevel.rdkt.core.data.sound.SubGroupAudioData
|
||||
import cn.rdlevel.rdkt.core.data.sound.subGroupAudioDataOf
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlin.jvm.JvmOverloads
|
||||
|
||||
/**
|
||||
* An audio group representing the heart explosion sound after hitting single hit.
|
||||
*
|
||||
* @param subTypes A map of subtype identifiers to their corresponding [SubGroupAudioData].
|
||||
*/
|
||||
@Serializable(HeartExplosionAudioGroup.Serializer::class)
|
||||
public class HeartExplosionAudioGroup(
|
||||
subTypes: Map<String, SubGroupAudioData> = mapOf(),
|
||||
) : AudioGroup(subTypes.toMutableMap()) {
|
||||
override val soundType: String = "HeartExplosion"
|
||||
|
||||
/**
|
||||
* The audio data of the sound.
|
||||
*/
|
||||
public var heartExplosion: SubGroupAudioData by this
|
||||
|
||||
init {
|
||||
heartExplosion // check existence
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a [HeartExplosionAudioGroup] with the specified [heartExplosion] audio data.
|
||||
*
|
||||
* @param heartExplosion The audio data for the HeartExplosion subtype.
|
||||
*/
|
||||
@JvmOverloads
|
||||
public constructor(heartExplosion: SubGroupAudioData = subGroupAudioDataOf()) : this(
|
||||
mapOf("HeartExplosion" to heartExplosion)
|
||||
)
|
||||
|
||||
public companion object Serializer : BaseSerializer<HeartExplosionAudioGroup>("HeartExplosion") {
|
||||
override fun toAudioGroup(map: Map<String, SubGroupAudioData>): HeartExplosionAudioGroup {
|
||||
return HeartExplosionAudioGroup(map)
|
||||
}
|
||||
|
||||
init {
|
||||
register()
|
||||
}
|
||||
}
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
package cn.rdlevel.rdkt.core.data.sound.audioGroup
|
||||
|
||||
import cn.rdlevel.rdkt.core.data.sound.SubGroupAudioData
|
||||
import cn.rdlevel.rdkt.core.data.sound.subGroupAudioDataOf
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* An audio group representing the sound when a hold is pulsing.
|
||||
*
|
||||
* @param subTypes A map of subtype identifiers to their corresponding [SubGroupAudioData].
|
||||
*/
|
||||
@Serializable(PulseSoundHoldAudioGroup.Serializer::class)
|
||||
public class PulseSoundHoldAudioGroup(
|
||||
subTypes: Map<String, SubGroupAudioData> = mapOf(),
|
||||
) : AudioGroup(subTypes.toMutableMap()) {
|
||||
override val soundType: String = "PulseSoundHold"
|
||||
|
||||
/**
|
||||
* The audio data of a hold pulse.
|
||||
*/
|
||||
public var pulseSoundHoldStart: SubGroupAudioData by this
|
||||
|
||||
/**
|
||||
* The audio data of the end of a short hold pulse.
|
||||
*/
|
||||
public var pulseSoundHoldShortEnd: SubGroupAudioData by this
|
||||
|
||||
/**
|
||||
* The audio data of the end of a hold pulse.
|
||||
*/
|
||||
public var pulseSoundHoldEnd: SubGroupAudioData by this
|
||||
|
||||
/**
|
||||
* The alternative audio data of a hold pulse.
|
||||
*/
|
||||
public var pulseSoundHoldStartAlt: SubGroupAudioData by this
|
||||
|
||||
/**
|
||||
* The alternative audio data of the end of a short hold pulse.
|
||||
*/
|
||||
public var pulseSoundHoldShortEndAlt: SubGroupAudioData by this
|
||||
|
||||
/**
|
||||
* The alternative audio data of the end of a hold pulse.
|
||||
*/
|
||||
public var pulseSoundHoldEndAlt: SubGroupAudioData by this
|
||||
|
||||
init {
|
||||
// check existence
|
||||
pulseSoundHoldStart
|
||||
pulseSoundHoldShortEnd
|
||||
pulseSoundHoldEnd
|
||||
pulseSoundHoldStartAlt
|
||||
pulseSoundHoldShortEndAlt
|
||||
pulseSoundHoldEndAlt
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a [ClapSoundHoldAudioGroup] with the specified audio data.
|
||||
*/
|
||||
public constructor(
|
||||
pulseSoundHoldStart: SubGroupAudioData = subGroupAudioDataOf(),
|
||||
pulseSoundHoldShortEnd: SubGroupAudioData = subGroupAudioDataOf(),
|
||||
pulseSoundHoldEnd: SubGroupAudioData = subGroupAudioDataOf(),
|
||||
pulseSoundHoldStartAlt: SubGroupAudioData = subGroupAudioDataOf(),
|
||||
pulseSoundHoldShortEndAlt: SubGroupAudioData = subGroupAudioDataOf(),
|
||||
pulseSoundHoldEndAlt: SubGroupAudioData = subGroupAudioDataOf(),
|
||||
) : this(
|
||||
mapOf(
|
||||
"PulseSoundHoldStart" to pulseSoundHoldStart,
|
||||
"PulseSoundHoldShortEnd" to pulseSoundHoldShortEnd,
|
||||
"PulseSoundHoldEnd" to pulseSoundHoldEnd,
|
||||
"PulseSoundHoldStartAlt" to pulseSoundHoldStartAlt,
|
||||
"PulseSoundHoldShortEndAlt" to pulseSoundHoldShortEndAlt,
|
||||
"PulseSoundHoldEndAlt" to pulseSoundHoldEndAlt,
|
||||
)
|
||||
)
|
||||
|
||||
public companion object Serializer : BaseSerializer<PulseSoundHoldAudioGroup>("PulseSoundHold") {
|
||||
override fun toAudioGroup(map: Map<String, SubGroupAudioData>): PulseSoundHoldAudioGroup {
|
||||
return PulseSoundHoldAudioGroup(map)
|
||||
}
|
||||
|
||||
init {
|
||||
register()
|
||||
}
|
||||
}
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
package cn.rdlevel.rdkt.core.data.sound.audioGroup
|
||||
|
||||
import cn.rdlevel.rdkt.core.data.sound.SubGroupAudioData
|
||||
import cn.rdlevel.rdkt.core.data.sound.subGroupAudioDataOf
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* An audio group representing the sound when a hold for player 2 is pulsing.
|
||||
*
|
||||
* @param subTypes A map of subtype identifiers to their corresponding [SubGroupAudioData].
|
||||
*/
|
||||
@Serializable(PulseSoundHoldP2AudioGroup.Serializer::class)
|
||||
public class PulseSoundHoldP2AudioGroup(
|
||||
subTypes: Map<String, SubGroupAudioData> = mapOf(),
|
||||
) : AudioGroup(subTypes.toMutableMap()) {
|
||||
override val soundType: String = "PulseSoundHoldP2"
|
||||
|
||||
/**
|
||||
* The audio data of a hold pulse.
|
||||
*/
|
||||
public var pulseSoundHoldStartP2: SubGroupAudioData by this
|
||||
|
||||
/**
|
||||
* The audio data of the end of a short hold pulse.
|
||||
*/
|
||||
public var pulseSoundHoldShortEndP2: SubGroupAudioData by this
|
||||
|
||||
/**
|
||||
* The audio data of the end of a hold pulse.
|
||||
*/
|
||||
public var pulseSoundHoldEndP2: SubGroupAudioData by this
|
||||
|
||||
/**
|
||||
* The alternative audio data of a hold pulse.
|
||||
*/
|
||||
public var pulseSoundHoldStartAltP2: SubGroupAudioData by this
|
||||
|
||||
/**
|
||||
* The alternative audio data of the end of a short hold pulse.
|
||||
*/
|
||||
public var pulseSoundHoldShortEndAltP2: SubGroupAudioData by this
|
||||
|
||||
/**
|
||||
* The alternative audio data of the end of a hold pulse.
|
||||
*/
|
||||
public var pulseSoundHoldEndAltP2: SubGroupAudioData by this
|
||||
|
||||
init {
|
||||
// check existence
|
||||
pulseSoundHoldStartP2
|
||||
pulseSoundHoldShortEndP2
|
||||
pulseSoundHoldEndP2
|
||||
pulseSoundHoldStartAltP2
|
||||
pulseSoundHoldShortEndAltP2
|
||||
pulseSoundHoldEndAltP2
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a [ClapSoundHoldAudioGroup] with the specified audio data.
|
||||
*/
|
||||
public constructor(
|
||||
pulseSoundHoldStartP2: SubGroupAudioData = subGroupAudioDataOf(),
|
||||
pulseSoundHoldShortEndP2: SubGroupAudioData = subGroupAudioDataOf(),
|
||||
pulseSoundHoldEndP2: SubGroupAudioData = subGroupAudioDataOf(),
|
||||
pulseSoundHoldStartAltP2: SubGroupAudioData = subGroupAudioDataOf(),
|
||||
pulseSoundHoldShortEndAltP2: SubGroupAudioData = subGroupAudioDataOf(),
|
||||
pulseSoundHoldEndAltP2: SubGroupAudioData = subGroupAudioDataOf(),
|
||||
) : this(
|
||||
mapOf(
|
||||
"PulseSoundHoldStartP2" to pulseSoundHoldStartP2,
|
||||
"PulseSoundHoldShortEndP2" to pulseSoundHoldShortEndP2,
|
||||
"PulseSoundHoldEndP2" to pulseSoundHoldEndP2,
|
||||
"PulseSoundHoldStartAltP2" to pulseSoundHoldStartAltP2,
|
||||
"PulseSoundHoldShortEndAltP2" to pulseSoundHoldShortEndAltP2,
|
||||
"PulseSoundHoldEndAltP2" to pulseSoundHoldEndAltP2,
|
||||
)
|
||||
)
|
||||
|
||||
public companion object Serializer : BaseSerializer<PulseSoundHoldP2AudioGroup>("PulseSoundHoldP2") {
|
||||
override fun toAudioGroup(map: Map<String, SubGroupAudioData>): PulseSoundHoldP2AudioGroup {
|
||||
return PulseSoundHoldP2AudioGroup(map)
|
||||
}
|
||||
|
||||
init {
|
||||
register()
|
||||
}
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
package cn.rdlevel.rdkt.core.data.sound.audioGroup
|
||||
|
||||
import cn.rdlevel.rdkt.core.data.sound.SubGroupAudioData
|
||||
import cn.rdlevel.rdkt.core.data.sound.subGroupAudioDataOf
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlin.jvm.JvmOverloads
|
||||
|
||||
/**
|
||||
* An audio group representing the skipshot sound.
|
||||
*
|
||||
* @param subTypes A map of subtype identifiers to their corresponding [SubGroupAudioData].
|
||||
*/
|
||||
@Serializable(SkipshotAudioGroup.Serializer::class)
|
||||
public class SkipshotAudioGroup(
|
||||
subTypes: Map<String, SubGroupAudioData> = mapOf(),
|
||||
) : AudioGroup(subTypes.toMutableMap()) {
|
||||
override val soundType: String = "Skipshot"
|
||||
|
||||
/**
|
||||
* The audio data of the sound.
|
||||
*/
|
||||
public var skipshot: SubGroupAudioData by this
|
||||
|
||||
init {
|
||||
skipshot // check existence
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a [SkipshotAudioGroup] with the specified [skipshot] audio data.
|
||||
*/
|
||||
@JvmOverloads
|
||||
public constructor(skipshot: SubGroupAudioData = subGroupAudioDataOf()) : this(
|
||||
mapOf("Skipshot" to skipshot)
|
||||
)
|
||||
|
||||
public companion object Serializer : BaseSerializer<SkipshotAudioGroup>("Skipshot") {
|
||||
override fun toAudioGroup(map: Map<String, SubGroupAudioData>): SkipshotAudioGroup {
|
||||
return SkipshotAudioGroup(map)
|
||||
}
|
||||
|
||||
init {
|
||||
register()
|
||||
}
|
||||
}
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
package cn.rdlevel.rdkt.core.data.sound.audioGroup
|
||||
|
||||
import cn.rdlevel.rdkt.core.data.sound.SubGroupAudioData
|
||||
import cn.rdlevel.rdkt.core.data.sound.subGroupAudioDataOf
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlin.jvm.JvmOverloads
|
||||
|
||||
/**
|
||||
* An audio group representing small mistake sound.
|
||||
*
|
||||
* @param subTypes A map of subtype identifiers to their corresponding [SubGroupAudioData].
|
||||
*/
|
||||
@Serializable(SmallMistakeAudioGroup.Serializer::class)
|
||||
public class SmallMistakeAudioGroup(
|
||||
subTypes: Map<String, SubGroupAudioData> = mapOf(),
|
||||
) : AudioGroup(subTypes.toMutableMap()) {
|
||||
override val soundType: String = "SmallMistake"
|
||||
|
||||
/**
|
||||
* The audio data of the sound.
|
||||
*/
|
||||
public var smallMistake: SubGroupAudioData by this
|
||||
|
||||
init {
|
||||
smallMistake // check existence
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a [SmallMistakeAudioGroup] with the specified [smallMistake] audio data.
|
||||
*
|
||||
* @param smallMistake The audio data for the small mistake subtype.
|
||||
*/
|
||||
@JvmOverloads
|
||||
public constructor(smallMistake: SubGroupAudioData = subGroupAudioDataOf()) : this(
|
||||
mapOf("SmallMistake" to smallMistake)
|
||||
)
|
||||
|
||||
public companion object Serializer : BaseSerializer<SmallMistakeAudioGroup>("SmallMistake") {
|
||||
override fun toAudioGroup(map: Map<String, SubGroupAudioData>): SmallMistakeAudioGroup {
|
||||
return SmallMistakeAudioGroup(map)
|
||||
}
|
||||
|
||||
init {
|
||||
register()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
package cn.rdlevel.rdkt.core.events
|
||||
|
||||
import cn.rdlevel.rdkt.core.RDLevel
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* An event that can be customized by the developers.
|
||||
* Developers are encouraged to use this interface to create their own custom events.
|
||||
*/
|
||||
@Serializable
|
||||
public abstract class CustomEvent : AbstractEvent() {
|
||||
/**
|
||||
* Applies this event to the given level.
|
||||
|
||||
@@ -42,6 +42,7 @@ public sealed interface Event {
|
||||
/**
|
||||
* An abstraction of an [Event] that provides default implementations for some properties.
|
||||
*/
|
||||
@Serializable
|
||||
public sealed class AbstractEvent: Event {
|
||||
override var bar: Int = 1
|
||||
set(value) {
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package cn.rdlevel.rdkt.core.events
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlin.jvm.JvmStatic
|
||||
|
||||
/**
|
||||
* An [Event] that can specify a beat manually.
|
||||
*/
|
||||
@@ -30,4 +33,22 @@ public interface DurationSpecificEvent : Event {
|
||||
* The duration of the event in beats.
|
||||
*/
|
||||
public var duration: Double
|
||||
}
|
||||
|
||||
/**
|
||||
* An [Event] that is specific to a single row.
|
||||
*/
|
||||
public interface RowSpecificEvent : Event {
|
||||
/**
|
||||
* The row id the event is specifying.
|
||||
*/
|
||||
@SerialName("row")
|
||||
public var rowId: Int
|
||||
|
||||
public companion object {
|
||||
@JvmStatic
|
||||
public fun requireRowInBound(rowId: Int) {
|
||||
require(rowId in 0..15) { "Row id must be between 0 and 15." }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +1,47 @@
|
||||
package cn.rdlevel.rdkt.core.events
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* An event that belongs to the sounds tab.
|
||||
*/
|
||||
@Serializable
|
||||
public sealed class SoundEvent : AbstractEvent(), YSpecificEvent {
|
||||
override var y: Int = 0
|
||||
}
|
||||
|
||||
/**
|
||||
* A [SoundEvent] that is specific to a beat.
|
||||
*/
|
||||
@Serializable
|
||||
public sealed class BeatSpecificSoundEvent : BeatSpecificEvent, SoundEvent() {
|
||||
override var beat: Double = 1.0
|
||||
set(value) {
|
||||
require(value >= 1) { "Beat must be greater than or equal to 1." }
|
||||
field = value
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An event that belongs to the rows tab.
|
||||
*/
|
||||
@Serializable
|
||||
public sealed class RowEvent : AbstractEvent()
|
||||
|
||||
/**
|
||||
* An event that belongs to the actions tab.
|
||||
*/
|
||||
@Serializable
|
||||
public sealed class ActionEvent : AbstractEvent()
|
||||
|
||||
/**
|
||||
* An event that belongs to the rooms tab.
|
||||
*/
|
||||
@Serializable
|
||||
public sealed class RoomEvent : AbstractEvent()
|
||||
|
||||
/**
|
||||
* An event that belongs to the decorations tab.
|
||||
*/
|
||||
@Serializable
|
||||
public sealed class DecorationEvent : AbstractEvent()
|
||||
@@ -0,0 +1,194 @@
|
||||
package cn.rdlevel.rdkt.core.events
|
||||
|
||||
|
||||
import cn.rdlevel.rdkt.core.data.RowPattern
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Transient
|
||||
import kotlin.jvm.JvmStatic
|
||||
|
||||
/**
|
||||
* An event that narrates information about a specific row, such as when it is connected, updated, or disconnected.
|
||||
*
|
||||
* @property infoType The type of information to narrate (e.g., Connect, Update, Disconnect).
|
||||
* @property soundOnly Whether to only play the narration sound without any additional information.
|
||||
* @property skipsUnstable Whether the row being narrated has unstable row pattern that constantly changed. Will be ignored if [infoType] is [InfoType.DISCONNECT].
|
||||
* @property customPlayer The custom player for which the narration is intended.
|
||||
*/
|
||||
@Serializable
|
||||
@SerialName("NarrateRowInfo")
|
||||
public class NarrateRowInfoEvent private constructor(
|
||||
public var infoType: InfoType = InfoType.CONNECT,
|
||||
public var soundOnly: Boolean = false,
|
||||
private var narrateSkipBeats: String = "On",
|
||||
public var skipsUnstable: Boolean = false,
|
||||
private var customPattern: RowPattern? = null,
|
||||
public var customPlayer: CustomPlayer = CustomPlayer.AUTO_DETECT,
|
||||
) : BeatSpecificSoundEvent(), RowSpecificEvent {
|
||||
@SerialName("row")
|
||||
override var rowId: Int = 0
|
||||
set(value) {
|
||||
RowSpecificEvent.requireRowInBound(value)
|
||||
field = value
|
||||
}
|
||||
|
||||
@Transient
|
||||
private var _rowPattern: RowPatternNarration = RowPatternNarration.On
|
||||
|
||||
/**
|
||||
* The pattern narration setting for the row.
|
||||
* Will be ignored if [infoType] is [InfoType.DISCONNECT].
|
||||
*/
|
||||
public var rowPattern: RowPatternNarration
|
||||
get() = _rowPattern
|
||||
set(value) {
|
||||
narrateSkipBeats = value.type
|
||||
customPattern = (value as? RowPatternNarration.Custom)?.pattern
|
||||
_rowPattern = value
|
||||
}
|
||||
|
||||
private fun initSkipPattern() {
|
||||
_rowPattern = when (narrateSkipBeats) {
|
||||
"On" -> RowPatternNarration.On
|
||||
"Off" -> RowPatternNarration.Off
|
||||
RowPatternNarration.Custom.TYPE -> customPattern?.let { RowPatternNarration.Custom(it) }
|
||||
?: error("Expected customPattern to be non-null when narrateSkipBeats is 'Custom'.")
|
||||
|
||||
else -> error("Invalid narrateSkipBeats value: $narrateSkipBeats")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The length of the row to be narrated when using a custom row pattern. Must be between 1 and 7. Setting this to null will use the default row length for narration.
|
||||
*/
|
||||
public var customRowLength: Int? = null
|
||||
set(value) {
|
||||
if (value != null) {
|
||||
require(value in 1..7) { "Row length must be between 1 and 7." }
|
||||
}
|
||||
field = value
|
||||
}
|
||||
|
||||
init {
|
||||
initSkipPattern()
|
||||
}
|
||||
|
||||
public companion object {
|
||||
/**
|
||||
* Creates a copy of the given [NarrateRowInfoEvent] with the same properties.
|
||||
*/
|
||||
@JvmStatic
|
||||
public fun of(event: NarrateRowInfoEvent): NarrateRowInfoEvent {
|
||||
return NarrateRowInfoEvent(
|
||||
infoType = event.infoType,
|
||||
soundOnly = event.soundOnly,
|
||||
narrateSkipBeats = event.narrateSkipBeats,
|
||||
skipsUnstable = event.skipsUnstable,
|
||||
customPattern = event.customPattern,
|
||||
customPlayer = event.customPlayer,
|
||||
).apply {
|
||||
rowId = event.rowId
|
||||
customRowLength = event.customRowLength
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new [NarrateRowInfoEvent] with the properties set by the given block.
|
||||
*/
|
||||
@JvmStatic
|
||||
public fun of(block: NarrateRowInfoEvent.() -> Unit): NarrateRowInfoEvent {
|
||||
return NarrateRowInfoEvent().apply(block)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents how the row pattern is narrated.
|
||||
*/
|
||||
@Serializable
|
||||
public sealed class RowPatternNarration(public val type: String) {
|
||||
/**
|
||||
* Narrate the row pattern as it is.
|
||||
*/
|
||||
@Serializable
|
||||
@SerialName("On")
|
||||
public object On : RowPatternNarration("On")
|
||||
|
||||
/**
|
||||
* Do not narrate the row pattern.
|
||||
*/
|
||||
@Serializable
|
||||
@SerialName("Off")
|
||||
public object Off : RowPatternNarration("Off")
|
||||
|
||||
/**
|
||||
* Narrate the row pattern with the provided pattern.
|
||||
*
|
||||
* @property pattern The custom row pattern to be narrated.
|
||||
*/
|
||||
@Serializable
|
||||
@SerialName("Custom")
|
||||
public class Custom(public val pattern: RowPattern) : RowPatternNarration(TYPE) {
|
||||
public companion object {
|
||||
public const val TYPE: String = "Custom"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the type of row information to be narrated.
|
||||
*/
|
||||
@Serializable
|
||||
public enum class InfoType {
|
||||
/**
|
||||
* The row is being connected.
|
||||
*/
|
||||
@SerialName("Connect")
|
||||
CONNECT,
|
||||
|
||||
/**
|
||||
* The row is being updated.
|
||||
*/
|
||||
@SerialName("Update")
|
||||
UPDATE,
|
||||
|
||||
/**
|
||||
* The row is being disconnected.
|
||||
*/
|
||||
@SerialName("Disconnect")
|
||||
DISCONNECT,
|
||||
|
||||
/**
|
||||
* The cpu for the row is being online.
|
||||
*/
|
||||
@SerialName("Online")
|
||||
ONLINE,
|
||||
|
||||
/**
|
||||
* The cpu for the row is being offline.
|
||||
*/
|
||||
@SerialName("Offline")
|
||||
OFFLINE,
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the custom player for which the narration is intended.
|
||||
*/
|
||||
@Serializable
|
||||
public enum class CustomPlayer {
|
||||
/**
|
||||
* The player to narrate will be automatically detected.
|
||||
*/
|
||||
@SerialName("AutoDetect")
|
||||
AUTO_DETECT,
|
||||
|
||||
/**
|
||||
* The player to narrate is Player 1.
|
||||
*/
|
||||
P1,
|
||||
|
||||
/**
|
||||
* The player to narrate is Player 2.
|
||||
*/
|
||||
P2,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package cn.rdlevel.rdkt.core.events
|
||||
|
||||
|
||||
import cn.rdlevel.rdkt.core.data.sound.AudioData
|
||||
import cn.rdlevel.rdkt.core.data.sound.audioDataOfMusic
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlin.jvm.JvmOverloads
|
||||
|
||||
/**
|
||||
* Plays a song in the level.
|
||||
*
|
||||
* @property song The song to play.
|
||||
*/
|
||||
@Serializable
|
||||
@SerialName("PlaySong")
|
||||
public class PlaySongEvent(
|
||||
public var song: AudioData,
|
||||
) : SoundEvent() {
|
||||
/**
|
||||
* The beats per minute (BPM) of the song.
|
||||
*/
|
||||
public var bpm: Double = 100.0
|
||||
set(value) {
|
||||
require(value >= 1) { "BPM must be at least 1." }
|
||||
field = value
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the song should loop.
|
||||
*/
|
||||
public var loop: Boolean = false
|
||||
|
||||
/**
|
||||
* Creates a new [PlaySongEvent] with the specified [fileName], [bpm], and [offset].
|
||||
*/
|
||||
@JvmOverloads
|
||||
public constructor(fileName: String = "sndOrientalTechno", bpm: Double = 100.0, offset: Int = 0) : this(
|
||||
audioDataOfMusic(fileName, offset)
|
||||
) {
|
||||
this.bpm = bpm
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package cn.rdlevel.rdkt.core.events
|
||||
|
||||
|
||||
import cn.rdlevel.rdkt.core.data.sound.AudioData
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlin.jvm.JvmOverloads
|
||||
|
||||
/**
|
||||
* Plays a sound.
|
||||
*/
|
||||
@Serializable
|
||||
@SerialName("PlaySound")
|
||||
public class PlaySoundEvent(
|
||||
/**
|
||||
* The sound to play.
|
||||
*/
|
||||
public var sound: AudioData,
|
||||
/**
|
||||
* The type of sound.
|
||||
*/
|
||||
public var customSoundType: SoundType = SoundType.CUE_SOUND,
|
||||
) : BeatSpecificSoundEvent() {
|
||||
/**
|
||||
* The type of the sound.
|
||||
*/
|
||||
@Serializable
|
||||
public enum class SoundType {
|
||||
@SerialName("CueSound")
|
||||
CUE_SOUND,
|
||||
|
||||
@SerialName("MusicSound")
|
||||
MUSIC_SOUND,
|
||||
|
||||
@SerialName("BeatSound")
|
||||
BEAT_SOUND,
|
||||
|
||||
@SerialName("HitSound")
|
||||
HIT_SOUND,
|
||||
|
||||
@SerialName("OtherSound")
|
||||
OTHER_SOUND,
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new [PlaySoundEvent] with the specified [fileName] and [customSoundType].
|
||||
*
|
||||
* @param fileName The name of the sound file.
|
||||
* @param customSoundType The type of sound.
|
||||
*/
|
||||
@JvmOverloads
|
||||
public constructor(fileName: String = "Shaker", customSoundType: SoundType = SoundType.CUE_SOUND) : this(
|
||||
AudioData(fileName),
|
||||
customSoundType
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package cn.rdlevel.rdkt.core.events
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlin.jvm.JvmOverloads
|
||||
|
||||
/**
|
||||
* An event that reads the specified text as a narration.
|
||||
*
|
||||
* @property text The text to be narrated.
|
||||
* @property category The category of the narration.
|
||||
*/
|
||||
@Serializable
|
||||
@SerialName("ReadNarration")
|
||||
public data class ReadNarrationEvent @JvmOverloads constructor(
|
||||
var text: String,
|
||||
var category: Category = Category.DESCRIPTION,
|
||||
) : BeatSpecificSoundEvent() {
|
||||
/**
|
||||
* Represents the category of the narration.
|
||||
*/
|
||||
public enum class Category {
|
||||
@SerialName("Notification")
|
||||
NOTIFICATION,
|
||||
|
||||
@SerialName("Description")
|
||||
DESCRIPTION,
|
||||
|
||||
@SerialName("Subtitles")
|
||||
SUBTITLES,
|
||||
|
||||
@SerialName("Instruction")
|
||||
INSTRUCTION,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
package cn.rdlevel.rdkt.core.events
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlin.jvm.JvmOverloads
|
||||
|
||||
/**
|
||||
* Event that gives players cues from the nurse for the rows.
|
||||
* You should use this event to cue oneshot rows.
|
||||
*/
|
||||
@Serializable
|
||||
@SerialName("SayReadyGetSetGo")
|
||||
public class SayReadyGetSetGoEvent @JvmOverloads constructor(
|
||||
/**
|
||||
* The phrase to be spoken by the nurse.
|
||||
*/
|
||||
public var phraseToSay: NursePhraseType,
|
||||
/**
|
||||
* The source of the voice for the nurse.
|
||||
*/
|
||||
public var voiceSource: NurseVoiceType = NurseVoiceType.NURSE,
|
||||
) {
|
||||
/**
|
||||
* The delay between each syllable spoken by the nurse in beats.
|
||||
* Setting this to 0 or lower will cause the nurse to speak immediately.
|
||||
* When the phrase only has one syllable, this value is ignored.
|
||||
*/
|
||||
public var tick: Double = 1.0
|
||||
|
||||
/**
|
||||
* The volume of the voice.
|
||||
* This value must be between 0 and 200.
|
||||
*/
|
||||
public var volume: Int = 100
|
||||
set(value) {
|
||||
require(value in 0..200) { "Volume must be between 0 and 200." }
|
||||
field = value
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a SayReadyGetSetGoEvent with the specified phrase, voice source, tick, and volume.
|
||||
*
|
||||
* @param phraseToSay The phrase to be spoken by the nurse.
|
||||
* @param voiceSource The source of the voice for the nurse.
|
||||
* @param tick The delay between each syllable spoken by the nurse in beats. Defaults to 1.0.
|
||||
* @param volume The volume of the voice, must be between 0 and 200. Defaults to 100.
|
||||
*/
|
||||
public constructor(
|
||||
phraseToSay: NursePhraseType,
|
||||
voiceSource: NurseVoiceType,
|
||||
tick: Double = 1.0,
|
||||
volume: Int = 100
|
||||
) : this(
|
||||
phraseToSay,
|
||||
voiceSource
|
||||
) {
|
||||
this.tick = tick
|
||||
this.volume = volume
|
||||
}
|
||||
|
||||
/**
|
||||
* Enum representing the different types of phrases that can be spoken by the nurse.
|
||||
*/
|
||||
@Serializable
|
||||
public enum class NursePhraseType {
|
||||
@SerialName("JustSayRea")
|
||||
REA,
|
||||
|
||||
@SerialName("JustSayDy")
|
||||
DY,
|
||||
|
||||
@SerialName("JustSayGet")
|
||||
GET,
|
||||
|
||||
@SerialName("JustSaySet")
|
||||
SET,
|
||||
|
||||
@SerialName("JustSayGo")
|
||||
GO,
|
||||
|
||||
@SerialName("JustSayReady")
|
||||
READY,
|
||||
|
||||
@SerialName("JustSayAnd")
|
||||
AND,
|
||||
|
||||
@SerialName("JustSayStop")
|
||||
STOP,
|
||||
|
||||
@SerialName("JustSayAndStop")
|
||||
AND_STOP,
|
||||
|
||||
@SerialName("SaySwitch")
|
||||
SWITCH,
|
||||
|
||||
@SerialName("SayWatch")
|
||||
WATCH,
|
||||
|
||||
@SerialName("SayListen")
|
||||
LISTEN,
|
||||
|
||||
@SerialName("SayGetSetGo")
|
||||
GET_SET_GO,
|
||||
|
||||
/**
|
||||
* The old version of [REA_DY_GET_SET_GO].
|
||||
* Avoid using this to cue oneshot rows.
|
||||
*/
|
||||
@SerialName("SayReadyGetSetGo")
|
||||
READY_GET_SET_GO,
|
||||
|
||||
/**
|
||||
* The new version of [REA_DY_GET_SET_GO].
|
||||
*/
|
||||
@SerialName("SayReaDyGetSetGoNew")
|
||||
REA_DY_GET_SET_GO,
|
||||
|
||||
@SerialName("SayGetSetOne")
|
||||
GET_SET_ONE,
|
||||
|
||||
@SerialName("SayReaDyGetSetOne")
|
||||
REA_DY_GET_SET_ONE,
|
||||
|
||||
@SerialName("Count1")
|
||||
COUNT1,
|
||||
|
||||
@SerialName("Count2")
|
||||
COUNT2,
|
||||
|
||||
@SerialName("Count3")
|
||||
COUNT3,
|
||||
|
||||
@SerialName("Count4")
|
||||
COUNT4,
|
||||
|
||||
@SerialName("Count5")
|
||||
COUNT5,
|
||||
|
||||
@SerialName("Count6")
|
||||
COUNT6,
|
||||
|
||||
@SerialName("Count7")
|
||||
COUNT7,
|
||||
|
||||
@SerialName("Count8")
|
||||
COUNT8,
|
||||
|
||||
@SerialName("Count9")
|
||||
COUNT9,
|
||||
|
||||
@SerialName("Count10")
|
||||
COUNT10,
|
||||
}
|
||||
|
||||
/**
|
||||
* Enum representing the different voice types for the nurse.
|
||||
*/
|
||||
@Serializable
|
||||
public enum class NurseVoiceType {
|
||||
@SerialName("Nurse")
|
||||
NURSE,
|
||||
|
||||
@SerialName("NurseTired")
|
||||
NURSE_TIRED,
|
||||
|
||||
@SerialName("NurseSwing")
|
||||
NURSE_SWING,
|
||||
|
||||
@SerialName("NurseSwingCalm")
|
||||
NURSE_SWING_CALM,
|
||||
|
||||
@SerialName("IanExcited")
|
||||
IAN_EXCITED,
|
||||
|
||||
@SerialName("IanCalm")
|
||||
IAN_CALM,
|
||||
|
||||
@SerialName("IanSlow")
|
||||
IAN_SLOW,
|
||||
|
||||
/**
|
||||
* No voice, only bottom lights.
|
||||
*/
|
||||
@SerialName("NoneBottom")
|
||||
NONE_BOTTOM,
|
||||
|
||||
/**
|
||||
* No voice, only top lights.
|
||||
*/
|
||||
@SerialName("NoneTop")
|
||||
NONE_TOP
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package cn.rdlevel.rdkt.core.events
|
||||
|
||||
import cn.rdlevel.rdkt.core.data.sound.AudioData
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* An event that sets the pulse sound for a specific row.
|
||||
*
|
||||
* @property sound The pulse sound [AudioData] to set for the specified row.
|
||||
*/
|
||||
@Serializable
|
||||
public data class SetBeatSoundEvent(
|
||||
var sound: AudioData,
|
||||
) : BeatSpecificSoundEvent(), RowSpecificEvent {
|
||||
@SerialName("row")
|
||||
override var rowId: Int = 0
|
||||
set(value) {
|
||||
RowSpecificEvent.requireRowInBound(value)
|
||||
field = value
|
||||
}
|
||||
|
||||
public constructor(rowId: Int, sound: AudioData) : this(sound) {
|
||||
this.rowId = rowId
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package cn.rdlevel.rdkt.core.events
|
||||
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* Sets the BPM (Beats Per Minute) for the level.
|
||||
* Note that this will override the BPM in [PlaySongEvent].
|
||||
*/
|
||||
@Serializable
|
||||
@SerialName("SetBeatsPerMinute")
|
||||
public class SetBeatsPerMinuteEvent() : BeatSpecificSoundEvent() {
|
||||
/**
|
||||
* The beats per minute for the level.
|
||||
*/
|
||||
public var beatsPerMinute: Double = 100.0
|
||||
set(value) {
|
||||
require(value >= 1) { "BPM must be at least 1." }
|
||||
field = value
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new [SetBeatsPerMinuteEvent] with the specified BPM.
|
||||
*
|
||||
* @param bpm The beats per minute for the level.
|
||||
*/
|
||||
public constructor(bpm: Double = 100.0) : this() {
|
||||
beatsPerMinute = bpm
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package cn.rdlevel.rdkt.core.events
|
||||
|
||||
|
||||
import cn.rdlevel.rdkt.core.data.sound.AudioData
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlin.jvm.JvmOverloads
|
||||
|
||||
/**
|
||||
* Sets the clap sounds for the players and CPU in the level.
|
||||
*/
|
||||
@Serializable
|
||||
@SerialName("SetClapSounds")
|
||||
public class SetClapSoundsEvent(
|
||||
/**
|
||||
* The type of rows this event applies to.
|
||||
*/
|
||||
public var rowType: RowType = RowType.CLASSIC,
|
||||
public var p1Sound: AudioData? = null,
|
||||
public var p2Sound: AudioData? = null,
|
||||
public var cpuSound: AudioData? = null,
|
||||
) : BeatSpecificSoundEvent() {
|
||||
/**
|
||||
* The type of rows.
|
||||
*/
|
||||
@Serializable
|
||||
public enum class RowType {
|
||||
/**
|
||||
* Classic row.
|
||||
*/
|
||||
@SerialName("Classic")
|
||||
CLASSIC,
|
||||
|
||||
/**
|
||||
* Oneshot rows.
|
||||
*/
|
||||
@SerialName("Oneshot")
|
||||
ONESHOT,
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new SetClapSoundsEvent with the specified row type and sound file names.
|
||||
*
|
||||
* @param rowType The type of rows this event applies to.
|
||||
* @param p1SoundFileName The sound file name for player 1.
|
||||
* @param p2SoundFileName The sound file name for player 2.
|
||||
* @param cpuSoundFileName The sound file name for the CPU.
|
||||
*/
|
||||
@JvmOverloads
|
||||
public constructor(
|
||||
rowType: RowType = RowType.CLASSIC,
|
||||
p1SoundFileName: String? = null,
|
||||
p2SoundFileName: String? = null,
|
||||
cpuSoundFileName: String? = null,
|
||||
) : this(
|
||||
rowType,
|
||||
p1SoundFileName?.let { AudioData(it) },
|
||||
p2SoundFileName?.let { AudioData(it) },
|
||||
cpuSoundFileName?.let { AudioData(it) },
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
package cn.rdlevel.rdkt.core.events
|
||||
|
||||
|
||||
import cn.rdlevel.rdkt.core.data.sound.AudioData
|
||||
import cn.rdlevel.rdkt.core.data.sound.VoiceSource
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Transient
|
||||
import kotlin.jvm.JvmOverloads
|
||||
import kotlin.jvm.JvmStatic
|
||||
|
||||
/**
|
||||
* An event that sets the counting sound for a specific row.
|
||||
*/
|
||||
@Serializable
|
||||
@SerialName("SetCountingSound")
|
||||
public class SetCountingSoundEvent private constructor(
|
||||
@SerialName("enabled")
|
||||
private var _enabled: Boolean,
|
||||
@SerialName("voiceSource")
|
||||
private var voiceSourceType: String? = null,
|
||||
private var sounds: Array<AudioData>? = null,
|
||||
) : BeatSpecificSoundEvent(), RowSpecificEvent {
|
||||
@SerialName("row")
|
||||
override var rowId: Int = 0
|
||||
set(value) {
|
||||
RowSpecificEvent.requireRowInBound(value)
|
||||
field = value
|
||||
}
|
||||
|
||||
@Transient
|
||||
private var _voiceSource: VoiceSource? = null
|
||||
|
||||
/**
|
||||
* The voice source for the counting sound. Setting this to null will disable the counting sound for the specified row.
|
||||
*/
|
||||
public var voiceSource: VoiceSource?
|
||||
get() = _voiceSource
|
||||
set(value) {
|
||||
_enabled = value != null
|
||||
voiceSourceType = value?.type
|
||||
sounds = (value as? VoiceSource.Custom)?.sounds
|
||||
_voiceSource = value
|
||||
}
|
||||
|
||||
private fun initVoiceSource() {
|
||||
_voiceSource = when (voiceSourceType) {
|
||||
null -> null
|
||||
VoiceSource.Custom.TYPE -> VoiceSource.Custom(sounds ?: error("Missing sounds for custom voice source."))
|
||||
else -> VoiceSource.fromType(voiceSourceType!!) ?: error("Unknown voice source type: $voiceSourceType")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The volume of the counting sound, between 0 and 100.
|
||||
*/
|
||||
public var volume: Int = 100
|
||||
set(value) {
|
||||
require(value in 0..100) { "Volume must be between 0 and 100." }
|
||||
field = value
|
||||
}
|
||||
|
||||
/**
|
||||
* The offset of the counting sound for oneshot row, between 0.0 and 1.0.
|
||||
* This is ignored for classic row.
|
||||
*/
|
||||
@SerialName("subdivOffset")
|
||||
public var subdivisionOffset: Double = 0.5
|
||||
set(value) {
|
||||
require(value in 0.0..1.0) { "Subdivision offset must be between 0.0 and 1.0." }
|
||||
field = value
|
||||
}
|
||||
|
||||
init {
|
||||
initVoiceSource()
|
||||
}
|
||||
|
||||
public companion object {
|
||||
/**
|
||||
* Creates a copy of the given [SetCountingSoundEvent].
|
||||
*/
|
||||
@JvmStatic
|
||||
public fun of(event: SetCountingSoundEvent): SetCountingSoundEvent {
|
||||
return SetCountingSoundEvent(event._enabled).apply {
|
||||
this.rowId = event.rowId
|
||||
this.voiceSource = event.voiceSource
|
||||
this.volume = event.volume
|
||||
this.subdivisionOffset = event.subdivisionOffset
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a [SetCountingSoundEvent] for a classic row with the given parameters.
|
||||
*
|
||||
* @param rowId The row id to set the counting sound for.
|
||||
* @param voiceSource The voice source to set for the counting sound. Setting this to null will disable the counting sound for the specified row.
|
||||
* @param volume The volume of the counting sound, between 0 and 100. Default is 100.
|
||||
*/
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
public fun ofClassic(
|
||||
rowId: Int,
|
||||
voiceSource: VoiceSource.Classic?,
|
||||
volume: Int = 100,
|
||||
): SetCountingSoundEvent {
|
||||
return SetCountingSoundEvent(false).apply {
|
||||
this.rowId = rowId
|
||||
this.voiceSource = voiceSource
|
||||
this.volume = volume
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a [SetCountingSoundEvent] for a oneshot row with the given parameters.
|
||||
*
|
||||
* @param rowId The row id to set the counting sound for.
|
||||
* @param voiceSource The voice source to set for the counting sound. Setting this to null will disable the counting sound for the specified row.
|
||||
* @param volume The volume of the counting sound, between 0 and 100. Default is 100.
|
||||
* @param subdivisionOffset The offset of the counting sound for oneshot row, between 0.0 and 1.0. Default is 0.5.
|
||||
*/
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
public fun ofOneshot(
|
||||
rowId: Int,
|
||||
voiceSource: VoiceSource.Oneshot?,
|
||||
volume: Int = 100,
|
||||
subdivisionOffset: Double = 0.5,
|
||||
): SetCountingSoundEvent {
|
||||
return SetCountingSoundEvent(false).apply {
|
||||
this.rowId = rowId
|
||||
this.voiceSource = voiceSource
|
||||
this.volume = volume
|
||||
this.subdivisionOffset = subdivisionOffset
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package cn.rdlevel.rdkt.core.events
|
||||
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* Sets the number of beats per bar and the visual beat multiplier.
|
||||
*/
|
||||
@Serializable
|
||||
@SerialName("SetCrotchetsPerBar")
|
||||
public class SetCrotchetsPerBarEvent() : SoundEvent() {
|
||||
/**
|
||||
* The beats per bar until the next same event that overrides this one.
|
||||
*/
|
||||
public var crotchetsPerBar: Int = 8
|
||||
set(value) {
|
||||
require(value > 0) { "Crotchets per bar must be greater than 0." }
|
||||
field = value
|
||||
}
|
||||
|
||||
/**
|
||||
* Controls the speed of some sprite animations.
|
||||
* The animations will be played with 4 / [visualBeatMultiplier] frames per beat.
|
||||
*/
|
||||
public var visualBeatMultiplier: Double = 1.0
|
||||
set(value) {
|
||||
require(value >= 0) { "Visual beat multiplier must be non-negative." }
|
||||
field = value
|
||||
}
|
||||
|
||||
public constructor(crotchetsPerBar: Int = 8, visualBeatMultiplier: Double = 1.0) : this() {
|
||||
this.crotchetsPerBar = crotchetsPerBar
|
||||
this.visualBeatMultiplier = visualBeatMultiplier
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
@file:OptIn(ExperimentalSerializationApi::class)
|
||||
|
||||
package cn.rdlevel.rdkt.core.events
|
||||
|
||||
import cn.rdlevel.rdkt.core.annotations.RDKTInternalAPI
|
||||
import cn.rdlevel.rdkt.core.data.sound.audioGroup.AudioGroup
|
||||
import cn.rdlevel.rdkt.core.data.sound.audioGroup.tryConvertTo
|
||||
import cn.rdlevel.rdkt.core.serialization.TransformSerializer
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.KeepGeneratedSerializer
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.*
|
||||
|
||||
/**
|
||||
* An event that sets the configurations for a specific group of game sounds.
|
||||
*
|
||||
* This event allows you to define and modify the audio settings for a particular
|
||||
* sound group identified by its type. The configurations are encapsulated within
|
||||
* an [AudioGroup] instance.
|
||||
*
|
||||
* @property soundType The type of sound group.
|
||||
* @property group The audio group configurations associated with this event.
|
||||
*/
|
||||
@ConsistentCopyVisibility
|
||||
@Serializable(SetGameSoundEvent.Serializer::class)
|
||||
@KeepGeneratedSerializer
|
||||
@SerialName("SetGameSound")
|
||||
public data class SetGameSoundEvent private constructor(
|
||||
@SerialName("soundType")
|
||||
private var _soundType: String,
|
||||
@SerialName("soundSubtypes")
|
||||
private var _group: AudioGroup,
|
||||
) : BeatSpecificSoundEvent() {
|
||||
/**
|
||||
* Creates a [SetGameSoundEvent] with the specified [AudioGroup].
|
||||
*
|
||||
* @param group The audio group configurations to be associated with this event.
|
||||
*/
|
||||
public constructor(group: AudioGroup) : this(group.soundType, group)
|
||||
|
||||
/**
|
||||
* The type of sound group.
|
||||
*
|
||||
* This property is immutable, reflecting the [AudioGroup.soundType] of the associated [AudioGroup].
|
||||
*
|
||||
* @see AudioGroup.soundType
|
||||
*/
|
||||
val soundType: String
|
||||
get() = _group.soundType
|
||||
|
||||
/**
|
||||
* The audio group configurations associated with this event.
|
||||
*/
|
||||
var group: AudioGroup
|
||||
get() = _group
|
||||
set(value) {
|
||||
_soundType = value.soundType
|
||||
_group = value
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "SetGameSoundEvent(soundType='$soundType', group=$group)"
|
||||
}
|
||||
|
||||
public object Serializer : JsonTransformingSerializer<SetGameSoundEvent>(DataSerializer) {
|
||||
@OptIn(RDKTInternalAPI::class)
|
||||
private object DataSerializer :
|
||||
TransformSerializer<SetGameSoundEvent, SetGameSoundEvent>(generatedSerializer()) {
|
||||
override fun toData(value: SetGameSoundEvent) = value
|
||||
|
||||
override fun fromData(data: SetGameSoundEvent): SetGameSoundEvent {
|
||||
data._group = data._group.tryConvertTo(data._soundType)
|
||||
return data
|
||||
}
|
||||
}
|
||||
|
||||
override fun transformDeserialize(element: JsonElement): JsonElement {
|
||||
require(element is JsonObject) { "Expected JsonObject for SetGameSoundEvent deserialization, but got $element." }
|
||||
val content = element.toMutableMap()
|
||||
content.remove("type")
|
||||
|
||||
if ("soundSubtypes" in element) {
|
||||
return JsonObject(content)
|
||||
}
|
||||
|
||||
val subGroupAudioDataElement = buildJsonObject {
|
||||
put(
|
||||
"groupSubtype",
|
||||
element["soundType"] ?: error("Missing 'soundType' field in SetGameSoundEvent JSON.")
|
||||
)
|
||||
put("used", true)
|
||||
listOf("used", "filename", "volume", "pitch", "pan", "offset").forEach { key ->
|
||||
element[key]?.let { put(key, it) }
|
||||
content.remove(key)
|
||||
}
|
||||
}
|
||||
content["soundSubtypes"] = JsonArray(listOf(subGroupAudioDataElement))
|
||||
return JsonObject(content)
|
||||
}
|
||||
}
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
package cn.rdlevel.rdkt.core.events
|
||||
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlin.jvm.JvmOverloads
|
||||
|
||||
/**
|
||||
* Set how the heart explosion interval is calculated after hitting a beat correctly.
|
||||
*/
|
||||
@Serializable
|
||||
@SerialName("SetHeartExplodeInterval")
|
||||
public class SetHeartExplodeIntervalEvent @JvmOverloads constructor(
|
||||
/**
|
||||
* The type of interval calculation.
|
||||
*/
|
||||
public var intervalType: HeartExplosionIntervalType = HeartExplosionIntervalType.COMBINE_ON_DOWNBEAT,
|
||||
/**
|
||||
* The interval in beats.
|
||||
*/
|
||||
public var interval: Double = 1.0,
|
||||
) {
|
||||
/**
|
||||
* The type of interval calculation for heart explosions.
|
||||
*/
|
||||
@Serializable
|
||||
public enum class HeartExplosionIntervalType {
|
||||
/**
|
||||
* The explosion will occur after the specified interval.
|
||||
*/
|
||||
@SerialName("OneBeatAfter")
|
||||
FIXED_INTERVAL,
|
||||
|
||||
/**
|
||||
* The explosions will be combined and rounded up by the specified interval.
|
||||
*/
|
||||
@SerialName("GatherAndCeil")
|
||||
COMBINE_ON_DOWNBEAT,
|
||||
|
||||
/**
|
||||
* The explosions will be combined by the specified interval.
|
||||
*/
|
||||
@SerialName("GatherNoCeil")
|
||||
COMBINE_ON_FIXED_INTERVAL,
|
||||
|
||||
/**
|
||||
* The explosion will occur immediately without sound.
|
||||
*/
|
||||
@SerialName("Instant")
|
||||
INSTANT,
|
||||
|
||||
/**
|
||||
* The explosion will be disabled.
|
||||
*/
|
||||
@SerialName("Disabled")
|
||||
DISABLED
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package cn.rdlevel.rdkt.core.events
|
||||
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* Set the volume of the heart explode sound when hitting a beat correctly.
|
||||
*/
|
||||
@Serializable
|
||||
@SerialName("SetHeartExplodeVolume")
|
||||
public class SetHeartExplodeVolumeEvent() : SoundEvent() {
|
||||
/**
|
||||
* The volume of the heart explode sound.
|
||||
* Must be greater than or equal to 0.
|
||||
*/
|
||||
public var volume: Int = 60
|
||||
set(value) {
|
||||
require(value >= 0) { "Volume must be greater than or equal to 0." }
|
||||
field = value
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new SetHeartExplodeVolumeEvent with the specified volume.
|
||||
*
|
||||
* @param volume The volume of the heart explode sound.
|
||||
*/
|
||||
public constructor(volume: Int = 60) : this() {
|
||||
this.volume = volume
|
||||
}
|
||||
}
|
||||
+9
-3
@@ -1,4 +1,4 @@
|
||||
package cn.rdlevel.rdkt.core.serializers
|
||||
package cn.rdlevel.rdkt.core.serialization
|
||||
|
||||
import cn.rdlevel.rdkt.core.annotations.RDKTInternalAPI
|
||||
import kotlinx.serialization.KSerializer
|
||||
@@ -32,10 +32,16 @@ public abstract class PolymorphicDelegatedSerializer<Sub : Base, Base>(
|
||||
|
||||
override fun deserialize(decoder: Decoder): Sub {
|
||||
val result = decoder.decodeSerializableValue(baseSerializer)
|
||||
runCatching {
|
||||
val throwable = runCatching {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return result as Sub
|
||||
}.exceptionOrNull()
|
||||
if (throwable is ClassCastException) {
|
||||
throw RuntimeException(
|
||||
"Unable to cast deserialized value to specified type. Check your base serializer.",
|
||||
throwable
|
||||
)
|
||||
}
|
||||
error("Unable to cast deserialized value to specified type. Check your base serializer.")
|
||||
throw throwable!!
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
@file:OptIn(RDKTInternalAPI::class)
|
||||
|
||||
package cn.rdlevel.rdkt.core.serializers
|
||||
package cn.rdlevel.rdkt.core.serialization
|
||||
|
||||
import cn.rdlevel.rdkt.core.annotations.RDKTInternalAPI
|
||||
import kotlinx.serialization.KSerializer
|
||||
+2
-1
@@ -1,4 +1,4 @@
|
||||
package cn.rdlevel.rdkt.core.serializers
|
||||
package cn.rdlevel.rdkt.core.serialization
|
||||
|
||||
import cn.rdlevel.rdkt.core.annotations.RDKTInternalAPI
|
||||
import kotlinx.serialization.KSerializer
|
||||
@@ -16,6 +16,7 @@ import kotlinx.serialization.encoding.Encoder
|
||||
* @param Data The data representation of the type.
|
||||
*/
|
||||
@RDKTInternalAPI
|
||||
@SubclassOptInRequired(RDKTInternalAPI::class)
|
||||
public abstract class TransformSerializer<T, Data>(
|
||||
private val dataSerializer: KSerializer<Data>
|
||||
) : KSerializer<T> {
|
||||
@@ -3,7 +3,7 @@
|
||||
package cn.rdlevel.rdkt.core.settings
|
||||
|
||||
import cn.rdlevel.rdkt.core.annotations.RDKTInternalAPI
|
||||
import cn.rdlevel.rdkt.core.serializers.MutableStringListSerializedInString
|
||||
import cn.rdlevel.rdkt.core.serialization.MutableStringListSerializedInString
|
||||
import cn.rdlevel.rdkt.core.settings.LevelSettings.Companion.CURRENT_LEVEL_VERSION
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlin.jvm.JvmName
|
||||
@@ -16,7 +16,7 @@ import kotlin.random.Random
|
||||
@Serializable
|
||||
public class LevelSettings {
|
||||
/**
|
||||
* The version of the level. Currently, it is set to [63][CURRENT_LEVEL_VERSION].
|
||||
* The version of the level. Currently, it is set to [64][CURRENT_LEVEL_VERSION].
|
||||
*
|
||||
* This library currently does not support levels with other versions.
|
||||
* If you want to use levels with other versions, please use the level editor to convert them to a supported version.
|
||||
@@ -197,7 +197,7 @@ public class LevelSettings {
|
||||
/**
|
||||
* The current level version the level editor is using.
|
||||
*/
|
||||
public const val CURRENT_LEVEL_VERSION: Int = 63
|
||||
public const val CURRENT_LEVEL_VERSION: Int = 64
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package cn.rdlevel.rdkt.core.util
|
||||
|
||||
/**
|
||||
* Converts a BigCamelCase string to smallCamelCase.
|
||||
*
|
||||
* @return The converted smallCamelCase string.
|
||||
*/
|
||||
public fun String.bigCamelToSmallCamel(): String {
|
||||
if (this.isEmpty()) return this
|
||||
return this[0].lowercaseChar() + this.substring(1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a smallCamelCase string to BigCamelCase.
|
||||
*
|
||||
* @return The converted BigCamelCase string.
|
||||
*/
|
||||
public fun String.smallCamelToBigCamel(): String {
|
||||
if (this.isEmpty()) return this
|
||||
return this[0].uppercaseChar() + this.substring(1)
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package cn.rdlevel.rdkt.core.test
|
||||
|
||||
import cn.rdlevel.rdkt.core.data.RowPattern
|
||||
import cn.rdlevel.rdkt.core.events.Event
|
||||
import cn.rdlevel.rdkt.core.events.NarrateRowInfoEvent
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlin.test.Test
|
||||
|
||||
class NarrateRowInfoEventTest {
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
@Test
|
||||
fun test() {
|
||||
val json = Json {
|
||||
isLenient = true
|
||||
allowTrailingComma = true
|
||||
encodeDefaults = true
|
||||
}
|
||||
val event: Event = NarrateRowInfoEvent.of {
|
||||
infoType = NarrateRowInfoEvent.InfoType.ONLINE
|
||||
rowPattern = NarrateRowInfoEvent.RowPatternNarration.Custom(RowPattern("-x-x-x"))
|
||||
customPlayer = NarrateRowInfoEvent.CustomPlayer.P2
|
||||
customRowLength = 5
|
||||
}
|
||||
val jsonText = json.encodeToString(event)
|
||||
println(jsonText)
|
||||
|
||||
val jsonText2 =
|
||||
"""{ "bar": 1, "beat": 1, "y": 0, "type": "NarrateRowInfo", "row": 0, "infoType": "Online", "soundOnly": false, "narrateSkipBeats": "Custom", "customPattern": "-xudbr", "customPlayer": "AutoDetect", "customRowLength": 7 }"""
|
||||
val event2: Event = json.decodeFromString(jsonText2)
|
||||
println(event2)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package cn.rdlevel.rdkt.core.test
|
||||
|
||||
import cn.rdlevel.rdkt.core.data.sound.VoiceSource
|
||||
import cn.rdlevel.rdkt.core.events.Event
|
||||
import cn.rdlevel.rdkt.core.events.SetCountingSoundEvent
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlin.test.Test
|
||||
|
||||
class SetCountingSoundEventTest {
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
@Test
|
||||
fun test() {
|
||||
val json = Json {
|
||||
isLenient = true
|
||||
allowTrailingComma = true
|
||||
encodeDefaults = true
|
||||
}
|
||||
|
||||
val event: Event = SetCountingSoundEvent.ofClassic(0, null)
|
||||
val event2: Event = SetCountingSoundEvent.ofOneshot(1, VoiceSource.Oneshot.IanCountEnglish)
|
||||
|
||||
val jsonText = json.encodeToString(event)
|
||||
println(jsonText)
|
||||
val jsonText2 = json.encodeToString(event2)
|
||||
println(jsonText2)
|
||||
|
||||
val event3: Event = json.decodeFromString(jsonText2)
|
||||
println(event3)
|
||||
|
||||
val jsonText4 =
|
||||
"""{ "bar": 1, "beat": 1, "y": 0, "type": "SetCountingSound", "row": 1, "enabled": true, "voiceSource": "Custom", "volume": 100, "sounds": [{"filename": "Jyi - ChineseCount1"}, {"filename": "Jyi - ChineseCount2"}, {"filename": "Jyi - ChineseCount3"}, {"filename": "Jyi - ChineseCount4"}, {"filename": "Jyi - ChineseCount5"}, {"filename": "Jyi - ChineseCount6"}, {"filename": "Jyi - ChineseCount7"}] }"""
|
||||
val event4: Event = json.decodeFromString(jsonText4)
|
||||
println(event4)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package cn.rdlevel.rdkt.core.test
|
||||
|
||||
import cn.rdlevel.rdkt.core.data.sound.audioGroup.SmallMistakeAudioGroup
|
||||
import cn.rdlevel.rdkt.core.data.sound.subGroupAudioDataOf
|
||||
import cn.rdlevel.rdkt.core.events.Event
|
||||
import cn.rdlevel.rdkt.core.events.SetGameSoundEvent
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlin.test.Test
|
||||
|
||||
class SetGameSoundEventTest {
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
@Test
|
||||
fun test() {
|
||||
val json = Json {
|
||||
isLenient = true
|
||||
allowTrailingComma = true
|
||||
encodeDefaults = true
|
||||
}
|
||||
|
||||
val event: Event = SetGameSoundEvent(
|
||||
SmallMistakeAudioGroup(subGroupAudioDataOf("bar"))
|
||||
).apply {
|
||||
bar = 114
|
||||
y = 514
|
||||
}
|
||||
val jsonText = json.encodeToString(event)
|
||||
println(jsonText)
|
||||
|
||||
val event2: Event = json.decodeFromString(jsonText)
|
||||
println(event2)
|
||||
|
||||
val event3: Event =
|
||||
json.decodeFromString("""{ "bar": 1, "beat": 1, "y": 0, "type": "SetGameSound", "soundType": "SmallMistake", "filename": "" }""")
|
||||
println(event3)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user