diff --git a/assets/mods/basegame/books/btex.xml b/assets/mods/basegame/books/btex.xml new file mode 100644 index 000000000..484a5b8c2 --- /dev/null +++ b/assets/mods/basegame/books/btex.xml @@ -0,0 +1,49 @@ + + + The Way to Mastery of Lorem Ipsum<br />Or, How To Write and Publish a Book + Terran Publishing + Test Edition + + + + + + + What Is a Book + +

This example book is designed to give you the exampe of the Book Language.

+ +
What Really Is a Book
+ +

A book is a collection of texts printed in a special way that allows them to be read easily, with enumerable pages and insertion of other helpful resources, such as illustrations and hyperlinks.

+ + + + + +

this page is intentionally left blank

+
+
+ + Writing Book Using Pen and Papers + +

If you open a book on a writing table, you will be welcomed with a toolbar used to put other book elements, such as chapters, sections.

+ + Writing Book Using Typewriter + +

Typewriters can only write single style of font, therefore chapters and sections are not available.

+ + Writing Book using Computer + +

Writing book using a computer requires a use of the Book Typesetting Engine Extended, or

+ +
Full Control of the Shape
+ +

With 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.

+ +

This style is controlled using the cover attribute on the root tag, with following values: typewriter, printout, hardcover

+ +

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.

+ +
+
diff --git a/assets/mods/basegame/books/examination.btex b/assets/mods/basegame/books/examination.btex new file mode 100644 index 000000000..26e1e9b4b --- /dev/null +++ b/assets/mods/basegame/books/examination.btex @@ -0,0 +1,60 @@ +\usestyle{examination} + +\begin{blocklut} +\0\{basegame:0} +\1\{basegame:32} +\100\{wall@basegame:32} +\end{blocklut} + + +\begin{examination} + +\section{Introduction} + +\begin{tiles}{5x4} +\terr{ +0,1,1,1,0, +1,0,0,0,1, +1,0,0,0,1, +0,1,1,1,0 +} +\wall{ +0,1,1,1,0, +1,1,1,1,1, +1,1,1,1,1, +0,1,1,1,0 +} +\end{tiles} + +This is the first page of the Examination. + +Examination is a way to instruct the player certain gameplay concepts. + +The system is popularised by the Minecraft Mod called ``Create'', of which it was called Pondering. + +Terrarum borrows the concept and implements it using its own language. + +\section{Language} + +\begin{tiles}{5x4} +\terr{ +0,1,1,1,0, +1,0,1,0,1, +1,0,1,0,1, +0,1,1,1,0 +} +\wall{ +0,1,1,1,0, +1,1,1,1,1, +1,1,1,1,1, +0,1,1,1,0 +} +\end{tiles} + +Examination relies on the script written in \BTeX. + +\BTeX\ is a \TeX-inspired language to write texts to be displayed onto the game, one example being a book. + +Examination is a special format of the "book" that operates on the same book system. + +\end{examination} diff --git a/assets/mods/basegame/books/examination.xml b/assets/mods/basegame/books/examination.xml new file mode 100644 index 000000000..71e76202c --- /dev/null +++ b/assets/mods/basegame/books/examination.xml @@ -0,0 +1,57 @@ + + + + + + + +
Introduction
+ + + + 0,1,1,1,0, + 1,0,0,0,1, + 1,0,0,0,1, + 0,1,1,1,0 + + + 0,1,1,1,0, + 1,1,1,1,1, + 1,1,1,1,1, + 0,1,1,1,0 + + + +

This is the first page of the Examination.

+ +

Examination is a way to instruct the player certain gameplay concepts.

+ +

The system is popularised by the Minecraft Mod called “Create”, of which it was called Pondering.

+ +

Terrarum borrows the concept and implements it using its own language.

+ +
Language
+ + + + 0,1,1,1,0, + 1,0,1,0,1, + 1,0,1,0,1, + 0,1,1,1,0 + + + 0,1,1,1,0, + 1,1,1,1,1, + 1,1,1,1,1, + 0,1,1,1,0 + + + +

