mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-07 12:21:52 +09:00
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:
@@ -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.
|
||||||
|
|||||||
17
README.md
17
README.md
@@ -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 |
@@ -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 d‘informations.";;;;;;;;;;;;;;;;;;"• 기본 정보 창을 꺼 주세요."
|
||||||
"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,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.
|
|
||||||
|
|
||||||
@@ -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
|
|
||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.
|
|
||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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))
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
|
||||||
@@ -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)
|
||||||
@@ -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)
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
132
src/com/torvald/terrarum/ABOUT.md
Normal file
132
src/com/torvald/terrarum/ABOUT.md
Normal 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.
|
||||||
|
|
||||||
9
src/com/torvald/terrarum/COPYING.md
Normal file
9
src/com/torvald/terrarum/COPYING.md
Normal 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.
|
||||||
125
src/com/torvald/terrarum/MECHNANICS.md
Normal file
125
src/com/torvald/terrarum/MECHNANICS.md
Normal 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
|
||||||
|
|
||||||
57
src/com/torvald/terrarum/SAVE_FORMAT.md
Normal file
57
src/com/torvald/terrarum/SAVE_FORMAT.md
Normal 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
|
||||||
@@ -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]
|
||||||
|
|||||||
@@ -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) }
|
||||||
|
|
||||||
|
|||||||
@@ -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"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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/"
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package com.torvald.terrarum.gameactors.scheduler
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ultima-like NPC scheduler
|
||||||
|
* Created by minjaesong on 16-03-26.
|
||||||
|
*/
|
||||||
|
interface NPCSchedule {
|
||||||
|
}
|
||||||
27
src/com/torvald/terrarum/realestate/RealEstateCodex.kt
Normal file
27
src/com/torvald/terrarum/realestate/RealEstateCodex.kt
Normal 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)]
|
||||||
|
}
|
||||||
14
src/com/torvald/terrarum/realestate/RealEstateUtility.kt
Normal file
14
src/com/torvald/terrarum/realestate/RealEstateUtility.kt
Normal 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())
|
||||||
|
}
|
||||||
@@ -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]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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)")
|
||||||
|
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user