mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-07 20:31:51 +09:00
Separated langpack, concept art for inventory UI
Former-commit-id: 7a98df93b4ef50b47283abcd99576d6fbefc9cc5 Former-commit-id: db6e34417ccf84e59ba68547f30459cb4b188eb7
This commit is contained in:
@@ -2,6 +2,7 @@ package net.torvald
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.JsonParser
|
||||
import java.io.File
|
||||
|
||||
import java.io.IOException
|
||||
import java.nio.file.FileSystems
|
||||
@@ -27,6 +28,17 @@ object JsonFetcher {
|
||||
return jsonObj
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
fun readJson(jsonFile: File): JsonObject {
|
||||
jsonString = StringBuffer() // reset buffer every time it called
|
||||
readJsonFileAsString(jsonFile.canonicalPath)
|
||||
|
||||
val jsonParser = JsonParser()
|
||||
val jsonObj = jsonParser.parse(jsonString!!.toString()).asJsonObject
|
||||
|
||||
return jsonObj
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
private fun readJsonFileAsString(path: String) {
|
||||
Files.lines(FileSystems.getDefault().getPath(path)).forEach(
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
## Atmosphere ##
|
||||
|
||||
* Serene (not all serene of course)
|
||||
* There's living village, abandoned/crumbling settlement and lone shacks on the mountainside
|
||||
* History, much like one in the _Dwarf Fortress_, decaying of the settlement is simulated by the very method, but 1-2 will be forced. The amount is dependent on the size of the map
|
||||
* Reference: _Legend of Zelda: Breath of the Wild Trailer_ (the trailer came much after this game but the video will help you to get a grip)
|
||||
|
||||
## Colour overlay ##
|
||||
|
||||
Colour overlay is set to 6 500 K as untouched. The value must be
|
||||
|
||||
48
src/net/torvald/terrarum/GAME_SYSTEM.md
Normal file
48
src/net/torvald/terrarum/GAME_SYSTEM.md
Normal file
@@ -0,0 +1,48 @@
|
||||
The game has elements that are:
|
||||
|
||||
- Actors
|
||||
- Controller
|
||||
- AI
|
||||
- JInput (Provided by LWJGL)
|
||||
- Factioning
|
||||
- Scheduler
|
||||
- Physics solver (simple, AABB-oriented)
|
||||
- _TODO: sprite assembler (character--avatar--maker)_
|
||||
- Imagefont
|
||||
- Font drawer
|
||||
- Hangul character assembler (aka JOHAB)
|
||||
- Sprite animator (really simple one)
|
||||
- De/serialiser
|
||||
- Concurrency helper (really simple one)
|
||||
- Tiles
|
||||
- Tile property database
|
||||
- Items
|
||||
- Item property database
|
||||
- _TODO: Material system_
|
||||
- Map drawer
|
||||
- Map camera
|
||||
- Map drawer
|
||||
- Lightmap renderer
|
||||
- Map generator
|
||||
- Utilises Joise Modular Noise Generator
|
||||
- Additional noise filters
|
||||
- Real estate
|
||||
- The registry (a book that records the current owner of tiles on the map. Owner can be a single NPC or faction)
|
||||
- Utility
|
||||
- Internationalisation
|
||||
- Language pack
|
||||
|
||||
The elements are connected like:
|
||||
|
||||
- Actors
|
||||
- Controller ← AI, Player Input
|
||||
- AI ← Factioning, Scheduler
|
||||
- class "Actor"
|
||||
- class "ActorWithBody"
|
||||
with properties like (AIControlled, Factionable, Luminous, Visible, etc.)
|
||||
- class "NPCIntelligentBase"
|
||||
- (NPCs that has AI and can be interacted)
|
||||
- (fixtures, player)
|
||||
- (any of the invisible actors)
|
||||
|
||||
|
||||
@@ -116,4 +116,10 @@ see SAVE_FORMAT.md
|
||||
## Ownership of lands ##
|
||||
|
||||
* Codex of Real Estate → assign owner to the tiles → copy the information to the NPC instance
|
||||
|
||||
|
||||
|
||||
## Health and magic point ##
|
||||
|
||||
* Works like Ampere in electronics.
|
||||
- Health: Regen per time
|
||||
- Magic: Out power per time
|
||||
|
||||
19
src/net/torvald/terrarum/SYSTEM_REQUIREMENTS.md
Normal file
19
src/net/torvald/terrarum/SYSTEM_REQUIREMENTS.md
Normal file
@@ -0,0 +1,19 @@
|
||||
## Minimum requirements ##
|
||||
|
||||
* Any CPU with 1.8 GHz in speed
|
||||
* GPU that supports OpenGL 2.0, is capable of 4K texture (8K for Chinese/Japanese language)
|
||||
* 2 GB of RAM
|
||||
* 2 GB of free disk space
|
||||
|
||||
## Recommended requirements ##
|
||||
|
||||
* Intel i5 with 2.4+ GHz in speed (or any CPU with 4 or more threads, 2.4+ GHz in speed)
|
||||
* GPU that supports OpenGL 2.0, is capable of 8K texture
|
||||
* 4 GB of RAM
|
||||
* 5 GB of free disk space
|
||||
|
||||
## Tested environment ##
|
||||
|
||||
(to devs: please extend this list with your test results!)
|
||||
|
||||
* MacBookPro9,2 (MacBook Pro 13 inch mid-2012)
|
||||
@@ -9,7 +9,7 @@ import org.newdawn.slick.state.StateBasedGame
|
||||
/**
|
||||
* Created by minjaesong on 16-06-28.
|
||||
*/
|
||||
class StateFontPrinter : BasicGameState() {
|
||||
class StateFontTester : BasicGameState() {
|
||||
val textToPrint = "Font printer 서체 인쇄기"
|
||||
|
||||
lateinit var canvas: Graphics
|
||||
@@ -80,32 +80,32 @@ constructor() : BasicGameState() {
|
||||
|
||||
val auth = Authenticator()
|
||||
|
||||
private var update_delta: Int = 0
|
||||
|
||||
val KEY_LIGHTMAP_RENDER = Key.F7
|
||||
val KEY_LIGHTMAP_SMOOTH = Key.F8
|
||||
|
||||
var DELTA_T: Int = 0
|
||||
var UPDATE_DELTA: Int = 0
|
||||
|
||||
@Throws(SlickException::class)
|
||||
override fun init(gameContainer: GameContainer, stateBasedGame: StateBasedGame) {
|
||||
KeyMap.build()
|
||||
|
||||
// load necessary shaders
|
||||
shader12BitCol = Shader.makeShader("./res/4096.vrt", "./res/4096.frg")
|
||||
shaderBlurH = Shader.makeShader("./res/blurH.vrt", "./res/blur.frg")
|
||||
shaderBlurV = Shader.makeShader("./res/blurV.vrt", "./res/blur.frg")
|
||||
|
||||
|
||||
// init skybox
|
||||
GRADIENT_IMAGE = Image("res/graphics/colourmap/sky_colour.png")
|
||||
skyBox = Rectangle(0f, 0f, Terrarum.WIDTH.toFloat(), Terrarum.HEIGHT.toFloat())
|
||||
|
||||
// init map as chosen size
|
||||
map = GameMap(8192, 2048)
|
||||
|
||||
// generate terrain for the map
|
||||
MapGenerator.attachMap(map)
|
||||
MapGenerator.SEED = 0x51621D2
|
||||
//mapgenerator.setSeed(new HQRNG().nextLong());
|
||||
MapGenerator.generateMap()
|
||||
|
||||
|
||||
RoguelikeRandomiser.seed = 0x540198
|
||||
//RoguelikeRandomiser.setSeed(new HQRNG().nextLong());
|
||||
|
||||
@@ -116,27 +116,27 @@ constructor() : BasicGameState() {
|
||||
//player.setNoClip(true);
|
||||
addActor(player)
|
||||
|
||||
// init console window
|
||||
consoleHandler = UIHandler(ConsoleWindow())
|
||||
consoleHandler.setPosition(0, 0)
|
||||
|
||||
// init debug window
|
||||
debugWindow = UIHandler(BasicDebugInfoWindow())
|
||||
debugWindow.setPosition(0, 0)
|
||||
|
||||
// init notifier
|
||||
notifier = UIHandler(Notification())
|
||||
notifier.setPosition(
|
||||
(Terrarum.WIDTH - notifier.UI.width) / 2, Terrarum.HEIGHT - notifier.UI.height)
|
||||
notifier.setVisibility(true)
|
||||
|
||||
if (Terrarum.getConfigBoolean("smoothlighting") == true)
|
||||
KeyToggler.forceSet(KEY_LIGHTMAP_SMOOTH, true)
|
||||
else
|
||||
KeyToggler.forceSet(KEY_LIGHTMAP_SMOOTH, false)
|
||||
// set smooth lighting as in config
|
||||
KeyToggler.forceSet(KEY_LIGHTMAP_SMOOTH, Terrarum.getConfigBoolean("smoothlighting"))
|
||||
}
|
||||
|
||||
override fun update(gc: GameContainer, sbg: StateBasedGame, delta: Int) {
|
||||
DELTA_T = delta
|
||||
UPDATE_DELTA = delta
|
||||
|
||||
update_delta = delta
|
||||
setAppTitle()
|
||||
|
||||
map.updateWorldTime(delta)
|
||||
@@ -157,10 +157,9 @@ constructor() : BasicGameState() {
|
||||
|
||||
updateActors(gc, delta)
|
||||
|
||||
// TODO thread pool
|
||||
// TODO thread pool(?)
|
||||
CollisionSolver.process()
|
||||
|
||||
// TODO thread pool
|
||||
uiContainer.forEach { ui -> ui.update(gc, delta) }
|
||||
consoleHandler.update(gc, delta)
|
||||
debugWindow.update(gc, delta)
|
||||
@@ -195,12 +194,14 @@ constructor() : BasicGameState() {
|
||||
MapCamera.renderBehind(gc, g)
|
||||
|
||||
// draw actors
|
||||
actorContainer.forEach { actor ->
|
||||
if (actor is Visible && actor.inScreen() && actor !is Player) { // if visible and within screen
|
||||
actor.drawBody(gc, g)
|
||||
run {
|
||||
actorContainer.forEach { actor ->
|
||||
if (actor is Visible && actor.inScreen() && actor !is Player) { // if visible and within screen
|
||||
actor.drawBody(gc, g)
|
||||
}
|
||||
}
|
||||
player.drawBody(gc, g)
|
||||
}
|
||||
player.drawBody(gc, g)
|
||||
|
||||
LightmapRenderer.renderLightMap()
|
||||
|
||||
@@ -208,21 +209,21 @@ constructor() : BasicGameState() {
|
||||
MapDrawer.render(gc, g)
|
||||
|
||||
setBlendMul()
|
||||
|
||||
MapDrawer.drawEnvOverlay(g)
|
||||
|
||||
if (!KeyToggler.isOn(KEY_LIGHTMAP_RENDER)) setBlendMul() else setBlendNormal()
|
||||
LightmapRenderer.draw(g)
|
||||
|
||||
setBlendNormal()
|
||||
|
||||
// draw actor glows
|
||||
actorContainer.forEach { actor ->
|
||||
if (actor is Visible && actor.inScreen() && actor !is Player) { // if visible and within screen
|
||||
actor.drawGlow(gc, g)
|
||||
run {
|
||||
actorContainer.forEach { actor ->
|
||||
if (actor is Visible && actor.inScreen() && actor !is Player) { // if visible and within screen
|
||||
actor.drawGlow(gc, g)
|
||||
}
|
||||
}
|
||||
player.drawGlow(gc, g)
|
||||
}
|
||||
player.drawGlow(gc, g)
|
||||
|
||||
// draw reference ID if debugWindow is open
|
||||
if (debugWindow.visible) {
|
||||
@@ -249,10 +250,12 @@ constructor() : BasicGameState() {
|
||||
}
|
||||
|
||||
// draw UIs
|
||||
uiContainer.forEach { ui -> ui.render(gc, g) }
|
||||
debugWindow.render(gc, g)
|
||||
consoleHandler.render(gc, g)
|
||||
notifier.render(gc, g)
|
||||
run {
|
||||
uiContainer.forEach { ui -> ui.render(gc, g) }
|
||||
debugWindow.render(gc, g)
|
||||
consoleHandler.render(gc, g)
|
||||
notifier.render(gc, g)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getGradientColour(row: Int, phase: Int) = GRADIENT_IMAGE!!.getColor(phase, row)
|
||||
@@ -327,46 +330,52 @@ constructor() : BasicGameState() {
|
||||
|
||||
/** Send message to notifier UI and toggle the UI as opened. */
|
||||
fun sendNotification(msg: Array<String>) {
|
||||
(notifier.UI as Notification).sendNotification(Terrarum.appgc, update_delta, msg)
|
||||
(notifier.UI as Notification).sendNotification(Terrarum.appgc, UPDATE_DELTA, msg)
|
||||
notifier.setAsOpening()
|
||||
}
|
||||
|
||||
fun wakeDormantActors() {
|
||||
// determine whether the dormant actor should be re-activated
|
||||
var actorContainerSize = actorContainerInactive.size
|
||||
var i = 0
|
||||
while (i < actorContainerSize) { // loop thru actorContainerInactive
|
||||
while (i < actorContainerSize) { // loop through actorContainerInactive
|
||||
val actor = actorContainerInactive[i]
|
||||
val actorIndex = i
|
||||
if (actor is Visible && actor.inUpdateRange()) {
|
||||
addActor(actor) // duplicates are checked here
|
||||
actorContainerInactive.removeAt(actorIndex)
|
||||
actorContainerSize -= 1
|
||||
i-- // array removed 1 elem, so also decrement counter by 1
|
||||
i-- // array removed 1 elem, so we also decrement counter by 1
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* determine whether the actor should be active or dormant by its distance from the player.
|
||||
* If the actor must be dormant, the target actor will be put to the list specifically for them.
|
||||
* if the actor is not to be dormant, it will be just ignored.
|
||||
*/
|
||||
fun InactivateDistantActors() {
|
||||
var actorContainerSize = actorContainer.size
|
||||
var i = 0
|
||||
// determine whether the actor should be active or dormant by its distance from the player.
|
||||
// If the actor must be dormant, the target actor will be put to the list specifically for them.
|
||||
// if the actor is not to be dormant, update it
|
||||
while (i < actorContainerSize) { // loop through the actorContainer
|
||||
while (i < actorContainerSize) { // loop through actorContainer
|
||||
val actor = actorContainer[i]
|
||||
val actorIndex = i
|
||||
if (actor is Visible && !actor.inUpdateRange()) {
|
||||
actorContainerInactive.add(actor) // naïve add; duplicates are checked when the actor is re-activated
|
||||
actorContainer.removeAt(actorIndex)
|
||||
actorContainerSize -= 1
|
||||
i-- // array removed 1 elem, so also decrement counter by 1
|
||||
i-- // array removed 1 elem, so we also decrement counter by 1
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update actors concurrently.
|
||||
*
|
||||
* NOTE: concurrency for actor updating is currently disabled because of it's poor performance
|
||||
*/
|
||||
fun updateActors(gc: GameContainer, delta: Int) {
|
||||
if (false) { // don't multithread this for now, it's SLOWER //if (Terrarum.MULTITHREAD) {
|
||||
val actors = actorContainer.size.toFloat()
|
||||
@@ -394,7 +403,7 @@ constructor() : BasicGameState() {
|
||||
get() = getGradientColour(2).getRGB24().rgb24ExpandToRgb30()
|
||||
fun globalLightByTime(t: Int): Int = getGradientColourByTime(2, t).getRGB24().rgb24ExpandToRgb30()
|
||||
|
||||
fun Color.getRGB24(): Int = (this.redByte shl 16) or (this.greenByte shl 8) or (this.blueByte)
|
||||
fun Color.getRGB24(): Int = this.redByte.shl(16) or this.greenByte.shl(8) or this.blueByte
|
||||
/** Remap 8-bit value (0.0-1.0) to 10-bit value (0.0-4.0) by prepending two bits of zero for each R, G and B. */
|
||||
fun Int.rgb24ExpandToRgb30(): Int = (this and 0xff) or
|
||||
(this and 0xff00).ushr(8).shl(10) or
|
||||
@@ -402,11 +411,13 @@ constructor() : BasicGameState() {
|
||||
|
||||
fun Double.sqr() = this * this
|
||||
fun Int.sqr() = this * this
|
||||
private fun distToActorSqr(a: Visible, p: Player): Float =
|
||||
(a.hitbox.centeredX - p.hitbox.centeredX).sqr().toFloat() + (a.hitbox.centeredY - p.hitbox.centeredY).sqr().toFloat()
|
||||
private fun distToActorSqr(a: Visible, p: Player): Double =
|
||||
(a.hitbox.centeredX - p.hitbox.centeredX).sqr() + (a.hitbox.centeredY - p.hitbox.centeredY).sqr()
|
||||
/** whether the actor is within screen */
|
||||
private fun Visible.inScreen() = distToActorSqr(this, player) <=
|
||||
(Terrarum.WIDTH.plus(this.hitbox.width.div(2)).times(1 / Terrarum.game.screenZoom).sqr() +
|
||||
Terrarum.HEIGHT.plus(this.hitbox.height.div(2)).times(1 / Terrarum.game.screenZoom).sqr())
|
||||
/** whether the actor is within update range */
|
||||
private fun Visible.inUpdateRange() = distToActorSqr(this, player) <= ACTOR_UPDATE_RANGE.sqr()
|
||||
/**
|
||||
* actorContainer extensions
|
||||
@@ -459,10 +470,9 @@ constructor() : BasicGameState() {
|
||||
}
|
||||
|
||||
private fun insertionSortLastElem(arr: ArrayList<Actor>) {
|
||||
var x: Actor
|
||||
var j: Int
|
||||
var index: Int = arr.size - 1
|
||||
x = arr[index]
|
||||
val index: Int = arr.size - 1
|
||||
val x = arr[index]
|
||||
j = index - 1
|
||||
while (j > 0 && arr[j] > x) {
|
||||
arr[j + 1] = arr[j]
|
||||
|
||||
@@ -34,11 +34,13 @@ constructor(gamename: String) : StateBasedGame(gamename) {
|
||||
if (!readFromDisk) readConfigJson()
|
||||
|
||||
// get locale from config
|
||||
gameLocale = gameConfig.getAsString("language") ?: sysLang
|
||||
val gameLocaleFromConfig = gameConfig.getAsString("language") ?: sysLang
|
||||
|
||||
// if bad game locale were set, use system locale
|
||||
if (gameLocale.length < 4)
|
||||
if (gameLocaleFromConfig.length < 2)
|
||||
gameLocale = sysLang
|
||||
else
|
||||
gameLocale = gameLocaleFromConfig
|
||||
|
||||
println("[terrarum] Locale: " + gameLocale)
|
||||
}
|
||||
@@ -98,7 +100,14 @@ constructor(gamename: String) : StateBasedGame(gamename) {
|
||||
lateinit var defaultSaveDir: String
|
||||
private set
|
||||
|
||||
var gameLocale = "" // locale override
|
||||
private val localeSimple = arrayOf("de", "en", "es", "it")
|
||||
var gameLocale = "en" // locale override
|
||||
set(value) {
|
||||
if (localeSimple.contains(value.substring(0..1)))
|
||||
field = value.substring(0..1)
|
||||
else
|
||||
field = value
|
||||
}
|
||||
|
||||
lateinit var gameFont: Font
|
||||
private set
|
||||
@@ -145,11 +154,14 @@ constructor(gamename: String) : StateBasedGame(gamename) {
|
||||
|
||||
appgc.setTargetFrameRate(TARGET_INTERNAL_FPS)
|
||||
appgc.setVSync(VSYNC)
|
||||
appgc.setMaximumLogicUpdateInterval(1000 / TARGET_INTERNAL_FPS)
|
||||
appgc.setMinimumLogicUpdateInterval(1000 / TARGET_INTERNAL_FPS - 1)
|
||||
appgc.setMaximumLogicUpdateInterval(1000 / TARGET_INTERNAL_FPS) // 10 ms
|
||||
appgc.setMinimumLogicUpdateInterval(1000 / TARGET_INTERNAL_FPS - 1) // 9 ms
|
||||
|
||||
appgc.setShowFPS(false)
|
||||
|
||||
// game will run normally even if it is not focused
|
||||
appgc.setUpdateOnlyWhenVisible(false)
|
||||
appgc.alwaysRender = true
|
||||
|
||||
appgc.start()
|
||||
}
|
||||
@@ -165,7 +177,6 @@ constructor(gamename: String) : StateBasedGame(gamename) {
|
||||
fileHandler.formatter = formatter
|
||||
|
||||
//logger.info()
|
||||
|
||||
println("The game has been crashed!")
|
||||
println("Crash log were saved to $filepath.")
|
||||
println("================================================================================")
|
||||
|
||||
@@ -41,7 +41,8 @@ object CommandDict {
|
||||
|
||||
// Test codes
|
||||
Pair("bulletintest", SetBulletin()),
|
||||
Pair("gsontest", GsonTest())
|
||||
Pair("gsontest", GsonTest()),
|
||||
Pair("tips", PrintRandomTips())
|
||||
)
|
||||
|
||||
fun getCommand(commandName: String): ConsoleCommand {
|
||||
|
||||
@@ -15,7 +15,16 @@ import java.util.regex.Pattern
|
||||
*/
|
||||
object CommandInterpreter {
|
||||
|
||||
private val commandsNoAuth = arrayOf("auth", "qqq", "zoom", "setlocale", "getlocale", "help", "version")
|
||||
private val commandsNoAuth = arrayOf(
|
||||
"auth",
|
||||
"qqq",
|
||||
"zoom",
|
||||
"setlocale",
|
||||
"getlocale",
|
||||
"help",
|
||||
"version",
|
||||
"tips"
|
||||
)
|
||||
|
||||
private val ccW = GameFontBase.colToCode["w"]
|
||||
private val ccG = GameFontBase.colToCode["g"]
|
||||
|
||||
@@ -16,7 +16,16 @@ internal class Echo : ConsoleCommand {
|
||||
}
|
||||
|
||||
fun execute(single_line: String) {
|
||||
(Terrarum.game.consoleHandler.UI as ConsoleWindow).sendMessage(single_line)
|
||||
val sb = StringBuilder()
|
||||
for (ch in single_line) {
|
||||
if (ch == '\n') {
|
||||
(Terrarum.game.consoleHandler.UI as ConsoleWindow).sendMessage(sb.toString())
|
||||
sb.delete(0, sb.length - 1)
|
||||
}
|
||||
else
|
||||
sb.append(ch)
|
||||
}
|
||||
(Terrarum.game.consoleHandler.UI as ConsoleWindow).sendMessage(sb.toString())
|
||||
}
|
||||
|
||||
override fun printUsage() {
|
||||
|
||||
@@ -10,9 +10,9 @@ class GetLocale : ConsoleCommand {
|
||||
override fun execute(args: Array<String>) {
|
||||
Echo().execute(
|
||||
"Locale: "
|
||||
+ Lang.get("LANGUAGE_THIS")
|
||||
+ Lang["MENU_LANGUAGE_THIS"]
|
||||
+ " ("
|
||||
+ Lang.get("LANGUAGE_EN")
|
||||
+ Lang["MENU_LANGUAGE_THIS_EN"]
|
||||
+ ")")
|
||||
}
|
||||
|
||||
|
||||
17
src/net/torvald/terrarum/console/PrintRandomTips.kt
Normal file
17
src/net/torvald/terrarum/console/PrintRandomTips.kt
Normal file
@@ -0,0 +1,17 @@
|
||||
package net.torvald.terrarum.console
|
||||
|
||||
import net.torvald.terrarum.langpack.Lang
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-07-04.
|
||||
*/
|
||||
class PrintRandomTips : ConsoleCommand {
|
||||
override fun execute(args: Array<String>) {
|
||||
Echo().execute(Lang["GAME_TIPS_${Random().nextInt(Lang.TIPS_COUNT) + 1}"])
|
||||
}
|
||||
|
||||
override fun printUsage() {
|
||||
Echo().execute("Prints random tips for game.")
|
||||
}
|
||||
}
|
||||
@@ -29,8 +29,7 @@ class SetLocale : ConsoleCommand {
|
||||
val echo = Echo()
|
||||
echo.execute("Locales:")
|
||||
|
||||
val record = Lang.getRecord("LANGUAGE_ID")
|
||||
record.forEach { field -> echo.execute("] " + field) }
|
||||
Lang.languageList.forEach { echo.execute("--> $it") }
|
||||
}
|
||||
else {
|
||||
printUsage()
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
package net.torvald.terrarum.console
|
||||
|
||||
import net.torvald.terrarum.Terrarum
|
||||
import net.torvald.terrarum.langpack.Lang
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-04-23.
|
||||
*/
|
||||
class Version : ConsoleCommand {
|
||||
override fun execute(args: Array<String>) {
|
||||
Echo().execute("${Terrarum.NAME} ${Terrarum.VERSION_STRING}")
|
||||
val echo = Echo()
|
||||
echo.execute("${Terrarum.NAME} ${Terrarum.VERSION_STRING}")
|
||||
echo.execute("Polyglot language pack version ${Lang.POLYGLOT_VERSION}")
|
||||
}
|
||||
|
||||
override fun printUsage() {
|
||||
|
||||
@@ -118,7 +118,7 @@ open class ActorWithBody : Actor(), Visible {
|
||||
var isNoCollideWorld = false
|
||||
var isNoSubjectToFluidResistance = false
|
||||
/** Time-freezing. The actor won't even budge but the velocity will accumulate
|
||||
* EXCEPT FOR friction, gravity and buoyancy SHOULD NOT.
|
||||
* EXCEPT FOR friction, gravity and buoyancy.
|
||||
*
|
||||
* (this would give something to do to the player otherwise it would be dull to travel far,
|
||||
* think of a grass cutting on the Zelda games. It would also make a great puzzle to solve.
|
||||
@@ -175,7 +175,7 @@ open class ActorWithBody : Actor(), Visible {
|
||||
@Transient private val CCD_TRY_MAX = 12800
|
||||
|
||||
// just some trivial magic numbers
|
||||
@Transient private val A_PIXEL = 1.4
|
||||
@Transient private val A_PIXEL = 1.0
|
||||
@Transient private val COLLIDING_TOP = 0
|
||||
@Transient private val COLLIDING_RIGHT = 1
|
||||
@Transient private val COLLIDING_BOTTOM = 2
|
||||
@@ -234,7 +234,7 @@ open class ActorWithBody : Actor(), Visible {
|
||||
baseHitboxH * scale)
|
||||
}
|
||||
|
||||
override fun run() = update(Terrarum.appgc, Terrarum.game.DELTA_T)
|
||||
override fun run() = update(Terrarum.appgc, Terrarum.game.UPDATE_DELTA)
|
||||
|
||||
/**
|
||||
* Add vector value to the velocity, in the time unit of single frame.
|
||||
@@ -274,19 +274,21 @@ open class ActorWithBody : Actor(), Visible {
|
||||
/**
|
||||
* Actual physics thing (altering velocity) starts from here
|
||||
*/
|
||||
// Actors are subject to the gravity and the buoyancy if they are not levitating
|
||||
if (!isNoSubjectToGrav) {
|
||||
applyGravitation()
|
||||
//applyBuoyancy()
|
||||
}
|
||||
|
||||
// hard limit velocity
|
||||
veloX = veloX.bipolarClamp(VELO_HARD_LIMIT)
|
||||
veloY = veloY.bipolarClamp(VELO_HARD_LIMIT)
|
||||
applyControllerMovement()
|
||||
|
||||
moveDelta.set(velocity + Vector2(walkX, walkY))
|
||||
// applyBuoyancy()
|
||||
|
||||
if (!isChronostasis) {
|
||||
// Actors are subject to the gravity and the buoyancy if they are not levitating
|
||||
if (!isNoSubjectToGrav) {
|
||||
applyGravitation()
|
||||
}
|
||||
|
||||
// hard limit velocity
|
||||
veloX = veloX.bipolarClamp(VELO_HARD_LIMIT)
|
||||
veloY = veloY.bipolarClamp(VELO_HARD_LIMIT)
|
||||
|
||||
// Set 'next' position (hitbox) from canonical and walking velocity
|
||||
setNewNextHitbox()
|
||||
|
||||
@@ -309,8 +311,25 @@ open class ActorWithBody : Actor(), Visible {
|
||||
clampHitbox()
|
||||
}
|
||||
|
||||
walledLeft = isColliding(hitbox, COLLIDING_LEFT)
|
||||
walledRight = isColliding(hitbox, COLLIDING_RIGHT)
|
||||
// useless
|
||||
walledLeft = false//isColliding(hitbox, COLLIDING_LEFT)
|
||||
walledRight = false//isColliding(hitbox, COLLIDING_RIGHT)
|
||||
}
|
||||
}
|
||||
|
||||
private fun applyControllerMovement() {
|
||||
// decide whether ignore walkX
|
||||
if (!(isCollidingSide(hitbox, COLLIDING_LEFT) && walkX < 0)
|
||||
|| !(isCollidingSide(hitbox, COLLIDING_RIGHT) && walkX > 0)
|
||||
) {
|
||||
moveDelta.x = veloX + walkX
|
||||
}
|
||||
|
||||
// decide whether ignore walkY
|
||||
if (!(isCollidingSide(hitbox, COLLIDING_TOP) && walkY < 0)
|
||||
|| !(isCollidingSide(hitbox, COLLIDING_BOTTOM) && walkY > 0)
|
||||
) {
|
||||
moveDelta.y = veloY + walkY
|
||||
}
|
||||
}
|
||||
|
||||
@@ -320,7 +339,7 @@ open class ActorWithBody : Actor(), Visible {
|
||||
* Apply only if not grounded; normal force is precessed separately.
|
||||
*/
|
||||
private fun applyGravitation() {
|
||||
if (!grounded) {//(!isColliding(COLLIDING_BOTTOM)) { // or !grounded
|
||||
if (!isTouchingSide(hitbox, COLLIDING_BOTTOM)) {//(!isColliding(COLLIDING_BOTTOM)) { // or !grounded
|
||||
/**
|
||||
* weight; gravitational force in action
|
||||
* W = mass * G (9.8 [m/s^2])
|
||||
@@ -346,7 +365,7 @@ open class ActorWithBody : Actor(), Visible {
|
||||
if (!isNoCollideWorld) {
|
||||
// axis Y
|
||||
if (moveDelta.y >= 0.0) { // was moving downward?
|
||||
if (ccdCollided) { // actor hit something on its bottom
|
||||
if (isTouchingSide(nextHitbox, COLLIDING_BOTTOM)) { // actor hit something on its bottom
|
||||
hitAndReflectY()
|
||||
grounded = true
|
||||
}
|
||||
@@ -356,14 +375,15 @@ open class ActorWithBody : Actor(), Visible {
|
||||
}
|
||||
else if (moveDelta.y < 0.0) { // or was moving upward?
|
||||
grounded = false
|
||||
if (ccdCollided) { // actor hit something on its top
|
||||
if (isTouchingSide(nextHitbox, COLLIDING_TOP)) { // actor hit something on its top
|
||||
hitAndReflectY()
|
||||
}
|
||||
else { // the actor is not grounded at all
|
||||
}
|
||||
}
|
||||
// axis X
|
||||
if (ccdCollided && moveDelta.x != 0.0) { // check right and left
|
||||
if (isTouchingSide(nextHitbox, COLLIDING_LEFT) && isTouchingSide(nextHitbox, COLLIDING_RIGHT)
|
||||
&& moveDelta.x != 0.0) { // check right and left
|
||||
// the actor is hitting the wall
|
||||
hitAndReflectX()
|
||||
}
|
||||
@@ -376,23 +396,26 @@ open class ActorWithBody : Actor(), Visible {
|
||||
private fun displaceByCCD() {
|
||||
if (!isNoCollideWorld){
|
||||
// do some CCD between hitbox and nextHitbox
|
||||
val ccdBox = hitbox.clone()
|
||||
val ccdDelta = hitbox.toVector() to nextHitbox.toVector()
|
||||
val ccdStep = Math.max(ccdDelta.x, ccdDelta.y)
|
||||
val ccdBox = nextHitbox.clone().snapToPixel()
|
||||
val ccdDelta = nextHitbox.toVector() to hitbox.toVector()
|
||||
val deltaMax = Math.max(ccdDelta.x.abs(), ccdDelta.y.abs())
|
||||
// set ccdDelta so that CCD move a pixel per round
|
||||
if (deltaMax > 1.0)
|
||||
ccdDelta *= (1.0 / deltaMax)
|
||||
|
||||
for (step in 0..ccdStep.floorInt()) {
|
||||
ccdBox.translate(ccdDelta * (step.toDouble() / ccdStep))
|
||||
//println("deltaMax: $deltaMax")
|
||||
//println("ccdDelta: $ccdDelta")
|
||||
|
||||
println(ccdDelta * (step.toDouble() / ccdStep))
|
||||
println(ccdBox)
|
||||
while (!ccdDelta.isZero && isColliding(ccdBox)) {
|
||||
nextHitbox.translate(ccdDelta)
|
||||
ccdCollided = true
|
||||
|
||||
if (isColliding(ccdBox)) {
|
||||
ccdCollided = true
|
||||
break
|
||||
}
|
||||
ccdBox.reassign(nextHitbox).snapToPixel()
|
||||
}
|
||||
|
||||
nextHitbox.reassign(ccdBox)
|
||||
nextHitbox.snapToPixel()
|
||||
|
||||
//println("ccdCollided: $ccdCollided")
|
||||
}
|
||||
else {
|
||||
ccdCollided = false
|
||||
@@ -470,10 +493,99 @@ open class ActorWithBody : Actor(), Visible {
|
||||
}
|
||||
}
|
||||
|
||||
val txStart = x1.div(TSIZE).floorInt()
|
||||
val txEnd = x2.div(TSIZE).floorInt()
|
||||
val tyStart = y1.div(TSIZE).floorInt()
|
||||
val tyEnd = y2.div(TSIZE).floorInt()
|
||||
val txStart = x1.div(TSIZE).roundInt()
|
||||
val txEnd = x2.div(TSIZE).roundInt()
|
||||
val tyStart = y1.div(TSIZE).roundInt()
|
||||
val tyEnd = y2.div(TSIZE).roundInt()
|
||||
|
||||
for (y in tyStart..tyEnd) {
|
||||
for (x in txStart..txEnd) {
|
||||
val tile = map.getTileFromTerrain(x, y)
|
||||
if (TilePropCodex.getProp(tile).isSolid)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private fun isTouchingSide(hitbox: Hitbox, option: Int): Boolean {
|
||||
val x1: Double; val x2: Double; val y1: Double; val y2: Double
|
||||
if (option == COLLIDING_TOP) {
|
||||
x1 = hitbox.posX
|
||||
x2 = hitbox.endPointX
|
||||
y1 = hitbox.posY - A_PIXEL
|
||||
y2 = y1
|
||||
}
|
||||
else if (option == COLLIDING_BOTTOM) {
|
||||
x1 = hitbox.posX
|
||||
x2 = hitbox.endPointX
|
||||
y1 = hitbox.endPointY + A_PIXEL
|
||||
y2 = y1
|
||||
}
|
||||
else if (option == COLLIDING_LEFT) {
|
||||
x1 = hitbox.posX - A_PIXEL
|
||||
x2 = x1
|
||||
y1 = hitbox.posY
|
||||
y2 = hitbox.endPointY
|
||||
}
|
||||
else if (option == COLLIDING_RIGHT) {
|
||||
x1 = hitbox.endPointX + A_PIXEL
|
||||
x2 = x1
|
||||
y1 = hitbox.posY
|
||||
y2 = hitbox.endPointY
|
||||
}
|
||||
else throw IllegalArgumentException()
|
||||
|
||||
val txStart = x1.div(TSIZE).roundInt()
|
||||
val txEnd = x2.div(TSIZE).roundInt()
|
||||
val tyStart = y1.div(TSIZE).roundInt()
|
||||
val tyEnd = y2.div(TSIZE).roundInt()
|
||||
|
||||
for (y in tyStart..tyEnd) {
|
||||
for (x in txStart..txEnd) {
|
||||
val tile = map.getTileFromTerrain(x, y)
|
||||
if (TilePropCodex.getProp(tile).isSolid)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
private fun isCollidingSide(hitbox: Hitbox, option: Int): Boolean {
|
||||
val x1: Double; val x2: Double; val y1: Double; val y2: Double
|
||||
if (option == COLLIDING_TOP) {
|
||||
x1 = hitbox.posX
|
||||
x2 = hitbox.endPointX
|
||||
y1 = hitbox.posY
|
||||
y2 = y1
|
||||
}
|
||||
else if (option == COLLIDING_BOTTOM) {
|
||||
x1 = hitbox.posX
|
||||
x2 = hitbox.endPointX
|
||||
y1 = hitbox.endPointY
|
||||
y2 = y1
|
||||
}
|
||||
else if (option == COLLIDING_LEFT) {
|
||||
x1 = hitbox.posX
|
||||
x2 = x1
|
||||
y1 = hitbox.posY
|
||||
y2 = hitbox.endPointY
|
||||
}
|
||||
else if (option == COLLIDING_RIGHT) {
|
||||
x1 = hitbox.endPointX
|
||||
x2 = x1
|
||||
y1 = hitbox.posY
|
||||
y2 = hitbox.endPointY
|
||||
}
|
||||
else throw IllegalArgumentException()
|
||||
|
||||
val txStart = x1.div(TSIZE).roundInt()
|
||||
val txEnd = x2.div(TSIZE).roundInt()
|
||||
val tyStart = y1.div(TSIZE).roundInt()
|
||||
val tyEnd = y2.div(TSIZE).roundInt()
|
||||
|
||||
for (y in tyStart..tyEnd) {
|
||||
for (x in txStart..txEnd) {
|
||||
@@ -792,7 +904,7 @@ open class ActorWithBody : Actor(), Visible {
|
||||
fun Double.sqr() = this * this
|
||||
fun Int.abs() = if (this < 0) -this else this
|
||||
fun Double.bipolarClamp(limit: Double) =
|
||||
if (this > 0 && this > limit) limit
|
||||
if (this > 0 && this > limit) limit
|
||||
else if (this < 0 && this < -limit) -limit
|
||||
else this
|
||||
|
||||
@@ -800,11 +912,12 @@ open class ActorWithBody : Actor(), Visible {
|
||||
// errors
|
||||
if (baseHitboxW == 0 || baseHitboxH == 0)
|
||||
throw RuntimeException("Hitbox dimension was not set.")
|
||||
if (sprite == null && isVisible)
|
||||
throw RuntimeException("Actor ${this.javaClass.canonicalName} is visible but the sprite was not set.")
|
||||
|
||||
// warnings
|
||||
if (!isVisible && sprite != null)
|
||||
println("[ActorWithBody] Caution: actor ${this.javaClass.canonicalName} is invisible but the sprite was given.")
|
||||
if (sprite == null && isVisible)
|
||||
println("[ActorWithBody] Caution: actor ${this.javaClass.simpleName} is visible but the sprite was not set.")
|
||||
else if (sprite != null && !isVisible)
|
||||
println("[ActorWithBody] Caution: actor ${this.javaClass.simpleName} is invisible but the sprite was given.")
|
||||
|
||||
assertPrinted = true
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@ class Hitbox(x1: Double, y1: Double, width: Double, height: Double) {
|
||||
var height: Double = 0.0
|
||||
private set
|
||||
|
||||
val HALF_PIXEL = 0.5
|
||||
|
||||
init {
|
||||
hitboxStart = Point2d(x1, y1)
|
||||
hitboxEnd = Point2d(x1 + width, y1 + height)
|
||||
@@ -54,62 +56,56 @@ class Hitbox(x1: Double, y1: Double, width: Double, height: Double) {
|
||||
* @param width
|
||||
* @param height
|
||||
*/
|
||||
fun set(x1: Double, y1: Double, width: Double, height: Double) {
|
||||
fun set(x1: Double, y1: Double, width: Double, height: Double): Hitbox {
|
||||
hitboxStart = Point2d(x1, y1)
|
||||
hitboxEnd = Point2d(x1 + width, y1 + height)
|
||||
this.width = width
|
||||
this.height = height
|
||||
return this
|
||||
}
|
||||
fun reassign(other: Hitbox) = set(other.posX, other.posY, other.width, other.height)
|
||||
|
||||
fun reassign(other: Hitbox) {
|
||||
set(other.posX, other.posY, other.width, other.height)
|
||||
}
|
||||
fun translate(x: Double, y: Double) = setPosition(posX + x, posY + y)
|
||||
fun translate(vec: Vector2) = translate(vec.x, vec.y)
|
||||
|
||||
fun translate(x: Double, y: Double) {
|
||||
setPosition(posX + x, posY + y)
|
||||
}
|
||||
|
||||
fun translate(vec: Vector2) {
|
||||
translate(vec.x, vec.y)
|
||||
}
|
||||
|
||||
fun setPosition(x1: Double, y1: Double) {
|
||||
fun setPosition(x1: Double, y1: Double): Hitbox {
|
||||
hitboxStart = Point2d(x1, y1)
|
||||
hitboxEnd = Point2d(x1 + width, y1 + height)
|
||||
return this
|
||||
}
|
||||
fun setPosition(vector: Vector2) = setPosition(vector.x, vector.y)
|
||||
|
||||
fun setPositionX(x: Double) {
|
||||
setPosition(x, posY)
|
||||
}
|
||||
fun setPositionX(x: Double) = setPosition(x, posY)
|
||||
fun setPositionY(y: Double) = setPosition(posX, y)
|
||||
|
||||
fun setPositionY(y: Double) {
|
||||
setPosition(posX, y)
|
||||
}
|
||||
|
||||
fun setPositionFromPoint(x1: Double, y1: Double) {
|
||||
fun setPositionFromPoint(x1: Double, y1: Double): Hitbox {
|
||||
hitboxStart = Point2d(x1 - width / 2, y1 - height)
|
||||
hitboxEnd = Point2d(hitboxStart.x + width, hitboxStart.y + height)
|
||||
return this
|
||||
}
|
||||
|
||||
fun setPositionXFromPoint(x: Double) {
|
||||
setPositionFromPoint(x, pointedY)
|
||||
}
|
||||
|
||||
fun setPositionYFromPoint(y: Double) {
|
||||
setPositionFromPoint(pointedX, y)
|
||||
}
|
||||
|
||||
fun translatePosX(d: Double) {
|
||||
fun translatePosX(d: Double): Hitbox {
|
||||
setPositionX(posX + d)
|
||||
return this
|
||||
}
|
||||
|
||||
fun translatePosY(d: Double) {
|
||||
fun translatePosY(d: Double): Hitbox {
|
||||
setPositionY(posY + d)
|
||||
return this
|
||||
}
|
||||
|
||||
fun setDimension(w: Double, h: Double) {
|
||||
fun setDimension(w: Double, h: Double): Hitbox {
|
||||
width = w
|
||||
height = h
|
||||
return this
|
||||
}
|
||||
|
||||
fun snapToPixel(): Hitbox {
|
||||
hitboxStart.x = Math.round(hitboxStart.x - HALF_PIXEL).toDouble()
|
||||
hitboxStart.y = Math.round(hitboxStart.y - HALF_PIXEL).toDouble()
|
||||
hitboxEnd.x = Math.round(hitboxEnd.x - HALF_PIXEL).toDouble()
|
||||
hitboxEnd.y = Math.round(hitboxEnd.y - HALF_PIXEL).toDouble()
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -132,9 +128,7 @@ class Hitbox(x1: Double, y1: Double, width: Double, height: Double) {
|
||||
val centeredY: Double
|
||||
get() = (hitboxStart.y + hitboxEnd.y) * 0.5f
|
||||
|
||||
fun toVector(): Vector2 {
|
||||
return Vector2(posX, posY)
|
||||
}
|
||||
fun toVector(): Vector2 = Vector2(posX, posY)
|
||||
|
||||
fun clone(): Hitbox = Hitbox(posX, posY, width, height)
|
||||
}
|
||||
|
||||
@@ -9,8 +9,7 @@ object KeyMap {
|
||||
|
||||
var map_code = Hashtable<EnumKeyFunc, Int>()
|
||||
|
||||
fun build() {
|
||||
|
||||
init {
|
||||
map_code.put(EnumKeyFunc.MOVE_UP, Key.E)
|
||||
map_code.put(EnumKeyFunc.MOVE_LEFT, Key.S)
|
||||
map_code.put(EnumKeyFunc.MOVE_DOWN, Key.D)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package net.torvald.terrarum.langpack
|
||||
|
||||
import net.torvald.CSVFetcher
|
||||
import net.torvald.JsonFetcher
|
||||
import net.torvald.imagefont.GameFontWhite
|
||||
import net.torvald.terrarum.Terrarum
|
||||
import org.apache.commons.csv.CSVRecord
|
||||
@@ -14,81 +15,106 @@ import java.util.*
|
||||
*/
|
||||
object Lang {
|
||||
|
||||
private val CSV_COLUMN_FIRST = "STRING_ID"
|
||||
/**
|
||||
* Get record by its STRING_ID
|
||||
*
|
||||
* HashMap<"$key_$language", Value>
|
||||
*/
|
||||
private var lang: HashMap<String, CSVRecord>
|
||||
private val FALLBACK_LANG_CODE = "enUS"
|
||||
private var langpack: HashMap<String, String>
|
||||
private val FALLBACK_LANG_CODE = "en"
|
||||
|
||||
private val HANGUL_SYL_START = 0xAC00
|
||||
|
||||
private val PATH_TO_CSV = "./res/locales/"
|
||||
private val CSV_MAIN = "polyglot.csv"
|
||||
private val NAMESET_PREFIX = "nameset_"
|
||||
val languageList: List<String>
|
||||
|
||||
private val PATH_TO_LANG = "./res/locales/"
|
||||
val POLYGLOT_VERSION = "100"
|
||||
private val PREFIX_POLYGLOT = "Polyglot-${POLYGLOT_VERSION}_"
|
||||
private val PREFIX_NAMESET = "nameset_"
|
||||
|
||||
private val HANGUL_POST_INDEX_ALPH = intArrayOf(// 0: 는, 가, ... 1: 은, 이, ...
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
|
||||
private val HANGUL_POST_RO_INDEX_ALPH = intArrayOf(// 0: 로 1: 으로
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
|
||||
|
||||
private val ENGLISH_WORD_NORMAL_PLURAL = arrayOf("photo")
|
||||
private val ENGLISH_WORD_NORMAL_PLURAL = arrayOf("photo", "demo")
|
||||
|
||||
private val FRENCH_WORD_NORMAL_PLURAL = arrayOf("bal", "banal", "fatal", "final")
|
||||
|
||||
var TIPS_COUNT = 0
|
||||
private set
|
||||
|
||||
init {
|
||||
lang = HashMap<String, CSVRecord>()
|
||||
langpack = HashMap<String, String>()
|
||||
val localesDir = File(PATH_TO_LANG)
|
||||
|
||||
// append CSV records to the main langpack
|
||||
val file = File(PATH_TO_CSV)
|
||||
val filter = FilenameFilter { dir, name -> name.contains(".csv") && !name.contains(NAMESET_PREFIX) }
|
||||
for (csvfilename in file.list(filter)) {
|
||||
val csv = CSVFetcher.readCSV(PATH_TO_CSV + csvfilename)
|
||||
//csv.forEach({ langPackCSV. })
|
||||
csv.forEach { it -> lang.put(it.get(CSV_COLUMN_FIRST), it) }
|
||||
// get all of the languages installed
|
||||
languageList = localesDir.listFiles().filter { it.isDirectory }.map { it.name }
|
||||
|
||||
for (lang in languageList) {
|
||||
// load polyglot first
|
||||
val polyglotFile = File("$PATH_TO_LANG$lang/$PREFIX_POLYGLOT$lang.json")
|
||||
val json = JsonFetcher.readJson(polyglotFile)
|
||||
/*
|
||||
* Polyglot JSON structure is:
|
||||
*
|
||||
* (root object)
|
||||
* "resources": object
|
||||
* "polyglot": object
|
||||
* (polyglot meta)
|
||||
* "data": array
|
||||
* [0]: object
|
||||
* n = "CONTEXT_CHARACTER_CLASS"
|
||||
* s = "Class"
|
||||
* [1]: object
|
||||
* n = "CONTEXT_CHARACTER_DELETE"
|
||||
* s = "Delecte Character"
|
||||
* (the array continues)
|
||||
*
|
||||
*/
|
||||
json.getAsJsonObject("resources").getAsJsonArray("data").forEach {
|
||||
langpack.put(
|
||||
"${it.asJsonObject["n"].asString}_$lang",
|
||||
it.asJsonObject["s"].asString
|
||||
)
|
||||
}
|
||||
|
||||
// and then the rest of the lang file
|
||||
val langFileList = ArrayList<File>()
|
||||
|
||||
// --> filter out files to retrieve a list of valid lang files
|
||||
val langFileListFiles = File("$PATH_TO_LANG$lang/").listFiles()
|
||||
langFileListFiles.forEach {
|
||||
if (!it.name.startsWith("Polyglot") && it.name.endsWith(".json"))
|
||||
langFileList.add(it)
|
||||
}
|
||||
|
||||
// --> put json entries in langpack
|
||||
for (langFile in langFileList) {
|
||||
val json = JsonFetcher.readJson(langFile)
|
||||
/*
|
||||
* Terrarum langpack JSON structure is:
|
||||
*
|
||||
* (root object)
|
||||
* "<<STRING ID>>" = "<<LOCALISED TEXT>>"
|
||||
*/
|
||||
//println(json.entrySet())
|
||||
json.entrySet().forEach {
|
||||
langpack.put("${it.key}_$lang", it.value.asString)
|
||||
|
||||
// count up TIPS_COUNT
|
||||
if (lang == "en" && it.key.startsWith("GAME_TIPS_")) TIPS_COUNT++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sort word lists
|
||||
Arrays.sort(ENGLISH_WORD_NORMAL_PLURAL)
|
||||
Arrays.sort(FRENCH_WORD_NORMAL_PLURAL)
|
||||
|
||||
// reload correct (C/J) unihan fonts if applicable
|
||||
try {
|
||||
(Terrarum.gameFont as GameFontWhite).reloadUnihan()
|
||||
}
|
||||
catch (e: SlickException) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun getRecord(key: String): CSVRecord {
|
||||
val record = lang[key]
|
||||
if (record == null) {
|
||||
println("[Lang] No such record: $key")
|
||||
throw NullPointerException()
|
||||
}
|
||||
return record
|
||||
}
|
||||
|
||||
operator fun get(key: String): String {
|
||||
fun fallback(): String = lang[key]!!.get(FALLBACK_LANG_CODE)
|
||||
fun fallback(): String = langpack["${key}_$FALLBACK_LANG_CODE"] ?: "ERRNULL:$key"
|
||||
|
||||
var value: String
|
||||
try {
|
||||
value = lang[key]!!.get(Terrarum.gameLocale)
|
||||
// fallback if empty string
|
||||
if (value.length == 0)
|
||||
value = fallback()
|
||||
}
|
||||
catch (e1: kotlin.KotlinNullPointerException) {
|
||||
value = "ERRNULL:$key"
|
||||
}
|
||||
catch (e: IllegalArgumentException) {
|
||||
//value = key
|
||||
value = fallback()
|
||||
}
|
||||
val ret = langpack["${key}_${Terrarum.gameLocale}"]
|
||||
|
||||
return value
|
||||
return if (ret.isNullOrEmpty()) fallback() else ret!!
|
||||
}
|
||||
|
||||
fun pluraliseLang(key: String, count: Int): String {
|
||||
@@ -185,8 +211,4 @@ object Lang {
|
||||
private fun getLastChar(s: String): Char {
|
||||
return s[s.length - 1]
|
||||
}
|
||||
|
||||
private fun appendToLangByStringID(record: CSVRecord) {
|
||||
lang.put(record.get(CSV_COLUMN_FIRST), record)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,12 +27,11 @@ class TileProp {
|
||||
/**
|
||||
* @param luminosity Raw RGB value, without alpha
|
||||
*/
|
||||
private var realLum: Int = 0
|
||||
var luminosity: Int
|
||||
var luminosity: Int = 0
|
||||
set(value) {
|
||||
realLum = value
|
||||
field = value
|
||||
}
|
||||
get() = TilePropUtil.getDynamicLumFunc(realLum, dynamicLuminosityFunction)
|
||||
get() = TilePropUtil.getDynamicLumFunc(field, dynamicLuminosityFunction)
|
||||
|
||||
var drop: Int = 0
|
||||
var dropDamage: Int = 0
|
||||
|
||||
Reference in New Issue
Block a user