music player widget as a separate module

This commit is contained in:
minjaesong
2023-12-24 03:13:35 +09:00
parent b5312da2f0
commit c4836a3fe2
24 changed files with 370 additions and 33 deletions

14
.idea/artifacts/MusicPlayer.xml generated Normal file
View File

@@ -0,0 +1,14 @@
<component name="ArtifactManager">
<artifact type="jar" name="MusicPlayer">
<output-path>$PROJECT_DIR$/assets/mods/musicplayer</output-path>
<root id="archive" name="MusicPlayer.jar">
<element id="module-output" name="MusicPlayer" />
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-test/1.8.21/kotlin-test-1.8.21.jar" path-in-jar="/" />
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.8.21/kotlin-stdlib-1.8.21.jar" path-in-jar="/" />
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.8.21/kotlin-stdlib-common-1.8.21.jar" path-in-jar="/" />
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" path-in-jar="/" />
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.8.21/kotlin-stdlib-jdk8-1.8.21.jar" path-in-jar="/" />
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.8.21/kotlin-stdlib-jdk7-1.8.21.jar" path-in-jar="/" />
</root>
</artifact>
</component>

View File

@@ -1,13 +1,30 @@
<component name="libraryTable"> <component name="libraryTable">
<library name="jetbrains.kotlin.test" type="repository"> <library name="jetbrains.kotlin.test" type="repository">
<properties maven-id="org.jetbrains.kotlin:kotlin-test:1.8.21" /> <properties maven-id="org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.21" />
<CLASSES> <CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-test/1.8.21/kotlin-test-1.8.21.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-test/1.8.21/kotlin-test-1.8.21.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.8.21/kotlin-stdlib-1.8.21.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.8.21/kotlin-stdlib-1.8.21.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.8.21/kotlin-stdlib-common-1.8.21.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.8.21/kotlin-stdlib-common-1.8.21.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.8.21/kotlin-stdlib-jdk8-1.8.21.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.8.21/kotlin-stdlib-1.8.21.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.8.21/kotlin-stdlib-common-1.8.21.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.8.21/kotlin-stdlib-jdk7-1.8.21.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC /> <JAVADOC>
<SOURCES /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.8.21/kotlin-stdlib-jdk8-1.8.21-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.8.21/kotlin-stdlib-1.8.21-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.8.21/kotlin-stdlib-common-1.8.21-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.8.21/kotlin-stdlib-jdk7-1.8.21-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.8.21/kotlin-stdlib-jdk8-1.8.21-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.8.21/kotlin-stdlib-1.8.21-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.8.21/kotlin-stdlib-common-1.8.21-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.8.21/kotlin-stdlib-jdk7-1.8.21-sources.jar!/" />
</SOURCES>
</library> </library>
</component> </component>

2
.idea/misc.xml generated
View File

@@ -38,7 +38,7 @@
<property name="caretWidth" class="java.lang.Integer" /> <property name="caretWidth" class="java.lang.Integer" />
</properties> </properties>
</component> </component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="17" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" /> <output url="file://$PROJECT_DIR$/out" />
</component> </component>
<component name="SuppressKotlinCodeStyleNotification"> <component name="SuppressKotlinCodeStyleNotification">

1
.idea/modules.xml generated
View File

@@ -6,6 +6,7 @@
<component name="ProjectModuleManager"> <component name="ProjectModuleManager">
<modules> <modules>
<module fileurl="file://$PROJECT_DIR$/ModuleComputers/ModuleComputers.iml" filepath="$PROJECT_DIR$/ModuleComputers/ModuleComputers.iml" /> <module fileurl="file://$PROJECT_DIR$/ModuleComputers/ModuleComputers.iml" filepath="$PROJECT_DIR$/ModuleComputers/ModuleComputers.iml" />
<module fileurl="file://$PROJECT_DIR$/MusicPlayer/MusicPlayer.iml" filepath="$PROJECT_DIR$/MusicPlayer/MusicPlayer.iml" />
<module fileurl="file://$PROJECT_DIR$/TerrarumBuild.iml" filepath="$PROJECT_DIR$/TerrarumBuild.iml" /> <module fileurl="file://$PROJECT_DIR$/TerrarumBuild.iml" filepath="$PROJECT_DIR$/TerrarumBuild.iml" />
</modules> </modules>
</component> </component>

