btex stuffs

This commit is contained in:
minjaesong
2024-04-24 01:13:46 +09:00
parent e2a87d0e14
commit 76dd9a98e4
18 changed files with 602 additions and 182 deletions

View File

@@ -1,4 +1,4 @@
<btex cover="hardcover" inner="standard" papersize="standard">
<btexdoc cover="hardcover" inner="standard" papersize="standard">
<cover>
<title>The Way to Mastery of Lorem Ipsum<br />Or, How To Write and Publish a Book</title>
<author>Terran Publishing</author>
@@ -46,4 +46,4 @@
<p>Typewriter and Printout are considered not bound and readers will only see one page at a time, while Hardcover is considered bound and two pages are presented to the readers.</p>
</manuscript>
</btex>
</btexdoc>

View File

@@ -1,4 +1,4 @@
<btex def="examination">
<btexdoc def="examination">
<blocklut>
<pair key="0" value="basegame:0" />
<pair key="1" value="basegame:32" />
@@ -54,4 +54,4 @@
<p>Examination is a special format of the “book” that operates on the same book system.</p>
</manuscript>
</btex>
</btexdoc>

Binary file not shown.

Binary file not shown.

View File

@@ -6,6 +6,7 @@ import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.badlogic.gdx.graphics.g2d.TextureRegion
import net.torvald.terrarum.App
import net.torvald.terrarum.ui.Toolkit
import net.torvald.terrarumsansbitmap.MovableType
/**
* Created by minjaesong on 2023-10-28.
@@ -17,7 +18,9 @@ class BTeXDocument {
var papersize = "standard"
var pageWidth = 420
var pageHeight = 25 * 24
var lineHeight = 24
var pageLines = 25
var pageHeight = pageLines * lineHeight
companion object {
val DEFAULT_PAGE_BACK = Color(0xe1e1d7ff.toInt())
@@ -61,13 +64,18 @@ class BTeXPage(
}
}
data class MovableTypeDrawCall(val movableType: MovableType, val rowStart: Int, val rowEnd: Int) {
fun draw(batch: SpriteBatch, x: Float, y: Float) {
movableType.draw(batch, x, y, rowStart, rowEnd)
}
}
class BTeXDrawCall(
val posX: Int,
val posY: Int,
val theme: String,
val colour: Color,
val font: BitmapFont,
val text: String? = null,
val text: MovableTypeDrawCall? = null,
val texture: TextureRegion? = null,
) {
@@ -82,7 +90,7 @@ class BTeXDrawCall(
batch.color = colour
if (text != null && texture == null) {
font.draw(batch, text, px, py)
text.draw(batch, px, py)
}
else if (text == null && texture != null) {
batch.draw(texture, px, py)

View File

@@ -4,13 +4,16 @@ import com.badlogic.gdx.files.FileHandle
import com.badlogic.gdx.graphics.Color
import net.torvald.terrarum.App
import net.torvald.terrarum.btex.BTeXDocument
import net.torvald.terrarum.btex.BTeXDrawCall
import net.torvald.terrarum.gameitems.ItemID
import org.xml.sax.Attributes
import org.xml.sax.InputSource
import org.xml.sax.helpers.DefaultHandler
import java.io.*
import java.util.*
import javax.xml.parsers.SAXParserFactory
import kotlin.reflect.KFunction
import kotlin.reflect.full.declaredFunctions
import kotlin.reflect.full.findAnnotation
/**
* Created by minjaesong on 2023-10-28.
@@ -27,18 +30,27 @@ object BTeXParser {
return doc
}
private class BTeXHandler(val doc: BTeXDocument) : DefaultHandler() {
operator fun invoke(string: String): BTeXDocument {
val doc = BTeXDocument()
val parser = SAXParserFactory.newDefaultInstance().newSAXParser()
parser.parse(InputSource(StringReader(string)), BTeXHandler(doc))
return doc
}
internal class BTeXHandler(val doc: BTeXDocument) : DefaultHandler() {
private val DEFAULT_FONTCOL = Color(0x222222ff)
private val LINE_HEIGHT = 24
private val LINE_HEIGHT = doc.lineHeight
private var cover = ""
private var inner = ""
private var papersize = ""
private var def = ""
private var pageWidth = 420
private var pageLines = 25
private var pageHeight = 25 * LINE_HEIGHT
private var btexOpened = false
private var pageWidth = doc.pageWidth
private var pageLines = doc.pageLines
private var pageHeight = doc.pageHeight
private val blockLut = HashMap<String, ItemID>()
@@ -51,66 +63,93 @@ object BTeXParser {
private var typeX = 0
private var typeY = 0
private val elemOpeners: HashMap<String, KFunction<*>> = HashMap()
private val elemClosers: HashMap<String, KFunction<*>> = HashMap()
init {
BTeXHandler::class.declaredFunctions.filter { it.findAnnotation<OpenTag>() != null }.forEach {
println("Tag opener: ${it.name}")
elemOpeners[it.name] = it
}
BTeXHandler::class.declaredFunctions.filter { it.findAnnotation<CloseTag>() != null }.forEach {
println("Tag closer: ${it.name}")
elemClosers[it.name] = it
}
}
private fun printdbg(message: String?) {
val CSI = "\u001B[32m"
val timeNow = System.currentTimeMillis()
val ss = (timeNow / 1000) % 60
val mm = (timeNow / 60000) % 60
val hh = (timeNow / 3600000) % 24
val ms = timeNow % 1000
val out = this.javaClass.getSimpleName()
val prompt = CSI + String.format("%02d:%02d:%02d.%03d [%s]%s ", hh, mm, ss, ms, out, App.csi0)
if (message == null) {
println(prompt + "null")
}
else {
val indentation = " ".repeat(out.length + 16)
val msgLines: Array<String> = message.toString().split("\\n".toRegex()).dropLastWhile { it.isEmpty() }
.toTypedArray()
for (i in msgLines.indices) {
println((if (i == 0) prompt else indentation) + msgLines[i])
}
}
}
override fun startElement(uri: String, localName: String, qName: String, attributes: Attributes) {
val tag = qName; if (tagStack.isEmpty() && tag != "btex") throw BTeXParsingException("Document is not btex")
val tag = qName; if (tagStack.isEmpty() && tag.lowercase() != "btexdoc") throw BTeXParsingException("Document is not BTeX")
tagStack.add(tag)
val attribs = HashMap<String, String>().also {
it.putAll((0 until attributes.length).map { attributes.getQName(it) to attributes.getValue(it) })
}
val mode = tagStack.getOrNull(1)
val theTag = tag.uppercase()
when (tag) {
"btex" -> {
if (attribs.containsKey("def"))
def = attribs["def"]!!
else {
cover = attribs["cover"] ?: "printout"
inner = attribs["inner"] ?: "standard"
papersize = attribs["papersize"] ?: "standard"
pageWidth = pageWidthMap[papersize]!!
pageLines = pageHeightMap[papersize]!!
pageHeight = pageLines * LINE_HEIGHT
doc.pageWidth = pageWidth
doc.pageHeight = pageHeight
elemOpeners["processElem$theTag"].let {
if (it == null)
System.err.println("Unknown tag: $theTag")
else {
try {
it.call(this, this, doc, theTag, uri, attribs)
}
catch (e: Throwable) {
throw BTeXParsingException(e.stackTraceToString())
}
}
"pair" -> {
if (tagStack.size == 3 && mode == "blocklut") {
blockLut[attribs["key"]!!] = attribs["value"]!!
}
else {
throw BTeXParsingException("<pair> used outside of <blocklut>")
}
}
"span" -> {
attribs["span"]?.let {
spanColour = it
}
}
}
// printdbg("Start element \t($tag)")
}
override fun endElement(uri: String, localName: String, qName: String) {
tagStack.removeLast()
val popped = tagStack.removeLast()
when (qName) {
"span" -> {
spanColour = null
val theTag = qName.uppercase()
elemClosers["closeElem$theTag"].let {
try {
it?.call(this, this, doc, theTag, uri)
}
catch (e: Throwable) {
throw BTeXParsingException(e.stackTraceToString())
}
}
// printdbg(" End element \t($popped)")
}
override fun characters(ch: CharArray, start: Int, length: Int) {
val str = String(ch.sliceArray(start until start+length)).replace('\n',' ').replace(Regex(" +"), " ").trim()
val font = getFont()
advanceCursorPre(font.getWidth(str), 0)
doc.appendDrawCall(BTeXDrawCall(typeX, typeY, inner, getSpanColour(), font, str))
advanceCursorPost(font.getWidth(str), 0)
val str =
String(ch.sliceArray(start until start + length)).replace('\n', ' ').replace(Regex(" +"), " ")//.trim()
if (str.isNotBlank()) {
printdbg("Characters \t\"$str\"")
}
}
private fun advanceCursorPre(w: Int, h: Int) {
@@ -149,8 +188,99 @@ object BTeXParser {
private val pageHeightMap = hashMapOf(
"standard" to 25
)
@OpenTag // reflective access is impossible with 'private'
fun processElemBTEXDOC(handler: BTeXHandler, doc: BTeXDocument, theTag: String, uri: String, attribs: HashMap<String, String>) {
if (handler.btexOpened) {
throw BTeXParsingException("BTEXDOC tag has already opened")
}
if (attribs.containsKey("def"))
handler.def = attribs["def"]!!
else {
handler.cover = attribs["cover"] ?: "printout"
handler.inner = attribs["inner"] ?: "standard"
handler.papersize = attribs["papersize"] ?: "standard"
//change the "default values" of the document
handler.pageWidth = pageWidthMap[papersize]!!
handler.pageLines = pageHeightMap[papersize]!!
handler.pageHeight = pageLines * LINE_HEIGHT
doc.pageWidth = pageWidth
doc.pageHeight = pageHeight
}
handler.btexOpened = true
printdbg("BTeX document: def=${handler.def}, cover=${handler.cover}, inner=${handler.inner}, papersize=${handler.papersize}, dim=${handler.pageWidth}x${handler.pageHeight} (${handler.pageLines} lines)")
}
@OpenTag // reflective access is impossible with 'private'
fun processElemPAIR(handler: BTeXHandler, doc: BTeXDocument, theTag: String, uri: String, attribs: HashMap<String, String>) {
if (tagStack.size == 3 && tagStack.getOrNull(1) == "blocklut") {
blockLut[attribs["key"]!!] = attribs["value"]!!
}
else {
throw BTeXParsingException("<pair> used outside of <blocklut>")
}
}
@OpenTag // reflective access is impossible with 'private'
fun processElemSPAN(handler: BTeXHandler, doc: BTeXDocument, theTag: String, uri: String, attribs: HashMap<String, String>) {
attribs["span"]?.let {
spanColour = it
}
}
@OpenTag // reflective access is impossible with 'private'
fun processElemTABLEOFCONTENTS(handler: BTeXHandler, doc: BTeXDocument, theTag: String, uri: String, attribs: HashMap<String, String>) {
// TODO add post-parsing hook to the handler
}
@OpenTag // reflective access is impossible with 'private'
fun processElemBR(handler: BTeXHandler, doc: BTeXDocument, theTag: String, uri: String, attribs: HashMap<String, String>) {
}
@OpenTag // reflective access is impossible with 'private'
fun processElemNEWPAGE(handler: BTeXHandler, doc: BTeXDocument, theTag: String, uri: String, attribs: HashMap<String, String>) {
}
@OpenTag // reflective access is impossible with 'private'
fun processElemP(handler: BTeXHandler, doc: BTeXDocument, theTag: String, uri: String, attribs: HashMap<String, String>) {
}
@CloseTag // reflective access is impossible with 'private'
fun closeElemP(handler: BTeXHandler, doc: BTeXDocument, theTag: String, uri: String) {
}
@OpenTag // reflective access is impossible with 'private'
fun processElemARST(handler: BTeXHandler, doc: BTeXDocument, theTag: String, uri: String, attribs: HashMap<String, String>) {
}
@CloseTag // reflective access is impossible with 'private'
fun closeElemSPAN(handler: BTeXHandler, doc: BTeXDocument, theTag: String, uri: String) {
spanColour = null
}
}
private annotation class OpenTag
private annotation class CloseTag
}

View File

@@ -37,7 +37,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
$BULLET Terrarum Sans Bitmap
© 2017-2024 Minjae Song ("CuriousTorvald") and the contributors
© 2017-2024 CuriousTorvald (minjaesong)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -58,6 +58,25 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
$BULLET LibGDX
This software contains modified version of the LibGDX.
© 2014-2023 LibGDX Contributors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
$BULLET Joise
@@ -125,7 +144,7 @@ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Kotlin translated and modified code Copyright (C) 2016 Minjaesong (CuriousTorvald)
Kotlin translated and modified code © 2016 CuriousTorvald (minjaesong)
@@ -187,6 +206,32 @@ SOFTWARE.
$BULLET OKHsvUtil.kt contains source code originally written by Björn Ottosson
© 2021 Björn Ottosson
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Kotlin translated and modified code © 2024 CuriousTorvald (minjaesong)
$BULLET GraalVM Community Edition
GraalVM Community Edition consists of multiple modules. The software as a whole,

View File

@@ -7,6 +7,10 @@ import com.badlogic.gdx.utils.Disposable
*/
abstract class AudioBank : Disposable {
/**
* If the audio bank is a virtual instrument, set this property to `true`; if the audio bank reads audio
* sample directly from the disk, set it to `false`
*/
open val notCopyable: Boolean = false
protected val hash = System.nanoTime()

View File

@@ -67,16 +67,22 @@ class AudioProcessBuf(val inputSamplingRate: Int, val audioReadFun: (FloatArray,
private val TAPS = 4 // 2*a tap lanczos intp. Lower = greater artefacts
private val Lcache = HashMap<Long, Double>(1048576)
// private val Lcache = HashMap<Long, Double>(1048576)
fun L(x: Double): Double {
return Lcache.getOrPut(x.toBits()) { // converting double to longbits allows faster cache lookup?!
/*return Lcache.getOrPut(x.toBits()) { // converting double to longbits allows faster cache lookup?!
if (x.absoluteValue < epsilon)
1.0
else if (-TAPS <= x && x < TAPS)
(TAPS * sin(PI * x) * sin(PI * x / TAPS)) / (PI * PI * x * x)
else
0.0
}
}*/
return if (x.absoluteValue < epsilon)
1.0
else if (-TAPS <= x && x < TAPS)
(TAPS * sin(PI * x) * sin(PI * x / TAPS)) / (PI * PI * x * x)
else
0.0
}
const val MP3_CHUNK_SIZE = 1152 // 1152 for 32k-48k, 576 for 16k-24k, 384 for 8k-12k

View File

@@ -2,6 +2,7 @@ package net.torvald.terrarum.modulebasegame
import net.torvald.terrarum.*
import net.torvald.terrarum.App.printdbg
import net.torvald.terrarum.modulebasegame.audio.audiobank.InstrumentLoader
import net.torvald.terrarum.modulebasegame.imagefont.WatchFont
import net.torvald.terrarum.weather.WeatherMixer
@@ -42,6 +43,9 @@ class EntryPoint : ModuleEntryPoint() {
WeatherCodex.weatherById["titlescreen"] =
WeatherCodex.getById("generic01")?.copy(identifier = "titlescreen", windSpeed = 1f) ?: WeatherMixer.DEFAULT_WEATHER
// load virtual instruments
printdbg(this, "Loading virtual instrument 'spieluhr@41'")
InstrumentLoader.load("spieluhr", "basegame", "audio/effects/notes/spieluhr.ogg", 41)
if (App.IS_DEVELOPMENT_BUILD) {
println("[EntryPoint] Crafting Recipes: ")

View File

@@ -76,7 +76,8 @@ class AudioBankMusicBox(override var songFinishedHook: (AudioBank) -> Unit = {})
bufferR.fill(0f)
// only copy over the past and current messages
messageQueue.filter { it.tick <= tickCount }.forEach {
// use cloned version of queue to prevent concurrent modification exception
messageQueue.toMutableList().filter { it.tick <= tickCount }.forEach {
// copy over the samples
it.notes.forEach { note ->
val noteSamples = getSample(note)

View File

@@ -0,0 +1,233 @@
package net.torvald.terrarum.modulebasegame.gameactors
/**
* Created by minjaesong on 2024-04-18.
*/
interface Bachomatic {
operator fun invoke(): List<Long>
}
object PreludeInCMaj : Bachomatic {
private val TICK_DIVISOR = 10
override fun invoke() = List(16*TICK_DIVISOR) { 0L } +
prel(24,28,31,36,40) +
prel(24,26,33,38,41) +
prel(23,26,31,38,41) +
prel(24,28,31,36,40) +
prel(24,28,33,40,45) +
prel(24,26,30,33,38) +
prel(23,26,31,38,43) +
prel(23,24,28,31,36) +
prel(21,24,28,31,36) +
prel(14,21,26,30,36) +
prel(19,23,26,31,35) +
prel(19,22,28,31,37) +
prel(17,21,26,33,38) +
prel(17,20,26,29,35) +
prel(16,19,24,31,36) +
prel(16,17,21,24,29) +
prel(14,17,21,24,29) +
prel( 7,14,19,23,29) +
prel(12,16,19,24,28) +
prel(12,19,22,24,28) +
prel( 5,17,21,24,28) +
prel( 6,12,21,24,27) +
prel( 8,17,23,24,26) +
prel( 7,17,19,23,26) +
prel( 7,16,19,24,28) +
prel( 7,14,19,24,29) +
prel( 7,14,19,23,29) +
prel( 7,15,21,24,30) +
prel( 7,16,19,24,31) +
prel( 7,14,19,24,29) +
prel( 7,14,19,23,29) +
prel( 0,12,19,22,28) +
end1( 0,12,17,21,24,29,21,17,14) +
end2( 0,11,31,35,38,41,26,29,28) +
end3( 0,12,28,31,36) + List(16*TICK_DIVISOR - 5) { 0L }
private fun prel(n1: Int, n2: Int, n3: Int, n4: Int, n5: Int): List<Long> {
return toPianoRoll(
1L shl n1 to TICK_DIVISOR+2, 1L shl n2 to TICK_DIVISOR, 1L shl n3 to TICK_DIVISOR, 1L shl n4 to TICK_DIVISOR-1,
1L shl n5 to TICK_DIVISOR, 1L shl n3 to TICK_DIVISOR, 1L shl n4 to TICK_DIVISOR, 1L shl n5 to TICK_DIVISOR,
1L shl n1 to TICK_DIVISOR, 1L shl n2 to TICK_DIVISOR, 1L shl n3 to TICK_DIVISOR, 1L shl n4 to TICK_DIVISOR-1,
1L shl n5 to TICK_DIVISOR, 1L shl n3 to TICK_DIVISOR, 1L shl n4 to TICK_DIVISOR, 1L shl n5 to TICK_DIVISOR)
}
private fun end1(n1: Int, n2: Int, n3: Int, n4: Int, n5: Int, n6: Int, n7: Int, n8: Int, n9: Int): List<Long> {
return toPianoRoll(
1L shl n1 to TICK_DIVISOR+2, 1L shl n2 to TICK_DIVISOR, 1L shl n3 to TICK_DIVISOR, 1L shl n4 to TICK_DIVISOR-1,
1L shl n5 to TICK_DIVISOR, 1L shl n6 to TICK_DIVISOR, 1L shl n5 to TICK_DIVISOR, 1L shl n4 to TICK_DIVISOR,
1L shl n5 to TICK_DIVISOR, 1L shl n7 to TICK_DIVISOR, 1L shl n8 to TICK_DIVISOR, 1L shl n7 to TICK_DIVISOR-1,
1L shl n8 to TICK_DIVISOR, 1L shl n9 to TICK_DIVISOR, 1L shl n8 to TICK_DIVISOR, 1L shl n9 to TICK_DIVISOR)
}
private fun end2(n1: Int, n2: Int, n3: Int, n4: Int, n5: Int, n6: Int, n7: Int, n8: Int, n9: Int): List<Long> {
return toPianoRoll(
1L shl n1 to TICK_DIVISOR+2, 1L shl n2 to TICK_DIVISOR+1, 1L shl n3 to TICK_DIVISOR+1, 1L shl n4 to TICK_DIVISOR+1,
1L shl n5 to TICK_DIVISOR+1, 1L shl n6 to TICK_DIVISOR+2, 1L shl n5 to TICK_DIVISOR+2, 1L shl n4 to TICK_DIVISOR+2,
1L shl n5 to TICK_DIVISOR+3, 1L shl n4 to TICK_DIVISOR+3, 1L shl n3 to TICK_DIVISOR+4, 1L shl n4 to TICK_DIVISOR+4,
1L shl n7 to TICK_DIVISOR+6, 1L shl n8 to TICK_DIVISOR+8, 1L shl n9 to TICK_DIVISOR+12, 1L shl n7 to TICK_DIVISOR+24)
}
private fun end3(vararg ns: Int): List<Long> {
return ns.map { 1L shl it } // arpeggiate
}
fun toPianoRoll(vararg noteAndLen: Pair<Long, Int>): List<Long> {
val ret = MutableList<Long>(noteAndLen.sumOf { it.second }) { 0 }
var c = 0
noteAndLen.forEach { (note, len) ->
ret[c] = note
c += len
}
return ret
}
fun toPianoRoll(vararg notes: Long) = List<Long>(notes.size * TICK_DIVISOR) {
if (it % TICK_DIVISOR == 0) notes[it / TICK_DIVISOR] else 0
}
}
object PreludeInCshMaj : Bachomatic {
private val TICK_DIVISOR = 8
private val TICK_GAP = List(TICK_DIVISOR - 1) { 0L }
override fun invoke() = List(16* TICK_DIVISOR) { 0L } +
n321232(32,37,41,13,25) +
n121212(37,42,15,25) +
n121212(37,44,17,25) +
n121212(37,46,18,25) +
n121212(37,44,17,25) +
n321231(39,41,42,15,24) +
n321231(37,39,41,13,25) +
tqSSSS454321(34,36,37,39,41,20,22,24) +
p321232(15,20,24,32,44) +
p121212(20,25,34,44) +
p121212(20,27,36,44) +
p121212(20,29,37,44) +
p121212(20,27,36,44) +
p321231(22,24,25,32,43) +
p321231(20,22,24,32,44) +
tqSSSS454321(17,18,20,22,23,39,41,42) +
n321232(34,39,42,15,27) +
n121212(39,44,17,27) +
n121212(39,46,18,27) +
n121212(39,47,20,27) +
n121212(39,46,18,27) +
n321231(41,42,44,17,26) +
n321231(39,41,42,15,27) +
tqSSSS454321(36,37,39,41,42,22,24,25) +
p321232(17,22,25,34,46) +
p121212(22,27,36,46) +
p121212(22,29,37,46) +
p121212(22,30,39,46) +
p121212(22,29,37,46) +
p321231(24,25,27,36,45) +
p321231(22,24,25,34,46) +
tqQQ321231(24,26,28,44,43) +
tQQQ212321b(28,29,31,44,32,44) +
tqQQ321231(22,24,26,43,41) +
tQQQ212321(39,41,42,27,15,27) +
tqQQ321231(41,43,45,25,24) +
tQQQ212321b(45,46,48,25,13,25) +
tqQQ321231(39,41,43,24,22) +
tQQQ321231(20,22,24,44,32,44) +
/* 40 */
tqQQ321231(22,24,26,42,41) +
tQQQ212321b(26,27,29,42,37,42) +
tqQQ321231(20,22,24,41,39) +
tQQQ321231(37,39,41,25,13,25) +
tqQQ321231(39,41,43,28,27) +
tQQQ212321b(43,44,46,23,11,23) +
tqQQ321231(37,39,41,22,20) +
/* 47 */
List(16* TICK_DIVISOR) { 0L }
private fun n321232(n1: Int, n2: Int, n3: Int, p1: Int, p2: Int) =
listOf((1L shl n3) or (1L shl p1)) + TICK_GAP +
listOf( 1L shl n2) + TICK_GAP +
listOf( 1L shl n1) + TICK_GAP +
listOf( 1L shl n2) + TICK_GAP +
listOf((1L shl n3) or (1L shl p2)) + TICK_GAP +
listOf( 1L shl n2) + TICK_GAP
private fun n321231(n1: Int, n2: Int, n3: Int, p1: Int, p2: Int) =
listOf((1L shl n3) or (1L shl p1)) + TICK_GAP +
listOf( 1L shl n2) + TICK_GAP +
listOf( 1L shl n1) + TICK_GAP +
listOf( 1L shl n2) + TICK_GAP +
listOf((1L shl n3) or (1L shl p2)) + TICK_GAP +
listOf( 1L shl n1) + TICK_GAP
private fun n121212(n1: Int, n2: Int, p1: Int, p2: Int) =
listOf((1L shl n2) or (1L shl p1)) + TICK_GAP +
listOf( 1L shl n1) + TICK_GAP +
listOf( 1L shl n2) + TICK_GAP +
listOf( 1L shl n1) + TICK_GAP +
listOf((1L shl n2) or (1L shl p2)) + TICK_GAP +
listOf( 1L shl n1) + TICK_GAP
private fun tqSSSS454321(n1: Int, n2: Int, n3: Int, n4: Int, n5: Int, p1: Int, p2: Int, p3: Int) =
listOf( 1L shl n4) + TICK_GAP +
listOf( 1L shl n5) + TICK_GAP +
listOf((1L shl n4) or (1L shl p3)) + TICK_GAP +
listOf((1L shl n3) or (1L shl p2)) + TICK_GAP +
listOf((1L shl n2) or (1L shl p1)) + TICK_GAP +
listOf((1L shl n1) or (1L shl p2)) + TICK_GAP
private fun p321232(n1: Int, n2: Int, n3: Int, p1: Int, p2: Int) = n321232(n1, n2, n3, p1, p2)
private fun p321231(n1: Int, n2: Int, n3: Int, p1: Int, p2: Int) = n321231(n1, n2, n3, p1, p2)
private fun p121212(n1: Int, n2: Int, p1: Int, p2: Int) = n121212(n1, n2, p1, p2)
private fun tqQQ321231(p1: Int, p2: Int, p3: Int, n1: Int, n2: Int) =
listOf( 1L shl p3) + TICK_GAP +
listOf( 1L shl p2) + TICK_GAP +
listOf((1L shl p1) or (1L shl n1)) + TICK_GAP +
listOf( 1L shl p2) + TICK_GAP +
listOf((1L shl p3) or (1L shl n2)) + TICK_GAP +
listOf( 1L shl p1) + TICK_GAP
private fun tQQQ321231(p1: Int, p2: Int, p3: Int, n1: Int, n2: Int, n3: Int) =
listOf((1L shl p3) or (1L shl n1)) + TICK_GAP +
listOf( 1L shl p2) + TICK_GAP +
listOf((1L shl p1) or (1L shl n2)) + TICK_GAP +
listOf( 1L shl p2) + TICK_GAP +
listOf((1L shl p3) or (1L shl n3)) + TICK_GAP +
listOf( 1L shl p1) + TICK_GAP
private fun tQQQ212321b(p1: Int, p2: Int, p3: Int, n1: Int, n2: Int, n3: Int) =
listOf((1L shl p2) or (1L shl n1)) + TICK_GAP +
listOf( 1L shl p1) + TICK_GAP +
listOf((1L shl p2) or (1L shl n2)) + TICK_GAP +
listOf( 1L shl p3) + TICK_GAP +
listOf((1L shl p2) or (1L shl n3)) + TICK_GAP +
listOf( 1L shl(p1-1)) + TICK_GAP
private fun tQQQ212321(p1: Int, p2: Int, p3: Int, n1: Int, n2: Int, n3: Int) =
listOf((1L shl p2) or (1L shl n1)) + TICK_GAP +
listOf( 1L shl p1) + TICK_GAP +
listOf((1L shl p2) or (1L shl n2)) + TICK_GAP +
listOf( 1L shl p3) + TICK_GAP +
listOf((1L shl p2) or (1L shl n3)) + TICK_GAP +
listOf( 1L shl p1) + TICK_GAP
}

View File

@@ -1,15 +1,12 @@
package net.torvald.terrarum.modulebasegame.gameactors
import com.badlogic.gdx.graphics.Color
import com.jme3.math.FastMath
import com.jme3.math.FastMath.DEG_TO_RAD
import net.torvald.colourutil.HUSLColorConverter
import net.torvald.colourutil.OKHsv
import net.torvald.colourutil.toColor
import net.torvald.colourutil.tosRGB
import net.torvald.random.HQRNG
import net.torvald.terrarum.App
import net.torvald.terrarum.App.printdbg
import net.torvald.terrarum.CommonResourcePool
import net.torvald.terrarum.INGAME
import net.torvald.terrarum.ModMgr
@@ -22,7 +19,6 @@ import net.torvald.terrarum.modulebasegame.audio.audiobank.AudioBankMusicBox
import net.torvald.terrarum.modulebasegame.gameitems.FixtureItemBase
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
import org.dyn4j.geometry.Vector2
import kotlin.math.pow
/**
* Created by minjaesong on 2024-04-15.
@@ -92,99 +88,18 @@ class FixtureMechanicalTines : Electric {
}
private fun findSetBits(num: Long): List<Int> {
val result = mutableListOf<Int>()
for (i in 0 until 61) {
if (num and (1L shl i) != 0L) {
result.add(i)
}
}
return result
}
companion object {
@Transient private val TICK_DIVISOR = 10
@Transient val testNotes = PreludeInCshMaj()
@Transient val testNotes = List(16*TICK_DIVISOR) { 0L } +
prel(24,28,31,36,40) +
prel(24,26,33,38,41) +
prel(23,26,31,38,41) +
prel(24,28,31,36,40) +
prel(24,28,33,40,45) +
prel(24,26,30,33,38) +
prel(23,26,31,38,43) +
prel(23,24,28,31,36) +
prel(21,24,28,31,36) +
prel(14,21,26,30,36) +
prel(19,23,26,31,35) +
prel(19,22,28,31,37) +
prel(17,21,26,33,38) +
prel(17,20,26,29,35) +
prel(16,19,24,31,36) +
prel(16,17,21,24,29) +
prel(14,17,21,24,29) +
prel( 7,14,19,23,29) +
prel(12,16,19,24,28) +
prel(12,19,22,24,28) +
prel( 5,17,21,24,28) +
prel( 6,12,21,24,27) +
prel( 8,17,23,24,26) +
prel( 7,17,19,23,26) +
prel( 7,16,19,24,28) +
prel( 7,14,19,24,29) +
prel( 7,14,19,23,29) +
prel( 7,15,21,24,30) +
prel( 7,16,19,24,31) +
prel( 7,14,19,24,29) +
prel( 7,14,19,23,29) +
prel( 0,12,19,22,28) +
end1( 0,12,17,21,24,29,21,17,14) +
end2( 0,11,31,35,38,41,26,29,28) +
end3( 0,12,28,31,36) + List(16*TICK_DIVISOR - 5) { 0L }
private fun prel(n1: Int, n2: Int, n3: Int, n4: Int, n5: Int): List<Long> {
return toPianoRoll(
1L shl n1 to TICK_DIVISOR+2, 1L shl n2 to TICK_DIVISOR, 1L shl n3 to TICK_DIVISOR, 1L shl n4 to TICK_DIVISOR-1,
1L shl n5 to TICK_DIVISOR, 1L shl n3 to TICK_DIVISOR, 1L shl n4 to TICK_DIVISOR, 1L shl n5 to TICK_DIVISOR,
1L shl n1 to TICK_DIVISOR, 1L shl n2 to TICK_DIVISOR, 1L shl n3 to TICK_DIVISOR, 1L shl n4 to TICK_DIVISOR-1,
1L shl n5 to TICK_DIVISOR, 1L shl n3 to TICK_DIVISOR, 1L shl n4 to TICK_DIVISOR, 1L shl n5 to TICK_DIVISOR)
}
private fun end1(n1: Int, n2: Int, n3: Int, n4: Int, n5: Int, n6: Int, n7: Int, n8: Int, n9: Int): List<Long> {
return toPianoRoll(
1L shl n1 to TICK_DIVISOR+2, 1L shl n2 to TICK_DIVISOR, 1L shl n3 to TICK_DIVISOR, 1L shl n4 to TICK_DIVISOR-1,
1L shl n5 to TICK_DIVISOR, 1L shl n6 to TICK_DIVISOR, 1L shl n5 to TICK_DIVISOR, 1L shl n4 to TICK_DIVISOR,
1L shl n5 to TICK_DIVISOR, 1L shl n7 to TICK_DIVISOR, 1L shl n8 to TICK_DIVISOR, 1L shl n7 to TICK_DIVISOR-1,
1L shl n8 to TICK_DIVISOR, 1L shl n9 to TICK_DIVISOR, 1L shl n8 to TICK_DIVISOR, 1L shl n9 to TICK_DIVISOR)
}
private fun end2(n1: Int, n2: Int, n3: Int, n4: Int, n5: Int, n6: Int, n7: Int, n8: Int, n9: Int): List<Long> {
return toPianoRoll(
1L shl n1 to TICK_DIVISOR+2, 1L shl n2 to TICK_DIVISOR+1, 1L shl n3 to TICK_DIVISOR+1, 1L shl n4 to TICK_DIVISOR+1,
1L shl n5 to TICK_DIVISOR+1, 1L shl n6 to TICK_DIVISOR+2, 1L shl n5 to TICK_DIVISOR+2, 1L shl n4 to TICK_DIVISOR+2,
1L shl n5 to TICK_DIVISOR+3, 1L shl n4 to TICK_DIVISOR+3, 1L shl n3 to TICK_DIVISOR+4, 1L shl n4 to TICK_DIVISOR+4,
1L shl n7 to TICK_DIVISOR+6, 1L shl n8 to TICK_DIVISOR+8, 1L shl n9 to TICK_DIVISOR+12, 1L shl n7 to TICK_DIVISOR+24)
}
private fun end3(vararg ns: Int): List<Long> {
return ns.map { 1L shl it } // arpeggiate
}
private fun toPianoRoll(vararg noteAndLen: Pair<Long, Int>): List<Long> {
val ret = MutableList<Long>(noteAndLen.sumOf { it.second }) { 0 }
var c = 0
noteAndLen.forEach { (note, len) ->
ret[c] = note
c += len
fun findSetBits(num: Long): List<Int> {
val result = mutableListOf<Int>()
for (i in 0 until 61) {
if (num and (1L shl i) != 0L) {
result.add(i)
}
}
return ret
return result
}
private fun toPianoRoll(vararg notes: Long) = List<Long>(notes.size * TICK_DIVISOR) {
if (it % TICK_DIVISOR == 0) notes[it / TICK_DIVISOR] else 0
}
}
}

View File

@@ -7,6 +7,7 @@ import net.torvald.terrarum.langpack.Lang
import net.torvald.terrarum.modulebasegame.gameitems.FixtureItemBase
import net.torvald.terrarum.modulebasegame.ui.UICrafting
import net.torvald.terrarum.modulebasegame.ui.UIEngravingTextSign
import net.torvald.terrarum.modulebasegame.ui.UIMusicalWorkbench
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
/**
@@ -86,3 +87,28 @@ class FixtureEngravingWorkbench : FixtureBase, CraftingStation {
}
}
/**
* Created by minjaesong on 2024-04-22.
*/
class FixtureMusicalWorkbench : FixtureBase, CraftingStation {
@Transient override val tags = listOf("musicbox")
constructor() : super(
BlockBox(BlockBox.NO_COLLISION, 2, 2),
nameFun = { Lang["ITEM_MUSICAL_WORKBENCH"] },
mainUI = UIMusicalWorkbench()
) {
val itemImage = FixtureItemBase.getItemImageFromSingleImage("basegame", "sprites/fixtures/musical_workbench.tga")
density = BlockCodex[Block.PLANK_NORMAL].density.toDouble()
setHitboxDimension(itemImage.texture.width, itemImage.texture.height, 0, 0)
makeNewSprite(TextureRegionPack(itemImage.texture, 32, 32)).let {
it.setRowsAndFrames(1,1)
}
actorValue[AVKey.BASEMASS] = 40.0
}
}

View File

@@ -0,0 +1,62 @@
package net.torvald.terrarum.modulebasegame.ui
import com.badlogic.gdx.graphics.OrthographicCamera
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import net.torvald.terrarum.modulebasegame.gameactors.FixtureMechanicalTines.Companion.findSetBits
import net.torvald.terrarum.ui.UICanvas
/**
* Created by minjaesong on 2024-04-22.
*/
class UIMusicalWorkbench : UICanvas(
toggleKeyLiteral = "control_key_inventory",
toggleButtonLiteral = "control_gamepad_start"
) {
data class ComposerRow(
var notes: Long,
var len: Int,
var arp: Int // 0: none, 1: up, 2: down
) {
fun toPunchedNotes(): List<Long> {
val ret = MutableList<Long>(len) { 0L }
when (arp) {
0 -> { ret[0] = notes }
1, 2 -> {
val arpNotes = findSetBits(notes).let { if (arp == 2) it.reversed() else it }
for (i in 0 until minOf(len, arpNotes.size)) {
ret[i] = 1L shl arpNotes[i]
}
}
}
return ret
}
}
private val rowBuf = ArrayList<ComposerRow>()
override var width: Int
get() = TODO("Not yet implemented")
set(value) {}
override var height: Int
get() = TODO("Not yet implemented")
set(value) {}
override fun updateImpl(delta: Float) {
TODO("Not yet implemented")
}
override fun renderImpl(frameDelta: Float, batch: SpriteBatch, camera: OrthographicCamera) {
TODO("Not yet implemented")
}
override fun dispose() {
TODO("Not yet implemented")
}
}

View File

@@ -1,5 +1,6 @@
package net.torvald.terrarum.tests
import net.torvald.btex.BTeXParser
import org.xml.sax.Attributes
import org.xml.sax.HandlerBase
import org.xml.sax.helpers.DefaultHandler
@@ -16,7 +17,7 @@ fun main() {
val csiG = "\u001B[32m"
val csi0 = "\u001B[m"
val tex = """<btex cover="hardcover" inner="standard" papersize="standard">
val tex = """<btexdoc cover="hardcover" inner="standard" papersize="standard">
<cover>
<title>The Way to Mastery of Lorem Ipsum<br />Or, How To Write and Publish a Book</title>
<author>Terran Publishing</author>
@@ -55,11 +56,11 @@ fun main() {
<chapter>Writing Book using Computer</chapter>
<p>Writing book using a computer requires a use of the Book Typesetting Engine Extended, or <BTeX /></p>
<p>Writing book using a computer requires a use of the Book Typesetting Engine Extended, or <btex /></p>
<section>Full Control of the Shape</section>
<p>With <BTeX /> you can fully control how your publishing would look like, from a pile of papers that
<p>With <btex /> you can fully control how your publishing would look like, from a pile of papers that
look like they have been typed out using typewriter, a pile of papers but a fully-featured printouts that
have illustrations in it, to a fully-featured hardcover book.</p>
@@ -70,31 +71,10 @@ fun main() {
while Hardcover is considered bound and two pages are presented to the readers.</p>
</manuscript>
</btex>
</btexdoc>
"""
val parser = SAXParserFactory.newDefaultInstance().newSAXParser()
val stream: InputStream = ByteArrayInputStream(tex.encodeToByteArray())
val hb = object : DefaultHandler() {
override fun startElement(uri: String, localName: String, qName: String, attributes: Attributes) {
println(" $csiG$qName$csi0 ${(0 until attributes.length).map { "${attributes.getQName(it)}=${attributes.getValue(it)}" }}")
}
override fun endElement(uri: String, localName: String, qName: String) {
println("$csiG/$qName$csi0")
}
override fun characters(ch: CharArray, start: Int, length: Int) {
val str = String(ch.sliceArray(start until start+length)).replace('\n',' ').replace(Regex(" +"), " ").trim()
if (str.isNotBlank()) {
println("$str|")
}
}
}
parser.parse(stream, hb)
val doku = BTeXParser(tex)
}

View File

@@ -713,7 +713,7 @@ internal object BlocksDrawer {
}
private var _tilesBufferAsTex: Texture = Texture(1, 1, Pixmap.Format.RGBA8888)
private val occlusionIntensity = 0.22222222f // too low value and dark-coloured walls won't darken enough
private val occlusionIntensity = 0.5f//0.22222222f // too low value and dark-coloured walls won't darken enough
private fun renderUsingBuffer(mode: Int, projectionMatrix: Matrix4, drawGlow: Boolean, drawEmissive: Boolean) {
//Gdx.gl.glClearColor(.094f, .094f, .094f, 0f)

Binary file not shown.