From bbef6cb342c91a532aea02273b35c61d03cf9741 Mon Sep 17 00:00:00 2001 From: xtex Date: Sun, 30 Jul 2023 09:57:56 +0800 Subject: [PATCH] feat: composer --- .../quaedam/projection/music/Composer.kt | 75 ++++++++++++++++--- .../projection/music/CyberInstrument.kt | 18 ++++- .../quaedam/projection/music/MusicPlayer.kt | 2 +- 3 files changed, 82 insertions(+), 13 deletions(-) diff --git a/common/src/main/kotlin/quaedam/projection/music/Composer.kt b/common/src/main/kotlin/quaedam/projection/music/Composer.kt index d6c1662..32a868b 100644 --- a/common/src/main/kotlin/quaedam/projection/music/Composer.kt +++ b/common/src/main/kotlin/quaedam/projection/music/Composer.kt @@ -1,20 +1,75 @@ package quaedam.projection.music +import kotlin.math.abs import kotlin.random.Random +import kotlin.random.nextInt -object Composer { +/** + * The composer for music. + * rhythmRandom is used for a better rhythm sync between different instruments. + */ +class Composer(val noteRandom: Random, val rhythmRandom: Random) { data class Note(val note: Int, val volume: Float, val time: Int) - fun composeMusic(random: Random) = listOf( - Note(0, 1.0f, 3), - Note(1, 1.0f, 3), - Note(2, 1.0f, 3), - Note(3, 1.0f, 3), - Note(4, 1.0f, 3), - Note(5, 1.0f, 3), - Note(6, 1.0f, 3), - Note(7, 1.0f, 3), + val baseTime = arrayOf(5, 5, 3, 3, 4, 4, 2, 2, 10).random(rhythmRandom) + val baseNote = noteRandom.nextInt(5..19) + + fun composeMusic() = decorate( + (0..rhythmRandom.nextInt(5)).flatMap { composeSection() } ) + fun decorate(notes: List) = notes.map { + if (noteRandom.nextInt(4) == 0) { + doDecorate(it) + } else { + it + } + } + + fun doDecorate(note: Note): Note { + var noteVal = note.note + if (noteRandom.nextInt(4) == 0) { + if (noteRandom.nextBoolean()) { + noteVal += 1 + } else { + noteVal -= 1 + } + } + var volume = note.volume + if (noteRandom.nextInt(4) == 0) { + volume *= noteRandom.nextFloat() * 0.8f + 0.6f + } + return Note(noteVal, volume, note.time) + } + + fun composeSection(depth: Int = 0): List { + if (depth < 3 && rhythmRandom.nextBoolean()) { + val notes = (0..rhythmRandom.nextInt(3 - depth)).flatMap { composeSection(depth + 1) } + if (depth == 2) { + return (0..rhythmRandom.nextInt(3)).flatMap { notes } + } else { + return notes + } + } else { + var notePointer = baseNote + noteRandom.nextInt(-3..3) + var direction = -1 + var directionCounter = 0 + return (0..rhythmRandom.nextInt(4..16)).map { + if (directionCounter == 0) { + // start new direction + directionCounter = rhythmRandom.nextInt(2..6) + direction = if (directionCounter % 2 == 0) { + rhythmRandom.nextInt(-2..2) + } else { + noteRandom.nextInt(-3..3) + } + } + notePointer = abs(notePointer + direction) % 25 + directionCounter-- + Note(notePointer, 1.0f, baseTime + rhythmRandom.nextInt(-1..1)) + } + } + } + } \ No newline at end of file diff --git a/common/src/main/kotlin/quaedam/projection/music/CyberInstrument.kt b/common/src/main/kotlin/quaedam/projection/music/CyberInstrument.kt index 6a358d9..a52e26a 100644 --- a/common/src/main/kotlin/quaedam/projection/music/CyberInstrument.kt +++ b/common/src/main/kotlin/quaedam/projection/music/CyberInstrument.kt @@ -27,6 +27,7 @@ import net.minecraft.world.level.material.MapColor import net.minecraft.world.phys.BlockHitResult import quaedam.Quaedam import quaedam.projector.Projector +import quaedam.utils.getChunksNearby import quaedam.utils.sendBlockUpdated object CyberInstrument { @@ -173,11 +174,24 @@ class CyberInstrumentBlockEntity(pos: BlockPos, state: BlockState) : private fun checkProjections() = Projector.findNearbyProjections(level!!, blockPos, MusicProjection.effect.get()).isNotEmpty() - fun startMusic() { - if (player == null && !level!!.isClientSide && checkProjections()) { + fun startMusic(force: Boolean = false, synced: Boolean = false) { + if ((player == null || force) && !level!!.isClientSide && checkProjections()) { player = MusicPlayer(level!!.random.nextLong(), level!!, blockPos) setChanged() sendBlockUpdated() + if (!synced) { + // sync start to other instruments + level!!.getChunksNearby(blockPos, 1) + .flatMap { + it.blockEntities + .filterValues { entity -> entity is CyberInstrumentBlockEntity } + .filterKeys { pos -> pos.distSqr(blockPos) < 100 } + .values + } + .filterNot { it == this } + .filterIsInstance() + .forEach { it.startMusic(force = true, synced = true) } + } } } diff --git a/common/src/main/kotlin/quaedam/projection/music/MusicPlayer.kt b/common/src/main/kotlin/quaedam/projection/music/MusicPlayer.kt index 42e2b3c..3e6c48e 100644 --- a/common/src/main/kotlin/quaedam/projection/music/MusicPlayer.kt +++ b/common/src/main/kotlin/quaedam/projection/music/MusicPlayer.kt @@ -31,7 +31,7 @@ class MusicPlayer(val seed: Long, val level: Level, val pos: BlockPos, val start tag.getLong(TAG_STARTED_AT) ) - var notes = Composer.composeMusic(Random(seed)).toMutableList() + var notes = Composer(Random(seed), Random(startedAt / 20 * 15)).composeMusic().toMutableList() val totalTime = notes.sumOf { it.time }.toLong() var remainingTime = totalTime val isEnd get() = remainingTime <= 0 || notes.isEmpty()