View File

@@ -10,6 +10,7 @@
<option name="RunConfigurationTask" enabled="true" run_configuration_name="Build Version Number" run_configuration_type="Application" /> <option name="RunConfigurationTask" enabled="true" run_configuration_name="Build Version Number" run_configuration_type="Application" />
<option name="BuildArtifacts" enabled="true"> <option name="BuildArtifacts" enabled="true">
<artifact name="ModuleComputers" /> <artifact name="ModuleComputers" />
<artifact name="MusicPlayer" />
<artifact name="TerrarumBuild" /> <artifact name="TerrarumBuild" />
</option> </option>
<option name="RunConfigurationTask" enabled="false" run_configuration_name="QuickDirtyLint" run_configuration_type="Application" /> <option name="RunConfigurationTask" enabled="false" run_configuration_name="QuickDirtyLint" run_configuration_type="Application" />

View File

@@ -10,6 +10,7 @@
<option name="RunConfigurationTask" enabled="true" run_configuration_name="Build Version Number" run_configuration_type="Application" /> <option name="RunConfigurationTask" enabled="true" run_configuration_name="Build Version Number" run_configuration_type="Application" />
<option name="BuildArtifacts" enabled="true"> <option name="BuildArtifacts" enabled="true">
<artifact name="ModuleComputers" /> <artifact name="ModuleComputers" />
<artifact name="MusicPlayer" />
<artifact name="TerrarumBuild" /> <artifact name="TerrarumBuild" />
</option> </option>
<option name="RunConfigurationTask" enabled="true" run_configuration_name="QuickDirtyLint" run_configuration_type="Application" /> <option name="RunConfigurationTask" enabled="true" run_configuration_name="QuickDirtyLint" run_configuration_type="Application" />

View File

@@ -8,16 +8,15 @@
<orderEntry type="inheritedJdk" /> <orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="ModuleComputersLib" level="project" /> <orderEntry type="library" name="ModuleComputersLib" level="project" />
<orderEntry type="module" module-name="TerrarumBuild" /> <orderEntry type="module" module-name="TerrarumBuild" scope="PROVIDED" />
<orderEntry type="library" name="graalvm-js 22.3.1" level="project" /> <orderEntry type="library" scope="PROVIDED" name="graalvm-js 22.3.1" level="project" />
<orderEntry type="library" name="TerrarumSansBitmap" level="project" /> <orderEntry type="library" scope="PROVIDED" name="TerrarumSansBitmap" level="project" />
<orderEntry type="library" name="badlogicgames.gdx" level="project" /> <orderEntry type="library" scope="PROVIDED" name="badlogicgames.gdx" level="project" />
<orderEntry type="library" name="badlogicgames.gdx.backend.lwjgl3" level="project" /> <orderEntry type="library" scope="PROVIDED" name="badlogicgames.gdx.backend.lwjgl3" level="project" />
<orderEntry type="library" name="KotlinJavaRuntime" level="project" /> <orderEntry type="library" scope="PROVIDED" name="KotlinJavaRuntime" level="project" />
<orderEntry type="library" name="jetbrains.kotlin.reflect" level="project" /> <orderEntry type="library" scope="PROVIDED" name="jetbrains.kotlin.reflect" level="project" />
<orderEntry type="library" name="jetbrains.kotlin.test" level="project" /> <orderEntry type="library" scope="PROVIDED" name="jetbrains.kotlin.test" level="project" />
<orderEntry type="library" name="io.github.classgraph" level="project" /> <orderEntry type="library" scope="PROVIDED" name="jetbrains.kotlinx.coroutines.core" level="project" />
<orderEntry type="library" name="jetbrains.kotlinx.coroutines.core" level="project" /> <orderEntry type="library" scope="PROVIDED" name="io.airlift.aircompressor" level="project" />
<orderEntry type="library" name="io.airlift.aircompressor" level="project" />
</component> </component>
</module> </module>

