diff --git a/src/net/torvald/terrarum/StateGraphicComputerTest.kt b/src/net/torvald/terrarum/StateGraphicComputerTest.kt index 55b5d09c1..2c8785106 100644 --- a/src/net/torvald/terrarum/StateGraphicComputerTest.kt +++ b/src/net/torvald/terrarum/StateGraphicComputerTest.kt @@ -1,6 +1,7 @@ package net.torvald.terrarum import net.torvald.random.HQRNG +import net.torvald.terrarum.Terrarum.UPDATE_DELTA import net.torvald.terrarum.gameactors.roundInt import net.torvald.terrarum.virtualcomputer.computer.TerrarumComputer import net.torvald.terrarum.virtualcomputer.peripheral.PeripheralVideoCard @@ -20,7 +21,7 @@ class StateGraphicComputerTest : BasicGameState() { val monitor = GraphicsTerminal(computer) init { - val videocard = PeripheralVideoCard() + val videocard = PeripheralVideoCard(computer) monitor.attachVideoCard(videocard) computer.attachTerminal(monitor) @@ -31,7 +32,11 @@ class StateGraphicComputerTest : BasicGameState() { val vcard = (computer.getPeripheral("ppu") as PeripheralVideoCard).vram // it's a-me, Mario! - /*(0..3).forEach { vcard.sprites[it].setPaletteSet(64,33,12,62) } + /*(0..3).forEach { + vcard.sprites[it].setPaletteSet(64,33,12,62) + vcard.sprites[it].isVisible = true + vcard.sprites[it].drawWide = true + } vcard.sprites[0].setAll(intArrayOf( 0,0,0,0,0,1,1,1, @@ -76,6 +81,8 @@ class StateGraphicComputerTest : BasicGameState() { } override fun update(container: GameContainer, game: StateBasedGame?, delta: Int) { + UPDATE_DELTA = delta + Terrarum.appgc.setTitle("VT — F: ${container.fps}" + " — M: ${Terrarum.memInUse}M / ${Terrarum.memTotal}M / ${Terrarum.memXmx}M" + " ${Random().nextInt(100)}") @@ -83,7 +90,11 @@ class StateGraphicComputerTest : BasicGameState() { computer.update(container, delta) - + /*val vcard = (computer.getPeripheral("ppu") as PeripheralVideoCard).vram + vcard.sprites[0].setPos(20, 20) + vcard.sprites[1].setPos(36, 20) + vcard.sprites[2].setPos(20, 28) + vcard.sprites[3].setPos(36, 28)*/ } override fun getID() = Terrarum.STATE_ID_TEST_TTY diff --git a/src/net/torvald/terrarum/StateInGame.kt b/src/net/torvald/terrarum/StateInGame.kt index 354322c85..44d846347 100644 --- a/src/net/torvald/terrarum/StateInGame.kt +++ b/src/net/torvald/terrarum/StateInGame.kt @@ -2,6 +2,7 @@ package net.torvald.terrarum import net.torvald.imagefont.GameFontBase import net.torvald.random.HQRNG +import net.torvald.terrarum.Terrarum.UPDATE_DELTA import net.torvald.terrarum.audio.AudioResourceLibrary import net.torvald.terrarum.concurrent.ThreadParallel import net.torvald.terrarum.console.* @@ -91,8 +92,6 @@ class StateInGame : BasicGameState() { val KEY_LIGHTMAP_RENDER = Key.F7 val KEY_LIGHTMAP_SMOOTH = Key.F8 - var UPDATE_DELTA: Int = 0 - // UI aliases val uiAliases = HashMap() private val UI_PIE_MENU = "uiPieMenu" diff --git a/src/net/torvald/terrarum/StateNoiseTexGen.kt b/src/net/torvald/terrarum/StateNoiseTexGen.kt index b6738f919..e74d13028 100644 --- a/src/net/torvald/terrarum/StateNoiseTexGen.kt +++ b/src/net/torvald/terrarum/StateNoiseTexGen.kt @@ -2,7 +2,7 @@ package net.torvald.terrarum import com.sudoplay.joise.Joise import com.sudoplay.joise.module.* -import net.torvald.terrarum.Terrarum.Companion.STATE_ID_TOOL_NOISEGEN +import net.torvald.terrarum.Terrarum.STATE_ID_TOOL_NOISEGEN import net.torvald.terrarum.concurrent.ThreadParallel import net.torvald.terrarum.gameactors.roundInt import org.newdawn.slick.* diff --git a/src/net/torvald/terrarum/StateShaderTest.kt b/src/net/torvald/terrarum/StateShaderTest.kt index 72aa635ab..9c2dd9922 100644 --- a/src/net/torvald/terrarum/StateShaderTest.kt +++ b/src/net/torvald/terrarum/StateShaderTest.kt @@ -1,6 +1,5 @@ package net.torvald.terrarum -import net.torvald.terrarum.Terrarum.Companion.STATE_ID_TEST_SHADER import org.lwjgl.opengl.* import org.newdawn.slick.GameContainer import org.newdawn.slick.Graphics @@ -13,6 +12,7 @@ import org.lwjgl.opengl.GL11 import org.lwjgl.opengl.ARBShaderObjects import com.sun.xml.internal.ws.streaming.XMLStreamReaderUtil.close import jdk.nashorn.internal.runtime.ScriptingFunctions.readLine +import net.torvald.terrarum.Terrarum.STATE_ID_TEST_SHADER import net.torvald.terrarum.gameworld.fmod import org.newdawn.slick.Color import org.newdawn.slick.opengl.TextureImpl diff --git a/src/net/torvald/terrarum/Terrarum.kt b/src/net/torvald/terrarum/Terrarum.kt index e37bb706d..ba5364e06 100644 --- a/src/net/torvald/terrarum/Terrarum.kt +++ b/src/net/torvald/terrarum/Terrarum.kt @@ -24,11 +24,150 @@ import java.util.logging.Level import java.util.logging.Logger import java.util.logging.SimpleFormatter +const val GAME_NAME = "Terrarum" + /** * Created by minjaesong on 15-12-30. */ -class Terrarum @Throws(SlickException::class) -constructor(gamename: String) : StateBasedGame(gamename) { +object Terrarum : StateBasedGame(GAME_NAME) { + + + val sysLang: String + get() { + val lan = System.getProperty("user.language") + val country = System.getProperty("user.country") + return lan + country + } + + /** + * To be used with physics simulator + */ + val TARGET_FPS = 50 + + /** + * To be used with render, to achieve smooth frame drawing + + * TARGET_INTERNAL_FPS > TARGET_FPS for smooth frame drawing + + * Must choose a value so that (1000 / VAL) is still integer + */ + val TARGET_INTERNAL_FPS = 100 + + lateinit var appgc: AppGameContainer + + var WIDTH = 1072 + var HEIGHT = 742 // IMAX ratio + var VSYNC = true + val VSYNC_TRIGGER_THRESHOLD = 56 + val HALFW: Int + get() = WIDTH.ushr(1) + val HALFH: Int + get() = HEIGHT.ushr(1) + + var gameStarted = false + + lateinit var ingame: StateInGame + lateinit var gameConfig: GameConfig + + val OSName = System.getProperty("os.name") + val OSVersion = System.getProperty("os.version") + lateinit var OperationSystem: String // all caps "WINDOWS, "OSX", "LINUX", "SOLARIS", "UNKNOWN" + private set + val isWin81: Boolean + get() = OperationSystem == "WINDOWS" && OSVersion.toDouble() >= 8.1 + lateinit var defaultDir: String + private set + lateinit var defaultSaveDir: String + private set + + val memInUse: Long + get() = (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) shr 20 + val memTotal: Long + get() = Runtime.getRuntime().totalMemory() shr 20 + val memXmx: Long + get() = Runtime.getRuntime().maxMemory() shr 20 + + lateinit var environment: RunningEnvironment + + private val localeSimple = arrayOf("de", "en", "es", "it") + var gameLocale = "####" // lateinit placeholder + set(value) { + if (localeSimple.contains(value.substring(0..1))) + field = value.substring(0..1) + else + field = value + + if (fontGame != null) (fontGame as GameFontImpl).reload() + } + + var fontGame: Font? = null + private set + lateinit var fontSmallNumbers: Font + private set + + var joypadLabelStart: Char = 0xE000.toChar() // lateinit + var joypadLableSelect: Char = 0xE000.toChar() // lateinit + var joypadLabelNinA: Char = 0xE000.toChar() // lateinit TODO + var joypadLabelNinB: Char = 0xE000.toChar() // lateinit TODO + var joypadLabelNinX: Char = 0xE000.toChar() // lateinit TODO + var joypadLabelNinY: Char = 0xE000.toChar() // lateinit TODO + var joypadLabelNinL: Char = 0xE000.toChar() // lateinit TODO + var joypadLabelNinR: Char = 0xE000.toChar() // lateinit TODO + var joypadLabelNinZL: Char = 0xE000.toChar() // lateinit TODO + var joypadLabelNinZR: Char = 0xE000.toChar() // lateinit TODO + val joypadLabelLEFT = 0xE068.toChar() + val joypadLabelDOWN = 0xE069.toChar() + val joypadLabelUP = 0xE06A.toChar() + val joypadLabelRIGHT = 0xE06B.toChar() + + // 0x0 - 0xF: Game-related + // 0x10 - 0x1F: Config + // 0x100 and onward: unit tests for dev + val STATE_ID_SPLASH = 0x0 + val STATE_ID_HOME = 0x1 + val STATE_ID_GAME = 0x3 + val STATE_ID_CONFIG_CALIBRATE = 0x11 + + val STATE_ID_TEST_FONT = 0x100 + val STATE_ID_TEST_LIGHTNING_GFX = 0x101 + val STATE_ID_TEST_TTY = 0x102 + val STATE_ID_TEST_BLUR = 0x103 + val STATE_ID_TEST_SHADER = 0x104 + + val STATE_ID_TOOL_NOISEGEN = 0x200 + + var controller: org.lwjgl.input.Controller? = null + private set + val CONTROLLER_DEADZONE = 0.1f + + /** Available CPU threads */ + val THREADS = Runtime.getRuntime().availableProcessors() + + /** + * If the game is multithreading. + * True if: + * + * THREADS >= 2 and config "multithread" is true + */ + val MULTITHREAD: Boolean + get() = THREADS >= 2 && getConfigBoolean("multithread") + + private lateinit var configDir: String + + /** + * 0xAA_BB_XXXX + * AA: Major version + * BB: Minor version + * XXXX: Revision (Repository commits) + * + * e.g. 0x02010034 can be translated as 2.1.52 + */ + const val VERSION_RAW = 0x000200E1 + const val VERSION_STRING: String = + "${VERSION_RAW.ushr(24)}.${VERSION_RAW.and(0xFF0000).ushr(16)}.${VERSION_RAW.and(0xFFFF)}" + const val NAME = "Terrarum" + + var UPDATE_DELTA: Int = 0 // these properties goes into the GameContainer @@ -83,6 +222,158 @@ constructor(gamename: String) : StateBasedGame(gamename) { } } + private fun getDefaultDirectory() { + val OS = System.getProperty("os.name").toUpperCase() + if (OS.contains("WIN")) { + OperationSystem = "WINDOWS" + defaultDir = System.getenv("APPDATA") + "/terrarum" + } + else if (OS.contains("OS X")) { + OperationSystem = "OSX" + defaultDir = System.getProperty("user.home") + "/Library/Application Support/terrarum" + } + else if (OS.contains("NUX") || OS.contains("NIX")) { + OperationSystem = "LINUX" + defaultDir = System.getProperty("user.home") + "/.terrarum" + } + else if (OS.contains("SUNOS")) { + OperationSystem = "SOLARIS" + defaultDir = System.getProperty("user.home") + "/.terrarum" + } + else { + OperationSystem = "UNKNOWN" + defaultDir = System.getProperty("user.home") + "/.terrarum" + } + + defaultSaveDir = defaultDir + "/Saves" + configDir = defaultDir + "/config.json" + + println("[Terrarum] os.name = $OSName") + println("[Terrarum] os.version = $OSVersion") + } + + private fun createDirs() { + val dirs = arrayOf(File(defaultSaveDir)) + dirs.forEach { if (!it.exists()) it.mkdirs() } + } + + @Throws(IOException::class) + private fun createConfigJson() { + val configFile = File(configDir) + + if (!configFile.exists() || configFile.length() == 0L) { + JsonWriter.writeToFile(DefaultConfig.fetch(), configDir) + } + } + + private fun readConfigJson(): Boolean { + try { + // read from disk and build config from it + val jsonObject = JsonFetcher(configDir) + + // make config + jsonObject.entrySet().forEach { entry -> gameConfig[entry.key] = entry.value } + + return true + } + catch (e: IOException) { + // write default config to game dir. Call this method again to read config from it. + try { + createConfigJson() + } + catch (e1: IOException) { + e.printStackTrace() + } + + return false + } + + } + + /** + * Return config from config set. If the config does not exist, default value will be returned. + * @param key + * * + * @return Config from config set or default config if it does not exist. + * * + * @throws NullPointerException if the specified config simply does not exist. + */ + fun getConfigInt(key: String): Int { + val cfg = getConfigMaster(key) + if (cfg is JsonPrimitive) + return cfg.asInt + else + return cfg as Int + } + + /** + * Return config from config set. If the config does not exist, default value will be returned. + * @param key + * * + * @return Config from config set or default config if it does not exist. + * * + * @throws NullPointerException if the specified config simply does not exist. + */ + fun getConfigString(key: String): String { + val cfg = getConfigMaster(key) + if (cfg is JsonPrimitive) + return cfg.asString + else + return cfg as String + } + + /** + * Return config from config set. If the config does not exist, default value will be returned. + * @param key + * * + * @return Config from config set or default config if it does not exist. + * * + * @throws NullPointerException if the specified config simply does not exist. + */ + fun getConfigBoolean(key: String): Boolean { + val cfg = getConfigMaster(key) + if (cfg is JsonPrimitive) + return cfg.asBoolean + else + return cfg as Boolean + } + + fun getConfigIntArray(key: String): IntArray { + val cfg = getConfigMaster(key) + if (cfg is JsonArray) { + val jsonArray = cfg.asJsonArray + return IntArray(jsonArray.size(), { i -> jsonArray[i].asInt }) + } + else + return cfg as IntArray + } + + private fun getConfigMaster(key: String): Any { + var cfg: Any? = null + try { + cfg = gameConfig[key.toLowerCase()]!! + } + catch (e: NullPointerException) { + try { + cfg = DefaultConfig.fetch()[key.toLowerCase()] + } + catch (e1: NullPointerException) { + e.printStackTrace() + } + } + return cfg!! + } + + val currentSaveDir: File + get() { + val file = File(defaultSaveDir + "/test") + + // failsafe? + if (!file.exists()) file.mkdir() + + return file // TODO TEST CODE + } + @Throws(SlickException::class) override fun initStatesList(gc: GameContainer) { gc.input.enableKeyRepeat() @@ -104,7 +395,6 @@ constructor(gamename: String) : StateBasedGame(gamename) { fontSmallNumbers = TinyAlphNum() - // search for real controller // exclude controllers with name "Mouse", "keyboard" val notControllerRegex = Regex("mouse|keyboard") @@ -154,337 +444,48 @@ constructor(gamename: String) : StateBasedGame(gamename) { throw Error("Please add or un-comment addState statements") } } - - companion object { - - val sysLang: String - get() { - val lan = System.getProperty("user.language") - val country = System.getProperty("user.country") - return lan + country - } - - /** - * To be used with physics simulator - */ - val TARGET_FPS = 50 - - /** - * To be used with render, to achieve smooth frame drawing - - * TARGET_INTERNAL_FPS > TARGET_FPS for smooth frame drawing - - * Must choose a value so that (1000 / VAL) is still integer - */ - val TARGET_INTERNAL_FPS = 100 - - lateinit var appgc: AppGameContainer - - var WIDTH = 1072 - var HEIGHT = 742 // IMAX ratio - var VSYNC = true - val VSYNC_TRIGGER_THRESHOLD = 56 - val HALFW: Int - get() = WIDTH.ushr(1) - val HALFH: Int - get() = HEIGHT.ushr(1) - - var gameStarted = false - - lateinit var ingame: StateInGame - lateinit var gameConfig: GameConfig - - val OSName = System.getProperty("os.name") - val OSVersion = System.getProperty("os.version") - lateinit var OperationSystem: String // all caps "WINDOWS, "OSX", "LINUX", "SOLARIS", "UNKNOWN" - private set - val isWin81: Boolean - get() = OperationSystem == "WINDOWS" && OSVersion.toDouble() >= 8.1 - lateinit var defaultDir: String - private set - lateinit var defaultSaveDir: String - private set - - val memInUse: Long - get() = (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) shr 20 - val memTotal: Long - get() = Runtime.getRuntime().totalMemory() shr 20 - val memXmx: Long - get() = Runtime.getRuntime().maxMemory() shr 20 - - lateinit var environment: RunningEnvironment - - private val localeSimple = arrayOf("de", "en", "es", "it") - var gameLocale = "####" // lateinit placeholder - set(value) { - if (localeSimple.contains(value.substring(0..1))) - field = value.substring(0..1) - else - field = value - - if (fontGame != null) (fontGame as GameFontImpl).reload() - } - - var fontGame: Font? = null - private set - lateinit var fontSmallNumbers: Font - private set - - var joypadLabelStart: Char = 0xE000.toChar() // lateinit - var joypadLableSelect:Char = 0xE000.toChar() // lateinit - var joypadLabelNinA: Char = 0xE000.toChar() // lateinit TODO - var joypadLabelNinB: Char = 0xE000.toChar() // lateinit TODO - var joypadLabelNinX: Char = 0xE000.toChar() // lateinit TODO - var joypadLabelNinY: Char = 0xE000.toChar() // lateinit TODO - var joypadLabelNinL: Char = 0xE000.toChar() // lateinit TODO - var joypadLabelNinR: Char = 0xE000.toChar() // lateinit TODO - var joypadLabelNinZL: Char = 0xE000.toChar() // lateinit TODO - var joypadLabelNinZR: Char = 0xE000.toChar() // lateinit TODO - val joypadLabelLEFT = 0xE068.toChar() - val joypadLabelDOWN = 0xE069.toChar() - val joypadLabelUP = 0xE06A.toChar() - val joypadLabelRIGHT = 0xE06B.toChar() - - // 0x0 - 0xF: Game-related - // 0x10 - 0x1F: Config - // 0x100 and onward: unit tests for dev - val STATE_ID_SPLASH = 0x0 - val STATE_ID_HOME = 0x1 - val STATE_ID_GAME = 0x3 - val STATE_ID_CONFIG_CALIBRATE = 0x11 - - val STATE_ID_TEST_FONT = 0x100 - val STATE_ID_TEST_LIGHTNING_GFX = 0x101 - val STATE_ID_TEST_TTY = 0x102 - val STATE_ID_TEST_BLUR = 0x103 - val STATE_ID_TEST_SHADER = 0x104 - - val STATE_ID_TOOL_NOISEGEN = 0x200 - - var controller: org.lwjgl.input.Controller? = null - private set - val CONTROLLER_DEADZONE = 0.1f - - /** Available CPU threads */ - val THREADS = Runtime.getRuntime().availableProcessors() - - /** - * If the game is multithreading. - * True if: - * - * THREADS >= 2 and config "multithread" is true - */ - val MULTITHREAD: Boolean - get() = THREADS >= 2 && getConfigBoolean("multithread") - - private lateinit var configDir: String - - /** - * 0xAA_BB_XXXX - * AA: Major version - * BB: Minor version - * XXXX: Revision (Repository commits) - * - * e.g. 0x02010034 can be translated as 2.1.52 - */ - const val VERSION_RAW = 0x000200E1 - const val VERSION_STRING: String = - "${VERSION_RAW.ushr(24)}.${VERSION_RAW.and(0xFF0000).ushr(16)}.${VERSION_RAW.and(0xFFFF)}" - const val NAME = "Terrarum" - - fun main(args: Array) { - System.setProperty("java.library.path", "lib") - System.setProperty("org.lwjgl.librarypath", File("lib").absolutePath) - - try { - appgc = AppGameContainer(Terrarum(NAME)) - appgc.setDisplayMode(WIDTH, HEIGHT, false) - - appgc.setTargetFrameRate(TARGET_INTERNAL_FPS) - appgc.setVSync(VSYNC) - appgc.setMaximumLogicUpdateInterval(1000 / TARGET_INTERNAL_FPS) // 10 ms - appgc.setMinimumLogicUpdateInterval(1000 / TARGET_INTERNAL_FPS - 1) // 9 ms - - appgc.setMultiSample(0) - - appgc.setShowFPS(false) - - // game will run normally even if it is not focused - appgc.setUpdateOnlyWhenVisible(false) - appgc.alwaysRender = true - - appgc.start() - } - catch (ex: Exception) { - val logger = Logger.getLogger(Terrarum::class.java.name) - val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH-mm-ss") - val calendar = Calendar.getInstance() - val filepath = "$defaultDir/crashlog-${dateFormat.format(calendar.time)}.txt" - val fileHandler = FileHandler(filepath) - logger.addHandler(fileHandler) - - val formatter = SimpleFormatter() - fileHandler.formatter = formatter - - //logger.info() - println("The game has crashed!") - println("Crash log were saved to $filepath.") - println("================================================================================") - logger.log(Level.SEVERE, null, ex) - } - - } - - private fun getDefaultDirectory() { - val OS = System.getProperty("os.name").toUpperCase() - if (OS.contains("WIN")) { - OperationSystem = "WINDOWS" - defaultDir = System.getenv("APPDATA") + "/terrarum" - } - else if (OS.contains("OS X")) { - OperationSystem = "OSX" - defaultDir = System.getProperty("user.home") + "/Library/Application Support/terrarum" - } - else if (OS.contains("NUX") || OS.contains("NIX")) { - OperationSystem = "LINUX" - defaultDir = System.getProperty("user.home") + "/.terrarum" - } - else if (OS.contains("SUNOS")) { - OperationSystem = "SOLARIS" - defaultDir = System.getProperty("user.home") + "/.terrarum" - } - else { - OperationSystem = "UNKNOWN" - defaultDir = System.getProperty("user.home") + "/.terrarum" - } - - defaultSaveDir = defaultDir + "/Saves" - configDir = defaultDir + "/config.json" - - println("[Terrarum] os.name = $OSName") - println("[Terrarum] os.version = $OSVersion") - } - - private fun createDirs() { - val dirs = arrayOf(File(defaultSaveDir)) - dirs.forEach { if (!it.exists()) it.mkdirs() } - } - - @Throws(IOException::class) - private fun createConfigJson() { - val configFile = File(configDir) - - if (!configFile.exists() || configFile.length() == 0L) { - JsonWriter.writeToFile(DefaultConfig.fetch(), configDir) - } - } - - private fun readConfigJson(): Boolean { - try { - // read from disk and build config from it - val jsonObject = JsonFetcher(configDir) - - // make config - jsonObject.entrySet().forEach { entry -> gameConfig[entry.key] = entry.value } - - return true - } - catch (e: IOException) { - // write default config to game dir. Call this method again to read config from it. - try { - createConfigJson() - } - catch (e1: IOException) { - e.printStackTrace() - } - - return false - } - - } - - /** - * Return config from config set. If the config does not exist, default value will be returned. - * @param key - * * - * @return Config from config set or default config if it does not exist. - * * - * @throws NullPointerException if the specified config simply does not exist. - */ - fun getConfigInt(key: String): Int { - val cfg = getConfigMaster(key) - if (cfg is JsonPrimitive) - return cfg.asInt - else - return cfg as Int - } - - /** - * Return config from config set. If the config does not exist, default value will be returned. - * @param key - * * - * @return Config from config set or default config if it does not exist. - * * - * @throws NullPointerException if the specified config simply does not exist. - */ - fun getConfigString(key: String): String { - val cfg = getConfigMaster(key) - if (cfg is JsonPrimitive) - return cfg.asString - else - return cfg as String - } - - /** - * Return config from config set. If the config does not exist, default value will be returned. - * @param key - * * - * @return Config from config set or default config if it does not exist. - * * - * @throws NullPointerException if the specified config simply does not exist. - */ - fun getConfigBoolean(key: String): Boolean { - val cfg = getConfigMaster(key) - if (cfg is JsonPrimitive) - return cfg.asBoolean - else - return cfg as Boolean - } - - fun getConfigIntArray(key: String): IntArray { - val cfg = getConfigMaster(key) - if (cfg is JsonArray) { - val jsonArray = cfg.asJsonArray - return IntArray(jsonArray.size(), { i -> jsonArray[i].asInt }) - } - else - return cfg as IntArray - } - - private fun getConfigMaster(key: String): Any { - var cfg: Any? = null - try { cfg = gameConfig[key.toLowerCase()]!! } - catch (e: NullPointerException) { - try { cfg = DefaultConfig.fetch()[key.toLowerCase()] } - catch (e1: NullPointerException) { e.printStackTrace() } - } - return cfg!! - } - - val currentSaveDir: File - get() { - val file = File(defaultSaveDir + "/test") - - // failsafe? - if (!file.exists()) file.mkdir() - - return file // TODO TEST CODE - } - } } fun main(args: Array) { - Terrarum.main(args) + System.setProperty("java.library.path", "lib") + System.setProperty("org.lwjgl.librarypath", File("lib").absolutePath) + + try { + Terrarum.appgc = AppGameContainer(Terrarum) + Terrarum.appgc.setDisplayMode(Terrarum.WIDTH, Terrarum.HEIGHT, false) + + Terrarum.appgc.setTargetFrameRate(Terrarum.TARGET_INTERNAL_FPS) + Terrarum.appgc.setVSync(Terrarum.VSYNC) + Terrarum.appgc.setMaximumLogicUpdateInterval(1000 / Terrarum.TARGET_INTERNAL_FPS) // 10 ms + Terrarum.appgc.setMinimumLogicUpdateInterval(1000 / Terrarum.TARGET_INTERNAL_FPS - 1) // 9 ms + + Terrarum.appgc.setMultiSample(0) + + Terrarum.appgc.setShowFPS(false) + + // game will run normally even if it is not focused + Terrarum.appgc.setUpdateOnlyWhenVisible(false) + Terrarum.appgc.alwaysRender = true + + Terrarum.appgc.start() + } + catch (ex: Exception) { + val logger = Logger.getLogger(Terrarum::class.java.name) + val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH-mm-ss") + val calendar = Calendar.getInstance() + val filepath = "${Terrarum.defaultDir}/crashlog-${dateFormat.format(calendar.time)}.txt" + val fileHandler = FileHandler(filepath) + logger.addHandler(fileHandler) + + val formatter = SimpleFormatter() + fileHandler.formatter = formatter + + //logger.info() + println("The game has crashed!") + println("Crash log were saved to $filepath.") + println("================================================================================") + logger.log(Level.SEVERE, null, ex) + } } /////////////////////////////////// @@ -566,6 +567,7 @@ fun Texture.getPixel(x: Int, y: Int): IntArray { ) } } + /** @return Intarray(R, G, B, A) */ fun Image.getPixel(x: Int, y: Int) = this.texture.getPixel(x, y) diff --git a/src/net/torvald/terrarum/gameactors/ActorWithSprite.kt b/src/net/torvald/terrarum/gameactors/ActorWithSprite.kt index f76a2e1f9..4950b9844 100644 --- a/src/net/torvald/terrarum/gameactors/ActorWithSprite.kt +++ b/src/net/torvald/terrarum/gameactors/ActorWithSprite.kt @@ -235,7 +235,7 @@ open class ActorWithSprite(renderOrder: ActorOrder, val immobileBody: Boolean = protected val gameContainer: GameContainer get() = Terrarum.appgc protected val updateDelta: Int - get() = Terrarum.ingame.UPDATE_DELTA + get() = Terrarum.UPDATE_DELTA /** * true: This actor had just made collision diff --git a/src/net/torvald/terrarum/gameactors/ParticleBase.kt b/src/net/torvald/terrarum/gameactors/ParticleBase.kt index 662426c63..4ee1bc6bb 100644 --- a/src/net/torvald/terrarum/gameactors/ParticleBase.kt +++ b/src/net/torvald/terrarum/gameactors/ParticleBase.kt @@ -20,7 +20,7 @@ open class ParticleBase(renderOrder: ActorOrder, maxLifeTime: Int? = null) : Run /** Will NOT actually delete from the CircularArray */ @Volatile var flagDespawn = false - override fun run() = update(Terrarum.appgc, Terrarum.ingame.UPDATE_DELTA) + override fun run() = update(Terrarum.appgc, Terrarum.UPDATE_DELTA) var isNoSubjectToGrav = false var dragCoefficient = 3.0 diff --git a/src/net/torvald/terrarum/virtualcomputer/assets/lua/ROMLIB.lua b/src/net/torvald/terrarum/virtualcomputer/assets/lua/ROMLIB.lua index f344bac09..bb7ea5666 100644 --- a/src/net/torvald/terrarum/virtualcomputer/assets/lua/ROMLIB.lua +++ b/src/net/torvald/terrarum/virtualcomputer/assets/lua/ROMLIB.lua @@ -75,40 +75,48 @@ end]] -- DELETED: use _G.input.isKeyDown(keycode) --- --- ---input.readLine = _G.__scanforline__ - ---[[io.__openfile__ = "stdin" +io.__openfile__ = "stdin" io.stdin = "stdin" io.stdout = "stdout" io.stderr = "stderr" -io.open = fs.open]] +io.open = fs.open ---[[io.input = function(luafile) +io.input = function(luafile) io.__openfile__ = luafile end io.read = function(option) if io.__openfile__ == "stdin" then - return _G.__scanforline__() + local input = "" + local inkey = null -- local variable init REQUIRED! + + -- RETURN not hit + while inkey ~= 13 do + inkey = machine.__readFromStdin() + if inkey >= 32 then + io.write(string.char(inkey)) + input = input..string.char(inkey) + end + end + -- RETURN finally hit + io.write("\n") + + return input end function _readAll() - return io.__openfile__.readAll() + return io.open(io.__openfile__).readAll() end function _readLine() - return io.__openfile__.readLine() + return io.open(io.__openfile__).readLine() end options = {} options["*n"] = function() error("Read number is not supported, yet!") end--_readNumber options["*a"] = _readAll options["*l"] = _readLine -end]] - -io.read = function() - return string.char(machine.__readFromStdin()) end ----------------- diff --git a/src/net/torvald/terrarum/virtualcomputer/computer/TerrarumComputer.kt b/src/net/torvald/terrarum/virtualcomputer/computer/TerrarumComputer.kt index c9a7732ed..4d49d03b2 100644 --- a/src/net/torvald/terrarum/virtualcomputer/computer/TerrarumComputer.kt +++ b/src/net/torvald/terrarum/virtualcomputer/computer/TerrarumComputer.kt @@ -213,23 +213,15 @@ class TerrarumComputer(peripheralSlots: Int) { fun keyPressed(key: Int, c: Char) { stdinInput = c.toInt() - - System.err.println("TerrarumComputer.keyPressed got input: $stdinInput") - - // wake thread runnableRunCommand.resume() - synchronized(stdin!!) { (stdin as java.lang.Object).notifyAll() } } fun openStdin() { - System.err.println("TerrarumComputer.openStdin") - - stdinInput = -1 // sleep the thread runnableRunCommand.pause() @@ -344,8 +336,6 @@ class TerrarumComputer(peripheralSlots: Int) { synchronized(pauseLock) { paused = false pauseLock.notifyAll() // Unblocks thread - - System.err.println("ThreadRunCommand resume()") } } } diff --git a/src/net/torvald/terrarum/virtualcomputer/peripheral/PeripheralVideoCard.kt b/src/net/torvald/terrarum/virtualcomputer/peripheral/PeripheralVideoCard.kt index d9f6f39cc..b0c34738f 100644 --- a/src/net/torvald/terrarum/virtualcomputer/peripheral/PeripheralVideoCard.kt +++ b/src/net/torvald/terrarum/virtualcomputer/peripheral/PeripheralVideoCard.kt @@ -1,16 +1,22 @@ package net.torvald.terrarum.virtualcomputer.peripheral +import net.torvald.terrarum.Terrarum import net.torvald.terrarum.gameactors.DecodeTapestry import net.torvald.terrarum.gameactors.ai.toLua +import net.torvald.terrarum.virtualcomputer.computer.TerrarumComputer +import net.torvald.terrarum.virtualcomputer.terminal.Terminal import org.luaj.vm2.* import org.luaj.vm2.lib.* import org.luaj.vm2.lib.ThreeArgFunction import org.newdawn.slick.* +import java.util.* /** + * Resolution: 640 x 200, non-square pixels + * * Created by SKYHi14 on 2017-02-08. */ -class PeripheralVideoCard(val termW: Int = 40, val termH: Int = 25) : +class PeripheralVideoCard(val host: TerrarumComputer, val termW: Int = 80, val termH: Int = 25) : Peripheral("ppu") { companion object { val blockW = 8 // MUST BE 8 @@ -46,6 +52,12 @@ class PeripheralVideoCard(val termW: Int = 40, val termH: Int = 25) : // hard-coded 8x8 var fontRom = Array(256, { IntArray(blockH) }) + var showCursor = true + + private var cursorBlinkOn = true + private var cursorBlinkTimer = 0 + private val cursorBlinkTime = 250 + init { // build it for first time resetTextRom() @@ -68,7 +80,13 @@ class PeripheralVideoCard(val termW: Int = 40, val termH: Int = 25) : } } + val cursorSprite = ImageBuffer(blockW, blockH * 2) + val cursorImage: Image + init { + Arrays.fill(cursorSprite.rgba, 0xFF.toByte()) + cursorImage = cursorSprite.image + fun composeSpriteObject(spriteIndex: Int) : LuaValue { val sprite = vram.sprites[spriteIndex] val t = LuaTable() @@ -124,8 +142,8 @@ class PeripheralVideoCard(val termW: Int = 40, val termH: Int = 25) : globals["ppu"]["clearFore"] = ClearForeground(this) globals["ppu"]["getSpritesCount"] = GetSpritesCount(this) - globals["ppu"]["getWidth"] = GetWidth(this) - globals["ppu"]["getHeight"] = GetHeight(this) + globals["ppu"]["width"] = GetWidth(this) + globals["ppu"]["height"] = GetHeight(this) globals["ppu"]["getSprite"] = GetSprite(this) @@ -137,27 +155,49 @@ class PeripheralVideoCard(val termW: Int = 40, val termH: Int = 25) : globals["ppu"]["drawString"] = DrawString(this) } - private val spriteBuffer = ImageBuffer(VSprite.width, VSprite.height) + private val spriteBuffer = ImageBuffer(VSprite.width * 2, VSprite.height) fun render(g: Graphics) { + cursorBlinkTimer += Terrarum.UPDATE_DELTA + if (cursorBlinkTimer > cursorBlinkTime) { + cursorBlinkTimer -= cursorBlinkTime + cursorBlinkOn = !cursorBlinkOn + } + fun VSprite.render() { - val h = VSprite.height - val w = VSprite.width - if (rotation and 1 == 0) { // deg 0, 180 - (if (rotation == 0 && !vFlip || rotation == 2 && vFlip) 0..h-1 else h-1 downTo 0).forEachIndexed { ordY, y -> - (if (rotation == 0 && !hFlip || rotation == 2 && hFlip) 0..w-1 else w-1 downTo 0).forEachIndexed { ordX, x -> - val pixelData = data[y].ushr(2 * x).and(0b11) - val col = getColourFromPalette(pixelData) - spriteBuffer.setRGBA(ordX, ordY, col.red, col.green, col.blue, col.alpha) + if (this.isVisible) { + val h = VSprite.height + val w = VSprite.width + if (rotation and 1 == 0) { // deg 0, 180 + (if (rotation == 0 && !vFlip || rotation == 2 && vFlip) 0..h - 1 else h - 1 downTo 0).forEachIndexed { ordY, y -> + (if (rotation == 0 && !hFlip || rotation == 2 && hFlip) 0..w - 1 else w - 1 downTo 0).forEachIndexed { ordX, x -> + val pixelData = data[y].ushr(2 * x).and(0b11) + val col = getColourFromPalette(pixelData) + + if (this.drawWide) { + spriteBuffer.setRGBA(ordX * 2, ordY, col.red, col.green, col.blue, col.alpha) + spriteBuffer.setRGBA(ordX * 2 + 1, ordY, col.red, col.green, col.blue, col.alpha) + } + else { + spriteBuffer.setRGBA(ordX, ordY, col.red, col.green, col.blue, col.alpha) + } + } } } - } - else { // deg 90, 270 - (if (rotation == 3 && !hFlip || rotation == 1 && hFlip) 0..w-1 else w-1 downTo 0).forEachIndexed { ordY, y -> - (if (rotation == 3 && !vFlip || rotation == 1 && vFlip) h-1 downTo 0 else 0..h-1).forEachIndexed { ordX, x -> - val pixelData = data[y].ushr(2 * x).and(0b11) - val col = getColourFromPalette(pixelData) - spriteBuffer.setRGBA(ordY, ordX, col.red, col.green, col.blue, col.alpha) + else { // deg 90, 270 + (if (rotation == 3 && !hFlip || rotation == 1 && hFlip) 0..w - 1 else w - 1 downTo 0).forEachIndexed { ordY, y -> + (if (rotation == 3 && !vFlip || rotation == 1 && vFlip) h - 1 downTo 0 else 0..h - 1).forEachIndexed { ordX, x -> + val pixelData = data[y].ushr(2 * x).and(0b11) + val col = getColourFromPalette(pixelData) + + if (this.drawWide) { + spriteBuffer.setRGBA(ordY * 2, ordX, col.red, col.green, col.blue, col.alpha) + spriteBuffer.setRGBA(ordY * 2 + 1, ordX, col.red, col.green, col.blue, col.alpha) + } + else { + spriteBuffer.setRGBA(ordY, ordX, col.red, col.green, col.blue, col.alpha) + } + } } } } @@ -182,7 +222,24 @@ class PeripheralVideoCard(val termW: Int = 40, val termH: Int = 25) : val img = frameBuffer.image img.filter = Image.FILTER_NEAREST - g.drawImage(img.getScaledCopy(2f), 0f, 0f) + g.drawImage(img.getScaledCopy(blockW * termW, blockH * termH * 2), 0f, 0f) + + + if (cursorBlinkOn && showCursor) { + g.drawImage( + cursorImage, + host.term.cursorX * blockW.toFloat(), + (host.term as Terminal).cursorY * blockH * 2f + ) + } + + + // scanlines + g.color = Color(0, 0, 0, 40) + g.lineWidth = 1f + for (i in 1..blockH * termH * 2 step 2) { + g.drawLine(0f, i.toFloat(), blockW * termW - 1f, i.toFloat()) + } img.destroy() } @@ -538,6 +595,7 @@ class VSprite { var isBackground = false var isVisible = false + var drawWide = false fun setPaletteSet(col0: Int, col1: Int, col2: Int, col3: Int) { pal0 = col0 @@ -557,6 +615,11 @@ class VSprite { return CLUT[clutIndex] } + fun setPos(x: Int, y: Int) { + posX = x + posY = y + } + fun setPixel(x: Int, y: Int, color: Int) { data[y] = data[y] xor data[y].and(3 shl (2 * x)) // mask off desired area to 0b00 data[y] = data[y] or (color shl (2 * x)) diff --git a/src/net/torvald/terrarum/virtualcomputer/terminal/GraphicsTerminal.kt b/src/net/torvald/terrarum/virtualcomputer/terminal/GraphicsTerminal.kt index 33cd828b2..6a6fceda3 100644 --- a/src/net/torvald/terrarum/virtualcomputer/terminal/GraphicsTerminal.kt +++ b/src/net/torvald/terrarum/virtualcomputer/terminal/GraphicsTerminal.kt @@ -206,6 +206,8 @@ class GraphicsTerminal(private val host: TerrarumComputer) : Terminal { ) (0..displacement - 1).forEach { rgba[it] = 0.toByte() } } + + cursorY += -amount } /** diff --git a/src/net/torvald/terrarum/virtualcomputer/terminal/TerminalInputStream.kt b/src/net/torvald/terrarum/virtualcomputer/terminal/TerminalInputStream.kt index ffcbb4a57..6c113c519 100644 --- a/src/net/torvald/terrarum/virtualcomputer/terminal/TerminalInputStream.kt +++ b/src/net/torvald/terrarum/virtualcomputer/terminal/TerminalInputStream.kt @@ -8,35 +8,16 @@ import java.io.InputStream */ class TerminalInputStream(val host: TerrarumComputer) : InputStream() { - private val pauseLock = java.lang.Object() - override fun read(): Int { - System.err.println("TerminalInputStream.read called") - //System.err.println(Thread.currentThread().name) // would display "LuaJ Separated", which means this InputStream will not block main thread - host.openStdin() - - synchronized(this) { (this as java.lang.Object).wait() } - System.err.println("TerminalInputStream.read exit") - - - return host.stdinInput } - override fun read(b: ByteArray?): Int { - TODO() - } - - override fun read(b: ByteArray?, off: Int, len: Int): Int { - TODO() - } - } \ No newline at end of file