Examination relies on the script written in .

+ +

is a XML-like markup language to write texts to be displayed onto the game, one example being a book.

+ +

Examination is a special format of the “book” that operates on the same book system.

+ +
+
diff --git a/src/net/torvald/btex/BTeXDocument.kt b/src/net/torvald/btex/BTeXDocument.kt new file mode 100644 index 000000000..4fadfcc73 --- /dev/null +++ b/src/net/torvald/btex/BTeXDocument.kt @@ -0,0 +1,93 @@ +package net.torvald.terrarum.btex + +import com.badlogic.gdx.graphics.Color +import com.badlogic.gdx.graphics.g2d.BitmapFont +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 + +/** + * Created by minjaesong on 2023-10-28. + */ +class BTeXDocument { + var context = "tome" // tome (cover=hardcover), sheets (cover=typewriter or cover=printout), examination (def=examination) + var font = "default" // default or typewriter + var inner = "standard" + var papersize = "standard" + + var pageWidth = 420 + var pageHeight = 25 * 24 + + companion object { + val DEFAULT_PAGE_BACK = Color(0xe1e1d7ff.toInt()) + val DEFAULT_PAGE_FORE = Color(0x131311ff) + } + + private val pages = ArrayList() + + + + fun addNewPage(back: Color = DEFAULT_PAGE_BACK) { + pages.add(BTeXPage(back, pageWidth, pageHeight)) + } + + fun appendDrawCall(drawCall: BTeXDrawCall) { + pages.last().appendDrawCall(drawCall) + } + + fun render(batch: SpriteBatch, page: Int, x: Int, y: Int) { + pages[page].render(batch, x, y) + } +} + +class BTeXPage( + val back: Color, + val width: Int, + val height: Int, +) { + private val drawCalls = ArrayList() + + fun appendDrawCall(drawCall: BTeXDrawCall) { + drawCalls.add(drawCall) + } + + fun render(batch: SpriteBatch, x: Int, y: Int) { + batch.color = back + Toolkit.fillArea(batch, x, y, width, height) + drawCalls.forEach { + it.draw(batch, x, y) + } + } +} + +class BTeXDrawCall( + val posX: Int, + val posY: Int, + val theme: String, + val colour: Color, + val font: BitmapFont, + val text: String? = null, + val texture: TextureRegion? = null, +) { + + fun draw(batch: SpriteBatch, x: Int, y: Int) { + val px = (posX + x).toFloat() + val py = (posY + y).toFloat() + + if (theme == "code") { + // todo draw code background + } + + batch.color = colour + + if (text != null && texture == null) { + font.draw(batch, text, px, py) + } + else if (text == null && texture != null) { + batch.draw(texture, px, py) + } + else throw Error("Text and Texture are both non-null") + } + +} \ No newline at end of file diff --git a/src/net/torvald/btex/BTeXParser.kt b/src/net/torvald/btex/BTeXParser.kt new file mode 100644 index 000000000..a72df9cb3 --- /dev/null +++ b/src/net/torvald/btex/BTeXParser.kt @@ -0,0 +1,159 @@ +package net.torvald.btex + +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.helpers.DefaultHandler +import java.io.* +import java.util.* +import javax.xml.parsers.SAXParserFactory + +/** + * Created by minjaesong on 2023-10-28. + */ +object BTeXParser { + + operator fun invoke(file: FileHandle) = invoke(file.file()) + + operator fun invoke(file: File): BTeXDocument { + val doc = BTeXDocument() + val parser = SAXParserFactory.newDefaultInstance().newSAXParser() + val stream = FileInputStream(file) + parser.parse(stream, BTeXHandler(doc)) + return doc + } + + private class BTeXHandler(val doc: BTeXDocument) : DefaultHandler() { + private val DEFAULT_FONTCOL = Color(0x222222ff) + private val LINE_HEIGHT = 24 + + 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 val blockLut = HashMap() + + private val tagStack = ArrayList() // index zero should be "btex" + + + private var spanColour: String? = null + + + private var typeX = 0 + private var typeY = 0 + + 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") + tagStack.add(tag) + + val attribs = HashMap().also { + it.putAll((0 until attributes.length).map { attributes.getQName(it) to attributes.getValue(it) }) + } + + val mode = tagStack.getOrNull(1) + + 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 + } + } + "pair" -> { + if (tagStack.size == 3 && mode == "blocklut") { + blockLut[attribs["key"]!!] = attribs["value"]!! + } + else { + throw BTeXParsingException(" used outside of ") + } + } + "span" -> { + attribs["span"]?.let { + spanColour = it + } + } + + } + } + + override fun endElement(uri: String, localName: String, qName: String) { + tagStack.removeLast() + + when (qName) { + "span" -> { + spanColour = null + } + } + } + + 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) + } + + private fun advanceCursorPre(w: Int, h: Int) { + if (typeX + w > pageWidth) { + typeY += LINE_HEIGHT + typeX = 0 + } + + if (typeY + h > pageHeight) { + typeX = 0 + typeY = 0 + doc.addNewPage() + } + } + + private fun advanceCursorPost(w: Int, h: Int) { + typeX += w + typeY += h + } + + + private fun getFont() = when (cover) { + "typewriter" -> TODO() + else -> App.fontGame + } + + private fun getSpanColour(): Color = spanColourMap.getOrDefault(spanColour, DEFAULT_FONTCOL) + + private val spanColourMap = hashMapOf( + "grey" to Color.LIGHT_GRAY + ) + + private val pageWidthMap = hashMapOf( + "standard" to 420 + ) + private val pageHeightMap = hashMapOf( + "standard" to 25 + ) + } + +} + + +class BTeXParsingException(s: String) : RuntimeException(s) { + +} diff --git a/src/net/torvald/terrarum/tests/BTeXTest.kt b/src/net/torvald/terrarum/tests/BTeXTest.kt new file mode 100644 index 000000000..b20061c3d --- /dev/null +++ b/src/net/torvald/terrarum/tests/BTeXTest.kt @@ -0,0 +1,100 @@ +package net.torvald.terrarum.tests + +import org.xml.sax.Attributes +import org.xml.sax.HandlerBase +import org.xml.sax.helpers.DefaultHandler +import java.io.ByteArrayInputStream +import java.io.InputStream +import javax.xml.parsers.SAXParserFactory + + +/** + * Created by minjaesong on 2023-10-28. + */ +fun main() { + val csiR = "\u001B[31m" + val csiG = "\u001B[32m" + val csi0 = "\u001B[m" + + val tex = """ + + The Way to Mastery of Lorem Ipsum<br />Or, How To Write and Publish a Book + Terran Publishing + Test Edition + + + + + + + What Is a Book + +

This example book is designed to give you the exampe of the Book Language.

+ +
What Really Is a Book
+ +

A book is a collection of texts printed in a special way that allows them to be read easily, with + enumerable pages and insertion of other helpful resources, such as illustrations and hyperlinks.

+ + + + + +

this page is intentionally left blank

+
+
+ + Writing Book Using Pen and Papers + +

If you open a book on a writing table, you will be welcomed with a toolbar used to put other book + elements, such as chapters, sections.

+ + Writing Book Using Typewriter + +

Typewriters can only write single style of font, therefore chapters and sections are not available.

+ + Writing Book using Computer + +

Writing book using a computer requires a use of the Book Typesetting Engine Extended, or

+ +
Full Control of the Shape
+ +

With 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.

+ +

This style is controlled using the cover attribute on the root tag, with following + values: typewriter, printout, hardcover

+ +

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.

+ +
+
+ + +""" + + 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) +} \ No newline at end of file