Binary file not shown.

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="jetbrains.kotlin.test" level="project" />
<orderEntry type="module" module-name="TerrarumBuild" scope="PROVIDED" />
<orderEntry type="library" scope="PROVIDED" name="badlogicgames.gdx" level="project" />
<orderEntry type="library" scope="PROVIDED" name="io.airlift.aircompressor" level="project" />
<orderEntry type="library" scope="PROVIDED" name="TerrarumSansBitmap" level="project" />
</component>
</module>

View File

@@ -0,0 +1,19 @@
package net.torvald.terrarum.musicplayer
import net.torvald.terrarum.IngameInstance
import net.torvald.terrarum.ModMgr
import net.torvald.terrarum.ModuleEntryPoint
import net.torvald.terrarum.modulebasegame.TerrarumIngame
import net.torvald.terrarum.musicplayer.gui.MusicPlayer
/**
* Created by minjaesong on 2023-12-23.
*/
class EntryPoint : ModuleEntryPoint() {
override fun invoke() {
ModMgr.GameExtraGuiLoader.register { ingame: TerrarumIngame -> MusicPlayer(ingame) }
}
override fun dispose() {
}
}

View File

@@ -0,0 +1,217 @@
package net.torvald.terrarum.musicplayer.gui
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.OrthographicCamera
import com.badlogic.gdx.graphics.Pixmap
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.badlogic.gdx.graphics.glutils.FrameBuffer
import com.jme3.math.FastMath
import net.torvald.reflection.extortField
import net.torvald.terrarum.*
import net.torvald.terrarum.audio.*
import net.torvald.terrarum.modulebasegame.TerrarumIngame
import net.torvald.terrarum.ui.BasicDebugInfoWindow
import net.torvald.terrarum.ui.Toolkit
import net.torvald.terrarum.ui.UICanvas
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
import kotlin.math.*
/**
* Created by minjaesong on 2023-12-23.
*/
class MusicPlayer(private val ingame: TerrarumIngame) : UICanvas() {
override var width = 120
override var height = 28
private var capsuleHeight = 28
private var capsuleMosaicSize = capsuleHeight / 2 + 1
private val maskOffWidth = 8
private val nameFBO = FrameBuffer(Pixmap.Format.RGBA8888, 400, capsuleHeight, false)
private val baloonTexture = ModMgr.getGdxFile("musicplayer", "gui/blob.tga").let {
TextureRegionPack(it, capsuleMosaicSize, capsuleMosaicSize)
}
private val textmask = ModMgr.getGdxFile("musicplayer", "gui/textmask.tga").let {
TextureRegionPack(it, maskOffWidth, capsuleHeight)
}
private var mode = 0
private var modeNext = 0
private var transitionAkku = 0f
private var textScroll = 0f
private val MODE_IDLE = 0
private val MODE_NOW_PLAYING = 1
private val MODE_PLAYING = 2
private val MODE_MOUSE_UP = 64
private val MODE_SHOW_LIST = 128
private var colourEdge = Color(0xFFFFFF_40.toInt())
private val colourBack = Color.BLACK
private val colourText = Color(0xf0f0f0ff.toInt())
private val colourMeter = Color(0xf0f0f0ff.toInt())
private val colourMeter2 = Color(0xf0f0f080.toInt())
init {
setAsAlwaysVisible()
}
override fun renderUI(batch: SpriteBatch, camera: OrthographicCamera) {
batch.end()
renderNameToFBO(batch, camera, AudioMixer.musicTrack.currentTrack?.name ?: "", 0f..(width - 2*STRIP_W - capsuleHeight + maskOffWidth))
batch.begin()
val posX = ((Toolkit.drawWidth - width) / 2).toFloat()
val posY = (App.scr.height - App.scr.tvSafeGraphicsHeight - height).toFloat()
blendNormalStraightAlpha(batch)
drawBaloon(batch, posX, posY, width.toFloat(), height - capsuleHeight.toFloat())
drawText(batch, posX, posY)
drawFreqMeter(batch, posX + width - 18, posY + height - (capsuleHeight / 2) + 1)
batch.color = Color.WHITE
}
private fun drawBaloon(batch: SpriteBatch, x: Float, y: Float, width: Float, height: Float) {
val x = x - capsuleMosaicSize
for (k in 0..3 step 3) {
batch.color = if (k == 0) colourEdge else colourBack
// top left
batch.draw(baloonTexture.get(k, 0), x, y)
// top
batch.draw(baloonTexture.get(k+1, 0), x + capsuleMosaicSize, y, width, capsuleMosaicSize.toFloat())
// top right
batch.draw(baloonTexture.get(k+2, 0), x + capsuleMosaicSize + width, y)
if (height > 0) {
// left
batch.draw(baloonTexture.get(k, 1), x, y + capsuleMosaicSize)
// centre
batch.draw(baloonTexture.get(k+1, 1), x + capsuleMosaicSize, y + capsuleMosaicSize, width, height)
// right
batch.draw(baloonTexture.get(k+2, 1), x + capsuleMosaicSize + width, y + capsuleMosaicSize)
}
// bottom left
batch.draw(baloonTexture.get(k, 2), x, y + capsuleMosaicSize + height)
// bottom
batch.draw(baloonTexture.get(k+1, 2), x + capsuleMosaicSize, y + capsuleMosaicSize + height, width, capsuleMosaicSize.toFloat())
// bottom right
batch.draw(baloonTexture.get(k+2, 2), x + capsuleMosaicSize + width, y + capsuleMosaicSize + height)
}
}
private fun drawText(batch: SpriteBatch, posX: Float, posY: Float) {
batch.color = colourText
batch.draw(nameFBO.colorBufferTexture, posX - maskOffWidth, posY - 1)
}
private fun renderNameToFBO(batch: SpriteBatch, camera: OrthographicCamera, str: String, window: ClosedFloatingPointRange<Float>) {
nameFBO.inAction(camera, batch) {
batch.inUse {
batch.color = Color.WHITE
// draw text
gdxClearAndEnableBlend(0f, 0f, 0f, 0f)
blendNormalStraightAlpha(batch)
App.fontGameFBO.draw(batch, str, maskOffWidth.toFloat() - textScroll, 0f)
// mask off the area
batch.color = Color.WHITE
blendAlphaMask(batch)
batch.draw(textmask.get(0, 0), window.start, 0f)
batch.draw(textmask.get(1, 0), window.start + maskOffWidth, 0f, window.endInclusive - window.start, capsuleHeight.toFloat())
batch.draw(textmask.get(2, 0), window.start + window.endInclusive + maskOffWidth, 0f)
batch.draw(textmask.get(3, 0), window.start + window.endInclusive + 2 * maskOffWidth, 0f, 1000f, capsuleHeight.toFloat())
}
}
}
private val FFTSIZE = 1024
private val inBuf = Array(2) { FloatArray(FFTSIZE) }
private fun sin2(x: Double) = sin(x).pow(2)
private val fftWin = FloatArray(FFTSIZE) { sin2(PI * it / FFTSIZE).toFloat() } // hann
private val oldFFTmagn = DoubleArray(FFTSIZE / 2) { 0.0 }
private val chsum = ComplexArray(FloatArray(FFTSIZE * 2))
private val fftOut = ComplexArray(FloatArray(FFTSIZE * 2))
private val binHeights = FloatArray(FFTSIZE / 2)
private val FFT_SMOOTHING_FACTOR = BasicDebugInfoWindow.getSmoothingFactor(1600)
private val lowlim = -36.0f
private val STRIP_W = 9f
private val fftBinIndices = arrayOf(
0..3,
4..12,
13..39,
40..121,
122 until FFTSIZE / 2
) // 60-18000 at 1024 (https://www.desmos.com/calculator/vkxhrzfam3)
private val fftBarHeights = FloatArray(5)
override fun updateUI(delta: Float) {
val inbuf = AudioMixer.musicTrack.extortField<MixerTrackProcessor>("processor")!!.extortField<List<FloatArray>>("fout1")!!
push(inbuf[0], inBuf[0])
push(inbuf[1], inBuf[1])
for (i in 0 until FFTSIZE) {
chsum.reim[2*i] = (inBuf[0][i] + inBuf[1][i]) * fftWin[i]
}
FFT.fftInto(chsum, fftOut)
}
private fun drawFreqMeter(batch: SpriteBatch, posX: Float, posY: Float) {
// apply slope to the fft bins, also converts fullscale to decibels
for (bin in binHeights.indices) {
val freqR = (TerrarumAudioMixerTrack.SAMPLING_RATED / FFTSIZE) * (bin + 1)
val magn0 = fftOut.reim[2 * bin].absoluteValue / FFTSIZE * (freqR / 10.0) // apply slope
val magn = FastMath.interpolateLinear(FFT_SMOOTHING_FACTOR, magn0, oldFFTmagn[bin])
val magnLog = fullscaleToDecibels(magn)
val h = (-(magnLog - lowlim) / lowlim * STRIP_W).toFloat().coerceAtLeast(0.5f)
binHeights[bin] = h
oldFFTmagn[bin] = magn
}
fftBinIndices.mapIndexed { i, range ->
fftBarHeights[i] = binHeights.slice(range).average().toFloat()
}
batch.color = colourMeter2
fftBarHeights.forEachIndexed { index, h ->
Toolkit.fillArea(batch, posX + index*4f, posY - h, 3f, 2*h + 1)
}
batch.color = colourMeter
fftBarHeights.forEachIndexed { index, h ->
Toolkit.fillArea(batch, posX + index*4f, posY - h, 2f, 2*h)
}
}
override fun dispose() {
baloonTexture.dispose()
}
private fun push(samples: FloatArray, buf: FloatArray) {
if (samples.size >= FFTSIZE) {
// overwrite
System.arraycopy(samples, samples.size - buf.size, buf, 0, buf.size)
}
else {
// shift samples
System.arraycopy(buf, samples.size, buf, 0, buf.size - samples.size)
// write to the buf
System.arraycopy(samples, 0, buf, buf.size - samples.size, samples.size)
}
}
}

