feat: composer
This commit is contained in:
parent
314957eeb5
commit
bbef6cb342
@ -1,20 +1,75 @@
|
|||||||
package quaedam.projection.music
|
package quaedam.projection.music
|
||||||
|
|
||||||
|
import kotlin.math.abs
|
||||||
import kotlin.random.Random
|
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)
|
data class Note(val note: Int, val volume: Float, val time: Int)
|
||||||
|
|
||||||
fun composeMusic(random: Random) = listOf<Note>(
|
val baseTime = arrayOf(5, 5, 3, 3, 4, 4, 2, 2, 10).random(rhythmRandom)
|
||||||
Note(0, 1.0f, 3),
|
val baseNote = noteRandom.nextInt(5..19)
|
||||||
Note(1, 1.0f, 3),
|
|
||||||
Note(2, 1.0f, 3),
|
fun composeMusic() = decorate(
|
||||||
Note(3, 1.0f, 3),
|
(0..rhythmRandom.nextInt(5)).flatMap { composeSection() }
|
||||||
Note(4, 1.0f, 3),
|
|
||||||
Note(5, 1.0f, 3),
|
|
||||||
Note(6, 1.0f, 3),
|
|
||||||
Note(7, 1.0f, 3),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fun decorate(notes: List<Note>) = 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<Note> {
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -27,6 +27,7 @@ import net.minecraft.world.level.material.MapColor
|
|||||||
import net.minecraft.world.phys.BlockHitResult
|
import net.minecraft.world.phys.BlockHitResult
|
||||||
import quaedam.Quaedam
|
import quaedam.Quaedam
|
||||||
import quaedam.projector.Projector
|
import quaedam.projector.Projector
|
||||||
|
import quaedam.utils.getChunksNearby
|
||||||
import quaedam.utils.sendBlockUpdated
|
import quaedam.utils.sendBlockUpdated
|
||||||
|
|
||||||
object CyberInstrument {
|
object CyberInstrument {
|
||||||
@ -173,11 +174,24 @@ class CyberInstrumentBlockEntity(pos: BlockPos, state: BlockState) :
|
|||||||
private fun checkProjections() =
|
private fun checkProjections() =
|
||||||
Projector.findNearbyProjections(level!!, blockPos, MusicProjection.effect.get()).isNotEmpty()
|
Projector.findNearbyProjections(level!!, blockPos, MusicProjection.effect.get()).isNotEmpty()
|
||||||
|
|
||||||
fun startMusic() {
|
fun startMusic(force: Boolean = false, synced: Boolean = false) {
|
||||||
if (player == null && !level!!.isClientSide && checkProjections()) {
|
if ((player == null || force) && !level!!.isClientSide && checkProjections()) {
|
||||||
player = MusicPlayer(level!!.random.nextLong(), level!!, blockPos)
|
player = MusicPlayer(level!!.random.nextLong(), level!!, blockPos)
|
||||||
setChanged()
|
setChanged()
|
||||||
sendBlockUpdated()
|
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<CyberInstrumentBlockEntity>()
|
||||||
|
.forEach { it.startMusic(force = true, synced = true) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ class MusicPlayer(val seed: Long, val level: Level, val pos: BlockPos, val start
|
|||||||
tag.getLong(TAG_STARTED_AT)
|
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()
|
val totalTime = notes.sumOf { it.time }.toLong()
|
||||||
var remainingTime = totalTime
|
var remainingTime = totalTime
|
||||||
val isEnd get() = remainingTime <= 0 || notes.isEmpty()
|
val isEnd get() = remainingTime <= 0 || notes.isEmpty()
|
||||||
|
Loading…
Reference in New Issue
Block a user