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

@@ -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

@@ -0,0 +1,31 @@
package com.torvald.terrarum.gameactors.faction
import com.torvald.JsonFetcher
import com.google.gson.JsonObject
import java.io.IOException
/**
* Created by minjaesong on 16-02-15.
*/
object FactionFactory {
const val JSONPATH = "./res/raw/factions/"
/**
* @param filename with extension
*/
@Throws(IOException::class)
fun create(filename: String): Faction {
val jsonObj = JsonFetcher.readJson(JSONPATH + filename)
val factionObj = Faction(jsonObj.get("factionname").asString)
jsonObj.get("factionamicable").asJsonArray.forEach { s -> factionObj.addFactionAmicable(s.asString) }
jsonObj.get("factionneutral").asJsonArray.forEach { s -> factionObj.addFactionNeutral(s.asString) }
jsonObj.get("factionhostile").asJsonArray.forEach { s -> factionObj.addFactionHostile(s.asString) }
jsonObj.get("factionfearful").asJsonArray.forEach { s -> factionObj.addFactionFearful(s.asString) }
return factionObj
}
}

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) {
}