Lightmap.get/MapLayer.get are now nullable, better overworld generator with Joise, still needs fix in the dirt layer part.

Former-commit-id: 0a5fa31bab45d093771ca109adbeb9c07fb8e90e
Former-commit-id: d8a2b5b77a36b6c742cf220b2d325895111c8f3a
This commit is contained in:
Song Minjae
2016-03-29 13:15:03 +09:00
parent 2a26c12821
commit aa914377fe
47 changed files with 894 additions and 791 deletions

View File

@@ -1,16 +1,19 @@
## From playtests ## # Unresolved #
### Character ### ### Character ###
* Cannot fit into single-tile width pit
Cause: Player/NPC looks slim enough to fit, but don't fit in the game
Solution: Draw them wider, allow them to fit into the pit (Phys resolver will glitch?)
* Arm behind the body seems unnatural
## Internal ##
### Phys ### ### Phys ###
* Actor stick to wall and not get off * Actor stick to wall and not get off
* Actor with mass <2 behaves erratically
* Actor with mass <2 behaves erratically
# Resolved #
* Cannot fit into single-tile width pit
Cause: Player/NPC looks slim enough to fit, but don't fit in the game
Solution: Draw them wider, allow them to fit into the pit (Phys resolver will glitch?)
__Solved__ — Player/NPC hitbox now have a width of 15 pixels.

View File

@@ -9,8 +9,11 @@ Documentations and resources for work (such as .psd) are also included in the re
Any contribution in this project must be made sorely in English, so be sure to use English in codes, comments, etc. Any contribution in this project must be made sorely in English, so be sure to use English in codes, comments, etc.
## Setup ## ## Setup ##
* Configuration
Just make sure you have JDK 8 or higher * Requirements:
- JDK 8 or higher
- Working copy of IntelliJ IDEA from JetBrains s.r.o., community edition is okay to use.
* Required libraries are included in the repository. * Required libraries are included in the repository.
@@ -20,8 +23,8 @@ Just make sure you have JDK 8 or higher
* Writing tests * Writing tests
* Code review * Code review
* Guidelines * Guidelines
Quintessential: write code that even an imbecile can understand. - Quintessential: write code that even an imbecile can understand.
### Contributing translations ### ### Contributing translations ###
@@ -42,10 +45,10 @@ Note: Right-to-left languages (arabic, hebrew, etc.) are not supported.
## 개요 ## ## 개요 ##
이 변변한 이름 없는 프로젝트는 사이드뷰 발판 게임 형식으로 더 친절한 〈드워프 포트리스〉의 모험가 모드를 지향하는 게임 제작 프로젝트입니다. 영구 사망, 무작위성, __넘쳐나는 재미__와 같이 로그라이크스러운 요소를 지닙니다. 이 변변한 이름 없는 프로젝트는 사이드뷰 발판 게임 형식으로 더 친절한 〈드워프 포트리스〉의 모험가 모드를 지향하는 게임 제작 프로젝트입니다. 영구 사망, 무작위성, __넘쳐나는 재미__와 같이 로그라이크스러운 요소를 지닙니다.
이 프로젝트는 주 언어로 코틀린, 자바를 사용하며 파이선·루아 등으로 작성된 툴을 이용합니다. 이 프로젝트는 주 언어로 코틀린·자바를 사용하며 파이선·루아 등으로 작성된 툴을 이용합니다.
문서와 작업용 리소스(psd 등) 또한 이 저장소에 포함되어 있습니다. gcx와 numbers 형식으로 된 문서를 읽고 수정하기 위해 맥 컴퓨터가 필요할 수 있습니다. 개발 문서와 작업용 리소스(psd 등) 또한 이 저장소에 포함되어 있습니다. gcx와 numbers 형식으로 된 문서를 읽고 수정하기 위해 맥 컴퓨터가 필요할 수 있습니다.
이 프로젝트에 대한 기여는 영어로 이루어져야 합니다. 따라서 코드나 주석 등을 작성할 때는 영어를 사용해 주십시오. 이 프로젝트에 대한 기여는 영어로 이루어져야 합니다. 따라서 코드나 주석 등을 작성할 때는 영어를 사용해 주십시오.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -1,14 +1,14 @@
"STRING_ID";"IETF language tag(s) without dash";"enUS";"frFR";"esES";"deDE";"itIT";"ptBR";"ptPT";"ruRU";"elGR";"trTR";"daDK";"noNB";"svSE";"nlNL";"plPL";"fiFI";"jaJP";"zhCN";"zhTW";"koKR";"csCZ";"huHU";"roRO";"thTH";"bgBG";"heIL";"jakanaJP";"isIC" "STRING_ID";"IETF language tag(s) without dash";"enUS";"frFR";"esES";"deDE";"itIT";"ptBR";"ptPT";"ruRU";"elGR";"trTR";"daDK";"noNB";"svSE";"nlNL";"plPL";"fiFI";"jaJP";"zhCN";"zhTW";"koKR";"csCZ";"huHU";"roRO";"thTH";"bgBG";"heIL";"jakanaJP";"isIC"
"HELP_OTF_MAIN_TEXT_1";;"Type “help slow” for the ways to make the game run faster.";;;;;;;;;;;;;;;;;;;"게임이 느리게 돌아간다면 “help slow”를 입력해 보세요." "HELP_OTF_MAIN_1";;"Type “help slow” for the ways to make the game run faster.";"Tapez « help slow » si votre jeu fonctionne lentement.";;;;;;;;;;;;;;;;;;"게임이 느리게 돌아간다면 “help slow”를 입력해 보세요."
"HELP_OTF_MAIN_TEXT_2";;"Press PageUp/PageDown to scroll the messages.";;;;;;;;;;;;;;;;;;;"PageUp/PageDown 키를 사용해 메시지를 스크롤할 수 있습니다." "HELP_OTF_MAIN_2";;"Press PageUp/PageDown to scroll the messages.";"Appuyez sur PageUp/Pagedown pour faire défiler les messages.";;;;;;;;;;;;;;;;;;"PageUp/PageDown 키를 사용해 메시지를 스크롤할 수 있습니다."
"HELP_OTF_MAIN_TEXT_3";;"Utility keys:";;;;;;;;;;;;;;;;;;;"기능 키:" "HELP_OTF_MAIN_3";;"Utility keys:";"Touches utilitaires:";;;;;;;;;;;;;;;;;;"기능 키:"
"HELP_OTF_MAIN_TEXT_4";;"• F3: basic information";;;;;;;;;;;;;;;;;;;"• F3: 기본 정보" "HELP_OTF_MAIN_4";;"• F3: basic information";"• F3: informations de base";;;;;;;;;;;;;;;;;;"• F3: 기본 정보"
"HELP_OTF_MAIN_TEXT_5";;"• F7: (debug only) toggle light blending";;;;;;;;;;;;;;;;;;;"• F7: (디버그용) 광원 블렌딩 켜고 끄기" "HELP_OTF_MAIN_5";;"• F7: (debug) toggle light blending";"• F7: (déboguer) basculer fusion de lumière";;;;;;;;;;;;;;;;;;"• F7: (디버그용) 광원 블렌딩 켜고 끄기"
"HELP_OTF_MAIN_TEXT_6";;"• F8: toggle smooth lighting";;;;;;;;;;;;;;;;;;;"• F8: 부드러운 광원 켜고 끄기" "HELP_OTF_MAIN_6";;"• F8: toggle smooth lighting";"• F8: basculer éclairage lisse";;;;;;;;;;;;;;;;;;"• F8: 부드러운 광원 켜고 끄기"
"HELP_OTF_SLOW_TEXT_1";;"To make your game run faster:";;;;;;;;;;;;;;;;;;;"게임을 빠르게 하려면" "HELP_OTF_SLOW_1";;"To make your game run faster:";"Pour rendre votre jeu courir plus vite :";;;;;;;;;;;;;;;;;;"게임을 빠르게 하려면"
"HELP_OTF_SLOW_TEXT_2";;"• Reset screen zoom to 1.";;;;;;;;;;;;;;;;;;;"• 화면 줌을 1로 돌려주세요. " "HELP_OTF_SLOW_IF_ZOOM";;"• Reset screen zoom to 1.";"• Réinitialisez le zoom de lécran à 1.";;;;;;;;;;;;;;;;;;"• 화면 줌을 1로 돌려주세요. "
"HELP_OTF_SLOW_TEXT_3";;"• Turn off basic information window using F3.";;;;;;;;;;;;;;;;;;;"• F3을 눌러 기본 정보 창을 꺼 주세요." "HELP_OTF_SLOW_IF_F3";;"• Turn off the basic information window.";"• Désactivez la fenêtre dinformations.";;;;;;;;;;;;;;;;;;"• 기본 정보 창을 꺼 주세요."
"HELP_OTF_SLOW_TEXT_4";;"• Turn off smooth lighting. You can do it now by pressing F8.";;;;;;;;;;;;;;;;;;;"• 부드러운 광원 효과를 꺼 주세요. F8을 사용할 수 있습니다." "HELP_OTF_SLOW_1";;"• Turn off smooth lighting. You can do it now by pressing F8.";"• Désactivez éclairage lisse en utilisant F8.";;;;;;;;;;;;;;;;;;"• 부드러운 광원 효과를 꺼 주세요. F8을 사용할 수 있습니다."
1 STRING_ID IETF language tag(s) without dash enUS frFR esES deDE itIT ptBR ptPT ruRU elGR trTR daDK noNB svSE nlNL plPL fiFI jaJP zhCN zhTW koKR csCZ huHU roRO thTH bgBG heIL jakanaJP isIC
2 HELP_OTF_MAIN_TEXT_1 HELP_OTF_MAIN_1 Type “help slow” for the ways to make the game run faster. Tapez « help slow » si votre jeu fonctionne lentement. 게임이 느리게 돌아간다면 “help slow”를 입력해 보세요.
3 HELP_OTF_MAIN_TEXT_2 HELP_OTF_MAIN_2 Press PageUp/PageDown to scroll the messages. Appuyez sur PageUp/Pagedown pour faire défiler les messages. PageUp/PageDown 키를 사용해 메시지를 스크롤할 수 있습니다.
4 HELP_OTF_MAIN_TEXT_3 HELP_OTF_MAIN_3 Utility keys: Touches utilitaires: 기능 키:
5 HELP_OTF_MAIN_TEXT_4 HELP_OTF_MAIN_4 • F3: basic information • F3: informations de base • F3: 기본 정보
6 HELP_OTF_MAIN_TEXT_5 HELP_OTF_MAIN_5 • F7: (debug only) toggle light blending • F7: (debug) toggle light blending • F7: (déboguer) basculer fusion de lumière • F7: (디버그용) 광원 블렌딩 켜고 끄기
7 HELP_OTF_MAIN_TEXT_6 HELP_OTF_MAIN_6 • F8: toggle smooth lighting • F8: basculer éclairage lisse • F8: 부드러운 광원 켜고 끄기
8 HELP_OTF_SLOW_TEXT_1 HELP_OTF_SLOW_1 To make your game run faster: Pour rendre votre jeu courir plus vite : 게임을 빠르게 하려면
9 HELP_OTF_SLOW_TEXT_2 HELP_OTF_SLOW_IF_ZOOM • Reset screen zoom to 1. • Réinitialisez le zoom de l‘écran à 1. • 화면 줌을 1로 돌려주세요.
10 HELP_OTF_SLOW_TEXT_3 HELP_OTF_SLOW_IF_F3 • Turn off basic information window using F3. • Turn off the basic information window. • Désactivez la fenêtre d‘informations. • F3을 눌러 기본 정보 창을 꺼 주세요. • 기본 정보 창을 꺼 주세요.
11 HELP_OTF_SLOW_TEXT_4 HELP_OTF_SLOW_1 • Turn off smooth lighting. You can do it now by pressing F8. • Désactivez éclairage lisse en utilisant F8. • 부드러운 광원 효과를 꺼 주세요. F8을 사용할 수 있습니다.
12
13
14

View File

