diff --git a/src/net/torvald/terrarum/gameactors/ai/AILuaAPI.kt b/src/net/torvald/terrarum/gameactors/ai/AILuaAPI.kt index 47f172bb7..d0df66530 100644 --- a/src/net/torvald/terrarum/gameactors/ai/AILuaAPI.kt +++ b/src/net/torvald/terrarum/gameactors/ai/AILuaAPI.kt @@ -84,14 +84,6 @@ internal class AILuaAPI(g: Globals, actor: ActorWithSprite) { return t } - fun Double.toLua() = LuaValue.valueOf(this) - fun Int.toLua() = LuaValue.valueOf(this) - fun String.toLua() = LuaValue.valueOf(this) - fun Double?.toLua() = if (this == null) LuaValue.NIL else this.toLua() - fun Int?.toLua() = if (this == null) LuaValue.NIL else this.toLua() - fun String?.toLua() = if (this == null) LuaValue.NIL else this.toLua() - fun Boolean.toInt() = if (this) 1 else 0 - operator fun LuaTable.set(index: Int, value: Int) { this[index] = value.toLua() } } @@ -405,3 +397,11 @@ internal class AILuaAPI(g: Globals, actor: ActorWithSprite) { } } } + +fun Double.toLua() = LuaValue.valueOf(this) +fun Int.toLua() = LuaValue.valueOf(this) +fun String.toLua() = LuaValue.valueOf(this) +fun Double?.toLua() = if (this == null) LuaValue.NIL else this.toLua() +fun Int?.toLua() = if (this == null) LuaValue.NIL else this.toLua() +fun String?.toLua() = if (this == null) LuaValue.NIL else this.toLua() +fun Boolean.toInt() = if (this) 1 else 0 diff --git a/src/net/torvald/terrarum/mapdrawer/FeaturesDrawer.kt b/src/net/torvald/terrarum/mapdrawer/FeaturesDrawer.kt index 52a5ea4dd..608b07923 100644 --- a/src/net/torvald/terrarum/mapdrawer/FeaturesDrawer.kt +++ b/src/net/torvald/terrarum/mapdrawer/FeaturesDrawer.kt @@ -18,7 +18,7 @@ object FeaturesDrawer { private val ENV_COLTEMP_LOWEST = 5500 private val ENV_COLTEMP_HIGHEST = 7500 - val ENV_COLTEMP_NOON = 6500 // 6500 == sRGB White == untouched! + val ENV_COLTEMP_NOON = 6500 // 6500 == sRGB White; do not touch! var colTemp: Int = 0 private set @@ -39,6 +39,10 @@ object FeaturesDrawer { fun render(gc: GameContainer, g: Graphics) { } + /** + * A colour filter used to provide effect that makes whole screen look warmer/cooler, + * usually targeted for the environmental temperature (desert/winterland), hence the name. + */ fun drawEnvOverlay(g: Graphics) { val onscreen_tiles_max = FastMath.ceil(Terrarum.HEIGHT * Terrarum.WIDTH / FastMath.sqr(TILE_SIZE.toFloat())) * 2 val onscreen_tiles_cap = onscreen_tiles_max / 4f @@ -53,7 +57,6 @@ object FeaturesDrawer { blendMul() g.color = ColourTemp(colTemp) - //g.color = getColourFromMap(3022) g.fillRect( MapCamera.x * zoom, MapCamera.y * zoom, diff --git a/src/net/torvald/terrarum/virtualcomputer/peripheral/PeripheralVideoCard.kt b/src/net/torvald/terrarum/virtualcomputer/peripheral/PeripheralVideoCard.kt new file mode 100644 index 000000000..8f67099ac --- /dev/null +++ b/src/net/torvald/terrarum/virtualcomputer/peripheral/PeripheralVideoCard.kt @@ -0,0 +1,288 @@ +package net.torvald.terrarum.virtualcomputer.peripheral + +import net.torvald.terrarum.gameactors.DecodeTapestry +import net.torvald.terrarum.gameactors.ai.toLua +import net.torvald.terrarum.getPixel +import org.luaj.vm2.Globals +import org.luaj.vm2.LuaTable +import org.luaj.vm2.LuaValue +import org.luaj.vm2.lib.OneArgFunction +import org.luaj.vm2.lib.ThreeArgFunction +import org.luaj.vm2.lib.TwoArgFunction +import org.luaj.vm2.lib.ZeroArgFunction +import org.newdawn.slick.* +import java.util.* + +/** + * Created by SKYHi14 on 2017-02-08. + */ +class PeripheralVideoCard(val globals: Globals, val termW: Int = 40, val termH: Int = 25) : Peripheral(globals, "ppu") { + companion object { + val blockW = 8 + val blockH = 8 + + /** + * Converts consecutive lua table indexed from 1 as IntArray. + * The lua table must not contain any nils in the sequence. + */ + fun LuaTable.toIntArray(): IntArray { + val arr = IntArray(this.keyCount()) + var k = 1 + while (true) { + if (this[k].isnil()) break + + arr[k - 1] = this[k].checkint() + k += 1 + } + + return arr + } + } + + val width = termW * blockW + val height = termH * blockH + + val vram = VRAM(width, height, 64) + + var fontRom = SpriteSheet("./assets/graphics/fonts/milky.tga", blockW, blockH) + + val CLUT = vram.CLUT + val coloursCount = CLUT.size + + override fun loadLib() { + super.loadLib() + globals["ppu"]["setColor"] = SetColor(this) + globals["ppu"]["getColor"] = GetColor(this) + globals["ppu"]["emitChar"] = EmitChar(this) + } + + fun render(g: Graphics) { + g.drawImage(vram.background.image, 0f, 0f) + vram.sprites.forEach { + if (it.isBackground) { + val spriteImage = it.data.image.getFlippedCopy(it.hFlip, it.vFlip) + spriteImage.rotate(90f * it.rotation) + g.drawImage(spriteImage, it.xpos.toFloat(), it.ypos.toFloat()) + } + } + g.drawImage(vram.foreground.image, 0f, 0f) + vram.sprites.forEach { + if (!it.isBackground) { + val spriteImage = it.data.image.getFlippedCopy(it.hFlip, it.vFlip) + spriteImage.rotate(90f * it.rotation) + g.drawImage(spriteImage, it.xpos.toFloat(), it.ypos.toFloat()) + } + } + } + + private var currentColour = 49 // white + fun getColor() = currentColour + fun setColor(value: Int) { currentColour = value } + + fun drawChar(c: Char, x: Int, y: Int, col: Int = currentColour) { + val glyph = fontRom.getSubImage(c.toInt() % 16, c.toInt() / 16) + val color = CLUT[col] + + // software render + for (gy in 0..blockH) { + for (gx in 0..blockW) { + val glyAlpha = glyph.getPixel(gx, gy)[3] + + if (glyAlpha > 0) { + vram.foreground.setRGBA(x * blockW + gx, y * blockH + gy, color.red, color.green, color.blue, 255) + } + } + } + } + + + fun clearBackground() { + for (i in 0..width * height - 1) { + vram.background.rgba[i] = if (i % 4 == 3) 0xFF.toByte() else 0x00.toByte() + } + } + + fun clearForeground() { + for (i in 0..width * height - 1) { + vram.foreground.rgba[i] = if (i % 4 == 3) 0xFF.toByte() else 0x00.toByte() + } + } + + fun clearAll() { + for (i in 0..width * height - 1) { + vram.background.rgba[i] = if (i % 4 == 3) 0xFF.toByte() else 0x00.toByte() + vram.foreground.rgba[i] = if (i % 4 == 3) 0xFF.toByte() else 0x00.toByte() + } + } + + fun getSprite(index: Int) = vram.sprites[index] + + + + fun setTextRom(data: Array) { + TODO("Not implemented") + } + + fun resetTextRom() { + fontRom = SpriteSheet("./assets/graphics/fonts/milky.tga", blockW, blockH) + } + + + class SetColor(val videoCard: PeripheralVideoCard) : OneArgFunction() { + override fun call(arg: LuaValue): LuaValue { + videoCard.setColor(arg.checkint()) + return LuaValue.NONE + } + } + class GetColor(val videoCard: PeripheralVideoCard) : ZeroArgFunction() { + override fun call(): LuaValue { + return videoCard.getColor().toLua() + } + } + class EmitChar(val videoCard: PeripheralVideoCard) : ThreeArgFunction() { + /** emitChar(char, x, y) */ + override fun call(arg1: LuaValue, arg2: LuaValue, arg3: LuaValue): LuaValue { + videoCard.drawChar(arg1.checkint().toChar(), arg2.checkint(), arg3.checkint()) + return LuaValue.NONE + } + } + + ///////////// + // Sprites // + ///////////// + + fun composeSpriteObject(spriteIndex: Int) : LuaValue { + val sprite = vram.sprites[spriteIndex] + val t = LuaTable() + + t["getColFromPal"] = SpriteGetColFromPal(sprite) + t["setPixel"] = SpriteSetPixel(sprite) + t["setPalSet"] = SpriteSetPaletteSet(sprite) + t["setLine"] = SpriteSetLine(sprite) + t["setAll"] = SpriteSetAll(sprite) + + return t + } + + private class SpriteGetColFromPal(val sprite: VSprite) : OneArgFunction() { + override fun call(arg: LuaValue): LuaValue { + return when (arg.checkint()) { + 0 -> sprite.pal0.toLua() + 1 -> sprite.pal1.toLua() + 2 -> sprite.pal2.toLua() + 3 -> sprite.pal3.toLua() + else -> throw IndexOutOfBoundsException("Palette size: 4, input: ${arg.checkint()}") + } + } + } + + private class SpriteSetPixel(val sprite: VSprite) : ThreeArgFunction() { + override fun call(arg1: LuaValue, arg2: LuaValue, arg3: LuaValue): LuaValue { + sprite.setPixel(arg1.checkint(), arg2.checkint(), arg3.checkint()) + return LuaValue.NONE + } + } + + private class SpriteSetPaletteSet(val sprite: VSprite) : OneArgFunction() { + override fun call(arg: LuaValue): LuaValue { + sprite.setPaletteSet(arg(1).checkint(), arg(2).checkint(), arg(3).checkint(), arg(4).checkint()) + return LuaValue.NONE + } + } + + private class SpriteSetLine(val sprite: VSprite) : TwoArgFunction() { + override fun call(arg1: LuaValue, arg2: LuaValue): LuaValue { + sprite.setLine(arg1.checkint(), arg2.checktable().toIntArray()) + return LuaValue.NONE + } + } + + private class SpriteSetAll(val sprite: VSprite) : OneArgFunction() { + override fun call(arg: LuaValue): LuaValue { + sprite.setAll(arg.checktable().toIntArray()) + return LuaValue.NONE + } + } +} + +class VRAM(pxlWidth: Int, pxlHeight: Int, nSprites: Int) { + val sprites = Array(nSprites, { VSprite() }) + + val background = ImageBuffer(pxlWidth, pxlHeight) + val foreground = ImageBuffer(pxlWidth, pxlHeight) // text mode glyphs rendered here + + var transparentKey: Int = 15 // black + + val CLUT = DecodeTapestry.colourIndices64 + + + fun setBackgroundPixel(x: Int, y: Int, color: Int) { + val col = CLUT[color] + background.setRGBA(x, y, col.red, col.green, col.blue, 255) + } + + fun setForegroundPixel(x: Int, y: Int, color: Int) { + val col = CLUT[color] + background.setRGBA(x, y, col.red, col.green, col.blue, if (color == transparentKey) 0 else 255) + } +} + +class VSprite { + private val width = 8 + private val height = 8 + + val CLUT = DecodeTapestry.colourIndices64 + val data = ImageBuffer(width, height) + + var pal0 = 15 // black + var pal1 = 56 // light cyan + var pal2 = 19 // magenta + var pal3 = 49 // white + + var transparentKey = 15 // black + + var xpos = 0 + var ypos = 0 + + var hFlip = false + var vFlip = false + var rotation = 0 + + var isBackground = false + var isVisible = false + + fun setPaletteSet(col0: Int, col1: Int, col2: Int, col3: Int) { + pal0 = col0 + pal1 = col1 + pal2 = col2 + pal3 = col3 + } + + fun getColourFromPalette(swatchNumber: Int): Color { + val clutIndex = when (swatchNumber) { + 0 -> pal0 + 1 -> pal1 + 2 -> pal2 + 3 -> pal3 + else -> throw IndexOutOfBoundsException("Palette size: 4, input: $swatchNumber") + } + return CLUT[clutIndex] + } + + fun setPixel(x: Int, y: Int, color: Int) { + val col = getColourFromPalette(color) + data.setRGBA(x, y, col.red, col.green, col.blue, if (color == transparentKey) 0 else 255) + } + + fun setLine(y: Int, rowData: IntArray) { + for (i in 0..width) { + setPixel(i, y, rowData[i]) + } + } + + fun setAll(data: IntArray) { + for (i in 0..width * height) { + setPixel(i % width, i / width, data[i]) + } + } +} \ No newline at end of file diff --git a/src/net/torvald/terrarum/virtualcomputer/terminal/GraphicsTerminal.kt b/src/net/torvald/terrarum/virtualcomputer/terminal/GraphicsTerminal.kt new file mode 100644 index 000000000..8031362aa --- /dev/null +++ b/src/net/torvald/terrarum/virtualcomputer/terminal/GraphicsTerminal.kt @@ -0,0 +1,179 @@ +package net.torvald.terrarum.virtualcomputer.terminal + +import net.torvald.terrarum.blendMul +import net.torvald.terrarum.virtualcomputer.computer.BaseTerrarumComputer +import net.torvald.terrarum.virtualcomputer.peripheral.PeripheralVideoCard +import org.newdawn.slick.Color +import org.newdawn.slick.GameContainer +import org.newdawn.slick.Graphics +import org.newdawn.slick.Image + +/** + * Created by SKYHi14 on 2017-02-08. + */ +class GraphicsTerminal( + private val host: BaseTerrarumComputer, val videoCard: PeripheralVideoCard +) : Terminal { + override val width = videoCard.termW + override val height = videoCard.termH + override val coloursCount = videoCard.coloursCount + override var cursorX = 0 + override var cursorY = 0 + override var cursorBlink = true + + override var backColour = 15 // black + override var foreColour = 48 // bright grey + + override var lastInputByte = -1 + + override fun getColor(index: Int) = videoCard.CLUT[index] + + override val displayW = videoCard.width //+ 2 * borderSize + override val displayH = videoCard.height //+ 2 * borderSize + + private val videoScreen = Image(videoCard.width, videoCard.height) + + override fun printChars(s: String) { + TODO("not implemented") + } + + override fun update(gc: GameContainer, delta: Int) { + wrap() + } + + // copied from SimpleTextTerminal + private fun wrap() { + // wrap cursor + if (cursorX < 0 && cursorY <= 0) { + setCursor(0, 0) + } + else if (cursorX >= width) { + setCursor(0, cursorY + 1) + } + else if (cursorX < 0) { + setCursor(width - 1, cursorY - 1) + } + // auto scroll up + if (cursorY >= height) { + scroll() + } + } + + override fun render(gc: GameContainer, g: Graphics) { + videoCard.render(videoScreen.graphics) + g.drawImage(videoScreen.getScaledCopy(2f), 0f, 0f) + } + + override fun keyPressed(key: Int, c: Char) { + TODO("not implemented") + } + + override fun writeChars(s: String) { + TODO("not implemented") + } + + /** Unlike lua function, this one in Zero-based. */ + override fun setCursor(x: Int, y: Int) { + cursorX = x + cursorY = y + } + + override fun openInput(echo: Boolean) { + TODO("not implemented") + } + + override fun emitChar(bufferChar: Int, x: Int, y: Int) { + TODO("not implemented") + } + + override fun closeInputKey(keyFromUI: Int): Int { + TODO("not implemented") + } + + override fun closeInputString(): String { + TODO("not implemented") + } + + override var lastStreamInput: String? = null + override var lastKeyPress: Int? = null + + override fun emitChar(c: Char, x: Int, y: Int) { + TODO("not implemented") + } + + override fun printChar(c: Char) { + TODO("not implemented") + } + + override fun emitString(s: String, x: Int, y: Int) { + TODO("not implemented") + } + + override fun printString(s: String, x: Int, y: Int) { + TODO("not implemented") + } + + override fun writeString(s: String, x: Int, y: Int) { + TODO("not implemented") + } + + override fun clear() { + videoCard.clearAll() + } + + override fun clearLine() { + TODO("not implemented") + } + + override fun newLine() { + TODO("not implemented") + } + + override fun scroll(amount: Int) { + TODO("not implemented") + } + + override fun setColour(back: Int, fore: Int) { + TODO("not implemented") + } + + override fun resetColour() { + TODO("not implemented") + } + + /** // copied from SimpleTextTerminal + * @param duration: milliseconds + * @param freg: Frequency (float) + */ + override fun emitTone(duration: Int, freq: Double) { + host.clearBeepQueue() + host.enqueueBeep(duration, freq) + } + + // copied from SimpleTextTerminal + /** for "emitTone code" on modern BIOS. */ + override fun bell(pattern: String) { + host.clearBeepQueue() + + val freq: Double = + if (host.luaJ_globals["computer"]["bellpitch"].isnil()) + 1000.0 + else + host.luaJ_globals["computer"]["bellpitch"].checkdouble() + + for (c in pattern) { + when (c) { + '.' -> { host.enqueueBeep(80, freq); host.enqueueBeep(50, 0.0) } + '-' -> { host.enqueueBeep(200, freq); host.enqueueBeep(50, 0.0) } + '=' -> { host.enqueueBeep(500, freq); host.enqueueBeep(50, 0.0) } + ' ' -> { host.enqueueBeep(200, 0.0) } + ',' -> { host.enqueueBeep(50, 0.0) } + else -> throw IllegalArgumentException("Unacceptable pattern: $c (from '$pattern')") + } + } + } + + override fun getKeyPress(): Int? { + TODO("not implemented") + } +} \ No newline at end of file diff --git a/src/net/torvald/terrarum/virtualcomputer/terminal/SimpleTextTerminal.kt b/src/net/torvald/terrarum/virtualcomputer/terminal/SimpleTextTerminal.kt index 75e1767ad..bc5bd089a 100644 --- a/src/net/torvald/terrarum/virtualcomputer/terminal/SimpleTextTerminal.kt +++ b/src/net/torvald/terrarum/virtualcomputer/terminal/SimpleTextTerminal.kt @@ -3,6 +3,7 @@ package net.torvald.terrarum.virtualcomputer.terminal import net.torvald.aa.AAFrame import net.torvald.aa.ColouredFastFont import net.torvald.terrarum.* +import net.torvald.terrarum.gameactors.DecodeTapestry import net.torvald.terrarum.gameactors.abs import net.torvald.terrarum.gamecontroller.Key import net.torvald.terrarum.virtualcomputer.computer.BaseTerrarumComputer @@ -15,7 +16,7 @@ import java.nio.ByteBuffer import java.util.* /** - * Default text terminal, four text colours (black, grey, lgrey, white). + * Default text terminal. * * Created by minjaesong on 16-09-07. */ @@ -30,36 +31,7 @@ open class SimpleTextTerminal( * Terminals must support AT LEAST 4 colours. * Color index 0 must be default background, index 3 must be default foreground */ - open protected val colours = if (colour) - // TODO colours updated; also update the documentation - arrayOf( - Color(0x000000), // 0 black - Color(0xffffff), // 1 white - Color(0x666666), // 2 dim grey - Color(0xcccccc), // 3 bright grey - - Color(0xffee00), // 4 yellow - Color(0xee6600), // 5 orange - Color(0xee0000), // 6 red - Color(0xee22aa), // 7 magenta - - Color(0x442277), // 8 purple - Color(0x3322ff), // 9 blue - Color(0x44aaff), //10 cyan - Color(0x55ff00), //11 lime - - Color(0x339900), //12 green - Color(0x335533), //13 dark green - Color(0x553333), //14 brown - Color(0xaa6633) //15 tan - ) // THESE ARE THE STANDARD - else - arrayOf( - Color(0x000000), // 0 black - Color(0xffffff), // 1 white - Color(0x666666), // 2 dim grey - Color(0xcccccc) // 3 bright grey - ) // THESE ARE THE STANDARD + open protected val colours = if (colour) CLUT else CLUT.copyOfRange(0, 3) val phosphor = if (colour) WHITE7500 else phosphorColour open val colourScreen = if (colour) Color(8, 8, 8) else Color(19, 19, 19) @@ -460,6 +432,8 @@ open class SimpleTextTerminal( ASCII_DC4, ASCII_DLE ) + + val CLUT = DecodeTapestry.colourIndices16 } }