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

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

View File

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

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,9 +1,5 @@
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.
*/
@@ -40,8 +36,11 @@ class MapLayer(var width: Int, var height: Int) : Iterable<Byte> {
}
}
internal fun getTile(x: Int, y: Int): Int {
return uint8ToInt32(data[y][x])
internal fun getTile(x: Int, y: Int): Int? {
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) {
@@ -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)
companion object {
@Transient @JvmStatic val RANGE = 256
@Transient val RANGE = 256
}
}

View File

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

View File

@@ -143,11 +143,11 @@ class WorldTime {
/**
* 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
@Transient val MINUTE_SEC: Int = 60
@Transient val HOUR_MIN: Int = 60
@Transient val GAME_MIN_TO_REAL_SEC: Float = 60f
val HOUR_SEC: Int = 3600
val MINUTE_SEC: Int = 60
val HOUR_MIN: Int = 60
val GAME_MIN_TO_REAL_SEC: Float = 60f
}
}

View File

@@ -39,9 +39,6 @@ object Lang {
init {
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
val file = File(PATH_TO_CSV)
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) }
}
// lang.put(record.get(CSV_COLUMN_FIRST), record)
// Fill lang table
// langPackCSV.forEach({ this.appendToLangByStringID(it) })
// sort word lists
Arrays.sort(ENGLISH_WORD_NORMAL_PLURAL)
Arrays.sort(FRENCH_WORD_NORMAL_PLURAL)
// reload correct (C/J) unihan fonts if applicable
try {
(Terrarum.gameFontWhite as GameFontWhite).reloadUnihan()
}

View File

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

View File

@@ -19,7 +19,7 @@ object LightmapRenderer {
* 8-Bit RGB values
*/
@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
/**
@@ -38,14 +38,14 @@ object LightmapRenderer {
private val TSIZE = MapDrawer.TILE_SIZE
// color model related vars
const val MUL = 256
const val MUL_2 = 256 * 256
const val CHANNEL_MAX = 255
const val CHANNEL_MAX_FLOAT = 255f
const val COLOUR_DOMAIN_SIZE = 256 * 256 * 256
const val MUL = 256 // modify this to 1024 to implement 30-bit RGB
const val MUL_2 = MUL * MUL
const val CHANNEL_MAX = MUL - 1
const val CHANNEL_MAX_FLOAT = CHANNEL_MAX.toFloat()
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)
fun addLantern(x: Int, y: Int, intensity: Int) {
@@ -75,8 +75,12 @@ object LightmapRenderer {
}
}
fun getLight(x: Int, y: Int): Int =
java.lang.Byte.toUnsignedInt(lightMapLSB!![y][x]) or (lightMapMSB!![y][x].toInt() shl 8)
fun getLight(x: Int, y: Int): Int? =
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) {
lightMapLSB!![y][x] = (colour and 0xFF).toByte()
@@ -164,7 +168,7 @@ object LightmapRenderer {
while (x < for_x_end) {
// smooth
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
&& getLight(x, y - 1) == 0) {
try {
@@ -204,32 +208,32 @@ object LightmapRenderer {
thisLightLevel
else
maximiseRGB(
getLight(x, y),
getLight(x, y - 1))
getLight(x, y) ?: 0,
getLight(x, y - 1) ?: 0)
val d = if (y == 0)
thisLightLevel
else if (y == Terrarum.game.map.height - 1)
thisLightLevel
else
maximiseRGB(
getLight(x, y),
getLight(x, y + 1))
getLight(x, y) ?: 0,
getLight(x, y + 1) ?: 0)
val b = if (x == 0)
thisLightLevel
else if (x == Terrarum.game.map.width - 1)
thisLightLevel
else
maximiseRGB(
getLight(x, y),
getLight(x - 1, y))
getLight(x, y) ?: 0,
getLight(x - 1, y) ?: 0)
val c = if (x == 0)
thisLightLevel
else if (x == Terrarum.game.map.width - 1)
thisLightLevel
else
maximiseRGB(
getLight(x, y),
getLight(x + 1, y))
getLight(x, y) ?: 0,
getLight(x + 1, y) ?: 0)
val colourMapItoL = IntArray(4)
colourMapItoL[0] = colourLinearMix(a, b)
colourMapItoL[1] = colourLinearMix(a, c)
@@ -260,7 +264,7 @@ object LightmapRenderer {
if (x + sameLevelCounter >= for_x_end) break
}
g.color = Color(getLight(x, y))
g.color = Color(getLight(x, y) ?: 0)
g.fillRect(
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())
@@ -347,13 +351,13 @@ object LightmapRenderer {
if (xoff != yoff && -xoff != yoff) {
// 'v' tiles
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) {
// 'a' tiles
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'
// 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
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) {
try {

View File

@@ -108,7 +108,7 @@ object MapCamera {
, TileNameCode.PLANK_EBONY
, TileNameCode.PLANK_NORMAL
, TileNameCode.SAND
, TileNameCode.SAND_BEACH
, TileNameCode.SAND_WHITE
, TileNameCode.SAND_RED
, TileNameCode.SAND_DESERT
, TileNameCode.SAND_BLACK
@@ -230,7 +230,7 @@ object MapCamera {
for (y in for_y_start..for_y_end - 1) {
for (x in for_x_start..for_x_end - 1) {
val thisTile: Int
val thisTile: Int?
if (mode % 3 == WALL)
thisTile = map.getTileFromWall(x, y)
else if (mode % 3 == TERRAIN)
@@ -248,12 +248,14 @@ object MapCamera {
(mode == WALL || mode == TERRAIN) // not an air tile
&& thisTile > 0
&& (thisTile ?: 0) > 0
&&
// check if light level of upper tile is zero and
// 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).toInt() > 0)) {
(y > 0 && !(LightmapRenderer.getValueFromMap(x, y) ?: 0.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
if (isWallSticker(thisTile)) {
@@ -269,11 +271,11 @@ object MapCamera {
val thisTileX: Int
if (!noDamageLayer)
thisTileX = PairedMapLayer.RANGE * (thisTile % PairedMapLayer.RANGE) + nearbyTilesInfo
thisTileX = PairedMapLayer.RANGE * ((thisTile ?: 0) % PairedMapLayer.RANGE) + nearbyTilesInfo
else
thisTileX = nearbyTilesInfo
val thisTileY = thisTile / PairedMapLayer.RANGE
val thisTileY = (thisTile ?: 0) / PairedMapLayer.RANGE
if (drawModeTilesBlendMul) {
if (isBlendMul(thisTile)) {
@@ -311,31 +313,12 @@ object MapCamera {
* *
* @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)
if (x == 0) {
nearbyTiles[NEARBY_TILE_KEY_LEFT] = 4096
} else {
nearbyTiles[NEARBY_TILE_KEY_LEFT] = map.getTileFrom(mode, x - 1, y)
}
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)
}
nearbyTiles[NEARBY_TILE_KEY_LEFT] = map.getTileFrom(mode, x - 1, y) ?: 4096
nearbyTiles[NEARBY_TILE_KEY_RIGHT] = map.getTileFrom(mode, x + 1, y) ?: 4096
nearbyTiles[NEARBY_TILE_KEY_UP] = map.getTileFrom(mode, x, y - 1) ?: 4906
nearbyTiles[NEARBY_TILE_KEY_DOWN] = map.getTileFrom(mode, x, y + 1) ?: 4096
// try for
var ret = 0
@@ -350,29 +333,10 @@ object MapCamera {
private fun getNearbyTilesInfoNonSolid(x: Int, y: Int, mode: Int): Int {
val nearbyTiles = IntArray(4)
if (x == 0) {
nearbyTiles[NEARBY_TILE_KEY_LEFT] = 4096
} else {
nearbyTiles[NEARBY_TILE_KEY_LEFT] = map.getTileFrom(mode, x - 1, y)
}
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)
}
nearbyTiles[NEARBY_TILE_KEY_LEFT] = map.getTileFrom(mode, x - 1, y) ?: 4096
nearbyTiles[NEARBY_TILE_KEY_RIGHT] = map.getTileFrom(mode, x + 1, y) ?: 4096
nearbyTiles[NEARBY_TILE_KEY_UP] = map.getTileFrom(mode, x, y - 1) ?: 4096
nearbyTiles[NEARBY_TILE_KEY_DOWN] = map.getTileFrom(mode, x, y + 1) ?: 4096
// try for
var ret = 0
@@ -392,25 +356,10 @@ object MapCamera {
private fun getNearbyTilesInfoWallSticker(x: Int, y: Int): Int {
val nearbyTiles = IntArray(4)
val NEARBY_TILE_KEY_BACK = NEARBY_TILE_KEY_UP
if (x == 0) {
nearbyTiles[NEARBY_TILE_KEY_LEFT] = 4096
} else {
nearbyTiles[NEARBY_TILE_KEY_LEFT] = map.getTileFrom(TERRAIN, x - 1, y)
}
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)
nearbyTiles[NEARBY_TILE_KEY_LEFT] = map.getTileFrom(TERRAIN, x - 1, y) ?: 4096
nearbyTiles[NEARBY_TILE_KEY_RIGHT] = map.getTileFrom(TERRAIN, x + 1, y) ?: 4096
nearbyTiles[NEARBY_TILE_KEY_DOWN] = map.getTileFrom(TERRAIN, x, y + 1) ?: 4096
nearbyTiles[NEARBY_TILE_KEY_BACK] = map.getTileFrom(WALL, x, y) ?: 4096
try {
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 getRenderEndY(): Int = clampHTile(getRenderStartY() + div16(renderHeight) + 2)
private fun isConnectSelf(b: Int): Boolean = TILES_CONNECT_SELF.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 isPlatform(b: Int): Boolean = TILES_WALL_STICKER_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 isWallSticker(b: Int?): Boolean = TILES_WALL_STICKER.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() {
GL11.glEnable(GL11.GL_BLEND)

View File

@@ -3,7 +3,7 @@ package com.torvald.terrarum.mapdrawer
import com.torvald.terrarum.gamemap.GameMap
import com.torvald.terrarum.Terrarum
import com.torvald.terrarum.tileproperties.TileNameCode
import com.torvald.terrarum.tilestats.TileStat
import com.torvald.terrarum.tilestats.TileStats
import com.jme3.math.FastMath
import org.newdawn.slick.*
@@ -44,8 +44,8 @@ object MapDrawer {
fun drawEnvOverlay(g: Graphics) {
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_cold_tiles = TileStat.getCount(*TILES_COLD).toFloat()
val onscreen_warm_tiles = TileStat.getCount(*TILES_WARM).toFloat()
val onscreen_cold_tiles = TileStats.getCount(*TILES_COLD).toFloat()
val onscreen_warm_tiles = TileStats.getCount(*TILES_WARM).toFloat()
val colTemp_cold = colTempLinearFunc(onscreen_cold_tiles / onscreen_tiles_cap)
val colTemp_warm = colTempLinearFunc(-(onscreen_warm_tiles / onscreen_tiles_cap))

View File

@@ -2,9 +2,10 @@ package com.torvald.terrarum.mapgenerator
import com.torvald.random.HQRNG
import com.torvald.terrarum.gamemap.GameMap
import com.torvald.terrarum.gamemap.MapLayer
import com.torvald.terrarum.tileproperties.TileNameCode
import com.jme3.math.FastMath
import com.sudoplay.joise.Joise
import com.sudoplay.joise.module.*
import java.util.*
object MapGenerator {
@@ -12,13 +13,14 @@ object MapGenerator {
private lateinit var map: GameMap
private lateinit var random: Random
//private static float[] noiseArray;
private var seed: Long = 0
private var width: Int = 0
private var height: Int = 0
private var SEED: Long = 0
private var WIDTH: 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 minimumFloatingIsleHeight: Int = 0
@@ -27,7 +29,8 @@ object MapGenerator {
private val noiseGrdCaveEnd = 0.54f
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
@@ -35,10 +38,6 @@ object MapGenerator {
private var SHORE_WIDTH = 120
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 val GLACIER_MOUNTAIN_HEIGHT = 300
@@ -59,54 +58,55 @@ object MapGenerator {
private val GRASSCUR_RIGHT = 1
private val GRASSCUR_DOWN = 2
private val GRASSCUR_LEFT = 3
@JvmStatic
fun attachMap(map: GameMap) {
this.map = map
width = map.width
height = map.height
WIDTH = map.width
HEIGHT = map.height
val widthMulFactor = width / 8192f
val widthMulFactor = WIDTH / 8192f
dirtThickness = (100 * height / 1024f).toInt()
minimumFloatingIsleHeight = (25 * (height / 1024f)).toInt()
TERRAIN_AVERAGE_HEIGHT = height / 4
DIRT_LAYER_DEPTH = (100 * HEIGHT / 1024f).toInt()
minimumFloatingIsleHeight = (25 * (HEIGHT / 1024f)).toInt()
TERRAIN_AVERAGE_HEIGHT = HEIGHT / 4
OCEAN_WIDTH = Math.round(OCEAN_WIDTH * widthMulFactor)
SHORE_WIDTH = Math.round(SHORE_WIDTH * widthMulFactor)
GLACIER_MOUNTAIN_WIDTH = Math.round(GLACIER_MOUNTAIN_WIDTH * widthMulFactor)
}
@JvmStatic
fun setSeed(seed: Long) {
this.seed = seed
this.SEED = seed
}
/**
* Generate terrain and override attached map
*/
@JvmStatic
fun generateMap() {
random = HQRNG(seed!!)
println("[mapgenerator] Seed: " + seed)
random = HQRNG(SEED)
println("[mapgenerator] Seed: " + SEED)
worldOceanPosition = if (random.nextBoolean()) TYPE_OCEAN_LEFT else TYPE_OCEAN_RIGHT
heightMap = raise2(MAX_HILL_HEIGHT / 2)
generateOcean(heightMap)
placeGlacierMount(heightMap)
heightMapToObjectMap(heightMap)
//heightMap = raise2(MAX_HILL_HEIGHT / 2)
//generateOcean(heightMap)
//placeGlacierMount(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: clustered gem distribution (Groups: [Ruby, Sapphire], Amethyst, Yellow topaz, emerald, diamond) -- use regular simplex noise
* Todo: Lakes! Aquifers! Lava chambers!
* Todo: deserts (variants: SAND_DESERT, SAND_RED)
* 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(
@@ -182,16 +182,16 @@ object MapGenerator {
/** TODO Cobaltite, Ilmenite, Aurichalcum (and possibly pitchblende?) */
floodBottomLava()
freeze()
fillOcean()
// freeze()
// fillOcean()
plantGrass()
//post-process
generateFloatingIslands()
//wire layer
for (i in 0..height - 1) {
for (j in 0..width - 1) {
for (i in 0..HEIGHT - 1) {
for (j in 0..WIDTH - 1) {
map.wireArray[i][j] = 0
}
}
@@ -211,21 +211,21 @@ object MapGenerator {
* @return
*/
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 simplexNoisePerturbMap = SimplexNoise(CAVEGEN_LARGEST_FEATURE_PERTURB, 0.5f, seed!! xor random.nextLong())
val simplexNoise = SimplexNoise(CAVEGEN_LARGEST_FEATURE, CAVEGEN_PERTURB_RATE, SEED)
val simplexNoisePerturbMap = SimplexNoise(CAVEGEN_LARGEST_FEATURE_PERTURB, 0.5f, SEED xor random.nextLong())
val xEnd = width * yStretch
val yEnd = height * xStretch
val xEnd = WIDTH * yStretch
val yEnd = HEIGHT * xStretch
var lowestNoiseVal = 10000f
var highestNoiseVal = -10000f
for (y in 0..height - 1) {
for (x in 0..width - 1) {
val ny = (y * (xEnd / width)).toInt()
val nx = (x * (yEnd / height)).toInt()
for (y in 0..HEIGHT - 1) {
for (x in 0..WIDTH - 1) {
val ny = (y * (xEnd / WIDTH)).toInt()
val nx = (x * (yEnd / HEIGHT)).toInt()
val noiseInit = simplexNoise.getNoise(nx, ny) // [-1 , 1]
val perturbInit = simplexNoisePerturbMap.getNoise(nx, ny) * 0.5f + 0.5f // [0 , 1]
@@ -244,8 +244,8 @@ object MapGenerator {
// Auto-scaling noise
for (y in 0..height - 1) {
for (x in 0..width - 1) {
for (y in 0..HEIGHT - 1) {
for (x in 0..WIDTH - 1) {
val noiseInit = noiseMap[y][x] - lowestNoiseVal
val noiseFin = noiseInit * (1f / (highestNoiseVal - lowestNoiseVal))
@@ -264,7 +264,7 @@ object MapGenerator {
}
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]!
*/
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 yStart = 0f
@@ -288,8 +288,8 @@ object MapGenerator {
/** higher = denser.
* Recommended: (width or height) * 3
*/
val xEnd = width * yStretch
val yEnd = height * xStretch
val xEnd = WIDTH * yStretch
val yEnd = HEIGHT * xStretch
var lowestNoiseVal = 10000f
var highestNoiseVal = -10000f
@@ -312,8 +312,8 @@ object MapGenerator {
// Auto-scaling noise
for (y in 0..height - 1) {
for (x in 0..width - 1) {
for (y in 0..HEIGHT - 1) {
for (x in 0..WIDTH - 1) {
val noiseInit = result[y][x] - lowestNoiseVal
val noiseFin = noiseInit * (1f / (highestNoiseVal - lowestNoiseVal))
@@ -364,83 +364,169 @@ object MapGenerator {
}
/**
* [http://freespace.virgin.net/hugo.elias/models/m_perlin.htm](null)
* @param maxval_div_2 max height (deviation from zero) divided by two.
* *
* @return noise array with range of [-maxval, maxval]
* http://accidentalnoise.sourceforge.net/minecraftworlds.html
*/
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
val perlinOctaves = FastMath.intLog2(maxval_div_2) + 1 - 1 // max: for every 2nd node
// Height = Terrain undulation times 2.
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
if (HILL_WIDTH.ushr(perlinOctaves - 1) == 0) {
throw RuntimeException("sample width of zero detected.")
}
/* Lowlands */
// sample noise and add
for (oct in 1..perlinOctaves) {
// perlinAmp: 16364 -> 8192 -> 4096 -> 2048 -> ...
// This applies persistence of 1/2
val perlinAmp = finalPerlinAmp.ushr(oct - 1)
val lowland_shape_fractal = ModuleFractal()
lowland_shape_fractal.setType(ModuleFractal.FractalType.FBM)
lowland_shape_fractal.setAllSourceBasisTypes(ModuleBasisFunction.BasisType.GRADIENT)
lowland_shape_fractal.setAllSourceInterpolationTypes(ModuleBasisFunction.InterpolationType.QUINTIC)
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 perlinSamples = IntArray(width / perlinSampleDist + 1)
for (sample in perlinSamples.indices) {
perlinSamples[sample] = random.nextInt(perlinAmp * 2) - perlinAmp
val lowland_scale = ModuleScaleOffset()
lowland_scale.setSource(lowland_autocorrect)
lowland_scale.setScale(0.8)
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
for (i in perlinMap.indices) {
val perlinPointLeft = perlinSamples[i / perlinSampleDist]
val perlinPointRight = perlinSamples[i / perlinSampleDist + 1]
perlinMap[i] += Math.round(
interpolateCosine(
(i % perlinSampleDist).toFloat() / perlinSampleDist, perlinPointLeft.toFloat(), perlinPointRight.toFloat()))// using cosine; making tops rounded
}
// fill the area bottom of the above map as 'filled'
for (y in TERRAIN_AVERAGE_HEIGHT + (TERRAIN_UNDULATION / 2)..HEIGHT - 1) {
for (x in 0..WIDTH) {
noiseMap[y].set(x, true)
}
}
for (k in 0..0) {
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
return noiseMap
}
/**
@@ -501,14 +587,14 @@ object MapGenerator {
println("[mapgenerator] Shaping world as processed...")
// iterate for heightmap
for (x in 0..width - 1) {
for (x in 0..WIDTH - 1) {
val medianPosition = TERRAIN_AVERAGE_HEIGHT
val pillarOffset = medianPosition - fs[x]
// 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.setTileWall(x, i + pillarOffset, TileNameCode.DIRT)
} else {
@@ -520,67 +606,71 @@ object MapGenerator {
}
}
private fun perturbTerrain() {
val perturbGen = SimplexNoise(TERRAIN_PERTURB_LARGESTFEATURE, TERRAIN_PERTURB_RATE, seed!! xor random.nextLong())
private fun terrainMapToObjectMap() {
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
val layerTerrain = map.terrainArray
val newLayerWall = MapLayer(width, height)
val newLayerTerrain = MapLayer(width, height)
// do interpolation
for (x in 0..dirtStoneLine.size) {
val x_1 = x / POINTS_GAP
var lowestNoiseVal = 10000f
var highestNoiseVal = -10000f
val splineX0 = splineControlPoints[ clamp(x_1 - 1, 0, dirtStoneLine.size / POINTS_GAP) ].first
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) {
for (x in 0..map.width - 1) {
val noise = perturbGen.getNoise(x, y) // [-1, 1]
perturbMap[y][x] = noise
if (noise < lowestNoiseVal) lowestNoiseVal = noise
if (noise > highestNoiseVal) highestNoiseVal = noise
val splineP0 = splineControlPoints[ clamp(x_1 - 1, 0, dirtStoneLine.size / POINTS_GAP) ].second.toFloat()
val splineP1 = splineControlPoints[x_1].second.toFloat()
val splineP2 = splineControlPoints[ clamp(x_1 + 1, 0, dirtStoneLine.size / POINTS_GAP) ].second.toFloat()
val splineP3 = splineControlPoints[ clamp(x_1 + 2, 0, dirtStoneLine.size / POINTS_GAP) ].second.toFloat()
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]
/**
* See ./work_files/Linear_autoscale.gcx
*/
for (y in 0..height - 1) {
for (x in 0..width - 1) {
val noiseInit = perturbMap[y][x]
val noiseFin = (noiseInit - (highestNoiseVal + lowestNoiseVal) / 2f) * (2f / (highestNoiseVal - lowestNoiseVal))
perturbMap[y][x] = noiseFin
}
}
// 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) {
// scan vertically
for (x in 0..WIDTH - 1) {
for (y in 0..HEIGHT - 1) {
if (terrainMap[clamp(y + DIRT_LAYER_DEPTH, 0, HEIGHT - 1)].get(x)) {
// map.setTileTerrain(x, y, TileNameCode.DIRT)
// map.setTileWall(x, y, TileNameCode.DIRT)
val tile =
if (y < dirtStoneLine[x]) TileNameCode.DIRT
else TileNameCode.STONE
map.setTileTerrain(x, y, tile)
map.setTileWall(x, y, tile)
}
}
}
// set reference (pointer) of original map layer to new layers
//map.overwriteLayerWall(newLayerWall);
//map.overwriteLayerTerrain(newLayerTerrain);
}
/* 2. Carve */
@@ -588,8 +678,8 @@ object MapGenerator {
private fun carveCave(noisemap: Array<FloatArray>, tile: Int, message: String) {
println("[mapgenerator] " + message)
for (i in 0..height - 1) {
for (j in 0..width - 1) {
for (i in 0..HEIGHT - 1) {
for (j in 0..WIDTH - 1) {
if (noisemap[i][j] > 0.9) {
map.setTileTerrain(j, i, tile)
}
@@ -610,8 +700,8 @@ object MapGenerator {
private fun carveByMap(noisemap: Array<FloatArray>, scarcity: Float, tile: Int, message: String) {
println("[mapgenerator] " + message)
for (i in 0..height - 1) {
for (j in 0..width - 1) {
for (i in 0..HEIGHT - 1) {
for (j in 0..WIDTH - 1) {
if (noisemap[i][j] > gradientQuadratic(i, noiseGradientStart, noiseGrdCaveEnd) * scarcity) {
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) {
println("[mapgenerator] " + message)
for (i in 0..height - 1) {
for (j in 0..width - 1) {
for (i in 0..HEIGHT - 1) {
for (j in 0..WIDTH - 1) {
if (noisemap[i][j] > getNoiseGradient(i, noiseGradientStart, noiseGradientEnd) * scarcity && map.getTileFromTerrain(j, i) == replaceFrom) {
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) {
println("[mapgenerator] " + message)
for (i in 0..height - 1) {
for (j in 0..width - 1) {
for (i in 0..HEIGHT - 1) {
for (j in 0..WIDTH - 1) {
if (noisemap[i][j] > getNoiseGradientInversed(i, noiseGradientEnd, noiseGradientStart) * scarcity
&& map.getTileFromTerrain(j, i) == replaceFrom) {
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) {
println("[mapgenerator] " + message)
for (i in 0..height - 1) {
for (j in 0..width - 1) {
for (i in 0..HEIGHT - 1) {
for (j in 0..WIDTH - 1) {
if (noisemap[i][j] > noiseGradientStart * scarcity && map.getTileFromTerrain(j, i) == replaceFrom) {
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)
for (i in 0..height - 1) {
for (j in 0..width - 1) {
for (i in 0..HEIGHT - 1) {
for (j in 0..WIDTH - 1) {
if (noisemap[i][j] > noiseGradientStart * scarcity
&& map.getTileFromTerrain(j, i) == replaceFrom
&& map.getTileFromWall(j, i) == TileNameCode.STONE) {
map.setTileTerrain(j, i, tile)
&& map.getTileFromTerrain(j, i) ?: 0 == replaceFrom
&& map.getTileFromWall(j, i) ?: 0 == replaceTo
) {
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) {
println("[mapgenerator] " + message)
for (i in 0..height - 1) {
for (j in 0..width - 1) {
for (i in 0..HEIGHT - 1) {
for (j in 0..WIDTH - 1) {
if (noisemap[i][j] > getNoiseGradient(i, noiseGradientStart, noiseGradientEnd) * scarcity && map.getTileFromTerrain(j, i) == replaceFrom) {
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 {
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) {
return start
} else if (func_argX >= height) {
} else if (func_argX >= HEIGHT) {
return end
} else {
return graph_gradient
@@ -769,12 +860,12 @@ object MapGenerator {
*/
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
(start - end) / FastMath.sqr(height.toFloat()) *
FastMath.sqr((func_argX - height).toFloat()) + end
(start - end) / FastMath.sqr(HEIGHT.toFloat()) *
FastMath.sqr((func_argX - HEIGHT).toFloat()) + end
if (func_argX < TERRAIN_AVERAGE_HEIGHT) {
return start
} else if (func_argX >= height) {
} else if (func_argX >= HEIGHT) {
return end
} else {
return graph_gradient
@@ -809,12 +900,12 @@ object MapGenerator {
*/
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
(start - end) / FastMath.pow(height.toFloat(), 3f) *
FastMath.pow((func_argX - height).toFloat(), 3f) + end
(start - end) / FastMath.pow(HEIGHT.toFloat(), 3f) *
FastMath.pow((func_argX - HEIGHT).toFloat(), 3f) + end
if (func_argX < TERRAIN_AVERAGE_HEIGHT) {
return start
} else if (func_argX >= height) {
} else if (func_argX >= HEIGHT) {
return end
} else {
return graph_gradient
@@ -848,13 +939,13 @@ object MapGenerator {
* @return
*/
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
(start - end) / FastMath.sqr(height.toFloat()) *
FastMath.sqr((func_argX - TERRAIN_AVERAGE_HEIGHT).toFloat()) + start
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()) *
FastMath.sqr((func_argX - TERRAIN_AVERAGE_HEIGHT).toFloat()) + start
if (func_argX < TERRAIN_AVERAGE_HEIGHT) {
return start
} else if (func_argX >= height) {
} else if (func_argX >= HEIGHT) {
return end
} else {
return graph_gradient
@@ -895,8 +986,8 @@ object MapGenerator {
private fun floodBottomLava() {
println("[mapgenerator] Flooding bottom lava...")
for (i in height * 14 / 15..height - 1) {
for (j in 0..width - 1) {
for (i in HEIGHT * 14 / 15..HEIGHT - 1) {
for (j in 0..WIDTH - 1) {
if (map.terrainArray[i][j].toInt() == 0) {
map.setTileTerrain(j, i, TileNameCode.LAVA)
}
@@ -915,17 +1006,16 @@ object MapGenerator {
* 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) {
val thisTile = map.getTileFromTerrain(x, y)
for (i in 0..8) {
var nearbyWallTile = -1
try {
nearbyWallTile = map.getTileFromWall(x + i % 3 - 1, y + i / 3 - 1)
} catch (e: ArrayIndexOutOfBoundsException) {
}
var nearbyWallTile: Int?
nearbyWallTile = map.getTileFromWall(x + i % 3 - 1, y + i / 3 - 1)
if (nearbyWallTile == null) break;
if (i != 4 && thisTile == TileNameCode.DIRT && nearbyWallTile == TileNameCode.AIR) {
map.setTileTerrain(x, y, TileNameCode.GRASS)
@@ -957,10 +1047,11 @@ object MapGenerator {
private fun fillOcean() {
val thisSandList = intArrayOf(
TileNameCode.SAND_BEACH, TileNameCode.SAND_BLACK, TileNameCode.SAND_GREEN,
TileNameCode.SAND_BEACH, TileNameCode.SAND_BEACH, TileNameCode.SAND_BLACK
)
val thisRand = HQRNG(seed!! xor random.nextLong())
TileNameCode.SAND, TileNameCode.SAND, TileNameCode.SAND, TileNameCode.SAND,
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 thisSand = thisSandList[thisRand.nextInt(thisSandList.size)]
// val thisSand = TileNameCode.SAND_GREEN
@@ -968,9 +1059,11 @@ object MapGenerator {
"black"
else if (thisSand == TileNameCode.SAND_GREEN)
"green"
else if (thisSand == TileNameCode.SAND)
"yellow"
else
"white"
println("[mapgenerator] Beach sand type: " + thisSandStr)
println("[mapgenerator] Beach sand type: $thisSandStr")
var ix = 0
while (ix < OCEAN_WIDTH * 1.5) {
@@ -1061,12 +1154,12 @@ object MapGenerator {
* @return
*/
private fun getTerrainHeightFromHeightMap(x: Int): Int {
return TERRAIN_AVERAGE_HEIGHT - heightMap[x]
TODO()
}
@JvmStatic
fun getGeneratorSeed(): Long {
return seed!!
return SEED
}
/* Utility */
@@ -1090,9 +1183,9 @@ object MapGenerator {
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 (Math.round(j + pointerX - halfBrushSize) > brushSize
&& Math.round(j + pointerX - halfBrushSize) < width - brushSize
&& Math.round(i + pointerY - halfBrushSize) > brushSize
&& Math.round(i + pointerY - halfBrushSize) < height - brushSize) {
&& Math.round(j + pointerX - halfBrushSize) < WIDTH - brushSize
&& Math.round(i + pointerY - halfBrushSize) > brushSize
&& Math.round(i + pointerY - halfBrushSize) < HEIGHT - brushSize) {
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()
}
@@ -1102,4 +1195,6 @@ object MapGenerator {
}
}
private fun clamp(x: Int, min: Int, max: Int): Int = if (x < min) min else if (x > max) max else x
}

View File

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

View File

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

View File

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

View File

@@ -26,7 +26,7 @@ object TileNameCode {
val TRUNK_BLOODROSE = TilePropCodex.indexDamageToArrayAddr(4, 3)
val 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_DESERT = TilePropCodex.indexDamageToArrayAddr(5, 3)
val SAND_BLACK = TilePropCodex.indexDamageToArrayAddr(5, 4)

View File

@@ -55,15 +55,15 @@ class TilePropCodex {
return tileProps[indexDamageToArrayAddr(index, damage)]
}
fun getProp(rawIndex: Int): TileProp {
fun getProp(rawIndex: Int?): TileProp {
try {
tileProps[rawIndex].id
tileProps[rawIndex ?: TileNameCode.STONE].id
}
catch (e: NullPointerException) {
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) {
@@ -88,7 +88,7 @@ class TilePropCodex {
if (prop.isFluid) prop.movementResistance = intVal(record, "movr")
print(formatNum3(prop.id) + ":" + formatNum2(prop.damage))
println("\t" + prop.name!!)
println("\t" + prop.name)
}
private fun intVal(rec: CSVRecord, s: String): Int {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -29,7 +29,8 @@
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* 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;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,6 @@
package com.torvald.terrarum.console
import com.torvald.terrarum.Terrarum
import com.torvald.terrarum.langpack.Lang
/**
@@ -9,13 +10,19 @@ class Help : ConsoleCommand {
override fun execute(args: Array<String>) {
val echo = Echo()
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") {
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 {
for (i in 1..6) echo.execute(Lang["HELP_OTF_MAIN_TEXT_$i"])
for (i in 1..6) echo.execute(Lang["HELP_OTF_MAIN_$i"])
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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