@@ -1,130 +0,0 @@
== CHALLENGING, NOT PUNISHING == https://www.youtube.com/watch?v=ea6UuRTjkKs
1. CONSISTENT RULES
- No arbitrary unstoppable death
2. Player's skill involved
- Can play around, not restart
3. Usability of in-game tools
- Players should be able to 'regret' their strategy and adjust.
4. Comfortable control
5. Make players overcome the challenge, not defeating them
6. Let players have "aha" moment when they failed.
- Make them hungry to retry with new strategies.
- Some small things they've could done differently
- e.g. "One-big-hit didn't worked, may I should've picked up high DPS one"
== MORE DEPTH, LESS COMPLEXITY == https://www.youtube.com/watch?v=jVL4st0blGU
1. Memorise less!
- Less burden to, even starting the game
- Start with gentle learning curve, getting slowly steep
- Intuitive UX (UI, control, ...)
- Good tutorial = lessens complexity
2. Intuitive!
3. Calculations per second
- reduce!
4. Players have to know everything to even begin the play == FAIL (irreducible complexity)
- Make them get familiar with rules of the game
- Dwarf Fortress failed this!
== Lots of things players can play with (aka don't make them bored) ==
- Combat, battle, building, mechanics, adventure, dungeon explore, spelunking
- Not scaled; easy combat, tough combat, tedious combat, etc.
== Achieving perfect imbalance == https://www.youtube.com/watch?v=e31OSVZF77w
- Make sure no matter how you skilled, your playable character cannot be good at everything
- Give players __wide pool of options__ to solve problem
(kill the boss, defend their adobe, fast transportation, etc.)
-=*## What feeling do you want to convey? ##*=-
== Always think WHY you want to add _something_ on the game ==
- e.g. Why are you adding RPG leveling system? What it would do to the players? How would they play with?
See also: HEARTS, CLUBS, DIAMONDS, SPADES: PLAYERS WHO SUIT MUDS
====================================
== Friendlier version of Dwarf Fortress Adventure mode ==
- Yet _lots of fun_
- Add Fortress mode features by 'make your own settling'
- Hard to actually die, but once you die, you're done.
+ Config: imtooyoungtodie for easy mode
- Genre: Adventure, Open world (towns in RPG, building, town managing (conquer existing one or
you build one and persuade existing people to move in) -> See Dwarf Fortress and Animal Crossing)
* Adventure: adventure this vast—5,5 km wide—world, discover new (and good/horrible) things
* Open world:
- Building: building your own houses, structures, etc.
- Town managing:
1. Build your own little hamlet and manage it
or-
2. Conquer existing one and become a ruler
The town is a special hamlet that can be tailored for your taste
- Survival:
mobs will trying to attack your assets (yourself, your hamlet, your people)
== Side view ==
== Interact menu w/ mouse right ==
== Pixelated sprites ==
- Use 2x sprites if rotating does not work well
== User experience ==
* Indicative mouse cursor
== Game mechanics ==
* 24 pixels == 1 metre
== Purpose of the game ==
* Boss
- Will be mentioned/shown as absolute _evil_.
- But actually is not.
* Theme
- Is an evil really really is what we think?
- Is there a thing as 'absolute evil'?
* Boss character
- From debugger character
- Name key: "Sigriðr hinn Dróttningin" (can be changed)
* Little setting
- A ruler, hated by people
* Mechanics
- Beating boss does not ends the game, but grants an ability to
create new character as it.

View File

@@ -1,6 +0,0 @@
* Terrarum by Torvald
Copyright 2015-2016 Torvald. All rights reserved.
mailto: alswo9628 *at* !gmail! *dot* !com!
* Simplex Noise Generator, version 2012-03-09 by Stefan Gustavson
Released as public domain

View File

@@ -14,13 +14,11 @@ import com.torvald.terrarum.mapdrawer.MapDrawer
import com.torvald.terrarum.mapgenerator.MapGenerator import com.torvald.terrarum.mapgenerator.MapGenerator
import com.torvald.terrarum.mapgenerator.RoguelikeRandomiser import com.torvald.terrarum.mapgenerator.RoguelikeRandomiser
import com.torvald.terrarum.tileproperties.TilePropCodex import com.torvald.terrarum.tileproperties.TilePropCodex
import com.torvald.terrarum.tilestats.TileStat import com.torvald.terrarum.tilestats.TileStats
import com.torvald.terrarum.ui.BasicDebugInfoWindow import com.torvald.terrarum.ui.BasicDebugInfoWindow
import com.torvald.terrarum.ui.ConsoleWindow import com.torvald.terrarum.ui.ConsoleWindow
import com.torvald.terrarum.ui.Notification import com.torvald.terrarum.ui.Notification
import com.torvald.terrarum.ui.UIHandler import com.torvald.terrarum.ui.UIHandler
import com.jme3.math.FastMath
import org.lwjgl.opengl.ARBShaderObjects
import org.lwjgl.opengl.GL11 import org.lwjgl.opengl.GL11
import org.newdawn.slick.* import org.newdawn.slick.*
import org.newdawn.slick.fills.GradientFill import org.newdawn.slick.fills.GradientFill
@@ -69,17 +67,17 @@ constructor() : BasicGameState() {
private val ENV_SUNLIGHT_DELTA = MapDrawer.ENV_COLTEMP_NOON - ENV_COLTEMP_SUNRISE private val ENV_SUNLIGHT_DELTA = MapDrawer.ENV_COLTEMP_NOON - ENV_COLTEMP_SUNRISE
var memInUse: Long = 0 val memInUse: Long
get() = ManagementFactory.getMemoryMXBean().heapMemoryUsage.used shr 20 get() = ManagementFactory.getMemoryMXBean().heapMemoryUsage.used shr 20
var totalVMMem: Long = 0 val totalVMMem: Long
get() = Runtime.getRuntime().maxMemory() shr 20 get() = Runtime.getRuntime().maxMemory() shr 20
var auth = Authenticator() val auth = Authenticator()
private var update_delta: Int = 0 private var update_delta: Int = 0
private val KEY_LIGHTMAP_RENDER = Key.F7 val KEY_LIGHTMAP_RENDER = Key.F7
private val KEY_LIGHTMAP_SMOOTH = Key.F8 val KEY_LIGHTMAP_SMOOTH = Key.F8
@Throws(SlickException::class) @Throws(SlickException::class)
override fun init(gameContainer: GameContainer, stateBasedGame: StateBasedGame) { override fun init(gameContainer: GameContainer, stateBasedGame: StateBasedGame) {
@@ -109,8 +107,8 @@ constructor() : BasicGameState() {
// add new player and put it to actorContainer // add new player and put it to actorContainer
//player = PFSigrid.build() player = PFSigrid.create()
player = PFCynthia.create() //player = PFCynthia.create()
//player.setNoClip(true); //player.setNoClip(true);
actorContainer.add(player) actorContainer.add(player)
@@ -141,7 +139,7 @@ constructor() : BasicGameState() {
GameController.processInput(gc.input) GameController.processInput(gc.input)
TileStat.update() TileStats.update()
MapDrawer.update(gc, delta) MapDrawer.update(gc, delta)
MapCamera.update(gc, delta) MapCamera.update(gc, delta)
@@ -189,8 +187,10 @@ constructor() : BasicGameState() {
MapCamera.renderBehind(gc, g) MapCamera.renderBehind(gc, g)
actorContainer.forEach { actor -> if (actor is Visible) actor.drawBody(gc, g) } actorContainer.forEach { actor ->
actorContainer.forEach { actor -> if (actor is Glowing) actor.drawGlow(gc, g) } if (actor is Visible) actor.drawBody(gc, g)
if (actor is Glowing) actor.drawGlow(gc, g)
}
player.drawBody(gc, g) player.drawBody(gc, g)
player.drawGlow(gc, g) player.drawGlow(gc, g)

View File

@@ -1,11 +1,3 @@
/*
* MapLoader version 1.2
* Release date 2013-05-20
* Copyright 2013 SKYHi14
*
* The program is distributed in GNU GPL Licence version 3.
* See http://www.gnu.org/licenses/gpl.html for information.
*/
package com.torvald.terrarum.gamemap package com.torvald.terrarum.gamemap
@@ -89,23 +81,33 @@ constructor(//properties
val damageDataArray: Array<ByteArray> val damageDataArray: Array<ByteArray>
get() = terrainDamage.dataPair get() = terrainDamage.dataPair
fun getTileFromWall(x: Int, y: Int): Int { fun getTileFromWall(x: Int, y: Int): Int? {
return layerWall.getTile(x, y) * PairedMapLayer.RANGE + getWallDamage(x, y) val wall: Int? = layerWall.getTile(x, y)
val wallDamage: Int? = getWallDamage(x, y)
return if (wall == null || wallDamage == null)
null
else
wall * PairedMapLayer.RANGE + wallDamage
} }
fun getTileFromTerrain(x: Int, y: Int): Int { fun getTileFromTerrain(x: Int, y: Int): Int? {
return layerTerrain.getTile(x, y) * PairedMapLayer.RANGE + getTerrainDamage(x, y) val terrain: Int? = layerTerrain.getTile(x, y)
val terrainDamage: Int? = getTerrainDamage(x, y)
return if (terrain == null || terrainDamage == null)
null
else
terrain * PairedMapLayer.RANGE + terrainDamage
} }
fun getTileFromWire(x: Int, y: Int): Int { fun getTileFromWire(x: Int, y: Int): Int? {
return layerWire.getTile(x, y) return layerWire.getTile(x, y)
} }
fun getWallDamage(x: Int, y: Int): Int { fun getWallDamage(x: Int, y: Int): Int? {
return wallDamage.getData(x, y) return wallDamage.getData(x, y)
} }
fun getTerrainDamage(x: Int, y: Int): Int { fun getTerrainDamage(x: Int, y: Int): Int? {
return terrainDamage.getData(x, y) return terrainDamage.getData(x, y)
} }
@@ -147,7 +149,7 @@ constructor(//properties
layerWire.data[y][x] = tile layerWire.data[y][x] = tile
} }
fun getTileFrom(mode: Int, x: Int, y: Int): Int { fun getTileFrom(mode: Int, x: Int, y: Int): Int? {
if (mode == TERRAIN) { if (mode == TERRAIN) {
return getTileFromTerrain(x, y) return getTileFromTerrain(x, y)
} }

View File

@@ -1,9 +1,5 @@
package com.torvald.terrarum.gamemap package com.torvald.terrarum.gamemap
import java.io.Serializable
import java.util.Spliterator
import java.util.function.Consumer
/** /**
* Created by minjaesong on 16-01-17. * Created by minjaesong on 16-01-17.
*/ */
@@ -40,8 +36,11 @@ class MapLayer(var width: Int, var height: Int) : Iterable<Byte> {
} }
} }
internal fun getTile(x: Int, y: Int): Int { internal fun getTile(x: Int, y: Int): Int? {
return uint8ToInt32(data[y][x]) return if (x !in 0..width - 1 || y !in 0..height - 1)
null
else
uint8ToInt32(data[y][x])
} }
internal fun setTile(x: Int, y: Int, tile: Byte) { internal fun setTile(x: Int, y: Int, tile: Byte) {
@@ -51,8 +50,7 @@ class MapLayer(var width: Int, var height: Int) : Iterable<Byte> {
private fun uint8ToInt32(x: Byte): Int = java.lang.Byte.toUnsignedInt(x) private fun uint8ToInt32(x: Byte): Int = java.lang.Byte.toUnsignedInt(x)
companion object { companion object {
@Transient val RANGE = 256
@Transient @JvmStatic val RANGE = 256
} }
} }

View File

@@ -53,13 +53,17 @@ class PairedMapLayer(width: Int, var height: Int) : Iterable<Byte> {
} }
} }
internal fun getData(x: Int, y: Int): Int { internal fun getData(x: Int, y: Int): Int? {
if (x and 0x1 == 0) return if (x !in 0..width * 2 - 1 || y !in 0..height - 1)
// higher four bits for i = 0, 2, 4, ... null
return (java.lang.Byte.toUnsignedInt(dataPair[y][x / 2]) and 0xF0) ushr 4 else {
else if (x and 0x1 == 0)
// lower four bits for i = 1, 3, 5, ... // higher four bits for i = 0, 2, 4, ...
return java.lang.Byte.toUnsignedInt(dataPair[y][x / 2]) and 0x0F (java.lang.Byte.toUnsignedInt(dataPair[y][x / 2]) and 0xF0) ushr 4
else
// lower four bits for i = 1, 3, 5, ...
java.lang.Byte.toUnsignedInt(dataPair[y][x / 2]) and 0x0F
}
} }
internal fun setData(x: Int, y: Int, data: Int) { internal fun setData(x: Int, y: Int, data: Int) {

View File

@@ -143,11 +143,11 @@ class WorldTime {
/** /**
* 22h * 22h
*/ */
@Transient val DAY_LENGTH = 79200 //must be the multiple of 3600 val DAY_LENGTH = 79200 //must be the multiple of 3600
@Transient val HOUR_SEC: Int = 3600 val HOUR_SEC: Int = 3600
@Transient val MINUTE_SEC: Int = 60 val MINUTE_SEC: Int = 60
@Transient val HOUR_MIN: Int = 60 val HOUR_MIN: Int = 60
@Transient val GAME_MIN_TO_REAL_SEC: Float = 60f val GAME_MIN_TO_REAL_SEC: Float = 60f
} }
} }

View File

@@ -39,9 +39,6 @@ object Lang {
init { init {
lang = HashMap<String, CSVRecord>() lang = HashMap<String, CSVRecord>()
// read polyglot.csv first and use this list as a pivot
var langPackCSV: List<CSVRecord> = CSVFetcher.readCSV(PATH_TO_CSV + CSV_MAIN)
// append CSV records to the main langpack // append CSV records to the main langpack
val file = File(PATH_TO_CSV) val file = File(PATH_TO_CSV)
val filter = FilenameFilter { dir, name -> name.contains(".csv") && !name.contains(NAMESET_PREFIX) } val filter = FilenameFilter { dir, name -> name.contains(".csv") && !name.contains(NAMESET_PREFIX) }
@@ -51,15 +48,11 @@ object Lang {
csv.forEach { it -> lang.put(it.get(CSV_COLUMN_FIRST), it) } csv.forEach { it -> lang.put(it.get(CSV_COLUMN_FIRST), it) }
} }
// lang.put(record.get(CSV_COLUMN_FIRST), record) // sort word lists
// Fill lang table
// langPackCSV.forEach({ this.appendToLangByStringID(it) })
Arrays.sort(ENGLISH_WORD_NORMAL_PLURAL) Arrays.sort(ENGLISH_WORD_NORMAL_PLURAL)
Arrays.sort(FRENCH_WORD_NORMAL_PLURAL) Arrays.sort(FRENCH_WORD_NORMAL_PLURAL)
// reload correct (C/J) unihan fonts if applicable
try { try {
(Terrarum.gameFontWhite as GameFontWhite).reloadUnihan() (Terrarum.gameFontWhite as GameFontWhite).reloadUnihan()
} }

View File

@@ -1,112 +0,0 @@
== Weapon tier ==
Natural / Common Stone -> Copper -> Iron -> Silver -> Titanium
Forging --------------> Steel --------^
Exotic ('elven') Glass Aurichalcum
Special (something 'adamant') ??? (Use material spec of CNT, tensile strength 180 GPa)
= Metal graphics
- Gold: Hue 43, low Saturation
- Aurichalcum: Hue 43, mid-high Saturation
- Copper: Hue 33,
- Copper rust: Hue 160
- Iron rust: Hue 21
== Size variation ==
Race base weapon/tool size <- 10 [kg]
Size tolerance <- (50% * str/1000), or say, 20%
If the size is bigger than tolerable, weapon speed severely slows down, tools become unusable
if use time >= 0.75 second, the weapon/tool cannot be equipped.
Small weapons/tools gains no (dis)advantage
When drawing: scale by (craftedWeaponSize / baseWeaponSize)
Crafted tool/weapon size is dependent to the baseRaceMass.
== Gemstone tier ==
Topaz -> R·G·B -> Diamond·Amethyst
== Colouring ==
Natural: Use 4096
Magical/Surreal: Use 24 Bits
= Colouring of potion
- Randomised, roguelike fashion
- Choose Col(R40, G40, B40) from set of finite cards:
39, 39, 19, 19, 0, 0
- MULTIPLY blend chosen colour with white texture
== Roguelike identity ==
= Randomised things
- E.g. potion
Lime-coloured potion
First play: "Potion (???)"
After drank: "Potion (Healing)" is revealed.
Second (new) play: "Potion (???)"
After drank: "Potion (Neurotoxin)" is revealed.
== Making sprite ==
* Layers
- (Optional) Glow
- (Optional) Hair foreground
- Right arm dress
- Right arm body
- Dress
- Boots
- Body
- (Optional) Hair accessory
- Hair
- Head
- Left arm dress
- Left arm body
- (Optional) SFX
* Size
- Regular sprite 'height' (hitbox height) : 40 px
- Apparent height may vary
== Chargen ==
* Select hair, colours, then compile them into single spritesheet
* NO gender distinction, but have masculine/neutral/feminine looks (in clothing, hairstyles, etc.)
* Colour: 4096 colours (12-bit 0x000 - 0xFFF)
* Base mass: 60 kg
== Custom pattern making ==
- Players can create their own décors (hang on wall), dresses.
- Two looms (216 colour mode, 4096 colour mode)
== Food/Potion dose ==
Scale ^ 3 ^ (3/4)
= (ThisWgt / TargetWgt) ^ (3/4)
== (De)serialisation ==
see SAVE_FORMAT
== Actor being universal ==
* Utility tiles that have states (e.g. noteblock) are implemented using Actor.

View File

@@ -19,7 +19,7 @@ object LightmapRenderer {
* 8-Bit RGB values * 8-Bit RGB values
*/ */
@Volatile private var lightMapMSB: Array<CharArray>? = null @Volatile private var lightMapMSB: Array<CharArray>? = null
@Volatile private var lightMapLSB: Array<ByteArray>? = null @Volatile private var lightMapLSB: Array<ByteArray>? = null // modify this to CharArray to implement 30-bit RGB
private var lightMapInitialised = false private var lightMapInitialised = false
/** /**
@@ -38,14 +38,14 @@ object LightmapRenderer {
private val TSIZE = MapDrawer.TILE_SIZE private val TSIZE = MapDrawer.TILE_SIZE
// color model related vars // color model related vars
const val MUL = 256 const val MUL = 256 // modify this to 1024 to implement 30-bit RGB
const val MUL_2 = 256 * 256 const val MUL_2 = MUL * MUL
const val CHANNEL_MAX = 255 const val CHANNEL_MAX = MUL - 1
const val CHANNEL_MAX_FLOAT = 255f const val CHANNEL_MAX_FLOAT = CHANNEL_MAX.toFloat()
const val COLOUR_DOMAIN_SIZE = 256 * 256 * 256 const val COLOUR_DOMAIN_SIZE = MUL * MUL_2
private const val deprecatedFeatureDebatable = "This feature is debatable. Do not use it yet." private const val deprecatedFeatureDebatable = "The usage of this feature is debatable. Do not use it yet."
@Deprecated(deprecatedFeatureDebatable) @Deprecated(deprecatedFeatureDebatable)
fun addLantern(x: Int, y: Int, intensity: Int) { fun addLantern(x: Int, y: Int, intensity: Int) {
@@ -75,8 +75,12 @@ object LightmapRenderer {
} }
} }
fun getLight(x: Int, y: Int): Int = fun getLight(x: Int, y: Int): Int? =
java.lang.Byte.toUnsignedInt(lightMapLSB!![y][x]) or (lightMapMSB!![y][x].toInt() shl 8) if (x !in 0..Terrarum.game.map.width - 1 || y !in 0..Terrarum.game.map.height - 1)
null
else
java.lang.Byte.toUnsignedInt(lightMapLSB!![y][x]) or (lightMapMSB!![y][x].toInt() shl 8)
fun setLight(x: Int, y: Int, colour: Int) { fun setLight(x: Int, y: Int, colour: Int) {
lightMapLSB!![y][x] = (colour and 0xFF).toByte() lightMapLSB!![y][x] = (colour and 0xFF).toByte()
@@ -164,7 +168,7 @@ object LightmapRenderer {
while (x < for_x_end) { while (x < for_x_end) {
// smooth // smooth
if (Terrarum.game.screenZoom >= 1 && Terrarum.gameConfig.getAsBoolean("smoothlighting") ?: false) { if (Terrarum.game.screenZoom >= 1 && Terrarum.gameConfig.getAsBoolean("smoothlighting") ?: false) {
val thisLightLevel = getLight(x, y) val thisLightLevel = getLight(x, y) ?: 0
if (y > 0 && x < for_x_end && thisLightLevel == 0 if (y > 0 && x < for_x_end && thisLightLevel == 0
&& getLight(x, y - 1) == 0) { && getLight(x, y - 1) == 0) {
try { try {
@@ -204,32 +208,32 @@ object LightmapRenderer {
thisLightLevel thisLightLevel
else else
maximiseRGB( maximiseRGB(
getLight(x, y), getLight(x, y) ?: 0,
getLight(x, y - 1)) getLight(x, y - 1) ?: 0)
val d = if (y == 0) val d = if (y == 0)
thisLightLevel thisLightLevel
else if (y == Terrarum.game.map.height - 1) else if (y == Terrarum.game.map.height - 1)
thisLightLevel thisLightLevel
else else
maximiseRGB( maximiseRGB(
getLight(x, y), getLight(x, y) ?: 0,
getLight(x, y + 1)) getLight(x, y + 1) ?: 0)
val b = if (x == 0) val b = if (x == 0)
thisLightLevel thisLightLevel
else if (x == Terrarum.game.map.width - 1) else if (x == Terrarum.game.map.width - 1)
thisLightLevel thisLightLevel
else else
maximiseRGB( maximiseRGB(
getLight(x, y), getLight(x, y) ?: 0,
getLight(x - 1, y)) getLight(x - 1, y) ?: 0)
val c = if (x == 0) val c = if (x == 0)
thisLightLevel thisLightLevel
else if (x == Terrarum.game.map.width - 1) else if (x == Terrarum.game.map.width - 1)
thisLightLevel thisLightLevel
else else
maximiseRGB( maximiseRGB(
getLight(x, y), getLight(x, y) ?: 0,
getLight(x + 1, y)) getLight(x + 1, y) ?: 0)
val colourMapItoL = IntArray(4) val colourMapItoL = IntArray(4)
colourMapItoL[0] = colourLinearMix(a, b) colourMapItoL[0] = colourLinearMix(a, b)
colourMapItoL[1] = colourLinearMix(a, c) colourMapItoL[1] = colourLinearMix(a, c)
@@ -260,7 +264,7 @@ object LightmapRenderer {
if (x + sameLevelCounter >= for_x_end) break if (x + sameLevelCounter >= for_x_end) break
} }
g.color = Color(getLight(x, y)) g.color = Color(getLight(x, y) ?: 0)
g.fillRect( g.fillRect(
Math.round(x.toFloat() * TSIZE.toFloat() * Terrarum.game.screenZoom).toFloat(), Math.round(y.toFloat() * TSIZE.toFloat() * Terrarum.game.screenZoom).toFloat(), (FastMath.ceil( Math.round(x.toFloat() * TSIZE.toFloat() * Terrarum.game.screenZoom).toFloat(), Math.round(y.toFloat() * TSIZE.toFloat() * Terrarum.game.screenZoom).toFloat(), (FastMath.ceil(
TSIZE * Terrarum.game.screenZoom) * sameLevelCounter).toFloat(), FastMath.ceil(TSIZE * Terrarum.game.screenZoom).toFloat()) TSIZE * Terrarum.game.screenZoom) * sameLevelCounter).toFloat(), FastMath.ceil(TSIZE * Terrarum.game.screenZoom).toFloat())
@@ -347,13 +351,13 @@ object LightmapRenderer {
if (xoff != yoff && -xoff != yoff) { if (xoff != yoff && -xoff != yoff) {
// 'v' tiles // 'v' tiles
if (!outOfMapBounds(x + xoff, y + yoff)) { if (!outOfMapBounds(x + xoff, y + yoff)) {
nearby = getLight(x + xoff, y + yoff) nearby = getLight(x + xoff, y + yoff) ?: 0
} }
} }
else if (xoff != 0 && yoff != 0) { else if (xoff != 0 && yoff != 0) {
// 'a' tiles // 'a' tiles
if (!outOfMapBounds(x + xoff, y + yoff)) { if (!outOfMapBounds(x + xoff, y + yoff)) {
nearby = darkenUniformInt(getLight(x + xoff, y + yoff), 12) //2 for 40step nearby = darkenUniformInt(getLight(x + xoff, y + yoff) ?: 0, 12) //2 for 40step
// mix some to have more 'spreading' // mix some to have more 'spreading'
// so that light spreads in a shape of an octagon instead of a diamond // so that light spreads in a shape of an octagon instead of a diamond
} }
@@ -631,7 +635,7 @@ object LightmapRenderer {
private fun clampFloat(i: Float): Float = if (i < 0) 0f else if (i > 1) 1f else i private fun clampFloat(i: Float): Float = if (i < 0) 0f else if (i > 1) 1f else i
fun getValueFromMap(x: Int, y: Int): Int = getLight(x, y) fun getValueFromMap(x: Int, y: Int): Int? = getLight(x, y)
private fun purgePartOfLightmap(x1: Int, y1: Int, x2: Int, y2: Int) { private fun purgePartOfLightmap(x1: Int, y1: Int, x2: Int, y2: Int) {
try { try {

View File

@@ -108,7 +108,7 @@ object MapCamera {
, TileNameCode.PLANK_EBONY , TileNameCode.PLANK_EBONY
, TileNameCode.PLANK_NORMAL , TileNameCode.PLANK_NORMAL
, TileNameCode.SAND , TileNameCode.SAND
, TileNameCode.SAND_BEACH , TileNameCode.SAND_WHITE
, TileNameCode.SAND_RED , TileNameCode.SAND_RED
, TileNameCode.SAND_DESERT , TileNameCode.SAND_DESERT
, TileNameCode.SAND_BLACK , TileNameCode.SAND_BLACK
@@ -230,7 +230,7 @@ object MapCamera {
for (y in for_y_start..for_y_end - 1) { for (y in for_y_start..for_y_end - 1) {
for (x in for_x_start..for_x_end - 1) { for (x in for_x_start..for_x_end - 1) {
val thisTile: Int val thisTile: Int?
if (mode % 3 == WALL) if (mode % 3 == WALL)
thisTile = map.getTileFromWall(x, y) thisTile = map.getTileFromWall(x, y)
else if (mode % 3 == TERRAIN) else if (mode % 3 == TERRAIN)
@@ -248,12 +248,14 @@ object MapCamera {
(mode == WALL || mode == TERRAIN) // not an air tile (mode == WALL || mode == TERRAIN) // not an air tile
&& thisTile > 0 && (thisTile ?: 0) > 0
&& &&
// check if light level of upper tile is zero and // check if light level of upper tile is zero and
// that of this tile is also zero // that of this tile is also zero
(y > 0 && !(LightmapRenderer.getValueFromMap(x, y).toInt() == 0 && LightmapRenderer.getValueFromMap(x, y - 1).toInt() == 0) || // check if light level of this tile is zero, for y = 0 (y > 0 && !(LightmapRenderer.getValueFromMap(x, y) ?: 0.toInt() == 0
y == 0 && LightmapRenderer.getValueFromMap(x, y).toInt() > 0)) { && LightmapRenderer.getValueFromMap(x, y - 1) ?: 0.toInt() == 0)
// check if light level of this tile is zero, for y = 0
|| y == 0 && LightmapRenderer.getValueFromMap(x, y) ?: 0.toInt() > 0)) {
val nearbyTilesInfo: Int val nearbyTilesInfo: Int
if (isWallSticker(thisTile)) { if (isWallSticker(thisTile)) {
@@ -269,11 +271,11 @@ object MapCamera {
val thisTileX: Int val thisTileX: Int
if (!noDamageLayer) if (!noDamageLayer)
thisTileX = PairedMapLayer.RANGE * (thisTile % PairedMapLayer.RANGE) + nearbyTilesInfo thisTileX = PairedMapLayer.RANGE * ((thisTile ?: 0) % PairedMapLayer.RANGE) + nearbyTilesInfo
else else
thisTileX = nearbyTilesInfo thisTileX = nearbyTilesInfo
val thisTileY = thisTile / PairedMapLayer.RANGE val thisTileY = (thisTile ?: 0) / PairedMapLayer.RANGE
if (drawModeTilesBlendMul) { if (drawModeTilesBlendMul) {
if (isBlendMul(thisTile)) { if (isBlendMul(thisTile)) {
@@ -311,31 +313,12 @@ object MapCamera {
* * * *
* @return [0-15] 1: up, 2: right, 4: down, 8: left * @return [0-15] 1: up, 2: right, 4: down, 8: left
*/ */
private fun getNearbyTilesInfo(x: Int, y: Int, mode: Int, mark: Int): Int { private fun getNearbyTilesInfo(x: Int, y: Int, mode: Int, mark: Int?): Int {
val nearbyTiles = IntArray(4) val nearbyTiles = IntArray(4)
if (x == 0) { nearbyTiles[NEARBY_TILE_KEY_LEFT] = map.getTileFrom(mode, x - 1, y) ?: 4096
nearbyTiles[NEARBY_TILE_KEY_LEFT] = 4096 nearbyTiles[NEARBY_TILE_KEY_RIGHT] = map.getTileFrom(mode, x + 1, y) ?: 4096
} else { nearbyTiles[NEARBY_TILE_KEY_UP] = map.getTileFrom(mode, x, y - 1) ?: 4906
nearbyTiles[NEARBY_TILE_KEY_LEFT] = map.getTileFrom(mode, x - 1, y) nearbyTiles[NEARBY_TILE_KEY_DOWN] = map.getTileFrom(mode, x, y + 1) ?: 4096
}
if (x == map.width - 1) {
nearbyTiles[NEARBY_TILE_KEY_RIGHT] = 4096
} else {
nearbyTiles[NEARBY_TILE_KEY_RIGHT] = map.getTileFrom(mode, x + 1, y)
}
if (y == 0) {
nearbyTiles[NEARBY_TILE_KEY_UP] = 0
} else {
nearbyTiles[NEARBY_TILE_KEY_UP] = map.getTileFrom(mode, x, y - 1)
}
if (y == map.height - 1) {
nearbyTiles[NEARBY_TILE_KEY_DOWN] = 4096
} else {
nearbyTiles[NEARBY_TILE_KEY_DOWN] = map.getTileFrom(mode, x, y + 1)
}
// try for // try for
var ret = 0 var ret = 0
@@ -350,29 +333,10 @@ object MapCamera {
private fun getNearbyTilesInfoNonSolid(x: Int, y: Int, mode: Int): Int { private fun getNearbyTilesInfoNonSolid(x: Int, y: Int, mode: Int): Int {
val nearbyTiles = IntArray(4) val nearbyTiles = IntArray(4)
if (x == 0) { nearbyTiles[NEARBY_TILE_KEY_LEFT] = map.getTileFrom(mode, x - 1, y) ?: 4096
nearbyTiles[NEARBY_TILE_KEY_LEFT] = 4096 nearbyTiles[NEARBY_TILE_KEY_RIGHT] = map.getTileFrom(mode, x + 1, y) ?: 4096
} else { nearbyTiles[NEARBY_TILE_KEY_UP] = map.getTileFrom(mode, x, y - 1) ?: 4096
nearbyTiles[NEARBY_TILE_KEY_LEFT] = map.getTileFrom(mode, x - 1, y) nearbyTiles[NEARBY_TILE_KEY_DOWN] = map.getTileFrom(mode, x, y + 1) ?: 4096
}
if (x == map.width - 1) {
nearbyTiles[NEARBY_TILE_KEY_RIGHT] = 4096
} else {
nearbyTiles[NEARBY_TILE_KEY_RIGHT] = map.getTileFrom(mode, x + 1, y)
}
if (y == 0) {
nearbyTiles[NEARBY_TILE_KEY_UP] = 0
} else {
nearbyTiles[NEARBY_TILE_KEY_UP] = map.getTileFrom(mode, x, y - 1)
}
if (y == map.height - 1) {
nearbyTiles[NEARBY_TILE_KEY_DOWN] = 4096
} else {
nearbyTiles[NEARBY_TILE_KEY_DOWN] = map.getTileFrom(mode, x, y + 1)
}
// try for // try for
var ret = 0 var ret = 0
@@ -392,25 +356,10 @@ object MapCamera {
private fun getNearbyTilesInfoWallSticker(x: Int, y: Int): Int { private fun getNearbyTilesInfoWallSticker(x: Int, y: Int): Int {
val nearbyTiles = IntArray(4) val nearbyTiles = IntArray(4)
val NEARBY_TILE_KEY_BACK = NEARBY_TILE_KEY_UP val NEARBY_TILE_KEY_BACK = NEARBY_TILE_KEY_UP
if (x == 0) { nearbyTiles[NEARBY_TILE_KEY_LEFT] = map.getTileFrom(TERRAIN, x - 1, y) ?: 4096
nearbyTiles[NEARBY_TILE_KEY_LEFT] = 4096 nearbyTiles[NEARBY_TILE_KEY_RIGHT] = map.getTileFrom(TERRAIN, x + 1, y) ?: 4096
} else { nearbyTiles[NEARBY_TILE_KEY_DOWN] = map.getTileFrom(TERRAIN, x, y + 1) ?: 4096
nearbyTiles[NEARBY_TILE_KEY_LEFT] = map.getTileFrom(TERRAIN, x - 1, y) nearbyTiles[NEARBY_TILE_KEY_BACK] = map.getTileFrom(WALL, x, y) ?: 4096
}
if (x == map.width - 1) {
nearbyTiles[NEARBY_TILE_KEY_RIGHT] = 4096
} else {
nearbyTiles[NEARBY_TILE_KEY_RIGHT] = map.getTileFrom(TERRAIN, x + 1, y)
}
if (y == map.height - 1) {
nearbyTiles[NEARBY_TILE_KEY_DOWN] = 4096
} else {
nearbyTiles[NEARBY_TILE_KEY_DOWN] = map.getTileFrom(TERRAIN, x, y + 1)
}
nearbyTiles[NEARBY_TILE_KEY_BACK] = map.getTileFrom(WALL, x, y)
try { try {
if (TilePropCodex.getProp(nearbyTiles[NEARBY_TILE_KEY_RIGHT]).isSolid && TilePropCodex.getProp(nearbyTiles[NEARBY_TILE_KEY_LEFT]).isSolid) { if (TilePropCodex.getProp(nearbyTiles[NEARBY_TILE_KEY_RIGHT]).isSolid && TilePropCodex.getProp(nearbyTiles[NEARBY_TILE_KEY_LEFT]).isSolid) {
@@ -499,12 +448,12 @@ object MapCamera {
fun getRenderEndX(): Int = clampWTile(getRenderStartX() + div16(renderWidth) + 2) fun getRenderEndX(): Int = clampWTile(getRenderStartX() + div16(renderWidth) + 2)
fun getRenderEndY(): Int = clampHTile(getRenderStartY() + div16(renderHeight) + 2) fun getRenderEndY(): Int = clampHTile(getRenderStartY() + div16(renderHeight) + 2)
private fun isConnectSelf(b: Int): Boolean = TILES_CONNECT_SELF.contains(b) private fun isConnectSelf(b: Int?): Boolean = TILES_CONNECT_SELF.contains(b)
private fun isConnectMutual(b: Int): Boolean = TILES_CONNECT_MUTUAL.contains(b) private fun isConnectMutual(b: Int?): Boolean = TILES_CONNECT_MUTUAL.contains(b)
private fun isWallSticker(b: Int): Boolean = TILES_WALL_STICKER.contains(b) private fun isWallSticker(b: Int?): Boolean = TILES_WALL_STICKER.contains(b)
private fun isPlatform(b: Int): Boolean = TILES_WALL_STICKER_CONNECT_SELF.contains(b) private fun isPlatform(b: Int?): Boolean = TILES_WALL_STICKER_CONNECT_SELF.contains(b)
private fun isBlendMul(b: Int): Boolean = TILES_BLEND_MUL.contains(b) private fun isBlendMul(b: Int?): Boolean = TILES_BLEND_MUL.contains(b)
private fun setBlendModeMul() { private fun setBlendModeMul() {
GL11.glEnable(GL11.GL_BLEND) GL11.glEnable(GL11.GL_BLEND)

View File

@@ -3,7 +3,7 @@ package com.torvald.terrarum.mapdrawer
import com.torvald.terrarum.gamemap.GameMap import com.torvald.terrarum.gamemap.GameMap
import com.torvald.terrarum.Terrarum import com.torvald.terrarum.Terrarum
import com.torvald.terrarum.tileproperties.TileNameCode import com.torvald.terrarum.tileproperties.TileNameCode
import com.torvald.terrarum.tilestats.TileStat import com.torvald.terrarum.tilestats.TileStats
import com.jme3.math.FastMath import com.jme3.math.FastMath
import org.newdawn.slick.* import org.newdawn.slick.*
@@ -44,8 +44,8 @@ object MapDrawer {
fun drawEnvOverlay(g: Graphics) { fun drawEnvOverlay(g: Graphics) {
val onscreen_tiles_max = FastMath.ceil(Terrarum.HEIGHT * Terrarum.WIDTH / FastMath.sqr(TILE_SIZE.toFloat())) * 2 val onscreen_tiles_max = FastMath.ceil(Terrarum.HEIGHT * Terrarum.WIDTH / FastMath.sqr(TILE_SIZE.toFloat())) * 2
val onscreen_tiles_cap = onscreen_tiles_max / 4f val onscreen_tiles_cap = onscreen_tiles_max / 4f
val onscreen_cold_tiles = TileStat.getCount(*TILES_COLD).toFloat() val onscreen_cold_tiles = TileStats.getCount(*TILES_COLD).toFloat()
val onscreen_warm_tiles = TileStat.getCount(*TILES_WARM).toFloat() val onscreen_warm_tiles = TileStats.getCount(*TILES_WARM).toFloat()
val colTemp_cold = colTempLinearFunc(onscreen_cold_tiles / onscreen_tiles_cap) val colTemp_cold = colTempLinearFunc(onscreen_cold_tiles / onscreen_tiles_cap)
val colTemp_warm = colTempLinearFunc(-(onscreen_warm_tiles / onscreen_tiles_cap)) val colTemp_warm = colTempLinearFunc(-(onscreen_warm_tiles / onscreen_tiles_cap))

View File

@@ -2,9 +2,10 @@ package com.torvald.terrarum.mapgenerator
import com.torvald.random.HQRNG import com.torvald.random.HQRNG
import com.torvald.terrarum.gamemap.GameMap import com.torvald.terrarum.gamemap.GameMap
import com.torvald.terrarum.gamemap.MapLayer
import com.torvald.terrarum.tileproperties.TileNameCode import com.torvald.terrarum.tileproperties.TileNameCode
import com.jme3.math.FastMath import com.jme3.math.FastMath
import com.sudoplay.joise.Joise
import com.sudoplay.joise.module.*
import java.util.* import java.util.*
object MapGenerator { object MapGenerator {
@@ -12,13 +13,14 @@ object MapGenerator {
private lateinit var map: GameMap private lateinit var map: GameMap
private lateinit var random: Random private lateinit var random: Random
//private static float[] noiseArray; //private static float[] noiseArray;
private var seed: Long = 0 private var SEED: Long = 0
private var width: Int = 0 private var WIDTH: Int = 0
private var height: Int = 0 private var HEIGHT: Int = 0
private lateinit var heightMap: IntArray //private lateinit var heightMap: IntArray
private lateinit var terrainMap: Array<BitSet>
private var dirtThickness: Int = 0 private var DIRT_LAYER_DEPTH: Int = 0
private var TERRAIN_AVERAGE_HEIGHT: Int = 0 private var TERRAIN_AVERAGE_HEIGHT: Int = 0
private var minimumFloatingIsleHeight: Int = 0 private var minimumFloatingIsleHeight: Int = 0
@@ -27,7 +29,8 @@ object MapGenerator {
private val noiseGrdCaveEnd = 0.54f private val noiseGrdCaveEnd = 0.54f
private val HILL_WIDTH = 256 // power of two! private val HILL_WIDTH = 256 // power of two!
private val MAX_HILL_HEIGHT = 100 //private val MAX_HILL_HEIGHT = 100
private val TERRAIN_UNDULATION = 250
private val CAVE_LARGEST_FEATURE = 200 private val CAVE_LARGEST_FEATURE = 200
@@ -35,10 +38,6 @@ object MapGenerator {
private var SHORE_WIDTH = 120 private var SHORE_WIDTH = 120
private val MAX_OCEAN_DEPTH = 200 private val MAX_OCEAN_DEPTH = 200
private val TERRAIN_PERTURB_OFFSETMAX = 0 // [-val , val]
private val TERRAIN_PERTURB_LARGESTFEATURE = 256
private val TERRAIN_PERTURB_RATE = 0.5f
private var GLACIER_MOUNTAIN_WIDTH = 900 private var GLACIER_MOUNTAIN_WIDTH = 900
private val GLACIER_MOUNTAIN_HEIGHT = 300 private val GLACIER_MOUNTAIN_HEIGHT = 300
@@ -59,54 +58,55 @@ object MapGenerator {
private val GRASSCUR_RIGHT = 1 private val GRASSCUR_RIGHT = 1
private val GRASSCUR_DOWN = 2 private val GRASSCUR_DOWN = 2
private val GRASSCUR_LEFT = 3 private val GRASSCUR_LEFT = 3
@JvmStatic
fun attachMap(map: GameMap) { fun attachMap(map: GameMap) {
this.map = map this.map = map
width = map.width WIDTH = map.width
height = map.height HEIGHT = map.height
val widthMulFactor = width / 8192f val widthMulFactor = WIDTH / 8192f
dirtThickness = (100 * height / 1024f).toInt() DIRT_LAYER_DEPTH = (100 * HEIGHT / 1024f).toInt()
minimumFloatingIsleHeight = (25 * (height / 1024f)).toInt() minimumFloatingIsleHeight = (25 * (HEIGHT / 1024f)).toInt()
TERRAIN_AVERAGE_HEIGHT = height / 4 TERRAIN_AVERAGE_HEIGHT = HEIGHT / 4
OCEAN_WIDTH = Math.round(OCEAN_WIDTH * widthMulFactor) OCEAN_WIDTH = Math.round(OCEAN_WIDTH * widthMulFactor)
SHORE_WIDTH = Math.round(SHORE_WIDTH * widthMulFactor) SHORE_WIDTH = Math.round(SHORE_WIDTH * widthMulFactor)
GLACIER_MOUNTAIN_WIDTH = Math.round(GLACIER_MOUNTAIN_WIDTH * widthMulFactor) GLACIER_MOUNTAIN_WIDTH = Math.round(GLACIER_MOUNTAIN_WIDTH * widthMulFactor)
} }
@JvmStatic
fun setSeed(seed: Long) { fun setSeed(seed: Long) {
this.seed = seed this.SEED = seed
} }
/** /**
* Generate terrain and override attached map * Generate terrain and override attached map
*/ */
@JvmStatic
fun generateMap() { fun generateMap() {
random = HQRNG(seed!!) random = HQRNG(SEED)
println("[mapgenerator] Seed: " + seed) println("[mapgenerator] Seed: " + SEED)
worldOceanPosition = if (random.nextBoolean()) TYPE_OCEAN_LEFT else TYPE_OCEAN_RIGHT worldOceanPosition = if (random.nextBoolean()) TYPE_OCEAN_LEFT else TYPE_OCEAN_RIGHT
heightMap = raise2(MAX_HILL_HEIGHT / 2) //heightMap = raise2(MAX_HILL_HEIGHT / 2)
generateOcean(heightMap) //generateOcean(heightMap)
placeGlacierMount(heightMap) //placeGlacierMount(heightMap)
heightMapToObjectMap(heightMap) //heightMapToObjectMap(heightMap)
perturbTerrain()
terrainMap = raise3()
terrainMapToObjectMap()
/** /**
* Todo: more perturbed overworld (harder to supra-navigate) * Done: more perturbed overworld (harder to supra-navigate)
* Todo: veined ore distribution (metals) -- use veined simplex noise * Todo: veined ore distribution (metals) -- use veined simplex noise
* Todo: clustered gem distribution (Groups: [Ruby, Sapphire], Amethyst, Yellow topaz, emerald, diamond) -- use regular simplex noise * Todo: clustered gem distribution (Groups: [Ruby, Sapphire], Amethyst, Yellow topaz, emerald, diamond) -- use regular simplex noise
* Todo: Lakes! Aquifers! Lava chambers! * Todo: Lakes! Aquifers! Lava chambers!
* Todo: deserts (variants: SAND_DESERT, SAND_RED) * Todo: deserts (variants: SAND_DESERT, SAND_RED)
* Todo: volcano(es?) * Todo: volcano(es?)
* Done: variants of beach (SAND_BEACH, SAND_BLACK, SAND_GREEN) * Done: variants of beach (SAND, SAND_BEACH, SAND_BLACK, SAND_GREEN)
*/ */
carveCave( carveCave(
@@ -182,16 +182,16 @@ object MapGenerator {
/** TODO Cobaltite, Ilmenite, Aurichalcum (and possibly pitchblende?) */ /** TODO Cobaltite, Ilmenite, Aurichalcum (and possibly pitchblende?) */
floodBottomLava() floodBottomLava()
freeze() // freeze()
fillOcean() // fillOcean()
plantGrass() plantGrass()
//post-process //post-process
generateFloatingIslands() generateFloatingIslands()
//wire layer //wire layer
for (i in 0..height - 1) { for (i in 0..HEIGHT - 1) {
for (j in 0..width - 1) { for (j in 0..WIDTH - 1) {
map.wireArray[i][j] = 0 map.wireArray[i][j] = 0
} }
} }
@@ -211,21 +211,21 @@ object MapGenerator {
* @return * @return
*/ */
private fun caveGen(xStretch: Float, yStretch: Float): Array<FloatArray> { private fun caveGen(xStretch: Float, yStretch: Float): Array<FloatArray> {
val noiseMap = Array(height) { FloatArray(width) } val noiseMap = Array(HEIGHT) { FloatArray(WIDTH) }
val simplexNoise = SimplexNoise(CAVEGEN_LARGEST_FEATURE, CAVEGEN_PERTURB_RATE, seed!!) val simplexNoise = SimplexNoise(CAVEGEN_LARGEST_FEATURE, CAVEGEN_PERTURB_RATE, SEED)
val simplexNoisePerturbMap = SimplexNoise(CAVEGEN_LARGEST_FEATURE_PERTURB, 0.5f, seed!! xor random.nextLong()) val simplexNoisePerturbMap = SimplexNoise(CAVEGEN_LARGEST_FEATURE_PERTURB, 0.5f, SEED xor random.nextLong())
val xEnd = width * yStretch val xEnd = WIDTH * yStretch
val yEnd = height * xStretch val yEnd = HEIGHT * xStretch
var lowestNoiseVal = 10000f var lowestNoiseVal = 10000f
var highestNoiseVal = -10000f var highestNoiseVal = -10000f
for (y in 0..height - 1) { for (y in 0..HEIGHT - 1) {
for (x in 0..width - 1) { for (x in 0..WIDTH - 1) {
val ny = (y * (xEnd / width)).toInt() val ny = (y * (xEnd / WIDTH)).toInt()
val nx = (x * (yEnd / height)).toInt() val nx = (x * (yEnd / HEIGHT)).toInt()
val noiseInit = simplexNoise.getNoise(nx, ny) // [-1 , 1] val noiseInit = simplexNoise.getNoise(nx, ny) // [-1 , 1]
val perturbInit = simplexNoisePerturbMap.getNoise(nx, ny) * 0.5f + 0.5f // [0 , 1] val perturbInit = simplexNoisePerturbMap.getNoise(nx, ny) * 0.5f + 0.5f // [0 , 1]
@@ -244,8 +244,8 @@ object MapGenerator {
// Auto-scaling noise // Auto-scaling noise
for (y in 0..height - 1) { for (y in 0..HEIGHT - 1) {
for (x in 0..width - 1) { for (x in 0..WIDTH - 1) {
val noiseInit = noiseMap[y][x] - lowestNoiseVal val noiseInit = noiseMap[y][x] - lowestNoiseVal
val noiseFin = noiseInit * (1f / (highestNoiseVal - lowestNoiseVal)) val noiseFin = noiseInit * (1f / (highestNoiseVal - lowestNoiseVal))
@@ -264,7 +264,7 @@ object MapGenerator {
} }
private fun generate2DSimplexNoiseWorldSize(xStretch: Float, yStretch: Float): Array<FloatArray> { private fun generate2DSimplexNoiseWorldSize(xStretch: Float, yStretch: Float): Array<FloatArray> {
return generate2DSimplexNoise(width, height, xStretch, yStretch) return generate2DSimplexNoise(WIDTH, HEIGHT, xStretch, yStretch)
} }
/** /**
@@ -280,7 +280,7 @@ object MapGenerator {
* @return matrix in ![x][y]! * @return matrix in ![x][y]!
*/ */
private fun generate2DSimplexNoise(sizeX: Int, sizeY: Int, xStretch: Float, yStretch: Float): Array<FloatArray> { private fun generate2DSimplexNoise(sizeX: Int, sizeY: Int, xStretch: Float, yStretch: Float): Array<FloatArray> {
val simplexNoise = SimplexNoise(CAVE_LARGEST_FEATURE, 0.1f, seed!! xor random.nextLong()) val simplexNoise = SimplexNoise(CAVE_LARGEST_FEATURE, 0.1f, SEED xor random.nextLong())
val xStart = 0f val xStart = 0f
val yStart = 0f val yStart = 0f
@@ -288,8 +288,8 @@ object MapGenerator {
/** higher = denser. /** higher = denser.
* Recommended: (width or height) * 3 * Recommended: (width or height) * 3
*/ */
val xEnd = width * yStretch val xEnd = WIDTH * yStretch
val yEnd = height * xStretch val yEnd = HEIGHT * xStretch
var lowestNoiseVal = 10000f var lowestNoiseVal = 10000f
var highestNoiseVal = -10000f var highestNoiseVal = -10000f
@@ -312,8 +312,8 @@ object MapGenerator {
// Auto-scaling noise // Auto-scaling noise
for (y in 0..height - 1) { for (y in 0..HEIGHT - 1) {
for (x in 0..width - 1) { for (x in 0..WIDTH - 1) {
val noiseInit = result[y][x] - lowestNoiseVal val noiseInit = result[y][x] - lowestNoiseVal
val noiseFin = noiseInit * (1f / (highestNoiseVal - lowestNoiseVal)) val noiseFin = noiseInit * (1f / (highestNoiseVal - lowestNoiseVal))
@@ -364,83 +364,169 @@ object MapGenerator {
} }
/** /**
* [http://freespace.virgin.net/hugo.elias/models/m_perlin.htm](null) * http://accidentalnoise.sourceforge.net/minecraftworlds.html
* @param maxval_div_2 max height (deviation from zero) divided by two.
* *
* @return noise array with range of [-maxval, maxval]
*/ */
private fun raise2(maxval_div_2: Int): IntArray { private fun raise3(): Array<BitSet> {
val noiseMap = Array(HEIGHT, { BitSet(WIDTH) })
val finalPerlinAmp = maxval_div_2 // 1 + 1/2 + 1/4 + 1/8 + ... == 2 // Height = Terrain undulation times 2.
val perlinOctaves = FastMath.intLog2(maxval_div_2) + 1 - 1 // max: for every 2nd node val SCALE_X: Double = (TERRAIN_UNDULATION * 0.5).toDouble()
val SCALE_Y: Double = (TERRAIN_UNDULATION * 0.25).toDouble()
val perlinMap = IntArray(width) // [-2 * finalPerlinAmp, finalPerlinAmp] val ground_gradient = ModuleGradient()
ground_gradient.setGradient(0.0, 0.0, 0.0, 1.0)
// assert /* Lowlands */
if (HILL_WIDTH.ushr(perlinOctaves - 1) == 0) {
throw RuntimeException("sample width of zero detected.")
}
// sample noise and add val lowland_shape_fractal = ModuleFractal()
for (oct in 1..perlinOctaves) { lowland_shape_fractal.setType(ModuleFractal.FractalType.FBM)
// perlinAmp: 16364 -> 8192 -> 4096 -> 2048 -> ... lowland_shape_fractal.setAllSourceBasisTypes(ModuleBasisFunction.BasisType.GRADIENT)
// This applies persistence of 1/2 lowland_shape_fractal.setAllSourceInterpolationTypes(ModuleBasisFunction.InterpolationType.QUINTIC)
val perlinAmp = finalPerlinAmp.ushr(oct - 1) lowland_shape_fractal.setNumOctaves(4)
lowland_shape_fractal.setFrequency(0.6)
lowland_shape_fractal.seed = SEED xor random.nextLong()
//println(lowland_shape_fractal.seed)
val perlinSampleDist = HILL_WIDTH.ushr(oct - 1) val lowland_autocorrect = ModuleAutoCorrect()
lowland_autocorrect.setLow(0.0)
lowland_autocorrect.setHigh(1.0)
lowland_autocorrect.setSource(lowland_shape_fractal)
// sample first val lowland_scale = ModuleScaleOffset()
val perlinSamples = IntArray(width / perlinSampleDist + 1) lowland_scale.setSource(lowland_autocorrect)
for (sample in perlinSamples.indices) { lowland_scale.setScale(0.8)
perlinSamples[sample] = random.nextInt(perlinAmp * 2) - perlinAmp lowland_scale.setOffset(-2.75)
val lowland_y_scale = ModuleScaleDomain()
lowland_y_scale.setSource(lowland_scale)
lowland_y_scale.setScaleY(0.0)
val lowland_terrain = ModuleTranslateDomain()
lowland_terrain.setSource(ground_gradient)
lowland_terrain.setAxisYSource(lowland_y_scale)
/* highlands */
val highland_shape_fractal = ModuleFractal()
highland_shape_fractal.setType(ModuleFractal.FractalType.RIDGEMULTI)
highland_shape_fractal.setAllSourceBasisTypes(ModuleBasisFunction.BasisType.GRADIENT)
highland_shape_fractal.setAllSourceInterpolationTypes(ModuleBasisFunction.InterpolationType.QUINTIC)
highland_shape_fractal.setNumOctaves(4)
highland_shape_fractal.setFrequency(0.5) // horizontal size. Higher == narrower
highland_shape_fractal.seed = SEED xor random.nextLong()
//println(highland_shape_fractal.seed)
val highland_autocorrect = ModuleAutoCorrect()
highland_autocorrect.setSource(highland_shape_fractal)
highland_autocorrect.setLow(0.0)
highland_autocorrect.setHigh(1.0)
val highland_scale = ModuleScaleOffset()
highland_scale.setSource(highland_autocorrect)
highland_scale.setScale(1.4) // vertical size. Higher == taller
highland_scale.setOffset(-2.25)
val highland_y_scale = ModuleScaleDomain()
highland_y_scale.setSource(highland_scale)
highland_y_scale.setScaleY(0.0)
val highland_terrain = ModuleTranslateDomain()
highland_terrain.setSource(ground_gradient)
highland_terrain.setAxisYSource(highland_y_scale)
/* mountains */
val mountain_shape_fractal = ModuleFractal()
mountain_shape_fractal.setType(ModuleFractal.FractalType.BILLOW)
mountain_shape_fractal.setAllSourceBasisTypes(ModuleBasisFunction.BasisType.GRADIENT)
mountain_shape_fractal.setAllSourceInterpolationTypes(ModuleBasisFunction.InterpolationType.QUINTIC)
mountain_shape_fractal.setNumOctaves(6)
mountain_shape_fractal.setFrequency(0.55)
mountain_shape_fractal.seed = SEED xor random.nextLong()
//println(mountain_shape_fractal.seed)
val mountain_autocorrect = ModuleAutoCorrect()
mountain_autocorrect.setSource(mountain_shape_fractal)
mountain_autocorrect.setLow(0.0)
mountain_autocorrect.setHigh(1.0)
val mountain_scale = ModuleScaleOffset()
mountain_scale.setSource(mountain_autocorrect)
mountain_scale.setScale(1.66)
mountain_scale.setOffset(-1.25)
val mountain_y_scale = ModuleScaleDomain()
mountain_y_scale.setSource(mountain_scale)
mountain_y_scale.setScaleY(0.1)
val mountain_terrain = ModuleTranslateDomain()
mountain_terrain.setSource(ground_gradient)
mountain_terrain.setAxisYSource(mountain_y_scale)
/* selection */
val terrain_type_fractal = ModuleFractal()
terrain_type_fractal.setType(ModuleFractal.FractalType.MULTI)
terrain_type_fractal.setAllSourceBasisTypes(ModuleBasisFunction.BasisType.GRADIENT)
terrain_type_fractal.setAllSourceInterpolationTypes(ModuleBasisFunction.InterpolationType.QUINTIC)
terrain_type_fractal.setNumOctaves(5)
terrain_type_fractal.setFrequency(0.4) // <= 0.33
terrain_type_fractal.seed = SEED xor random.nextLong()
//println(terrain_type_fractal.seed)
val terrain_autocorrect = ModuleAutoCorrect()
terrain_autocorrect.setSource(terrain_type_fractal)
terrain_autocorrect.setLow(0.0)
terrain_autocorrect.setHigh(1.0)
val terrain_type_scale = ModuleScaleDomain()
terrain_type_scale.setScaleY(0.33)
terrain_type_scale.setSource(terrain_autocorrect)
val terrain_type_cache = ModuleCache()
terrain_type_cache.setSource(terrain_type_scale)
val highland_mountain_select = ModuleSelect()
highland_mountain_select.setLowSource(highland_terrain)
highland_mountain_select.setHighSource(mountain_terrain)
highland_mountain_select.setControlSource(terrain_type_cache)
highland_mountain_select.setThreshold(0.55)
highland_mountain_select.setFalloff(0.15)
val highland_lowland_select = ModuleSelect()
highland_lowland_select.setLowSource(lowland_terrain)
highland_lowland_select.setHighSource(highland_mountain_select)
highland_lowland_select.setControlSource(terrain_type_cache)
highland_lowland_select.setThreshold(0.25)
highland_lowland_select.setFalloff(0.15)
val ground_select = ModuleSelect()
ground_select.setLowSource(0.0)
ground_select.setHighSource(1.0)
ground_select.setThreshold(0.5)
ground_select.setControlSource(highland_lowland_select)
val joise = Joise(ground_select)
// fill the area as Joise map
for (y in 0..(TERRAIN_UNDULATION - 1)) {
for (x in 0..WIDTH) {
val map: Boolean = (
joise.get(
x / SCALE_X,
y / SCALE_Y
) == 1.0)
noiseMap[y + TERRAIN_AVERAGE_HEIGHT - (TERRAIN_UNDULATION / 2)].set(x, map)
} }
}
// add interpolated value to map // fill the area bottom of the above map as 'filled'
for (i in perlinMap.indices) { for (y in TERRAIN_AVERAGE_HEIGHT + (TERRAIN_UNDULATION / 2)..HEIGHT - 1) {
val perlinPointLeft = perlinSamples[i / perlinSampleDist] for (x in 0..WIDTH) {
val perlinPointRight = perlinSamples[i / perlinSampleDist + 1] noiseMap[y].set(x, true)
perlinMap[i] += Math.round(
interpolateCosine(
(i % perlinSampleDist).toFloat() / perlinSampleDist, perlinPointLeft.toFloat(), perlinPointRight.toFloat()))// using cosine; making tops rounded
} }
} }
for (k in 0..0) { return noiseMap
for (i in perlinMap.indices) {
// averaging smoothing
if (i > 1 && i < perlinMap.size - 2) {
perlinMap[i] = Math.round(
((perlinMap[i - 1] + perlinMap[i + 1]) / 2).toFloat())
}
}
}
// single bump removal
for (i in perlinMap.indices) {
if (i > 1 && i < perlinMap.size - 2) {
val p1 = perlinMap[i - 1]
val p2 = perlinMap[i]
val p3 = perlinMap[i + 1]
// _-_ / -_- -> ___ / ---
if (p1 == p3 && p1 != p2) {
perlinMap[i] = p1
} else if (p1 > p3 && p2 > p1) {
perlinMap[i] = p1
} else if (p3 > p1 && p2 > p3) {
perlinMap[i] = p3
} else if (p3 > p1 && p2 < p1) {
perlinMap[i] = p1
} else if (p1 > p3 && p2 < p3) {
perlinMap[i] = p1
}// ^_- -> ^--
// -_^ -> --^
// _^- -> _--
// -^_ -> --_
}
}
return perlinMap
} }
/** /**
@@ -501,14 +587,14 @@ object MapGenerator {
println("[mapgenerator] Shaping world as processed...") println("[mapgenerator] Shaping world as processed...")
// iterate for heightmap // iterate for heightmap
for (x in 0..width - 1) { for (x in 0..WIDTH - 1) {
val medianPosition = TERRAIN_AVERAGE_HEIGHT val medianPosition = TERRAIN_AVERAGE_HEIGHT
val pillarOffset = medianPosition - fs[x] val pillarOffset = medianPosition - fs[x]
// for pillar length // for pillar length
for (i in 0..height - pillarOffset - 1) { for (i in 0..HEIGHT - pillarOffset - 1) {
if (i < dirtThickness) { if (i < DIRT_LAYER_DEPTH) {
map.setTileTerrain(x, i + pillarOffset, TileNameCode.DIRT) map.setTileTerrain(x, i + pillarOffset, TileNameCode.DIRT)
map.setTileWall(x, i + pillarOffset, TileNameCode.DIRT) map.setTileWall(x, i + pillarOffset, TileNameCode.DIRT)
} else { } else {
@@ -520,67 +606,71 @@ object MapGenerator {
} }
} }
private fun perturbTerrain() { private fun terrainMapToObjectMap() {
val perturbGen = SimplexNoise(TERRAIN_PERTURB_LARGESTFEATURE, TERRAIN_PERTURB_RATE, seed!! xor random.nextLong()) println("[mapgenerator] Shaping world as processed...")
// generate dirt-stone transition line
// use catmull spline
val dirtStoneLine = IntArray(WIDTH)
val POINTS_GAP = 64 // power of two!
val splineControlPoints = Array((WIDTH / POINTS_GAP) + 1, { Pair(0, 0) })
val perturbMap = Array(height) { FloatArray(width) } // get spline points
for (x in 0..(WIDTH / POINTS_GAP)) {
for (y in 0..TERRAIN_AVERAGE_HEIGHT + TERRAIN_UNDULATION) {
splineControlPoints[x] = Pair(x * POINTS_GAP, y)
if (terrainMap[y].get(splineControlPoints[x].first)) break
}
println("Spline[$x] x: ${splineControlPoints[x].first}, " +
"y: ${splineControlPoints[x].second}")
}
val layerWall = map.wallArray // do interpolation
val layerTerrain = map.terrainArray for (x in 0..dirtStoneLine.size) {
val newLayerWall = MapLayer(width, height) val x_1 = x / POINTS_GAP
val newLayerTerrain = MapLayer(width, height)
var lowestNoiseVal = 10000f val splineX0 = splineControlPoints[ clamp(x_1 - 1, 0, dirtStoneLine.size / POINTS_GAP) ].first
var highestNoiseVal = -10000f val splineX1 = splineControlPoints[x_1].first
val splineX2 = splineControlPoints[ clamp(x_1 + 1, 0, dirtStoneLine.size / POINTS_GAP) ].first
val splineX3 = splineControlPoints[ clamp(x_1 + 2, 0, dirtStoneLine.size / POINTS_GAP) ].first
for (y in 0..map.height - 1) { val splineP0 = splineControlPoints[ clamp(x_1 - 1, 0, dirtStoneLine.size / POINTS_GAP) ].second.toFloat()
for (x in 0..map.width - 1) { val splineP1 = splineControlPoints[x_1].second.toFloat()
val noise = perturbGen.getNoise(x, y) // [-1, 1] val splineP2 = splineControlPoints[ clamp(x_1 + 1, 0, dirtStoneLine.size / POINTS_GAP) ].second.toFloat()
perturbMap[y][x] = noise val splineP3 = splineControlPoints[ clamp(x_1 + 2, 0, dirtStoneLine.size / POINTS_GAP) ].second.toFloat()
if (noise < lowestNoiseVal) lowestNoiseVal = noise
if (noise > highestNoiseVal) highestNoiseVal = noise if (x in POINTS_GAP - 1..WIDTH - 2 * POINTS_GAP) {
dirtStoneLine[x] = Math.round(FastMath.interpolateCatmullRom(
(x - splineX1) / POINTS_GAP.toFloat(),
0.01f,
splineP0,
splineP1,
splineP2,
splineP3
))
}
else {
interpolateCosine(
(x - splineX1) / POINTS_GAP.toFloat(),
splineP1,
splineP2
)
} }
} }
// Auto-scale noise [-1, 1] // scan vertically
/** for (x in 0..WIDTH - 1) {
* See ./work_files/Linear_autoscale.gcx for (y in 0..HEIGHT - 1) {
*/ if (terrainMap[clamp(y + DIRT_LAYER_DEPTH, 0, HEIGHT - 1)].get(x)) {
for (y in 0..height - 1) { // map.setTileTerrain(x, y, TileNameCode.DIRT)
for (x in 0..width - 1) { // map.setTileWall(x, y, TileNameCode.DIRT)
val noiseInit = perturbMap[y][x] val tile =
val noiseFin = (noiseInit - (highestNoiseVal + lowestNoiseVal) / 2f) * (2f / (highestNoiseVal - lowestNoiseVal)) if (y < dirtStoneLine[x]) TileNameCode.DIRT
else TileNameCode.STONE
perturbMap[y][x] = noiseFin map.setTileTerrain(x, y, tile)
} map.setTileWall(x, y, tile)
}
// Perturb to x-axis, apply to newLayer
for (y in 0..height - 1) {
for (x in 0..width - 1) {
val offsetOrigin = perturbMap[y][x] * 0.5f + 0.5f // [0 , 1]
val offset = Math.round(offsetOrigin * TERRAIN_PERTURB_OFFSETMAX)
val tileWall = layerWall[y][x]
val tileTerrain = layerTerrain[y][x]
try {
//newLayerWall.setTile(x + offset, y, tileWall);
//newLayerTerrain.setTile(x + offset, y, tileTerrain);
//layerWall[y][x] = 0;
//layerTerrain[y][x] = 0;
layerWall[y - offset][x] = tileWall
layerTerrain[y - offset][x] = tileTerrain
} catch (e: ArrayIndexOutOfBoundsException) {
} }
} }
} }
// set reference (pointer) of original map layer to new layers
//map.overwriteLayerWall(newLayerWall);
//map.overwriteLayerTerrain(newLayerTerrain);
} }
/* 2. Carve */ /* 2. Carve */
@@ -588,8 +678,8 @@ object MapGenerator {
private fun carveCave(noisemap: Array<FloatArray>, tile: Int, message: String) { private fun carveCave(noisemap: Array<FloatArray>, tile: Int, message: String) {
println("[mapgenerator] " + message) println("[mapgenerator] " + message)
for (i in 0..height - 1) { for (i in 0..HEIGHT - 1) {
for (j in 0..width - 1) { for (j in 0..WIDTH - 1) {
if (noisemap[i][j] > 0.9) { if (noisemap[i][j] > 0.9) {
map.setTileTerrain(j, i, tile) map.setTileTerrain(j, i, tile)
} }
@@ -610,8 +700,8 @@ object MapGenerator {
private fun carveByMap(noisemap: Array<FloatArray>, scarcity: Float, tile: Int, message: String) { private fun carveByMap(noisemap: Array<FloatArray>, scarcity: Float, tile: Int, message: String) {
println("[mapgenerator] " + message) println("[mapgenerator] " + message)
for (i in 0..height - 1) { for (i in 0..HEIGHT - 1) {
for (j in 0..width - 1) { for (j in 0..WIDTH - 1) {
if (noisemap[i][j] > gradientQuadratic(i, noiseGradientStart, noiseGrdCaveEnd) * scarcity) { if (noisemap[i][j] > gradientQuadratic(i, noiseGradientStart, noiseGrdCaveEnd) * scarcity) {
map.setTileTerrain(j, i, tile) map.setTileTerrain(j, i, tile)
} }
@@ -634,8 +724,8 @@ object MapGenerator {
private fun fillByMap(noisemap: Array<FloatArray>, scarcity: Float, replaceFrom: Int, tile: Int, message: String) { private fun fillByMap(noisemap: Array<FloatArray>, scarcity: Float, replaceFrom: Int, tile: Int, message: String) {
println("[mapgenerator] " + message) println("[mapgenerator] " + message)
for (i in 0..height - 1) { for (i in 0..HEIGHT - 1) {
for (j in 0..width - 1) { for (j in 0..WIDTH - 1) {
if (noisemap[i][j] > getNoiseGradient(i, noiseGradientStart, noiseGradientEnd) * scarcity && map.getTileFromTerrain(j, i) == replaceFrom) { if (noisemap[i][j] > getNoiseGradient(i, noiseGradientStart, noiseGradientEnd) * scarcity && map.getTileFromTerrain(j, i) == replaceFrom) {
map.setTileTerrain(j, i, tile) map.setTileTerrain(j, i, tile)
} }
@@ -658,8 +748,8 @@ object MapGenerator {
private fun fillByMapInverseGradFilter(noisemap: Array<FloatArray>, scarcity: Float, replaceFrom: Int, tile: Int, message: String) { private fun fillByMapInverseGradFilter(noisemap: Array<FloatArray>, scarcity: Float, replaceFrom: Int, tile: Int, message: String) {
println("[mapgenerator] " + message) println("[mapgenerator] " + message)
for (i in 0..height - 1) { for (i in 0..HEIGHT - 1) {
for (j in 0..width - 1) { for (j in 0..WIDTH - 1) {
if (noisemap[i][j] > getNoiseGradientInversed(i, noiseGradientEnd, noiseGradientStart) * scarcity if (noisemap[i][j] > getNoiseGradientInversed(i, noiseGradientEnd, noiseGradientStart) * scarcity
&& map.getTileFromTerrain(j, i) == replaceFrom) { && map.getTileFromTerrain(j, i) == replaceFrom) {
map.setTileTerrain(j, i, tile) map.setTileTerrain(j, i, tile)
@@ -686,8 +776,8 @@ object MapGenerator {
private fun fillByMapNoFilter(noisemap: Array<FloatArray>, scarcity: Float, replaceFrom: Int, tile: Int, message: String) { private fun fillByMapNoFilter(noisemap: Array<FloatArray>, scarcity: Float, replaceFrom: Int, tile: Int, message: String) {
println("[mapgenerator] " + message) println("[mapgenerator] " + message)
for (i in 0..height - 1) { for (i in 0..HEIGHT - 1) {
for (j in 0..width - 1) { for (j in 0..WIDTH - 1) {
if (noisemap[i][j] > noiseGradientStart * scarcity && map.getTileFromTerrain(j, i) == replaceFrom) { if (noisemap[i][j] > noiseGradientStart * scarcity && map.getTileFromTerrain(j, i) == replaceFrom) {
map.setTileTerrain(j, i, tile) map.setTileTerrain(j, i, tile)
} }
@@ -695,15 +785,16 @@ object MapGenerator {
} }
} }
private fun fillByMapNoFilterUnderground(noisemap: Array<FloatArray>, scarcity: Float, replaceFrom: Int, tile: Int, message: String) { private fun fillByMapNoFilterUnderground(noisemap: Array<FloatArray>, scarcity: Float, replaceFrom: Int, replaceTo: Int, message: String) {
println("[mapgenerator] " + message) println("[mapgenerator] " + message)
for (i in 0..height - 1) { for (i in 0..HEIGHT - 1) {
for (j in 0..width - 1) { for (j in 0..WIDTH - 1) {
if (noisemap[i][j] > noiseGradientStart * scarcity if (noisemap[i][j] > noiseGradientStart * scarcity
&& map.getTileFromTerrain(j, i) == replaceFrom && map.getTileFromTerrain(j, i) ?: 0 == replaceFrom
&& map.getTileFromWall(j, i) == TileNameCode.STONE) { && map.getTileFromWall(j, i) ?: 0 == replaceTo
map.setTileTerrain(j, i, tile) ) {
map.setTileTerrain(j, i, replaceTo)
} }
} }
} }
@@ -712,8 +803,8 @@ object MapGenerator {
private fun fillByMap(noisemap: Array<FloatArray>, scarcity: Float, replaceFrom: Int, tile: IntArray, message: String) { private fun fillByMap(noisemap: Array<FloatArray>, scarcity: Float, replaceFrom: Int, tile: IntArray, message: String) {
println("[mapgenerator] " + message) println("[mapgenerator] " + message)
for (i in 0..height - 1) { for (i in 0..HEIGHT - 1) {
for (j in 0..width - 1) { for (j in 0..WIDTH - 1) {
if (noisemap[i][j] > getNoiseGradient(i, noiseGradientStart, noiseGradientEnd) * scarcity && map.getTileFromTerrain(j, i) == replaceFrom) { if (noisemap[i][j] > getNoiseGradient(i, noiseGradientStart, noiseGradientEnd) * scarcity && map.getTileFromTerrain(j, i) == replaceFrom) {
map.setTileTerrain(j, i, tile[random.nextInt(tile.size)]) map.setTileTerrain(j, i, tile[random.nextInt(tile.size)])
} }
@@ -730,11 +821,11 @@ object MapGenerator {
} }
private fun gradientSqrt(func_argX: Int, start: Float, end: Float): Float { private fun gradientSqrt(func_argX: Int, start: Float, end: Float): Float {
val graph_gradient = (end - start) / FastMath.sqrt((height - TERRAIN_AVERAGE_HEIGHT).toFloat()) * FastMath.sqrt((func_argX - TERRAIN_AVERAGE_HEIGHT).toFloat()) + start val graph_gradient = (end - start) / FastMath.sqrt((HEIGHT - TERRAIN_AVERAGE_HEIGHT).toFloat()) * FastMath.sqrt((func_argX - TERRAIN_AVERAGE_HEIGHT).toFloat()) + start
if (func_argX < TERRAIN_AVERAGE_HEIGHT) { if (func_argX < TERRAIN_AVERAGE_HEIGHT) {
return start return start
} else if (func_argX >= height) { } else if (func_argX >= HEIGHT) {
return end return end
} else { } else {
return graph_gradient return graph_gradient
@@ -769,12 +860,12 @@ object MapGenerator {
*/ */
private fun gradientQuadratic(func_argX: Int, start: Float, end: Float): Float { private fun gradientQuadratic(func_argX: Int, start: Float, end: Float): Float {
val graph_gradient = FastMath.pow(FastMath.sqr((1 - TERRAIN_AVERAGE_HEIGHT).toFloat()), -1f) * // 1/4 -> 3/4 -> 9/16 -> 16/9 val graph_gradient = FastMath.pow(FastMath.sqr((1 - TERRAIN_AVERAGE_HEIGHT).toFloat()), -1f) * // 1/4 -> 3/4 -> 9/16 -> 16/9
(start - end) / FastMath.sqr(height.toFloat()) * (start - end) / FastMath.sqr(HEIGHT.toFloat()) *
FastMath.sqr((func_argX - height).toFloat()) + end FastMath.sqr((func_argX - HEIGHT).toFloat()) + end
if (func_argX < TERRAIN_AVERAGE_HEIGHT) { if (func_argX < TERRAIN_AVERAGE_HEIGHT) {
return start return start
} else if (func_argX >= height) { } else if (func_argX >= HEIGHT) {
return end return end
} else { } else {
return graph_gradient return graph_gradient
@@ -809,12 +900,12 @@ object MapGenerator {
*/ */
private fun gradientCubic(func_argX: Int, start: Float, end: Float): Float { private fun gradientCubic(func_argX: Int, start: Float, end: Float): Float {
val graph_gradient = -FastMath.pow(FastMath.pow((1 - TERRAIN_AVERAGE_HEIGHT).toFloat(), 3f), -1f) * // 1/4 -> 3/4 -> 9/16 -> 16/9 val graph_gradient = -FastMath.pow(FastMath.pow((1 - TERRAIN_AVERAGE_HEIGHT).toFloat(), 3f), -1f) * // 1/4 -> 3/4 -> 9/16 -> 16/9
(start - end) / FastMath.pow(height.toFloat(), 3f) * (start - end) / FastMath.pow(HEIGHT.toFloat(), 3f) *
FastMath.pow((func_argX - height).toFloat(), 3f) + end FastMath.pow((func_argX - HEIGHT).toFloat(), 3f) + end
if (func_argX < TERRAIN_AVERAGE_HEIGHT) { if (func_argX < TERRAIN_AVERAGE_HEIGHT) {
return start return start
} else if (func_argX >= height) { } else if (func_argX >= HEIGHT) {
return end return end
} else { } else {
return graph_gradient return graph_gradient
@@ -848,13 +939,13 @@ object MapGenerator {
* @return * @return
*/ */
private fun gradientMinusQuadratic(func_argX: Int, start: Float, end: Float): Float { private fun gradientMinusQuadratic(func_argX: Int, start: Float, end: Float): Float {
val graph_gradient = -FastMath.pow(FastMath.sqr((1 - TERRAIN_AVERAGE_HEIGHT).toFloat()), -1f) *// 1/4 -> 3/4 -> 9/16 -> 16/9 val graph_gradient = -FastMath.pow(FastMath.sqr((1 - TERRAIN_AVERAGE_HEIGHT).toFloat()), -1f) * // 1/4 -> 3/4 -> 9/16 -> 16/9
(start - end) / FastMath.sqr(height.toFloat()) * (start - end) / FastMath.sqr(HEIGHT.toFloat()) *
FastMath.sqr((func_argX - TERRAIN_AVERAGE_HEIGHT).toFloat()) + start FastMath.sqr((func_argX - TERRAIN_AVERAGE_HEIGHT).toFloat()) + start
if (func_argX < TERRAIN_AVERAGE_HEIGHT) { if (func_argX < TERRAIN_AVERAGE_HEIGHT) {
return start return start
} else if (func_argX >= height) { } else if (func_argX >= HEIGHT) {
return end return end
} else { } else {
return graph_gradient return graph_gradient
@@ -895,8 +986,8 @@ object MapGenerator {
private fun floodBottomLava() { private fun floodBottomLava() {
println("[mapgenerator] Flooding bottom lava...") println("[mapgenerator] Flooding bottom lava...")
for (i in height * 14 / 15..height - 1) { for (i in HEIGHT * 14 / 15..HEIGHT - 1) {
for (j in 0..width - 1) { for (j in 0..WIDTH - 1) {
if (map.terrainArray[i][j].toInt() == 0) { if (map.terrainArray[i][j].toInt() == 0) {
map.setTileTerrain(j, i, TileNameCode.LAVA) map.setTileTerrain(j, i, TileNameCode.LAVA)
} }
@@ -915,17 +1006,16 @@ object MapGenerator {
* under another certain level, use background stone with dirt peckles. * under another certain level, use background stone with dirt peckles.
*/ */
for (y in TERRAIN_AVERAGE_HEIGHT - MAX_HILL_HEIGHT..TERRAIN_AVERAGE_HEIGHT + MAX_HILL_HEIGHT - 1) { for (y in TERRAIN_AVERAGE_HEIGHT - TERRAIN_UNDULATION..TERRAIN_AVERAGE_HEIGHT + TERRAIN_UNDULATION - 1) {
for (x in 0..map.width - 1) { for (x in 0..map.width - 1) {
val thisTile = map.getTileFromTerrain(x, y) val thisTile = map.getTileFromTerrain(x, y)
for (i in 0..8) { for (i in 0..8) {
var nearbyWallTile = -1 var nearbyWallTile: Int?
try { nearbyWallTile = map.getTileFromWall(x + i % 3 - 1, y + i / 3 - 1)
nearbyWallTile = map.getTileFromWall(x + i % 3 - 1, y + i / 3 - 1)
} catch (e: ArrayIndexOutOfBoundsException) { if (nearbyWallTile == null) break;
}
if (i != 4 && thisTile == TileNameCode.DIRT && nearbyWallTile == TileNameCode.AIR) { if (i != 4 && thisTile == TileNameCode.DIRT && nearbyWallTile == TileNameCode.AIR) {
map.setTileTerrain(x, y, TileNameCode.GRASS) map.setTileTerrain(x, y, TileNameCode.GRASS)
@@ -957,10 +1047,11 @@ object MapGenerator {
private fun fillOcean() { private fun fillOcean() {
val thisSandList = intArrayOf( val thisSandList = intArrayOf(
TileNameCode.SAND_BEACH, TileNameCode.SAND_BLACK, TileNameCode.SAND_GREEN, TileNameCode.SAND, TileNameCode.SAND, TileNameCode.SAND, TileNameCode.SAND,
TileNameCode.SAND_BEACH, TileNameCode.SAND_BEACH, TileNameCode.SAND_BLACK TileNameCode.SAND_WHITE, TileNameCode.SAND_WHITE, TileNameCode.SAND_WHITE,
) TileNameCode.SAND_BLACK, TileNameCode.SAND_BLACK, TileNameCode.SAND_GREEN
val thisRand = HQRNG(seed!! xor random.nextLong()) )
val thisRand = HQRNG(SEED xor random.nextLong())
val thisSand = thisSandList[thisRand.nextInt(thisSandList.size)] val thisSand = thisSandList[thisRand.nextInt(thisSandList.size)]
// val thisSand = TileNameCode.SAND_GREEN // val thisSand = TileNameCode.SAND_GREEN
@@ -968,9 +1059,11 @@ object MapGenerator {
"black" "black"
else if (thisSand == TileNameCode.SAND_GREEN) else if (thisSand == TileNameCode.SAND_GREEN)
"green" "green"
else if (thisSand == TileNameCode.SAND)
"yellow"
else else
"white" "white"
println("[mapgenerator] Beach sand type: " + thisSandStr) println("[mapgenerator] Beach sand type: $thisSandStr")
var ix = 0 var ix = 0
while (ix < OCEAN_WIDTH * 1.5) { while (ix < OCEAN_WIDTH * 1.5) {
@@ -1061,12 +1154,12 @@ object MapGenerator {
* @return * @return
*/ */
private fun getTerrainHeightFromHeightMap(x: Int): Int { private fun getTerrainHeightFromHeightMap(x: Int): Int {
return TERRAIN_AVERAGE_HEIGHT - heightMap[x] TODO()
} }
@JvmStatic
fun getGeneratorSeed(): Long { fun getGeneratorSeed(): Long {
return seed!! return SEED
} }
/* Utility */ /* Utility */
@@ -1090,9 +1183,9 @@ object MapGenerator {
for (pointerX in 0..brushSize - 1) { for (pointerX in 0..brushSize - 1) {
if (getDistance(j.toFloat(), i.toFloat(), j + pointerX - halfBrushSize, i + pointerY - halfBrushSize) <= FastMath.floor((brushSize / 2).toFloat()) - 1) { if (getDistance(j.toFloat(), i.toFloat(), j + pointerX - halfBrushSize, i + pointerY - halfBrushSize) <= FastMath.floor((brushSize / 2).toFloat()) - 1) {
if (Math.round(j + pointerX - halfBrushSize) > brushSize if (Math.round(j + pointerX - halfBrushSize) > brushSize
&& Math.round(j + pointerX - halfBrushSize) < width - brushSize && Math.round(j + pointerX - halfBrushSize) < WIDTH - brushSize
&& Math.round(i + pointerY - halfBrushSize) > brushSize && Math.round(i + pointerY - halfBrushSize) > brushSize
&& Math.round(i + pointerY - halfBrushSize) < height - brushSize) { && Math.round(i + pointerY - halfBrushSize) < HEIGHT - brushSize) {
if (map.terrainArray[Math.round(i + pointerY - halfBrushSize)][Math.round(j + pointerX - halfBrushSize)] == fillFrom.toByte()) { if (map.terrainArray[Math.round(i + pointerY - halfBrushSize)][Math.round(j + pointerX - halfBrushSize)] == fillFrom.toByte()) {
map.terrainArray[Math.round(i + pointerY - halfBrushSize)][Math.round(j + pointerX - halfBrushSize)] = fill.toByte() map.terrainArray[Math.round(i + pointerY - halfBrushSize)][Math.round(j + pointerX - halfBrushSize)] = fill.toByte()
} }
@@ -1102,4 +1195,6 @@ object MapGenerator {
} }
} }
private fun clamp(x: Int, min: Int, max: Int): Int = if (x < min) min else if (x > max) max else x
} }

View File

@@ -11,9 +11,7 @@ class SimplexNoise
* * * *
* @param seed * @param seed
*/ */
(internal var largestFeature: (internal var largestFeature: Int, internal var persistence: Float, internal var seed: Long) {
Int, internal var persistence: Float, internal var seed: Long) {
internal var octaves: Array<SimplexNoise_octave> internal var octaves: Array<SimplexNoise_octave>
internal var frequencys: FloatArray internal var frequencys: FloatArray

View File

@@ -1,54 +0,0 @@
* Save meta
- Binary (for more security)
- Filename : world (with no extension)
Type Mnemonic Description
Byte[4] TESV Magic
Byte[n] name Savegame name, UTF-8
Byte NULL String terminator
Byte[8] terraseed Terrain seed
Byte[8] rogueseed Randomiser seed
Byte[32] hash1 SHA-256 hash of worldinfo1 being stored
Byte[32] hash2 SHA-256 hash of worldinfo2 being stored
Byte[32] hash3 SHA-256 hash of worldinfo3 being stored
Byte[32] hash4 SHA-256 hash of worldinfo4 being stored (TEMD data) [32, 214, 42, 3, 76, ...]
Endianness: Big
* Actor data
- GZip'd GSON
- Filename : <refid> (with no extension)
* Prop data
- GZip'd CSV
- Filename : worldinfo2 -- tileprop.csv
worldinfo3 -- itemprop.csv
worldinfo4 -- materialprop.csv
(with no extension)
* Human-readable
- Tiles_list.txt -- list of tiles in csv
- Items_list.txt -- list of items in csv
- Materials_list.txt -- list of materials in csv
== How it works ==
* If hash discrepancy is detected, (hash of csv in save dir != stored hash || hash of TEMD != stored hash)
printout "Save file corrupted. Continue?" with prompt "Yes/No"
Directory:
+--- <save1>
--- 2a93bc5fd...f823 Actor data
--- 423bdc838...93bd Actor data
--- Items_list.txt Human-readable
--- Tiles_list.txt Human-readable
--- world save meta
--- worldinfo1 TEMD binary
--- worldinfo2 tileprop
--- worldinfo3 itemprop
--- worldinfo4 materialprop

View File

@@ -103,9 +103,9 @@ constructor(gamename: String) : StateBasedGame(gamename) {
private lateinit var configDir: String private lateinit var configDir: String
@JvmStatic fun main(args: Array<String>) { fun main(args: Array<String>) {
try { try {
appgc = AppGameContainer(Terrarum("terrarum")) appgc = AppGameContainer(Terrarum("Terrarum"))
appgc.setDisplayMode(WIDTH, HEIGHT, false) appgc.setDisplayMode(WIDTH, HEIGHT, false)
appgc.setTargetFrameRate(TARGET_INTERNAL_FPS) appgc.setTargetFrameRate(TARGET_INTERNAL_FPS)
@@ -327,4 +327,6 @@ constructor(gamename: String) : StateBasedGame(gamename) {
return cfg return cfg
} }
} }
} }
fun main(args: Array<String>) = Terrarum.main(args)

View File

@@ -26,7 +26,7 @@ object TileNameCode {
val TRUNK_BLOODROSE = TilePropCodex.indexDamageToArrayAddr(4, 3) val TRUNK_BLOODROSE = TilePropCodex.indexDamageToArrayAddr(4, 3)
val SAND = TilePropCodex.indexDamageToArrayAddr(5, 0) val SAND = TilePropCodex.indexDamageToArrayAddr(5, 0)
val SAND_BEACH = TilePropCodex.indexDamageToArrayAddr(5, 1) val SAND_WHITE = TilePropCodex.indexDamageToArrayAddr(5, 1)
val SAND_RED = TilePropCodex.indexDamageToArrayAddr(5, 2) val SAND_RED = TilePropCodex.indexDamageToArrayAddr(5, 2)
val SAND_DESERT = TilePropCodex.indexDamageToArrayAddr(5, 3) val SAND_DESERT = TilePropCodex.indexDamageToArrayAddr(5, 3)
val SAND_BLACK = TilePropCodex.indexDamageToArrayAddr(5, 4) val SAND_BLACK = TilePropCodex.indexDamageToArrayAddr(5, 4)

View File

@@ -55,15 +55,15 @@ class TilePropCodex {
return tileProps[indexDamageToArrayAddr(index, damage)] return tileProps[indexDamageToArrayAddr(index, damage)]
} }
fun getProp(rawIndex: Int): TileProp { fun getProp(rawIndex: Int?): TileProp {
try { try {
tileProps[rawIndex].id tileProps[rawIndex ?: TileNameCode.STONE].id
} }
catch (e: NullPointerException) { catch (e: NullPointerException) {
throw NullPointerException("Tile prop with raw id $rawIndex does not exist.") throw NullPointerException("Tile prop with raw id $rawIndex does not exist.")
} }
return tileProps[rawIndex] return tileProps[rawIndex ?: TileNameCode.STONE]
} }
private fun setProp(prop: TileProp, record: CSVRecord) { private fun setProp(prop: TileProp, record: CSVRecord) {
@@ -88,7 +88,7 @@ class TilePropCodex {
if (prop.isFluid) prop.movementResistance = intVal(record, "movr") if (prop.isFluid) prop.movementResistance = intVal(record, "movr")
print(formatNum3(prop.id) + ":" + formatNum2(prop.damage)) print(formatNum3(prop.id) + ":" + formatNum2(prop.damage))
println("\t" + prop.name!!) println("\t" + prop.name)
} }
private fun intVal(rec: CSVRecord, s: String): Int { private fun intVal(rec: CSVRecord, s: String): Int {

View File

@@ -82,6 +82,7 @@ class ActorInventory() {
* Get clone of the itemList * Get clone of the itemList
* @return * @return
*/ */
@Suppress("UNCHECKED_CAST")
fun getCopyOfItemList(): Map<Long, Int>? { fun getCopyOfItemList(): Map<Long, Int>? {
return itemList.clone() as Map<Long, Int> return itemList.clone() as Map<Long, Int>
} }

View File

@@ -7,6 +7,7 @@ import com.torvald.terrarum.mapdrawer.MapDrawer
import com.torvald.terrarum.tileproperties.TilePropCodex import com.torvald.terrarum.tileproperties.TilePropCodex
import com.torvald.spriteanimation.SpriteAnimation import com.torvald.spriteanimation.SpriteAnimation
import com.jme3.math.FastMath import com.jme3.math.FastMath
import com.torvald.terrarum.tileproperties.TileNameCode
import org.newdawn.slick.GameContainer import org.newdawn.slick.GameContainer
import org.newdawn.slick.Graphics import org.newdawn.slick.Graphics
@@ -424,7 +425,7 @@ open class ActorWithBody constructor() : Actor, Visible, Glowing {
} }
// evaluate // evaluate
if (TilePropCodex.getProp(map.getTileFromTerrain(tileX, tileY)).isSolid) { if (TilePropCodex.getProp(map.getTileFromTerrain(tileX, tileY) ?: TileNameCode.STONE).isSolid) {
contactAreaCounter += 1 contactAreaCounter += 1
} }
} }

View File

@@ -13,13 +13,14 @@ import java.io.IOException
*/ */
object CreatureRawInjector { object CreatureRawInjector {
const val JSONPATH = "./res/raw/" const val JSONPATH = "./res/raw/creatures/"
private const val MULTIPLIER_RAW_ELEM_SUFFIX = "mult" private const val MULTIPLIER_RAW_ELEM_SUFFIX = "mult"
/** /**
* 'Injects' creature raw ActorValue to the ActorValue reference provided. * 'Injects' creature raw ActorValue to the ActorValue reference provided.
* *
* @param actorValueRef ActorValue object to be injected. * @param actorValueRef ActorValue object to be injected.
* @param jsonFileName with extension
*/ */
@Throws(IOException::class, SlickException::class) @Throws(IOException::class, SlickException::class)
fun inject(actorValueRef: ActorValue, jsonFileName: String) { fun inject(actorValueRef: ActorValue, jsonFileName: String) {

View File

@@ -4,6 +4,7 @@ import com.torvald.JsonFetcher
import com.torvald.terrarum.gameactors.faction.Faction import com.torvald.terrarum.gameactors.faction.Faction
import com.torvald.spriteanimation.SpriteAnimation import com.torvald.spriteanimation.SpriteAnimation
import com.google.gson.JsonObject import com.google.gson.JsonObject
import com.torvald.terrarum.gameactors.faction.FactionFactory
import org.newdawn.slick.SlickException import org.newdawn.slick.SlickException
import java.io.IOException import java.io.IOException
@@ -13,8 +14,6 @@ import java.io.IOException
object PFSigrid { object PFSigrid {
private val FACTION_PATH = "./res/raw/"
fun create(): Player { fun create(): Player {
val p = Player() val p = Player()
@@ -66,27 +65,8 @@ object PFSigrid {
p.setPosition((4096 * 16).toFloat(), (300 * 16).toFloat()) p.setPosition((4096 * 16).toFloat(), (300 * 16).toFloat())
p.faction.add(loadFactioningData("FactionSigrid.json")) p.faction.add(FactionFactory.create("FactionSigrid.json"))
return p return p
} }
private fun loadFactioningData(filename: String): Faction {
var jsonObject: JsonObject = JsonObject()
try {
jsonObject = JsonFetcher.readJson(FACTION_PATH + filename)
} catch (e: IOException) {
e.printStackTrace()
System.exit(-1)
}
val faction = Faction(jsonObject.get("factionname").asString)
jsonObject.get("factionamicable").asJsonArray.forEach { jobj -> faction.addFactionAmicable(jobj.asString) }
jsonObject.get("factionneutral").asJsonArray.forEach { jobj -> faction.addFactionNeutral(jobj.asString) }
jsonObject.get("factionhostile").asJsonArray.forEach { jobj -> faction.addFactionHostile(jobj.asString) }
jsonObject.get("factionfearful").asJsonArray.forEach { jobj -> faction.addFactionFearful(jobj.asString) }
return faction
}
} }

View File

@@ -1,5 +1,6 @@
package com.torvald.terrarum.gameactors.faction package com.torvald.terrarum.gameactors.faction
import com.torvald.random.HQRNG
import java.util.HashSet import java.util.HashSet
/** /**
@@ -12,6 +13,7 @@ class Faction(factionName: String) {
lateinit var factionNeutral: HashSet<String> lateinit var factionNeutral: HashSet<String>
lateinit var factionHostile: HashSet<String> lateinit var factionHostile: HashSet<String>
lateinit var factionFearful: HashSet<String> lateinit var factionFearful: HashSet<String>
var factionID: Long = HQRNG().nextLong()
init { init {
this.factionName = factionName this.factionName = factionName

View File

@@ -29,7 +29,8 @@
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
* This source is a custom modification of the original code. * This source, used in the project 'Terrarum' is a derivative of the original code.
* I claim no ownership to this FastMath module. --Torvald
*/ */
package com.jme3.math; package com.jme3.math;

View File

@@ -0,0 +1,132 @@
## About gamemaking ##
### CHALLENGING, NOT PUNISHING ###
https://www.youtube.com/watch?v=ea6UuRTjkKs
1. CONSISTENT RULES
- No arbitrary unstoppable death
2. Player's skill involved
- Can play around, not restart
3. Usability of in-game tools
- Players should be able to 'regret' their strategy and adjust.
4. Comfortable control
5. Make players overcome the challenge, not defeating them
6. Let players have "aha" moment when they failed.
- Make them hungry to retry with new strategies.
- Some small things they've could done differently
- e.g. "One-big-hit didn't worked, may I should've picked up high DPS one"
### MORE DEPTH, LESS COMPLEXITY ###
https://www.youtube.com/watch?v=jVL4st0blGU
1. Memorise less!
- Less burden to, even starting the game
- Start with gentle learning curve, getting slowly steep
- Intuitive UX (UI, control, ...)
- Good tutorial = lessens complexity
2. Intuitive!
3. Calculations per second
- reduce!
4. Players have to know everything to even begin the play ### FAIL (irreducible complexity)
- Make them get familiar with rules of the game
- Dwarf Fortress failed this!
### Lots of things players can play with (aka don't make them bored) ###
- Combat, battle, building, mechanics, adventure, dungeon explore, spelunking
- Not scaled; easy combat, tough combat, tedious combat, etc.
### Achieving perfect imbalance ###
https://www.youtube.com/watch?v=e31OSVZF77w
- Make sure no matter how you skilled, your playable character cannot be good at everything
- Give players __wide pool of options__ to solve problem
(kill the boss, defend their adobe, fast transportation, etc.)
**_What feeling do you want to convey?_**
### Always think WHY you want to add _something_ on the game ###
- e.g. Why are you adding RPG leveling system? What it would do to the players? How would they play with?
See also: *HEARTS, CLUBS, DIAMONDS, SPADES: PLAYERS WHO SUIT MUDS*
## About this very game ##
### Friendlier version of Dwarf Fortress Adventure mode ###
- Yet _lots of fun_
- Add Fortress mode features by 'make your own settling'
- Hard to actually die, but once you die, you're done.
+ Config: imtooyoungtodie for easy mode
- Genre: Adventure, Open world (towns in RPG, building, town managing (conquer existing one or
you build one and persuade existing people to move in) -> See Dwarf Fortress and Animal Crossing)
* Adventure: adventure this vast—5,5 km wide—world, discover new (and good/horrible) things
* Open world:
- Building: building your own houses, structures, etc.
- Town managing:
1. Build your own little hamlet and manage it
or-
2. Conquer existing one and become a ruler
The town is a special hamlet that can be tailored for your taste
- Survival:
mobs will trying to attack your assets (yourself, your hamlet, your people)
### Side view ###
### Interact menu w/ mouse right ###
### Pixelated sprites ###
- Use 2x sprites if rotating does not work well
### User experience ###
* Indicative mouse cursor
### Game mechanics ###
* 24 pixels == 1 metre
### Purpose of the game ###
* Boss
- Will be mentioned/shown as absolute _evil_.
- But actually is not.
* Theme
- Is an evil really really is what we think?
- Is there a thing as 'absolute evil'?
* Boss character
- From debugger character
- Name key: "Sigriðr hinn Dróttningin" (can be changed)
* Little setting
- A ruler, hated by people
* Mechanics
- Beating boss does not ends the game, but grants an ability to
create new character as it.

View File

@@ -0,0 +1,9 @@
* *Terrarum* by Torvald
Copyright 2015-2016 Torvald. All rights reserved.
mailto: skyhi14 *64* __115875741922660__ *46* __6516589__
* *Simplex Noise Generator*, version 2012-03-09 by Stefan Gustavson
Released as public domain
* *Joise* modular noise library
Copyright (C) 2013 Jason Taylor. Released as open-source under Apache License, Version 2.0.

View File

@@ -0,0 +1,125 @@
## Weapon tier ##
Natural / Common Stone -> Copper -> Iron -> Silver -> Titanium
Forging --------------> Steel --------^
Exotic ('elven') Glass Aurichalcum
Special (something 'adamant') ??? (Use material spec of CNT, tensile strength 180 GPa)
* Metal graphics
- Gold: Hue 43, low Saturation
- Aurichalcum: Hue 43, mid-high Saturation
- Copper: Hue 33,
- Copper rust: Hue 160
- Iron rust: Hue 21
## Size variation ##
Race base weapon/tool size <- 10 [kg]
Size tolerance <- (50% * str/1000), or say, 20%
If the size is bigger than tolerable, weapon speed severely slows down, tools become unusable
if use time >* 0.75 second, the weapon/tool cannot be equipped.
Small weapons/tools gains no (dis)advantage
When drawing: scale by (craftedWeaponSize / baseWeaponSize)
Crafted tool/weapon size is dependent to the baseRaceMass.
## Gemstone tier ##
Topaz -> R·G·B -> Diamond·Amethyst
## Colouring ##
Natural: Use 4096
Magical/Surreal: Use 24 Bits
* Colouring of potion
- Randomised, roguelike fashion
- Choose Col(R40, G40, B40) from set of finite cards:
39, 39, 19, 19, 0, 0
- MULTIPLY blend chosen colour with white texture
## Roguelike identity ##
* Randomised things
- E.g. potion
Lime-coloured potion
First play: "Potion (???)"
After drank: "Potion (Healing)" is revealed.
Second (new) play: "Potion (???)"
After drank: "Potion (Neurotoxin)" is revealed.
## Making sprite ##
* Layers
- (Optional) Glow
- (Optional) Hair foreground
- Right arm dress
- Right arm body
- Dress
- Boots
- Body
- (Optional) Hair accessory
- Hair
- Head
- Left arm dress
- Left arm body
- (Optional) SFX
* Size
- Regular sprite 'height' (hitbox height) : 40 px
- Apparent height may vary
## Chargen ##
* Select hair, colours, then compile them into single spritesheet
* NO gender distinction, but have masculine/neutral/feminine looks (in clothing, hairstyles, etc.)
* Colour: 4096 colours (12-bit 0x000 - 0xFFF)
* Base mass: 60 kg
## Custom pattern making ##
- Players can create their own décors (hang on wall), dresses.
- Two looms (216 colour mode, 4096 colour mode)
## Food/Potion dose ##
Scale ^ 3 ^ (3/4) == (ThisWgt / TargetWgt) ^ (3/4)
## (De)serialisation ##
see SAVE_FORMAT.md
## Actor as universal tool ##
* Utility tiles that have states (e.g. noteblock) are implemented using Actor.
## NPC Killing ##
* AI:
Attacked first: Warn player to not attack
Attacked second: The NPC becomes hostile until player sheathe the weapon
Attacked thrice: All the NPCs within the same faction become hostile until the player is away
## Ownership of lands ##
* Codex of Real Estate → assign owner to the tiles → copy the information to the NPC instance

View File

@@ -0,0 +1,57 @@
## Format ##
* Save meta
- Binary (for more security)
- Filename : world (with no extension)
|Type |Mnemonic |Description |
|------------|------------|---------------------------------------|
|Byte[4] |TESV |Magic |
|Byte[n] |name |Savegame name, UTF-8 |
|Byte |NULL |String terminator |
|Byte[8] |terraseed |Terrain seed |
|Byte[8] |rogueseed |Randomiser seed |
|Byte[32] |hash1 |SHA-256 hash of worldinfo1 being stored|
|Byte[32] |hash2 |SHA-256 hash of worldinfo2 being stored|
|Byte[32] |hash3 |SHA-256 hash of worldinfo3 being stored|
|Byte[32] |hash4 |SHA-256 hash of worldinfo4 being stored|
Endianness: Big
* Actor/Faction data
- GZip'd GSON
- Filename : (refid) (with no extension)
* Prop data
- GZip'd CSV
- Filename : worldinfo2 -- tileprop.csv
worldinfo3 -- itemprop.csv
worldinfo4 -- materialprop.csv
(with no extension)
* Human-readable
- Tiles_list.txt -- list of tiles in csv
- Items_list.txt -- list of items in csv
- Materials_list.txt -- list of materials in csv
## How it works ##
* If hash discrepancy has detected, (hash of csv in save dir != stored hash || hash of TEMD != stored hash)
printout "Save file corrupted. Continue?" with prompt "Yes/No"
Directory:
+--- <save1>
--- 2a93bc5fd...f823 Actor/Faction/etc. data
--- 423bdc838...93bd Actor/Faction/etc. data
--- Items_list.txt Human-readable
--- Materials_list.txt Human-redable
--- Tiles_list.txt Human-readable
--- world save meta (binary)
--- worldinfo1 TEMD (binary)
--- worldinfo2 tileprop
--- worldinfo3 itemprop
--- worldinfo4 materialprop

View File

@@ -4,12 +4,8 @@ import com.torvald.colourutil.Col4096
import com.torvald.RasterWriter import com.torvald.RasterWriter
import com.torvald.terrarum.Terrarum import com.torvald.terrarum.Terrarum
import javax.imageio.ImageIO
import java.awt.*
import java.awt.color.ColorSpace
import java.awt.image.*
import java.io.* import java.io.*
import java.util.Hashtable import java.util.HashMap
/** /**
* Created by minjaesong on 16-01-17. * Created by minjaesong on 16-01-17.
@@ -19,7 +15,7 @@ class ExportMap : ConsoleCommand {
//private var mapData: ByteArray? = null //private var mapData: ByteArray? = null
// private var mapDataPointer = 0 // private var mapDataPointer = 0
private val colorTable = Hashtable<Byte, Col4096>() private val colorTable = HashMap<Byte, Col4096>()
override fun execute(args: Array<String>) { override fun execute(args: Array<String>) {
if (args.size == 2) { if (args.size == 2) {
@@ -29,8 +25,8 @@ class ExportMap : ConsoleCommand {
var mapDataPointer = 0 var mapDataPointer = 0
for (tile in Terrarum.game.map.layerTerrain) { for (tile in Terrarum.game.map.layerTerrain) {
val colArray = (colorTable as java.util.Map<Byte, Col4096>) val colArray = (colorTable as Map<Byte, Col4096>)
.getOrDefault(tile, Col4096(0xFFF)).toByteArray() .getOrElse(tile, { Col4096(0xFFF) }).toByteArray()
for (i in 0..2) { for (i in 0..2) {
mapData[mapDataPointer + i] = colArray[i] mapData[mapDataPointer + i] = colArray[i]

View File

@@ -20,8 +20,8 @@ class GetFactioning : ConsoleCommand {
// get all factioning data of player // get all factioning data of player
val factionSet = Terrarum.game.player.faction val factionSet = Terrarum.game.player.faction
if (factionSet == null) { if (factionSet.isEmpty()) {
echo.execute("The actor has null faction set.") echo.execute("The actor has empty faction set.")
return return
} }
@@ -29,7 +29,7 @@ class GetFactioning : ConsoleCommand {
echo.execute(count.toString() + Lang.pluralise(" faction", count) + " assigned.") echo.execute(count.toString() + Lang.pluralise(" faction", count) + " assigned.")
for (faction in factionSet) { for (faction in factionSet) {
echo.execute("faction \"" + faction.factionName + "\"") echo.execute("faction ${faction.factionName}")
echo.execute(" Amicable") echo.execute(" Amicable")
faction.factionAmicable.forEach { s -> echo.execute(PRINT_INDENTATION + s) } faction.factionAmicable.forEach { s -> echo.execute(PRINT_INDENTATION + s) }

View File

@@ -1,5 +1,6 @@
package com.torvald.terrarum.console package com.torvald.terrarum.console
import com.torvald.terrarum.Terrarum
import com.torvald.terrarum.langpack.Lang import com.torvald.terrarum.langpack.Lang
/** /**
@@ -9,13 +10,19 @@ class Help : ConsoleCommand {
override fun execute(args: Array<String>) { override fun execute(args: Array<String>) {
val echo = Echo() val echo = Echo()
if (args.size == 1) { if (args.size == 1) {
for (i in 1..6) echo.execute(Lang["HELP_OTF_MAIN_TEXT_$i"]) for (i in 1..6) echo.execute(Lang["HELP_OTF_TEXT_$i"])
} }
else if (args[1].toLowerCase() == "slow") { else if (args[1].toLowerCase() == "slow") {
for (i in 1..4) echo.execute(Lang["HELP_OTF_SLOW_TEXT_$i"]) if (Terrarum.game.screenZoom < 1)
echo.execute(Lang["HELP_OTF_SLOW_IF_ZOOM"])
if (Terrarum.game.debugWindow.isVisible)
echo.execute(Lang["HELP_OTF_SLOW_IF_F3"])
for (i in 1..1) echo.execute(Lang["HELP_OTF_SLOW_$i"])
} }
else { else {
for (i in 1..6) echo.execute(Lang["HELP_OTF_MAIN_TEXT_$i"]) for (i in 1..6) echo.execute(Lang["HELP_OTF_MAIN_$i"])
} }
} }

View File

@@ -8,10 +8,15 @@ import java.io.IOException
/** /**
* Created by minjaesong on 16-02-15. * Created by minjaesong on 16-02-15.
*/ */
class FactionRelatorFactory { object FactionFactory {
const val JSONPATH = "./res/raw/factions/"
/**
* @param filename with extension
*/
@Throws(IOException::class) @Throws(IOException::class)
fun build(filename: String): Faction { fun create(filename: String): Faction {
val jsonObj = JsonFetcher.readJson(JSONPATH + filename) val jsonObj = JsonFetcher.readJson(JSONPATH + filename)
val factionObj = Faction(jsonObj.get("factionname").asString) val factionObj = Faction(jsonObj.get("factionname").asString)
@@ -23,10 +28,4 @@ class FactionRelatorFactory {
return factionObj return factionObj
} }
companion object {
val JSONPATH = "./res/raw/"
}
} }

View File

@@ -0,0 +1,8 @@
package com.torvald.terrarum.gameactors.scheduler
/**
* Ultima-like NPC scheduler
* Created by minjaesong on 16-03-26.
*/
interface NPCSchedule {
}

View File

@@ -0,0 +1,27 @@
package com.torvald.terrarum.realestate
import java.io.Serializable
import java.util.*
/**
* Created by minjaesong on 16-03-27.
*/
object RealEstateCodex {
/**
* HashMap<Absolute tile number, Actor/Faction ID>
*
* Note that a tile can have only ONE owner (as an Actor or Faction ID)
*/
private var ownershipRegistry: HashMap<Long, Long> = HashMap()
fun setOwner(tileX: Int, tileY: Int, refID: Long) {
ownershipRegistry[RealEstateUtility.getAbsoluteTileNumber(tileX, tileY)] = refID
}
fun removeOwner(tileX: Int, tileY: Int) {
ownershipRegistry.remove(RealEstateUtility.getAbsoluteTileNumber(tileX, tileY))
}
fun getOwner(tileX: Int, tileY: Int): Long? =
ownershipRegistry[RealEstateUtility.getAbsoluteTileNumber(tileX, tileY)]
}

View File

@@ -0,0 +1,14 @@
package com.torvald.terrarum.realestate
import com.torvald.terrarum.Terrarum
/**
* Created by minjaesong on 16-03-27.
*/
object RealEstateUtility {
fun getAbsoluteTileNumber(x: Int, y: Int): Long =
(Terrarum.game.map.width * y).toLong() + x
fun resolveAbsoluteTileNumber(t: Long): Pair<Int, Int> =
Pair((t % Terrarum.game.map.width).toInt(), (t / Terrarum.game.map.width).toInt())
}

View File

@@ -13,7 +13,7 @@ import java.util.Arrays
/** /**
* Created by minjaesong on 16-02-01. * Created by minjaesong on 16-02-01.
*/ */
object TileStat { object TileStats {
private val tilestat = ShortArray(GameMap.TILES_SUPPORTED) private val tilestat = ShortArray(GameMap.TILES_SUPPORTED)
@@ -47,8 +47,8 @@ object TileStat {
for (x in for_x_start..for_x_end - 1) { for (x in for_x_start..for_x_end - 1) {
val tileWall = map.getTileFromWall(x, y) val tileWall = map.getTileFromWall(x, y)
val tileTerrain = map.getTileFromTerrain(x, y) val tileTerrain = map.getTileFromTerrain(x, y)
++tilestat[tileWall] ++tilestat[tileWall ?: 0]
++tilestat[tileTerrain] ++tilestat[tileTerrain ?: 0]
} }
} }
} }

View File

@@ -32,7 +32,7 @@ class BasicDebugInfoWindow : UICanvas {
} }
override fun update(gc: GameContainer, delta_t: Int) { override fun update(gc: GameContainer, delta: Int) {
val player = Terrarum.game.player val player = Terrarum.game.player
val hitbox = player.hitbox!! val hitbox = player.hitbox!!
@@ -80,32 +80,26 @@ class BasicDebugInfoWindow : UICanvas {
val lightVal: String val lightVal: String
var mtX = mouseTileX.toString() var mtX = mouseTileX.toString()
var mtY = mouseTileY.toString() var mtY = mouseTileY.toString()
try { val valRaw = LightmapRenderer.getValueFromMap(mouseTileX, mouseTileY) ?: -1
val valRaw = LightmapRenderer.getValueFromMap(mouseTileX, mouseTileY) val rawR = LightmapRenderer.getRawR(valRaw)
val rawR = LightmapRenderer.getRawR(valRaw) val rawG = LightmapRenderer.getRawG(valRaw)
val rawG = LightmapRenderer.getRawG(valRaw) val rawB = LightmapRenderer.getRawB(valRaw)
val rawB = LightmapRenderer.getRawB(valRaw) lightVal = if (valRaw == -1)
lightVal = valRaw.toInt().toString() + " (" + ""
else
valRaw.toInt().toString() + " (" +
rawR.toString() + " " + rawR.toString() + " " +
rawG.toString() + " " + rawG.toString() + " " +
rawB.toString() + ")" rawB.toString() + ")"
} catch (e: ArrayIndexOutOfBoundsException) {
lightVal = "out of bounds"
mtX = "---"
mtY = "---"
}
printLine(g, 7, "light at cursor : " + lightVal) printLine(g, 7, "light at cursor : " + lightVal)
val tileNo: String val tileNo: String
try { val tileNumRaw = Terrarum.game.map.getTileFromTerrain(mouseTileX, mouseTileY) ?: -1
val tileNumRaw = Terrarum.game.map.getTileFromTerrain(mouseTileX, mouseTileY) val tilenum = tileNumRaw / PairedMapLayer.RANGE
val tilenum = tileNumRaw / PairedMapLayer.RANGE val tiledmg = tileNumRaw % PairedMapLayer.RANGE
val tiledmg = tileNumRaw % PairedMapLayer.RANGE tileNo = if (tileNumRaw == -1) "" else "$tilenum:$tiledmg"
tileNo = "$tilenum:$tiledmg"
} catch (e: ArrayIndexOutOfBoundsException) {
tileNo = "-"
}
printLine(g, 8, "tile at cursor : $tileNo ($mtX, $mtY)") printLine(g, 8, "tile at cursor : $tileNo ($mtX, $mtY)")

View File

@@ -43,7 +43,7 @@ class ConsoleWindow : UICanvas, UITypable {
reset() reset()
} }
override fun update(gc: GameContainer, delta_t: Int) { override fun update(gc: GameContainer, delta: Int) {
} }

View File

@@ -53,7 +53,7 @@ constructor(override var width: Int, isBlackVariant: Boolean) : UICanvas {
this.messagesList = messagesList this.messagesList = messagesList
} }
override fun update(gc: GameContainer, delta_t: Int) { override fun update(gc: GameContainer, delta: Int) {
} }