View File

@@ -54,6 +54,7 @@ version=0.4.0
jar= jar=
# Sha256sum of the External JAR, if any # Sha256sum of the External JAR, if any
# The hash will not be checked if the game is running on the development mode
jarhash= jarhash=
# Modules that must be pre-installed, separated by semicolons (;) # Modules that must be pre-installed, separated by semicolons (;)

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,10 @@
propername=Terrarum Music Player
description=Simple Music Player Widget
author=CuriousTo\uA75Bvald
package=net.torvald.terrarum.musicplayer
entrypoint=net.torvald.terrarum.musicplayer.EntryPoint
releasedate=2023-12-31
version=1.0.0
jar=MusicPlayer.jar
jarhash=0
dependency=basegame 0.4.0

View File

@@ -481,6 +481,7 @@ public class App implements ApplicationListener {
if (Gdx.app != null) { if (Gdx.app != null) {
Gdx.app.exit(); Gdx.app.exit();
} }
e.printStackTrace();
new GameCrashHandler(e); new GameCrashHandler(e);
} }
} }

View File

@@ -16,9 +16,11 @@ import net.torvald.terrarum.gameitems.ItemID
import net.torvald.terrarum.itemproperties.ItemCodex import net.torvald.terrarum.itemproperties.ItemCodex
import net.torvald.terrarum.itemproperties.MaterialCodex import net.torvald.terrarum.itemproperties.MaterialCodex
import net.torvald.terrarum.langpack.Lang import net.torvald.terrarum.langpack.Lang
import net.torvald.terrarum.modulebasegame.TerrarumIngame
import net.torvald.terrarum.modulebasegame.worldgenerator.OregenParams import net.torvald.terrarum.modulebasegame.worldgenerator.OregenParams
import net.torvald.terrarum.modulebasegame.worldgenerator.Worldgen import net.torvald.terrarum.modulebasegame.worldgenerator.Worldgen
import net.torvald.terrarum.serialise.Common import net.torvald.terrarum.serialise.Common
import net.torvald.terrarum.ui.UICanvas
import net.torvald.terrarum.utils.CSVFetcher import net.torvald.terrarum.utils.CSVFetcher
import net.torvald.terrarum.utils.JsonFetcher import net.torvald.terrarum.utils.JsonFetcher
import net.torvald.terrarum.utils.forEachSiblings import net.torvald.terrarum.utils.forEachSiblings
@@ -275,16 +277,16 @@ object ModMgr {
digester.reset() digester.reset()
val hash = digester.digest(File(jarFilePath).readBytes()).joinToString("","","") { it.toInt().and(255).toString(16).uppercase().padStart(2,'0') } val hash = digester.digest(File(jarFilePath).readBytes()).joinToString("","","") { it.toInt().and(255).toString(16).uppercase().padStart(2,'0') }
if (jarHash != hash) { if (!App.IS_DEVELOPMENT_BUILD && jarHash != hash) {
printdbg(this, "Hash expected: $jarHash, got: $hash") printdbg(this, "Hash expected: $jarHash, got: $hash")
throw IllegalStateException("Module Jarfile hash mismatch") throw IllegalStateException("Module Jarfile hash mismatch")
} }
// check for module-info.java // check for module-info.java
val moduleInfoPath = cl.getResources("module-info.class").toList().filter { it.toString().contains("$moduleName/$jar!/module-info.class") && it.toString().endsWith("module-info.class")} /*val moduleInfoPath = cl.getResources("module-info.class").toList().filter { it.toString().contains("$moduleName/$jar!/module-info.class") && it.toString().endsWith("module-info.class")}
if (moduleInfoPath.isEmpty()) { if (moduleInfoPath.isEmpty()) {
throw IllegalStateException("module-info not found on $moduleName") throw IllegalStateException("module-info not found on $moduleName")
} }*/
newClass = cl.loadClass(entryPoint) newClass = cl.loadClass(entryPoint)
} }
@@ -771,6 +773,14 @@ object ModMgr {
} }
} }
object GameExtraGuiLoader {
internal val guis = ArrayList<(TerrarumIngame) -> UICanvas>()
@JvmStatic fun register(uiCreationFun: (TerrarumIngame) -> UICanvas) {
guis.add(uiCreationFun)
}
}
} }
private class JarFileLoader(urls: Array<URL>) : URLClassLoader(urls) { private class JarFileLoader(urls: Array<URL>) : URLClassLoader(urls) {

View File

@@ -472,6 +472,11 @@ fun blendMul(batch: SpriteBatch) {
batch.setBlendFunction(GL20.GL_DST_COLOR, GL20.GL_ONE_MINUS_SRC_ALPHA) batch.setBlendFunction(GL20.GL_DST_COLOR, GL20.GL_ONE_MINUS_SRC_ALPHA)
} }
fun blendAlphaMask(batch: SpriteBatch) {
batch.enableBlending()
batch.setBlendFunction(GL20.GL_ZERO, GL20.GL_SRC_ALPHA)
}
/** /**
* Use demultiplier shader on GL Source (foreground) if source has semitransparency * Use demultiplier shader on GL Source (foreground) if source has semitransparency
*/ */

View File

@@ -12,10 +12,10 @@ import net.torvald.terrarum.ui.Toolkit
import kotlin.math.* import kotlin.math.*
class Scope : TerrarumAudioFilter() { class Scope : TerrarumAudioFilter() {
val backbufL = Array((4096f / AUDIO_BUFFER_SIZE).roundToInt().coerceAtLeast(1)) { val backbufL = Array((6144f / AUDIO_BUFFER_SIZE).roundToInt().coerceAtLeast(1)) {
FloatArray(AUDIO_BUFFER_SIZE) FloatArray(AUDIO_BUFFER_SIZE)
} }
val backbufR = Array((4096f / AUDIO_BUFFER_SIZE).roundToInt().coerceAtLeast(1)) { val backbufR = Array((6144f / AUDIO_BUFFER_SIZE).roundToInt().coerceAtLeast(1)) {
FloatArray(AUDIO_BUFFER_SIZE) FloatArray(AUDIO_BUFFER_SIZE)
} }
@@ -64,11 +64,11 @@ class Scope : TerrarumAudioFilter() {
// plot dots // plot dots
for (i in 0 until TerrarumAudioMixerTrack.AUDIO_BUFFER_SIZE) { for (i in 0 until TerrarumAudioMixerTrack.AUDIO_BUFFER_SIZE) {
val y0 = inbuf[0][i] * 0.7 val y0 = +inbuf[0][i] * 2f
val x0 = -inbuf[1][i] * 0.7 // rotate the domain by -90 deg val x0 = -inbuf[1][i] * 2f// rotate the domain by -90 deg
val x = (+x0*sqrt2p -y0*sqrt2p) * 1.414 val x = (+x0*sqrt2p -y0*sqrt2p) * 1.4142
val y = (-x0*sqrt2p -y0*sqrt2p) * 1.414 // further rotate by -45 deg then flip along the y axis val y = (-x0*sqrt2p -y0*sqrt2p) * 1.4142 // further rotate by -45 deg then flip along the y axis
backbufL[0][i] = x.toFloat() backbufL[0][i] = x.toFloat()
backbufR[0][i] = y.toFloat() backbufR[0][i] = y.toFloat()

View File

@@ -641,7 +641,10 @@ open class TerrarumIngame(batch: FlippingSpriteBatch) : IngameInstance(batch) {
ingameUpdateThread = ThreadIngameUpdate(this) ingameUpdateThread = ThreadIngameUpdate(this)
updateThreadWrapper = Thread(ingameUpdateThread, "Terrarum UpdateThread") updateThreadWrapper = Thread(ingameUpdateThread, "Terrarum UpdateThread")
// add extra UIs from the other modules
ModMgr.GameExtraGuiLoader.guis.forEach {
uiContainer.add(it(this))
}
// these need to appear on top of any others // these need to appear on top of any others
uiContainer.add(notifier) uiContainer.add(notifier)

View File

@@ -24,7 +24,7 @@ data class MusicContainer(
val name: String, val name: String,
val file: File, val file: File,
val gdxMusic: Music, val gdxMusic: Music,
val songFinishedHook: (Music) -> Unit internal var songFinishedHook: (Music) -> Unit = {}
) { ) {
val samplingRate: Int val samplingRate: Int
val codec: String val codec: String
@@ -142,7 +142,9 @@ class TerrarumMusicGovernor : MusicGovernor() {
it.nameWithoutExtension.replace('_', ' ').split(" ").map { it.capitalize() }.joinToString(" "), it.nameWithoutExtension.replace('_', ' ').split(" ").map { it.capitalize() }.joinToString(" "),
it, it,
Gdx.audio.newMusic(Gdx.files.absolute(it.absolutePath)) Gdx.audio.newMusic(Gdx.files.absolute(it.absolutePath))
) { stopMusic() } ).also { muscon ->
muscon.songFinishedHook = { stopMusic(muscon) }
}
} }
catch (e: GdxRuntimeException) { catch (e: GdxRuntimeException) {
e.printStackTrace() e.printStackTrace()
@@ -170,7 +172,16 @@ class TerrarumMusicGovernor : MusicGovernor() {
private var ambientsBin: ArrayList<Int> = ArrayList(ambients.indices.toList().shuffled()) private var ambientsBin: ArrayList<Int> = ArrayList(ambients.indices.toList().shuffled())
private val musicStartHooks = ArrayList<(MusicContainer) -> Unit>()
private val musicStopHooks = ArrayList<(MusicContainer) -> Unit>()
fun addMusicStartHook(f: (MusicContainer) -> Unit) {
musicStartHooks.add(f)
}
fun addMusicStopHook(f: (MusicContainer) -> Unit) {
musicStopHooks.add(f)
}
init { init {
songs.forEach { songs.forEach {
@@ -193,18 +204,20 @@ class TerrarumMusicGovernor : MusicGovernor() {
protected var ambState = 0 protected var ambState = 0
protected var ambFired = false protected var ambFired = false
private fun stopMusic() { private fun stopMusic(song: MusicContainer?) {
musicState = STATE_INTERMISSION musicState = STATE_INTERMISSION
intermissionAkku = 0f intermissionAkku = 0f
intermissionLength = 30f + 30f * Math.random().toFloat() // 30s-60s intermissionLength = 30f + 30f * Math.random().toFloat() // 30s-60s
musicFired = false musicFired = false
musicStopHooks.forEach { if (song != null) { it(song) } }
printdbg(this, "Intermission: $intermissionLength seconds") printdbg(this, "Intermission: $intermissionLength seconds")
} }
private fun startMusic(song: MusicContainer) { private fun startMusic(song: MusicContainer) {
AudioMixer.startMusic(song) AudioMixer.startMusic(song)
printdbg(this, "Now playing: $song") printdbg(this, "Now playing: $song")
INGAME.sendNotification("Now Playing $EMDASH ${song.name}") // INGAME.sendNotification("Now Playing $EMDASH ${song.name}")
musicStartHooks.forEach { it(song) }
musicState = STATE_PLAYING musicState = STATE_PLAYING
} }
@@ -220,7 +233,7 @@ class TerrarumMusicGovernor : MusicGovernor() {
private fun startAmbient(song: MusicContainer) { private fun startAmbient(song: MusicContainer) {
AudioMixer.startAmb(song) AudioMixer.startAmb(song)
printdbg(this, "Now playing: $song") printdbg(this, "Now playing: $song")
INGAME.sendNotification("Now Playing $EMDASH ${song.name}") // INGAME.sendNotification("Now Playing $EMDASH ${song.name}")
ambState = STATE_PLAYING ambState = STATE_PLAYING
} }
@@ -285,7 +298,7 @@ class TerrarumMusicGovernor : MusicGovernor() {
override fun dispose() { override fun dispose() {
AudioMixer.requestFadeOut(AudioMixer.fadeBus, AudioMixer.DEFAULT_FADEOUT_LEN) // explicit call for fade-out when the game instance quits AudioMixer.requestFadeOut(AudioMixer.fadeBus, AudioMixer.DEFAULT_FADEOUT_LEN) // explicit call for fade-out when the game instance quits
stopMusic() stopMusic(AudioMixer.musicTrack.currentTrack)
stopAmbient() stopAmbient()
} }
} }

View File

@@ -279,7 +279,7 @@ class UILoadList(val full: UILoadSavegame) : UICanvas() {
} }
override fun touchUp(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean { override fun touchUp(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean {
playerCells.forEach { it.touchUp(screenX, screenY, pointer, button) } playerCells.slice(playerCells.indices).forEach { it.touchUp(screenX, screenY, pointer, button) } // to prevent ConcurrentModificationException
return true return true
} }

View File

@@ -418,7 +418,7 @@ class BasicDebugInfoWindow : UICanvas() {
fun getSmoothingFactor(sampleCount: Int) = (1.0 - (256.0 / sampleCount)) fun getSmoothingFactor(sampleCount: Int) = (1.0 - (256.0 / sampleCount))
val PEAK_SMOOTHING_FACTOR = getSmoothingFactor(640) val PEAK_SMOOTHING_FACTOR = getSmoothingFactor(640)
val FFT_SMOOTHING_FACTOR = getSmoothingFactor(960) val FFT_SMOOTHING_FACTOR = getSmoothingFactor(1200)
val LAMP_SMOOTHING_FACTOR = getSmoothingFactor(3200) val LAMP_SMOOTHING_FACTOR = getSmoothingFactor(3200)
val RMS_SMOOTHING_FACTOR = getSmoothingFactor(12000) val RMS_SMOOTHING_FACTOR = getSmoothingFactor(12000)
} }