mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-06-09 18:14:06 +09:00
working io.read from STDIN
Former-commit-id: b1168a53fd5fbd09c6a5a76506402560fc4e0fd7 Former-commit-id: a0d455da14a6eb7bf0127e136949273ec5a28628
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
package net.torvald.terrarum
|
package net.torvald.terrarum
|
||||||
|
|
||||||
import net.torvald.random.HQRNG
|
import net.torvald.random.HQRNG
|
||||||
|
import net.torvald.terrarum.Terrarum.UPDATE_DELTA
|
||||||
import net.torvald.terrarum.gameactors.roundInt
|
import net.torvald.terrarum.gameactors.roundInt
|
||||||
import net.torvald.terrarum.virtualcomputer.computer.TerrarumComputer
|
import net.torvald.terrarum.virtualcomputer.computer.TerrarumComputer
|
||||||
import net.torvald.terrarum.virtualcomputer.peripheral.PeripheralVideoCard
|
import net.torvald.terrarum.virtualcomputer.peripheral.PeripheralVideoCard
|
||||||
@@ -20,7 +21,7 @@ class StateGraphicComputerTest : BasicGameState() {
|
|||||||
val monitor = GraphicsTerminal(computer)
|
val monitor = GraphicsTerminal(computer)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val videocard = PeripheralVideoCard()
|
val videocard = PeripheralVideoCard(computer)
|
||||||
monitor.attachVideoCard(videocard)
|
monitor.attachVideoCard(videocard)
|
||||||
|
|
||||||
computer.attachTerminal(monitor)
|
computer.attachTerminal(monitor)
|
||||||
@@ -31,7 +32,11 @@ class StateGraphicComputerTest : BasicGameState() {
|
|||||||
val vcard = (computer.getPeripheral("ppu") as PeripheralVideoCard).vram
|
val vcard = (computer.getPeripheral("ppu") as PeripheralVideoCard).vram
|
||||||
|
|
||||||
// it's a-me, Mario!
|
// 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(
|
vcard.sprites[0].setAll(intArrayOf(
|
||||||
0,0,0,0,0,1,1,1,
|
0,0,0,0,0,1,1,1,
|
||||||
@@ -76,6 +81,8 @@ class StateGraphicComputerTest : BasicGameState() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun update(container: GameContainer, game: StateBasedGame?, delta: Int) {
|
override fun update(container: GameContainer, game: StateBasedGame?, delta: Int) {
|
||||||
|
UPDATE_DELTA = delta
|
||||||
|
|
||||||
Terrarum.appgc.setTitle("VT — F: ${container.fps}" +
|
Terrarum.appgc.setTitle("VT — F: ${container.fps}" +
|
||||||
" — M: ${Terrarum.memInUse}M / ${Terrarum.memTotal}M / ${Terrarum.memXmx}M" +
|
" — M: ${Terrarum.memInUse}M / ${Terrarum.memTotal}M / ${Terrarum.memXmx}M" +
|
||||||
" ${Random().nextInt(100)}")
|
" ${Random().nextInt(100)}")
|
||||||
@@ -83,7 +90,11 @@ class StateGraphicComputerTest : BasicGameState() {
|
|||||||
computer.update(container, delta)
|
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
|
override fun getID() = Terrarum.STATE_ID_TEST_TTY
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package net.torvald.terrarum
|
|||||||
|
|
||||||
import net.torvald.imagefont.GameFontBase
|
import net.torvald.imagefont.GameFontBase
|
||||||
import net.torvald.random.HQRNG
|
import net.torvald.random.HQRNG
|
||||||
|
import net.torvald.terrarum.Terrarum.UPDATE_DELTA
|
||||||
import net.torvald.terrarum.audio.AudioResourceLibrary
|
import net.torvald.terrarum.audio.AudioResourceLibrary
|
||||||
import net.torvald.terrarum.concurrent.ThreadParallel
|
import net.torvald.terrarum.concurrent.ThreadParallel
|
||||||
import net.torvald.terrarum.console.*
|
import net.torvald.terrarum.console.*
|
||||||
@@ -91,8 +92,6 @@ class StateInGame : BasicGameState() {
|
|||||||
val KEY_LIGHTMAP_RENDER = Key.F7
|
val KEY_LIGHTMAP_RENDER = Key.F7
|
||||||
val KEY_LIGHTMAP_SMOOTH = Key.F8
|
val KEY_LIGHTMAP_SMOOTH = Key.F8
|
||||||
|
|
||||||
var UPDATE_DELTA: Int = 0
|
|
||||||
|
|
||||||
// UI aliases
|
// UI aliases
|
||||||
val uiAliases = HashMap<String, UIHandler>()
|
val uiAliases = HashMap<String, UIHandler>()
|
||||||
private val UI_PIE_MENU = "uiPieMenu"
|
private val UI_PIE_MENU = "uiPieMenu"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package net.torvald.terrarum
|
|||||||
|
|
||||||
import com.sudoplay.joise.Joise
|
import com.sudoplay.joise.Joise
|
||||||
import com.sudoplay.joise.module.*
|
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.concurrent.ThreadParallel
|
||||||
import net.torvald.terrarum.gameactors.roundInt
|
import net.torvald.terrarum.gameactors.roundInt
|
||||||
import org.newdawn.slick.*
|
import org.newdawn.slick.*
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package net.torvald.terrarum
|
package net.torvald.terrarum
|
||||||
|
|
||||||
import net.torvald.terrarum.Terrarum.Companion.STATE_ID_TEST_SHADER
|
|
||||||
import org.lwjgl.opengl.*
|
import org.lwjgl.opengl.*
|
||||||
import org.newdawn.slick.GameContainer
|
import org.newdawn.slick.GameContainer
|
||||||
import org.newdawn.slick.Graphics
|
import org.newdawn.slick.Graphics
|
||||||
@@ -13,6 +12,7 @@ import org.lwjgl.opengl.GL11
|
|||||||
import org.lwjgl.opengl.ARBShaderObjects
|
import org.lwjgl.opengl.ARBShaderObjects
|
||||||
import com.sun.xml.internal.ws.streaming.XMLStreamReaderUtil.close
|
import com.sun.xml.internal.ws.streaming.XMLStreamReaderUtil.close
|
||||||
import jdk.nashorn.internal.runtime.ScriptingFunctions.readLine
|
import jdk.nashorn.internal.runtime.ScriptingFunctions.readLine
|
||||||
|
import net.torvald.terrarum.Terrarum.STATE_ID_TEST_SHADER
|
||||||
import net.torvald.terrarum.gameworld.fmod
|
import net.torvald.terrarum.gameworld.fmod
|
||||||
import org.newdawn.slick.Color
|
import org.newdawn.slick.Color
|
||||||
import org.newdawn.slick.opengl.TextureImpl
|
import org.newdawn.slick.opengl.TextureImpl
|
||||||
|
|||||||
@@ -24,11 +24,150 @@ import java.util.logging.Level
|
|||||||
import java.util.logging.Logger
|
import java.util.logging.Logger
|
||||||
import java.util.logging.SimpleFormatter
|
import java.util.logging.SimpleFormatter
|
||||||
|
|
||||||
|
const val GAME_NAME = "Terrarum"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by minjaesong on 15-12-30.
|
* Created by minjaesong on 15-12-30.
|
||||||
*/
|
*/
|
||||||
class Terrarum @Throws(SlickException::class)
|
object Terrarum : StateBasedGame(GAME_NAME) {
|
||||||
constructor(gamename: String) : StateBasedGame(gamename) {
|
|
||||||
|
|
||||||
|
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
|
// 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)
|
@Throws(SlickException::class)
|
||||||
override fun initStatesList(gc: GameContainer) {
|
override fun initStatesList(gc: GameContainer) {
|
||||||
gc.input.enableKeyRepeat()
|
gc.input.enableKeyRepeat()
|
||||||
@@ -104,7 +395,6 @@ constructor(gamename: String) : StateBasedGame(gamename) {
|
|||||||
fontSmallNumbers = TinyAlphNum()
|
fontSmallNumbers = TinyAlphNum()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// search for real controller
|
// search for real controller
|
||||||
// exclude controllers with name "Mouse", "keyboard"
|
// exclude controllers with name "Mouse", "keyboard"
|
||||||
val notControllerRegex = Regex("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")
|
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<String>) {
|
|
||||||
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<String>) {
|
fun main(args: Array<String>) {
|
||||||
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) */
|
/** @return Intarray(R, G, B, A) */
|
||||||
fun Image.getPixel(x: Int, y: Int) = this.texture.getPixel(x, y)
|
fun Image.getPixel(x: Int, y: Int) = this.texture.getPixel(x, y)
|
||||||
|
|
||||||
|
|||||||
@@ -235,7 +235,7 @@ open class ActorWithSprite(renderOrder: ActorOrder, val immobileBody: Boolean =
|
|||||||
protected val gameContainer: GameContainer
|
protected val gameContainer: GameContainer
|
||||||
get() = Terrarum.appgc
|
get() = Terrarum.appgc
|
||||||
protected val updateDelta: Int
|
protected val updateDelta: Int
|
||||||
get() = Terrarum.ingame.UPDATE_DELTA
|
get() = Terrarum.UPDATE_DELTA
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* true: This actor had just made collision
|
* true: This actor had just made collision
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ open class ParticleBase(renderOrder: ActorOrder, maxLifeTime: Int? = null) : Run
|
|||||||
/** Will NOT actually delete from the CircularArray */
|
/** Will NOT actually delete from the CircularArray */
|
||||||
@Volatile var flagDespawn = false
|
@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 isNoSubjectToGrav = false
|
||||||
var dragCoefficient = 3.0
|
var dragCoefficient = 3.0
|
||||||
|
|||||||
@@ -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.stdin = "stdin"
|
||||||
io.stdout = "stdout"
|
io.stdout = "stdout"
|
||||||
io.stderr = "stderr"
|
io.stderr = "stderr"
|
||||||
|
|
||||||
io.open = fs.open]]
|
io.open = fs.open
|
||||||
|
|
||||||
--[[io.input = function(luafile)
|
io.input = function(luafile)
|
||||||
io.__openfile__ = luafile
|
io.__openfile__ = luafile
|
||||||
end
|
end
|
||||||
|
|
||||||
io.read = function(option)
|
io.read = function(option)
|
||||||
if io.__openfile__ == "stdin" then
|
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
|
end
|
||||||
|
|
||||||
function _readAll()
|
function _readAll()
|
||||||
return io.__openfile__.readAll()
|
return io.open(io.__openfile__).readAll()
|
||||||
end
|
end
|
||||||
|
|
||||||
function _readLine()
|
function _readLine()
|
||||||
return io.__openfile__.readLine()
|
return io.open(io.__openfile__).readLine()
|
||||||
end
|
end
|
||||||
|
|
||||||
options = {}
|
options = {}
|
||||||
options["*n"] = function() error("Read number is not supported, yet!") end--_readNumber
|
options["*n"] = function() error("Read number is not supported, yet!") end--_readNumber
|
||||||
options["*a"] = _readAll
|
options["*a"] = _readAll
|
||||||
options["*l"] = _readLine
|
options["*l"] = _readLine
|
||||||
end]]
|
|
||||||
|
|
||||||
io.read = function()
|
|
||||||
return string.char(machine.__readFromStdin())
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-----------------
|
-----------------
|
||||||
|
|||||||
@@ -213,23 +213,15 @@ class TerrarumComputer(peripheralSlots: Int) {
|
|||||||
fun keyPressed(key: Int, c: Char) {
|
fun keyPressed(key: Int, c: Char) {
|
||||||
stdinInput = c.toInt()
|
stdinInput = c.toInt()
|
||||||
|
|
||||||
|
|
||||||
System.err.println("TerrarumComputer.keyPressed got input: $stdinInput")
|
|
||||||
|
|
||||||
|
|
||||||
// wake thread
|
// wake thread
|
||||||
runnableRunCommand.resume()
|
runnableRunCommand.resume()
|
||||||
|
|
||||||
|
|
||||||
synchronized(stdin!!) {
|
synchronized(stdin!!) {
|
||||||
(stdin as java.lang.Object).notifyAll()
|
(stdin as java.lang.Object).notifyAll()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun openStdin() {
|
fun openStdin() {
|
||||||
System.err.println("TerrarumComputer.openStdin")
|
|
||||||
|
|
||||||
|
|
||||||
stdinInput = -1
|
stdinInput = -1
|
||||||
// sleep the thread
|
// sleep the thread
|
||||||
runnableRunCommand.pause()
|
runnableRunCommand.pause()
|
||||||
@@ -344,8 +336,6 @@ class TerrarumComputer(peripheralSlots: Int) {
|
|||||||
synchronized(pauseLock) {
|
synchronized(pauseLock) {
|
||||||
paused = false
|
paused = false
|
||||||
pauseLock.notifyAll() // Unblocks thread
|
pauseLock.notifyAll() // Unblocks thread
|
||||||
|
|
||||||
System.err.println("ThreadRunCommand resume()")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,22 @@
|
|||||||
package net.torvald.terrarum.virtualcomputer.peripheral
|
package net.torvald.terrarum.virtualcomputer.peripheral
|
||||||
|
|
||||||
|
import net.torvald.terrarum.Terrarum
|
||||||
import net.torvald.terrarum.gameactors.DecodeTapestry
|
import net.torvald.terrarum.gameactors.DecodeTapestry
|
||||||
import net.torvald.terrarum.gameactors.ai.toLua
|
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.*
|
||||||
import org.luaj.vm2.lib.*
|
import org.luaj.vm2.lib.*
|
||||||
import org.luaj.vm2.lib.ThreeArgFunction
|
import org.luaj.vm2.lib.ThreeArgFunction
|
||||||
import org.newdawn.slick.*
|
import org.newdawn.slick.*
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Resolution: 640 x 200, non-square pixels
|
||||||
|
*
|
||||||
* Created by SKYHi14 on 2017-02-08.
|
* 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") {
|
Peripheral("ppu") {
|
||||||
companion object {
|
companion object {
|
||||||
val blockW = 8 // MUST BE 8
|
val blockW = 8 // MUST BE 8
|
||||||
@@ -46,6 +52,12 @@ class PeripheralVideoCard(val termW: Int = 40, val termH: Int = 25) :
|
|||||||
// hard-coded 8x8
|
// hard-coded 8x8
|
||||||
var fontRom = Array<IntArray>(256, { IntArray(blockH) })
|
var fontRom = Array<IntArray>(256, { IntArray(blockH) })
|
||||||
|
|
||||||
|
var showCursor = true
|
||||||
|
|
||||||
|
private var cursorBlinkOn = true
|
||||||
|
private var cursorBlinkTimer = 0
|
||||||
|
private val cursorBlinkTime = 250
|
||||||
|
|
||||||
init {
|
init {
|
||||||
// build it for first time
|
// build it for first time
|
||||||
resetTextRom()
|
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 {
|
init {
|
||||||
|
Arrays.fill(cursorSprite.rgba, 0xFF.toByte())
|
||||||
|
cursorImage = cursorSprite.image
|
||||||
|
|
||||||
fun composeSpriteObject(spriteIndex: Int) : LuaValue {
|
fun composeSpriteObject(spriteIndex: Int) : LuaValue {
|
||||||
val sprite = vram.sprites[spriteIndex]
|
val sprite = vram.sprites[spriteIndex]
|
||||||
val t = LuaTable()
|
val t = LuaTable()
|
||||||
@@ -124,8 +142,8 @@ class PeripheralVideoCard(val termW: Int = 40, val termH: Int = 25) :
|
|||||||
globals["ppu"]["clearFore"] = ClearForeground(this)
|
globals["ppu"]["clearFore"] = ClearForeground(this)
|
||||||
|
|
||||||
globals["ppu"]["getSpritesCount"] = GetSpritesCount(this)
|
globals["ppu"]["getSpritesCount"] = GetSpritesCount(this)
|
||||||
globals["ppu"]["getWidth"] = GetWidth(this)
|
globals["ppu"]["width"] = GetWidth(this)
|
||||||
globals["ppu"]["getHeight"] = GetHeight(this)
|
globals["ppu"]["height"] = GetHeight(this)
|
||||||
|
|
||||||
|
|
||||||
globals["ppu"]["getSprite"] = GetSprite(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)
|
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) {
|
fun render(g: Graphics) {
|
||||||
|
cursorBlinkTimer += Terrarum.UPDATE_DELTA
|
||||||
|
if (cursorBlinkTimer > cursorBlinkTime) {
|
||||||
|
cursorBlinkTimer -= cursorBlinkTime
|
||||||
|
cursorBlinkOn = !cursorBlinkOn
|
||||||
|
}
|
||||||
|
|
||||||
fun VSprite.render() {
|
fun VSprite.render() {
|
||||||
val h = VSprite.height
|
if (this.isVisible) {
|
||||||
val w = VSprite.width
|
val h = VSprite.height
|
||||||
if (rotation and 1 == 0) { // deg 0, 180
|
val w = VSprite.width
|
||||||
(if (rotation == 0 && !vFlip || rotation == 2 && vFlip) 0..h-1 else h-1 downTo 0).forEachIndexed { ordY, y ->
|
if (rotation and 1 == 0) { // deg 0, 180
|
||||||
(if (rotation == 0 && !hFlip || rotation == 2 && hFlip) 0..w-1 else w-1 downTo 0).forEachIndexed { ordX, x ->
|
(if (rotation == 0 && !vFlip || rotation == 2 && vFlip) 0..h - 1 else h - 1 downTo 0).forEachIndexed { ordY, y ->
|
||||||
val pixelData = data[y].ushr(2 * x).and(0b11)
|
(if (rotation == 0 && !hFlip || rotation == 2 && hFlip) 0..w - 1 else w - 1 downTo 0).forEachIndexed { ordX, x ->
|
||||||
val col = getColourFromPalette(pixelData)
|
val pixelData = data[y].ushr(2 * x).and(0b11)
|
||||||
spriteBuffer.setRGBA(ordX, ordY, col.red, col.green, col.blue, col.alpha)
|
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
|
||||||
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 && !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 ->
|
||||||
(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 pixelData = data[y].ushr(2 * x).and(0b11)
|
val col = getColourFromPalette(pixelData)
|
||||||
val col = getColourFromPalette(pixelData)
|
|
||||||
spriteBuffer.setRGBA(ordY, ordX, col.red, col.green, col.blue, col.alpha)
|
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
|
val img = frameBuffer.image
|
||||||
img.filter = Image.FILTER_NEAREST
|
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()
|
img.destroy()
|
||||||
}
|
}
|
||||||
@@ -538,6 +595,7 @@ class VSprite {
|
|||||||
|
|
||||||
var isBackground = false
|
var isBackground = false
|
||||||
var isVisible = false
|
var isVisible = false
|
||||||
|
var drawWide = false
|
||||||
|
|
||||||
fun setPaletteSet(col0: Int, col1: Int, col2: Int, col3: Int) {
|
fun setPaletteSet(col0: Int, col1: Int, col2: Int, col3: Int) {
|
||||||
pal0 = col0
|
pal0 = col0
|
||||||
@@ -557,6 +615,11 @@ class VSprite {
|
|||||||
return CLUT[clutIndex]
|
return CLUT[clutIndex]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setPos(x: Int, y: Int) {
|
||||||
|
posX = x
|
||||||
|
posY = y
|
||||||
|
}
|
||||||
|
|
||||||
fun setPixel(x: Int, y: Int, color: Int) {
|
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] xor data[y].and(3 shl (2 * x)) // mask off desired area to 0b00
|
||||||
data[y] = data[y] or (color shl (2 * x))
|
data[y] = data[y] or (color shl (2 * x))
|
||||||
|
|||||||
@@ -206,6 +206,8 @@ class GraphicsTerminal(private val host: TerrarumComputer) : Terminal {
|
|||||||
)
|
)
|
||||||
(0..displacement - 1).forEach { rgba[it] = 0.toByte() }
|
(0..displacement - 1).forEach { rgba[it] = 0.toByte() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cursorY += -amount
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -8,35 +8,16 @@ import java.io.InputStream
|
|||||||
*/
|
*/
|
||||||
class TerminalInputStream(val host: TerrarumComputer) : InputStream() {
|
class TerminalInputStream(val host: TerrarumComputer) : InputStream() {
|
||||||
|
|
||||||
private val pauseLock = java.lang.Object()
|
|
||||||
|
|
||||||
override fun read(): Int {
|
override fun read(): Int {
|
||||||
System.err.println("TerminalInputStream.read called")
|
|
||||||
|
|
||||||
//System.err.println(Thread.currentThread().name)
|
//System.err.println(Thread.currentThread().name)
|
||||||
// would display "LuaJ Separated", which means this InputStream will not block main thread
|
// would display "LuaJ Separated", which means this InputStream will not block main thread
|
||||||
|
|
||||||
|
|
||||||
host.openStdin()
|
host.openStdin()
|
||||||
|
|
||||||
|
|
||||||
synchronized(this) {
|
synchronized(this) {
|
||||||
(this as java.lang.Object).wait()
|
(this as java.lang.Object).wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
System.err.println("TerminalInputStream.read exit")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return host.stdinInput
|
return host.stdinInput
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun read(b: ByteArray?): Int {
|
|
||||||
TODO()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun read(b: ByteArray?, off: Int, len: Int): Int {
|
|
||||||
TODO()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user