mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-07 20:31:51 +09:00
test play music from appdata/Custom/Music
This commit is contained in:
@@ -545,6 +545,8 @@ open class IngameInstance(val batch: FlippingSpriteBatch, val isMultiplayer: Boo
|
||||
|
||||
fun onConfigChange() {
|
||||
}
|
||||
|
||||
open val musicGovernor: MusicGovernor = MusicGovernor()
|
||||
}
|
||||
|
||||
inline fun Lock.lock(body: () -> Unit) {
|
||||
|
||||
38
src/net/torvald/terrarum/MusicGovernor.kt
Normal file
38
src/net/torvald/terrarum/MusicGovernor.kt
Normal file
@@ -0,0 +1,38 @@
|
||||
package net.torvald.terrarum
|
||||
|
||||
open class MusicGovernor {
|
||||
|
||||
open fun update(ingameInstance: IngameInstance, delta: Float) {
|
||||
|
||||
}
|
||||
|
||||
protected var state = 0 // 0: disabled, 1: playing, 2: waiting
|
||||
protected var intermissionAkku = 0f
|
||||
protected var intermissionLength = 1f
|
||||
protected var musicFired = false
|
||||
|
||||
protected var fadeoutAkku = 0f
|
||||
protected var fadeoutLength = 0f
|
||||
protected var fadeoutFired = false
|
||||
protected var fadeinFired = false
|
||||
|
||||
fun requestFadeOut(length: Float) {
|
||||
if (!fadeoutFired) {
|
||||
fadeoutLength = length
|
||||
fadeoutAkku = 0f
|
||||
fadeoutFired = true
|
||||
}
|
||||
}
|
||||
|
||||
fun requestFadeIn(length: Float) {
|
||||
if (!fadeoutFired) {
|
||||
fadeoutLength = length
|
||||
fadeoutAkku = 0f
|
||||
fadeinFired = true
|
||||
}
|
||||
}
|
||||
open fun dispose() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2029,18 +2029,20 @@ open class ActorWithBody : Actor {
|
||||
val particleCount = (collisionDamage / 24.0).pow(0.75)
|
||||
val trueParticleCount = particleCount.toInt() + (Math.random() < (particleCount % 1.0)).toInt()
|
||||
|
||||
if (collisionDamage > 1.0 / 1024.0) printdbg(this, "Collision damage: $collisionDamage N, count: $particleCount, velocity: $vecSum, mass: ${this.mass}")
|
||||
if (collisionDamage > 1.0 / 1024.0) {
|
||||
// printdbg(this, "Collision damage: $collisionDamage N, count: $particleCount, velocity: $vecSum, mass: ${this.mass}")
|
||||
|
||||
val feetTiles = getFeetTiles()
|
||||
val feetTileIndices = feetTiles.indices.toList().toIntArray()
|
||||
val feetTiles = getFeetTiles()
|
||||
val feetTileIndices = feetTiles.indices.toList().toIntArray()
|
||||
|
||||
for (i in 0 until trueParticleCount) {
|
||||
if (i % feetTiles.size == 0) feetTileIndices.shuffle()
|
||||
for (i in 0 until trueParticleCount) {
|
||||
if (i % feetTiles.size == 0) feetTileIndices.shuffle()
|
||||
|
||||
feetTiles[feetTileIndices[i % feetTiles.size]].second.let { tile ->
|
||||
val px = hitbox.startX + Math.random() * hitbox.width
|
||||
val py = hitbox.endY
|
||||
makeDust0(tile, px, py, particleCount, collisionDamage, vecSum)
|
||||
feetTiles[feetTileIndices[i % feetTiles.size]].second.let { tile ->
|
||||
val px = hitbox.startX + Math.random() * hitbox.width
|
||||
val py = hitbox.endY
|
||||
makeDust0(tile, px, py, particleCount, collisionDamage, vecSum)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -434,6 +434,13 @@ open class GameWorld(
|
||||
setWireGraphOfUnsafe(blockAddr, tile, connection)
|
||||
}
|
||||
|
||||
fun setTileOnLayerUnsafe(layer: Int, x: Int, y: Int, tile: Int) {
|
||||
(getLayer(layer) ?: throw IllegalArgumentException("Unknown layer index: $layer")).let {
|
||||
if (it !is BlockLayerI16) throw IllegalArgumentException("Block layers other than BlockLayer16 is not supported yet)")
|
||||
it.unsafeSetTile(x, y, tile)
|
||||
}
|
||||
}
|
||||
|
||||
fun removeTileWire(x: Int, y: Int, tile: ItemID, bypassEvent: Boolean) {
|
||||
val (x, y) = coerceXY(x, y)
|
||||
val blockAddr = LandUtil.getBlockAddr(this, x, y)
|
||||
@@ -790,8 +797,8 @@ open class GameWorld(
|
||||
override fun equals(other: Any?) = layerTerrain.ptr == (other as GameWorld).layerTerrain.ptr
|
||||
|
||||
companion object {
|
||||
@Transient const val WALL = 1
|
||||
@Transient const val TERRAIN = 0
|
||||
@Transient const val WALL = 1
|
||||
@Transient const val ORES = 2
|
||||
|
||||
@Transient val TILES_SUPPORTED = ReferencingRanges.TILES.last + 1
|
||||
|
||||
@@ -258,6 +258,7 @@ open class TerrarumIngame(batch: FlippingSpriteBatch) : IngameInstance(batch) {
|
||||
override var gameFullyLoaded = false
|
||||
internal set
|
||||
|
||||
override val musicGovernor = TerrarumMusicGovernor()
|
||||
|
||||
|
||||
//////////////
|
||||
@@ -883,12 +884,11 @@ open class TerrarumIngame(batch: FlippingSpriteBatch) : IngameInstance(batch) {
|
||||
oldSelectedWireRenderClass = selectedWireRenderClass
|
||||
}
|
||||
|
||||
musicGovernor.update(this, delta)
|
||||
|
||||
////////////////////////
|
||||
// ui-related updates //
|
||||
////////////////////////
|
||||
//uiContainer.forEach { it.update(delta) }
|
||||
//debugWindow.update(delta)
|
||||
//notifier.update(delta)
|
||||
// open/close fake blur UI according to what's opened
|
||||
if (uiInventoryPlayer.isVisible ||
|
||||
getUIFixture.get()?.isVisible == true || worldTransitionOngoing) {
|
||||
@@ -1520,6 +1520,8 @@ open class TerrarumIngame(batch: FlippingSpriteBatch) : IngameInstance(batch) {
|
||||
catch (e: IllegalArgumentException) {}
|
||||
}
|
||||
|
||||
musicGovernor.dispose()
|
||||
|
||||
super.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
132
src/net/torvald/terrarum/modulebasegame/TerrarumMusicGovernor.kt
Normal file
132
src/net/torvald/terrarum/modulebasegame/TerrarumMusicGovernor.kt
Normal file
@@ -0,0 +1,132 @@
|
||||
package net.torvald.terrarum.modulebasegame
|
||||
|
||||
import com.badlogic.gdx.Gdx
|
||||
import com.badlogic.gdx.audio.Music
|
||||
import com.badlogic.gdx.utils.GdxRuntimeException
|
||||
import net.torvald.terrarum.App
|
||||
import net.torvald.terrarum.App.printdbg
|
||||
import net.torvald.terrarum.IngameInstance
|
||||
import net.torvald.terrarum.MusicGovernor
|
||||
import net.torvald.terrarum.tryDispose
|
||||
import java.io.File
|
||||
|
||||
data class MusicContainer(
|
||||
val name: String,
|
||||
val file: File,
|
||||
val gdxMusic: Music
|
||||
) {
|
||||
override fun toString() = if (name.isEmpty()) file.nameWithoutExtension else name
|
||||
}
|
||||
|
||||
class TerrarumMusicGovernor : MusicGovernor() {
|
||||
|
||||
private val songs: List<MusicContainer> =
|
||||
File(App.customMusicDir).listFiles()?.mapNotNull {
|
||||
printdbg(this, "Music: ${it.absolutePath}")
|
||||
try {
|
||||
MusicContainer(
|
||||
it.nameWithoutExtension,
|
||||
it,
|
||||
Gdx.audio.newMusic(Gdx.files.absolute(it.absolutePath))
|
||||
)
|
||||
}
|
||||
catch (e: GdxRuntimeException) {
|
||||
e.printStackTrace()
|
||||
null
|
||||
}
|
||||
} ?: emptyList() // TODO test code
|
||||
|
||||
private var currentMusic: MusicContainer? = null
|
||||
|
||||
private var musicBin: ArrayList<Int> = ArrayList(songs.indices.toList().shuffled())
|
||||
|
||||
|
||||
private fun stopMusic() {
|
||||
printdbg(this, "Now stopping: $currentMusic")
|
||||
state = 2
|
||||
intermissionAkku = 0f
|
||||
intermissionLength = 30f + 60f * Math.random().toFloat() // 30s-90m
|
||||
musicFired = false
|
||||
currentMusic = null
|
||||
fadeoutFired = false
|
||||
printdbg(this, "Intermission: $intermissionLength seconds")
|
||||
}
|
||||
|
||||
|
||||
private var warningPrinted = false
|
||||
|
||||
private val musicVolume: Float
|
||||
get() = (App.getConfigDouble("musicvolume") * App.getConfigDouble("mastervolume")).toFloat()
|
||||
|
||||
override fun update(ingame: IngameInstance, delta: Float) {
|
||||
if (songs.isEmpty()) {
|
||||
if (!warningPrinted) {
|
||||
warningPrinted = true
|
||||
printdbg(this, "Warning: songs list is empty")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
val ingame = ingame as TerrarumIngame
|
||||
if (state == 0) state = 2
|
||||
|
||||
|
||||
when (state) {
|
||||
1 -> {
|
||||
if (!musicFired) {
|
||||
musicFired = true
|
||||
|
||||
val song = songs[musicBin.removeAt(0)]
|
||||
// prevent same song to play twice
|
||||
if (musicBin.isEmpty()) {
|
||||
musicBin = ArrayList(songs.indices.toList().shuffled())
|
||||
}
|
||||
|
||||
song.gdxMusic.volume = musicVolume
|
||||
song.gdxMusic.play()
|
||||
printdbg(this, "Now playing: $song")
|
||||
|
||||
currentMusic = song
|
||||
|
||||
// process fadeout request
|
||||
if (fadeoutFired) {
|
||||
fadeoutAkku += delta
|
||||
currentMusic?.gdxMusic?.volume = 1f - musicVolume * (fadeoutAkku / fadeoutLength)
|
||||
|
||||
if (fadeoutAkku >= fadeoutLength) {
|
||||
currentMusic?.gdxMusic?.pause()
|
||||
}
|
||||
}
|
||||
// process fadein request
|
||||
else if (fadeinFired) {
|
||||
if (currentMusic?.gdxMusic?.isPlaying == false) {
|
||||
currentMusic?.gdxMusic?.play()
|
||||
}
|
||||
fadeoutAkku += delta
|
||||
currentMusic?.gdxMusic?.volume = musicVolume * (fadeoutAkku / fadeoutLength)
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (currentMusic?.gdxMusic?.isPlaying == false) {
|
||||
stopMusic()
|
||||
}
|
||||
}
|
||||
}
|
||||
2 -> {
|
||||
intermissionAkku += delta
|
||||
|
||||
if (intermissionAkku >= intermissionLength) {
|
||||
intermissionAkku = 0f
|
||||
state = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
currentMusic?.gdxMusic?.stop()
|
||||
stopMusic()
|
||||
songs.forEach { it.gdxMusic.tryDispose() }
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,8 @@ import com.badlogic.gdx.utils.JsonValue
|
||||
import net.torvald.terrarum.TerrarumAppConfiguration
|
||||
import net.torvald.terrarum.gameitems.ItemID
|
||||
import net.torvald.terrarum.gameworld.BlockLayerI16
|
||||
import net.torvald.terrarum.gameworld.GameWorld
|
||||
import net.torvald.terrarum.linearSearchBy
|
||||
import net.torvald.terrarum.utils.HashArray
|
||||
|
||||
/**
|
||||
@@ -47,16 +49,31 @@ class PointOfInterest(
|
||||
override fun read(json: Json, jsonData: JsonValue) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
/**
|
||||
* Place the specified layers onto the world. The name of the layers are case-insensitive.
|
||||
*/
|
||||
fun placeOnWorld(layerNames: List<String>, world: GameWorld, bottomCentreX: Int, bottomCenterY: Int) {
|
||||
val layers = layerNames.map { name ->
|
||||
(layers.linearSearchBy { it.name.equals(name, true) } ?: throw IllegalArgumentException("Layer with name '$name' not found"))
|
||||
}
|
||||
layers.forEach {
|
||||
it.placeOnWorld(world, bottomCentreX, bottomCenterY)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name name of the layer, case-insensitive.
|
||||
*/
|
||||
class POILayer(
|
||||
name: String
|
||||
) : Json.Serializable {
|
||||
constructor() : this("undefined")
|
||||
|
||||
@Transient val name = name
|
||||
@Transient val blockLayer = ArrayList<BlockLayerI16>()
|
||||
@Transient private lateinit var dat: Array<ByteArray>
|
||||
@Transient internal lateinit var blockLayer: ArrayList<BlockLayerI16>
|
||||
@Transient internal lateinit var dat: Array<ByteArray>
|
||||
|
||||
fun getUniqueTiles(): List<Int> {
|
||||
return blockLayer.flatMap { layer ->
|
||||
@@ -69,6 +86,9 @@ class POILayer(
|
||||
*
|
||||
* Tilenum: tile number in the block layer, identical to the any other block layers
|
||||
* TileSymbol: condensed version of the Tilenum, of which the higheset number is equal to the number of unique tiles used in the layer
|
||||
*
|
||||
* `tilenumToItemID[-1]` should return `Block.NULL`
|
||||
* `itemIDtoTileSym[Block.NULL]` should return -1, so that it would return 0xFF on any length of the word
|
||||
*/
|
||||
fun getReadyForSerialisation(tilenumToItemID: Map<Int, ItemID>, itemIDtoTileSym: Map<ItemID, Long>, byteLength: Int) {
|
||||
dat = blockLayer.map { layer ->
|
||||
@@ -87,10 +107,15 @@ class POILayer(
|
||||
|
||||
/**
|
||||
* Converts `dat` into `blockLayer` so the Layer can be actually utilised.
|
||||
*
|
||||
* `itemIDtoTileNum[Block.NULL]` should return `-1`
|
||||
* `tileSymbolToItemId[255]` and `tileSymbolToItemId[65535]` should return `Block.NULL`
|
||||
*/
|
||||
fun getReadyToBeUsed(tileSymbolToItemId: HashArray<ItemID>, itemIDtoTileNum: Map<ItemID, Int>, width: Int, height: Int, byteLength: Int) {
|
||||
blockLayer.forEach { it.dispose() }
|
||||
blockLayer.clear()
|
||||
if (::blockLayer.isInitialized) {
|
||||
blockLayer.forEach { it.dispose() }
|
||||
}
|
||||
blockLayer = ArrayList<BlockLayerI16>()
|
||||
|
||||
dat.forEachIndexed { layerIndex, layer ->
|
||||
val currentBlockLayer = BlockLayerI16(width, height).also {
|
||||
@@ -106,7 +131,23 @@ class POILayer(
|
||||
}
|
||||
}
|
||||
|
||||
fun placeOnWorld(world: GameWorld, bottomCentreX: Int, bottomCenterY: Int) {
|
||||
val topLeftX = bottomCentreX - blockLayer[0].width / 2
|
||||
val topLeftY = bottomCenterY - blockLayer[0].height
|
||||
|
||||
blockLayer.forEachIndexed { layerIndex, layer ->
|
||||
for (x in 0 until layer.width) { for (y in 0 until layer.height) {
|
||||
val tile = layer.unsafeGetTile(x, y)
|
||||
if (tile != -1) {
|
||||
val (wx, wy) = world.coerceXY(x + topLeftX, y + topLeftY)
|
||||
world.setTileOnLayerUnsafe(layerIndex, wx, wy, tile)
|
||||
}
|
||||
} }
|
||||
}
|
||||
}
|
||||
|
||||
override fun write(json: Json) {
|
||||
if (!::dat.isInitialized) throw IllegalStateException("Internal data is not prepared! please run getReadyForSerialisation() before writing!")
|
||||
json.setTypeName(null)
|
||||
json.writeValue("name", name)
|
||||
json.writeValue("dat", dat)
|
||||
|
||||
Reference in New Issue
Block a user