fixes, bits and pieces, changes in ID referencing, terrain and wall takes damage, working test pickaxe, and a new issue

This commit is contained in:
Song Minjae
2017-04-17 02:18:52 +09:00
parent 6087072d3d
commit f2ae2d9449
40 changed files with 502 additions and 249 deletions

View File

@@ -1,7 +1,9 @@
|Range|Description| |Range|Description|
|-----|-----------| |-----|-----------|
|0..4095|Tiles| |0..4095|Tiles|
|4096..32767|Items (static)| |4096..8191|Walls|
|8192..8447|Wires|
|8448..32767|Items (static)|
|32768..1048575|Items (dynamic\*)| |32768..1048575|Items (dynamic\*)|
|1048576..0x7FFF_FFFF|Actors| |1048576..0x7FFF_FFFF|Actors|
|0x8000_0000..0xFFFF_FFFF (all negative numbers)|Faction| |0x8000_0000..0xFFFF_FFFF (all negative numbers)|Faction|

View File

@@ -26,10 +26,15 @@
"TILE_SAND": "Sand", "TILE_SAND": "Sand",
"TILE_SAND_PLURAL": "Sands", "TILE_SAND_PLURAL": "Sands",
"TILE_SAND_WHITE": "White sand", "TILE_SAND_WHITE": "White sand",
"TILE_SAND_WHITE_PLURAL": "White sands",
"TILE_SAND_RED": "Red sand", "TILE_SAND_RED": "Red sand",
"TILE_SAND_RED_PLURAL": "Red sands",
"TILE_SAND_DESERT": "Sand", "TILE_SAND_DESERT": "Sand",
"TILE_SAND_DESERT_PLURAL": "Sands",
"TILE_SAND_BLACK": "Black sand", "TILE_SAND_BLACK": "Black sand",
"TILE_SAND_BLACK_PLURAL": "Black sands",
"TILE_SAND_GREEN": "Green sand", "TILE_SAND_GREEN": "Green sand",
"TILE_SAND_GREEN_PLURAL": "Green sands",
"TILE_GRAVEL": "Gravel", "TILE_GRAVEL": "Gravel",
"TILE_GRAVEL_PLURAL": "Gravels", "TILE_GRAVEL_PLURAL": "Gravels",
"TILE_ORE_MALACHITE": "Malachite", "TILE_ORE_MALACHITE": "Malachite",
@@ -77,11 +82,17 @@
"TILE_TORCH": "Torch", "TILE_TORCH": "Torch",
"TILE_TORCH_PLURAL": "Torches", "TILE_TORCH_PLURAL": "Torches",
"TILE_SANDSTONE": "Sandstone", "TILE_SANDSTONE": "Sandstone",
"TILE_SANDSTONE_PLURAL": "Sandstones",
"TILE_SANDSTONE_WHITE": "White sandstone", "TILE_SANDSTONE_WHITE": "White sandstone",
"TILE_SANDSTONE_WHITE_PLURAL": "White sandstones",
"TILE_SANDSTONE_RED": "Red sandstone", "TILE_SANDSTONE_RED": "Red sandstone",
"TILE_SANDSTONE_RED_PLURAL": "Red sandstones",
"TILE_SANDSTONE_DESERT": "Sandstone", "TILE_SANDSTONE_DESERT": "Sandstone",
"TILE_SANDSTONE_DESERT_PLURAL": "Sandstones",
"TILE_SANDSTONE_BLACK": "Black sandstone", "TILE_SANDSTONE_BLACK": "Black sandstone",
"TILE_SANDSTONE_BLACK_PLURAL": "Black sandstones",
"TILE_SANDSTONE_GREEN": "Green sandstone", "TILE_SANDSTONE_GREEN": "Green sandstone",
"TILE_SANDSTONE_GREEN_PLURAL": "Green sandstones",
"TILE_WATER": "Water", "TILE_WATER": "Water",
"TILE_WATER_PLURAL": "Waters", "TILE_WATER_PLURAL": "Waters",
"TILE_LAVA": "Lava", "TILE_LAVA": "Lava",

View File

@@ -7,22 +7,22 @@
"TILE_GRAS_PLURALS": "잔디", "TILE_GRAS_PLURALS": "잔디",
"TILE_PLANK_NORMAL": "나무판자", "TILE_PLANK_NORMAL": "나무판자",
"TILE_PLANK_NORMAL_PLURAL": "나무판자", "TILE_PLANK_NORMAL_PLURAL": "나무판자",
"TILE_PLANK_EBONY": "흑단", "TILE_PLANK_EBONY": "검정 나무판자",
"TILE_PLANK_EBONY_PLURAL": "흑단", "TILE_PLANK_EBONY_PLURAL": "검정 나무판자",
"TILE_PLANK_BIRCH": "백단", "TILE_PLANK_BIRCH": "하양 나무판자",
"TILE_PLANK_BIRCH_PLURAL": "백단", "TILE_PLANK_BIRCH_PLURAL": "하양 나무판자",
"TILE_PLANK_BLOODROSE": "자", "TILE_PLANK_BLOODROSE": "빨강 나무판자",
"TILE_PLANK_BLOODROSE_PLURAL": "자", "TILE_PLANK_BLOODROSE_PLURAL": "빨강 나무판자",
"TILE_TRUNK_NORMAL": "통나무", "TILE_TRUNK_NORMAL": "통나무",
"TILE_TRUNK_NORMAL_PLURAL": "통나무", "TILE_TRUNK_NORMAL_PLURAL": "통나무",
"TILE_TRUNK_EBONY": "흑단나무", "TILE_TRUNK_EBONY": "흑단나무",
"TILE_TRUNK_EBONY_PLURAL": "흑단나무", "TILE_TRUNK_EBONY_PLURAL": "흑단나무",
"TILE_TRUNK_BIRCH": "백단나무", "TILE_TRUNK_BIRCH": "자작나무",
"TILE_TRUNK_BIRCH_PLURAL": "백단나무", "TILE_TRUNK_BIRCH_PLURAL": "자작나무",
"TILE_TRUNK_BLOODROSE": "자단나무", "TILE_TRUNK_BLOODROSE": "자단나무",
"TILE_TRUNK_BLOODROSE_PLURAL": "자단나무", "TILE_TRUNK_BLOODROSE_PLURAL": "자단나무",
"TILE_STONE_QUARRIED": "캔 돌", "TILE_STONE_QUARRIED": "석재",
"TILE_STONE_QUARRIED_PLURAL": "캔 돌", "TILE_STONE_QUARRIED_PLURAL": "석재",
"TILE_SAND": "모래", "TILE_SAND": "모래",
"TILE_SAND_PLURAL": "모래", "TILE_SAND_PLURAL": "모래",
"TILE_GRAVEL": "자갈", "TILE_GRAVEL": "자갈",

View File

@@ -43,9 +43,9 @@ object WriteGameMapData {
{ b -> Files.write(tempPath, byteArrayOf(b)) }) { b -> Files.write(tempPath, byteArrayOf(b)) })
map.layerWall.forEach( map.layerWall.forEach(
{ b -> Files.write(tempPath, byteArrayOf(b)) }) { b -> Files.write(tempPath, byteArrayOf(b)) })
map.terrainDamage.forEach( map.layerTerrainLowBits.forEach(
{ b -> Files.write(tempPath, byteArrayOf(b)) }) { b -> Files.write(tempPath, byteArrayOf(b)) })
map.wallDamage.forEach( map.layerWallLowBits.forEach(
{ b -> Files.write(tempPath, byteArrayOf(b)) }) { b -> Files.write(tempPath, byteArrayOf(b)) })
map.layerWire.forEach( map.layerWire.forEach(
{ b -> Files.write(tempPath, byteArrayOf(b)) }) { b -> Files.write(tempPath, byteArrayOf(b)) })

View File

@@ -3,6 +3,7 @@ package net.torvald.terrarum
import com.google.gson.JsonPrimitive import com.google.gson.JsonPrimitive
import java.util.* import java.util.*
import java.util.function.Consumer import java.util.function.Consumer
import kotlin.collections.HashMap
typealias ActorValue = KVHashMap typealias ActorValue = KVHashMap
typealias ItemValue = KVHashMap typealias ItemValue = KVHashMap
@@ -13,7 +14,15 @@ typealias GameConfig = KVHashMap
*/ */
class KVHashMap { class KVHashMap {
private val hashMap = HashMap<String, Any>() constructor() {
hashMap = HashMap<String, Any>()
}
private constructor(newMap: HashMap<String, Any>) {
hashMap = newMap
}
private val hashMap: HashMap<String, Any>
/** /**
* Add key-value pair to the configuration table. * Add key-value pair to the configuration table.
@@ -108,4 +117,9 @@ class KVHashMap {
hashMap.remove(key, hashMap[key]!!) hashMap.remove(key, hashMap[key]!!)
} }
fun clone(): KVHashMap {
val cloneOfMap = hashMap.clone() as HashMap<String, Any>
return KVHashMap(cloneOfMap)
}
} }

View File

@@ -52,9 +52,9 @@ class StateUITest : BasicGameState() {
override var originalName: String = "Test tool" override var originalName: String = "Test tool"
override var baseMass: Double = 12.0 override var baseMass: Double = 12.0
override var baseToolSize: Double? = 8.0 override var baseToolSize: Double? = 8.0
override var category: String = InventoryItem.Category.TOOL override var inventoryCategory: String = InventoryItem.Category.TOOL
override var maxDurability: Double = 10.0 override var maxDurability: Int = 143
override var durability: Double = 6.43 override var durability: Float = 64f
override var consumable = false override var consumable = false
}) })
actor.inventory.getByID(5656)!!.item.name = "Test tool" actor.inventory.getByID(5656)!!.item.name = "Test tool"
@@ -68,7 +68,7 @@ class StateUITest : BasicGameState() {
override var originalName: String = "CONTEXT_ITEM_QUEST_NOUN" override var originalName: String = "CONTEXT_ITEM_QUEST_NOUN"
override var baseMass: Double = 1.4 override var baseMass: Double = 1.4
override var baseToolSize: Double? = null override var baseToolSize: Double? = null
override var category: String = InventoryItem.Category.MISC override var inventoryCategory: String = InventoryItem.Category.MISC
override var consumable = false override var consumable = false
}) })

View File

@@ -106,7 +106,7 @@ class UIItemInventoryElem(
g.lineWidth = 3f g.lineWidth = 3f
g.drawLine(barOffset, posY + durabilityBarOffY, barOffset + barFullLen, posY + durabilityBarOffY) g.drawLine(barOffset, posY + durabilityBarOffY, barOffset + barFullLen, posY + durabilityBarOffY)
g.color = durabilityCol g.color = durabilityCol
g.drawLine(barOffset, posY + durabilityBarOffY, barOffset + barFullLen * (item!!.durability / item!!.maxDurability).toFloat(), posY + durabilityBarOffY) g.drawLine(barOffset, posY + durabilityBarOffY, barOffset + barFullLen * (item!!.durability / item!!.maxDurability), posY + durabilityBarOffY)
} }

View File

@@ -50,9 +50,9 @@ abstract class Actor(val renderOrder: RenderOrder) : Comparable<Actor>, Runnable
fun hasCollision(value: Int) = fun hasCollision(value: Int) =
try { try {
Terrarum.ingame!!.theGameHasActor(value) || Terrarum.ingame!!.theGameHasActor(value) ||
value < ItemCodex.ITEM_COUNT_MAX || value < ItemCodex.ACTOR_ID_MIN ||
value < when (renderOrder) { value < when (renderOrder) {
RenderOrder.BEHIND -> ItemCodex.ITEM_COUNT_MAX RenderOrder.BEHIND -> ItemCodex.ACTOR_ID_MIN
RenderOrder.MIDDLE -> 0x10000000 RenderOrder.MIDDLE -> 0x10000000
RenderOrder.MIDTOP -> 0x60000000 RenderOrder.MIDTOP -> 0x60000000
RenderOrder.FRONT -> 0x70000000 RenderOrder.FRONT -> 0x70000000

View File

@@ -138,7 +138,7 @@ open class ActorHumanoid(birth: GameDate, death: GameDate? = null)
override val isUnique: Boolean = false override val isUnique: Boolean = false
override var baseMass: Double = 0.0 override var baseMass: Double = 0.0
override var baseToolSize: Double? = null override var baseToolSize: Double? = null
override var category = "should_not_be_seen" override var inventoryCategory = "should_not_be_seen"
override val originalName: String = actorValue.getAsString(AVKey.NAME) ?: "(no name)" override val originalName: String = actorValue.getAsString(AVKey.NAME) ?: "(no name)"
override var consumable = false override var consumable = false
} }

View File

@@ -126,7 +126,9 @@ class ActorInventory(val actor: Pocketed, var maxCapacity: Int, var capacityMode
remove(item, 1) remove(item, 1)
} }
else { else {
// TODO decrement durability item.durability -= 1f
if (item.durability <= 0)
remove(item, 1)
} }
} }

View File

@@ -12,7 +12,7 @@ import org.newdawn.slick.Graphics
class DroppedItem(private val item: InventoryItem) : ActorWithSprite(Actor.RenderOrder.MIDTOP) { class DroppedItem(private val item: InventoryItem) : ActorWithSprite(Actor.RenderOrder.MIDTOP) {
init { init {
if (item.id >= ItemCodex.ITEM_COUNT_MAX) if (item.id >= ItemCodex.ACTOR_ID_MIN)
throw RuntimeException("Attempted to create DroppedItem actor of a real actor; the real actor must be dropped instead.") throw RuntimeException("Attempted to create DroppedItem actor of a real actor; the real actor must be dropped instead.")
isVisible = true isVisible = true

View File

@@ -3,6 +3,8 @@ package net.torvald.terrarum.gameactors
import net.torvald.terrarum.gameworld.WorldTime import net.torvald.terrarum.gameworld.WorldTime
import org.newdawn.slick.Input import org.newdawn.slick.Input
typealias AnyPlayer = HistoricalFigure
/** /**
* An actor (NPC) which has life and death, * An actor (NPC) which has life and death,
* though death might not exist if it has achieved immortality :) * though death might not exist if it has achieved immortality :)

View File

@@ -1,9 +1,12 @@
package net.torvald.terrarum.gameactors package net.torvald.terrarum.gameactors
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.gameactors.ActorHumanoid import net.torvald.terrarum.gameactors.ActorHumanoid
import net.torvald.terrarum.gameactors.ai.AILuaAPI import net.torvald.terrarum.gameactors.ai.AILuaAPI
import net.torvald.terrarum.gameactors.ai.ActorAI import net.torvald.terrarum.gameactors.ai.ActorAI
import net.torvald.terrarum.gameactors.ai.LuaAIWrapper import net.torvald.terrarum.gameactors.ai.LuaAIWrapper
import net.torvald.terrarum.gamecontroller.mouseX
import net.torvald.terrarum.gamecontroller.mouseY
import net.torvald.terrarum.gameitem.InventoryItem import net.torvald.terrarum.gameitem.InventoryItem
import org.luaj.vm2.* import org.luaj.vm2.*
import org.luaj.vm2.compiler.LuaC import org.luaj.vm2.compiler.LuaC
@@ -51,13 +54,22 @@ open class HumanoidNPC(
set(value) { set(value) {
actorValue[AVKey.SCALE] = value actorValue[AVKey.SCALE] = value
} }
override var category = "npc" override var inventoryCategory = "npc"
override val originalName: String = actorValue.getAsString(AVKey.NAME) ?: "NPC" override val originalName: String = actorValue.getAsString(AVKey.NAME) ?: "NPC"
override var consumable = false override var consumable = true
override fun secondaryUse(gc: GameContainer, delta: Int): Boolean { override fun secondaryUse(gc: GameContainer, delta: Int): Boolean {
return false try {
// TODO place this Actor to the world // place the actor to the world
this@HumanoidNPC.setPosition(gc.mouseX, gc.mouseY)
Terrarum.ingame!!.addNewActor(this@HumanoidNPC)
// successful
return true
}
catch (e: Exception) {
e.printStackTrace()
return false
}
} }
} }

View File

@@ -15,8 +15,6 @@ import java.security.SecureRandom
*/ */
object InjectCreatureRaw { object InjectCreatureRaw {
// FIXME strength not injected properly?
const val JSONPATH = "./assets/raw/creatures/" const val JSONPATH = "./assets/raw/creatures/"
private const val JSONMULT = "mult" // one appears in JSON files private const val JSONMULT = "mult" // one appears in JSON files

View File

@@ -58,6 +58,7 @@ object PlayerBuilderSigrid {
p.actorValue[AVKey.INTELLIGENT] = true p.actorValue[AVKey.INTELLIGENT] = true
p.actorValue[AVKey.LUMINOSITY] = Color(0x434aff).to10bit() p.actorValue[AVKey.LUMINOSITY] = Color(0x434aff).to10bit()
//p.actorValue[AVKey.LUMINOSITY] = 214127943 // bright purple
p.actorValue[AVKey.BASEDEFENCE] = 141 p.actorValue[AVKey.BASEDEFENCE] = 141
@@ -65,7 +66,7 @@ object PlayerBuilderSigrid {
//p.actorValue["__selectedtile"] = 147 // test code; replace with <tile_item>.primaryUse(gc, delta) //p.actorValue["__selectedtile"] = 147 // test code; replace with <tile_item>.primaryUse(gc, delta)
p.actorValue["__aimhelper"] = true // TODO when you'll gonna implement it? p.actorValue["__aimhelper"] = true // TODO when you'll gonna implement it?
p.setHitboxDimension(15, p.actorValue.getAsInt(AVKey.BASEHEIGHT)!!, 11, 0) // FIXME offsetY of -2: Have no idea about the error; it's just supposed to be zero p.setHitboxDimension(15, p.actorValue.getAsInt(AVKey.BASEHEIGHT)!!, 11, 0)
p.inventory = ActorInventory(p, 0, ActorInventory.CAPACITY_MODE_NO_ENCUMBER) p.inventory = ActorInventory(p, 0, ActorInventory.CAPACITY_MODE_NO_ENCUMBER)
@@ -85,6 +86,7 @@ object PlayerBuilderSigrid {
Tile.STONE_QUARRIED, Tile.STONE_TILE_WHITE, Tile.TORCH Tile.STONE_QUARRIED, Tile.STONE_TILE_WHITE, Tile.TORCH
) )
tiles.forEach { p.inventory.add(it, 999) } tiles.forEach { p.inventory.add(it, 999) }
p.inventory.add(ItemCodex.ITEM_STATIC.first)

View File

@@ -3,7 +3,6 @@ package net.torvald.terrarum.gameactors
import net.torvald.terrarum.Terrarum import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.gameitem.InventoryItem import net.torvald.terrarum.gameitem.InventoryItem
import net.torvald.terrarum.itemproperties.ItemCodex import net.torvald.terrarum.itemproperties.ItemCodex
import java.util.*
/** /**
* Created by minjaesong on 16-01-15. * Created by minjaesong on 16-01-15.
@@ -17,10 +16,13 @@ interface Pocketed {
*/ */
fun unequipItem(item: InventoryItem) { fun unequipItem(item: InventoryItem) {
if (item.equipPosition == InventoryItem.EquipPosition.NULL) if (item.equipPosition == InventoryItem.EquipPosition.NULL)
throw Error("Unequipping the item that cannot be equipped") throw Error("Unequipping the item that cannot be equipped in the first place")
if (!inventory.contains(item)) if (!inventory.contains(item)) {
throw Error("Unequipping the item that does not exist in inventory") //throw Error("Unequipping the item that does not exist in inventory")
System.err.println("[Pocketed] Warning -- Unequipping the item that does not exist in inventory")
return // just do nothing
}
inventory.itemEquipped[item.equipPosition] = null inventory.itemEquipped[item.equipPosition] = null
item.effectOnUnequip(Terrarum.appgc, Terrarum.UPDATE_DELTA) item.effectOnUnequip(Terrarum.appgc, Terrarum.UPDATE_DELTA)

View File

@@ -239,7 +239,7 @@ internal class AILuaAPI(g: Globals, actor: ActorWithSprite) {
luatable[y - feetTilePos[1]] = LuaTable() luatable[y - feetTilePos[1]] = LuaTable()
for (x in feetTilePos[0] - radius..feetTilePos[0] + radius) { for (x in feetTilePos[0] - radius..feetTilePos[0] + radius) {
val tile = TileCodex[Terrarum.ingame!!.world.getTileFromTerrain(x, y) ?: 4096] val tile = TileCodex[Terrarum.ingame!!.world.getTileFromTerrain(x, y) ?: Tile.NULL]
val solidity = tile.isSolid.toInt() val solidity = tile.isSolid.toInt()
val liquidity = tile.isFluid.toInt() val liquidity = tile.isFluid.toInt()
val gravity = tile.isFallable.toInt() val gravity = tile.isFallable.toInt()

View File

@@ -16,13 +16,6 @@ import org.newdawn.slick.GameContainer
open abstract class DynamicItem(val baseItemID: Int?, newMass: Double? = null, newScale: Double? = null) open abstract class DynamicItem(val baseItemID: Int?, newMass: Double? = null, newScale: Double? = null)
: InventoryItem() { : InventoryItem() {
/* /*
/**
* Internal ID of an Item, Long
* 0-4096: Tiles
* 4097-32767: Static items
* 32768-16777215: Dynamic items
* >= 16777216: Actor RefID
*/
override val id: Int = generateUniqueDynamicItemID() override val id: Int = generateUniqueDynamicItemID()
private fun generateUniqueDynamicItemID(): Int { private fun generateUniqueDynamicItemID(): Int {

View File

@@ -7,6 +7,9 @@ object IVKey {
const val ITEMTYPE = "itemtype" // "sword1h", "sword2h", "pick", "hammer", "tile", "wall", etc const val ITEMTYPE = "itemtype" // "sword1h", "sword2h", "pick", "hammer", "tile", "wall", etc
const val UUID = "uuid" // some items need UUID to be stored const val UUID = "uuid" // some items need UUID to be stored
const val BASE_WEAPON_POWER = "baseweaponpower"
const val BASE_PICK_POWER = "basepickpower"
object ItemType { object ItemType {
const val BLOCK = "tile" const val BLOCK = "tile"

View File

@@ -10,17 +10,12 @@ import org.newdawn.slick.GameContainer
/** /**
* Created by minjaesong on 16-01-16. * Created by minjaesong on 16-01-16.
*/ */
abstract class InventoryItem : Comparable<InventoryItem> { abstract class InventoryItem : Comparable<InventoryItem>, Cloneable {
/**
* Internal ID of an Item,
* 0-4095: Tiles
* 4096-32767: Unique items (isUnique = true), brand-new tools
* 32768-16777215: Dynamic items (e.g. tools with damage)
* >= 16777216: Actor RefID
*/
abstract val id: Int abstract val id: Int
/** /**
*
* e.g. Key Items (in a Pokémon sense), floppies * e.g. Key Items (in a Pokémon sense), floppies
*/ */
abstract val isUnique: Boolean abstract val isUnique: Boolean
@@ -47,11 +42,11 @@ abstract class InventoryItem : Comparable<InventoryItem> {
abstract var baseToolSize: Double? abstract var baseToolSize: Double?
abstract var category: String // "weapon", "tool", "armor", etc. (all smallcaps) abstract var inventoryCategory: String // "weapon", "tool", "armor", etc. (all smallcaps)
var itemProperties = ItemValue() var itemProperties = ItemValue()
/** Single-use then destroyed (e.g. Tiles) */ /** Single-use then destroyed (e.g. Tiles), aka negation of "stackable" */
abstract var consumable: Boolean abstract var consumable: Boolean
/** /**
@@ -94,9 +89,12 @@ abstract class InventoryItem : Comparable<InventoryItem> {
/** /**
* Set to zero if durability not applicable * Set to zero if durability not applicable
*/ */
open var maxDurability: Double = 0.0 open var maxDurability: Int = 0
open var durability: Double = 0.0 /**
* Float. NOT A MISTAKE
*/
open var durability: Float = 0f
/** /**
* Effects applied continuously while in pocket * Effects applied continuously while in pocket
@@ -115,6 +113,9 @@ abstract class InventoryItem : Comparable<InventoryItem> {
* @return true when used successfully, false otherwise * @return true when used successfully, false otherwise
* *
* note: DO NOT super(gc, g) this! * note: DO NOT super(gc, g) this!
*
* Consumption function is executed in net.torvald.terrarum.gamecontroller.GameController,
* in which the function itself is defined in net.torvald.terrarum.gameactors.ActorInventory
*/ */
open fun primaryUse(gc: GameContainer, delta: Int): Boolean = false open fun primaryUse(gc: GameContainer, delta: Int): Boolean = false
@@ -212,4 +213,12 @@ abstract class InventoryItem : Comparable<InventoryItem> {
const val WALL = "wall" const val WALL = "wall"
const val MISC = "misc" const val MISC = "misc"
} }
override public fun clone(): InventoryItem {
val clonedItem = super.clone()
// properly clone ItemValue
(clonedItem as InventoryItem).itemProperties = this.itemProperties.clone()
return clonedItem
}
} }

View File

@@ -1,35 +1,30 @@
package net.torvald.terrarum.gameworld package net.torvald.terrarum.gameworld
import net.torvald.terrarum.realestate.RealEstateUtility
import org.dyn4j.geometry.Vector2 import org.dyn4j.geometry.Vector2
import org.newdawn.slick.SlickException import org.newdawn.slick.SlickException
class GameWorld typealias TileAddress = Long
/** typealias TileDamage = Int
* @param width
* * class GameWorld(val width: Int, val height: Int) {
* @param height
* *
* @throws SlickException
*/
@Throws(SlickException::class)
constructor(//properties
val width: Int, val height: Int) {
//layers //layers
val layerWall: MapLayer val layerWall: MapLayer
/**
* Get MapLayer object of terrain
* @return MapLayer terrain layer
*/
val layerTerrain: MapLayer val layerTerrain: MapLayer
val layerWire: MapLayer val layerWire: MapLayer
val wallDamage: PairedMapLayer
val terrainDamage: PairedMapLayer val layerWallLowBits: PairedMapLayer
val layerTerrainLowBits: PairedMapLayer
val spawnX: Int val spawnX: Int
val spawnY: Int val spawnY: Int
val wallDamages = HashMap<TileAddress, TileDamage>()
val terrainDamages = HashMap<TileAddress, TileDamage>()
//public World physWorld = new World( new Vec2(0, -TerrarumMain.game.gravitationalAccel) ); //public World physWorld = new World( new Vec2(0, -TerrarumMain.game.gravitationalAccel) );
//physics //physics
/** Meter per second squared. Currently only the downward gravity is supported. No reverse gravity :p */ /** Meter per second squared. Currently only the downward gravity is supported. No reverse gravity :p */
@@ -47,8 +42,8 @@ constructor(//properties
layerTerrain = MapLayer(width, height) layerTerrain = MapLayer(width, height)
layerWall = MapLayer(width, height) layerWall = MapLayer(width, height)
layerWire = MapLayer(width, height) layerWire = MapLayer(width, height)
terrainDamage = PairedMapLayer(width, height) layerTerrainLowBits = PairedMapLayer(width, height)
wallDamage = PairedMapLayer(width, height) layerWallLowBits = PairedMapLayer(width, height)
time = WorldTime( time = WorldTime(
71 * WorldTime.DAY_LENGTH + 71 * WorldTime.DAY_LENGTH +
@@ -87,11 +82,11 @@ constructor(//properties
* @return byte[][] damage code pair * @return byte[][] damage code pair
*/ */
val damageDataArray: Array<ByteArray> val damageDataArray: Array<ByteArray>
get() = terrainDamage.dataPair get() = layerTerrainLowBits.dataPair
fun getTileFromWall(x: Int, y: Int): Int? { fun getTileFromWall(x: Int, y: Int): Int? {
val wall: Int? = layerWall.getTile(x fmod width, y) val wall: Int? = layerWall.getTile(x fmod width, y)
val wallDamage: Int? = getWallDamage(x fmod width, y) val wallDamage: Int? = getWallLowBits(x fmod width, y)
return if (wall == null || wallDamage == null) return if (wall == null || wallDamage == null)
null null
else else
@@ -100,7 +95,7 @@ constructor(//properties
fun getTileFromTerrain(x: Int, y: Int): Int? { fun getTileFromTerrain(x: Int, y: Int): Int? {
val terrain: Int? = layerTerrain.getTile(x fmod width, y) val terrain: Int? = layerTerrain.getTile(x fmod width, y)
val terrainDamage: Int? = getTerrainDamage(x fmod width, y) val terrainDamage: Int? = getTerrainLowBits(x fmod width, y)
return if (terrain == null || terrainDamage == null) return if (terrain == null || terrainDamage == null)
null null
else else
@@ -111,12 +106,12 @@ constructor(//properties
return layerWire.getTile(x fmod width, y) return layerWire.getTile(x fmod width, y)
} }
fun getWallDamage(x: Int, y: Int): Int? { fun getWallLowBits(x: Int, y: Int): Int? {
return wallDamage.getData(x fmod width, y) return layerWallLowBits.getData(x fmod width, y)
} }
fun getTerrainDamage(x: Int, y: Int): Int? { fun getTerrainLowBits(x: Int, y: Int): Int? {
return terrainDamage.getData(x fmod width, y) return layerTerrainLowBits.getData(x fmod width, y)
} }
/** /**
@@ -145,12 +140,12 @@ constructor(//properties
fun setTileWall(x: Int, y: Int, tile: Byte, damage: Int) { fun setTileWall(x: Int, y: Int, tile: Byte, damage: Int) {
layerWall.setTile(x fmod width, y, tile) layerWall.setTile(x fmod width, y, tile)
wallDamage.setData(x fmod width, y, damage) layerWallLowBits.setData(x fmod width, y, damage)
} }
fun setTileTerrain(x: Int, y: Int, tile: Byte, damage: Int) { fun setTileTerrain(x: Int, y: Int, tile: Byte, damage: Int) {
layerTerrain.setTile(x fmod width, y, tile) layerTerrain.setTile(x fmod width, y, tile)
terrainDamage.setData(x fmod width, y, damage) layerTerrainLowBits.setData(x fmod width, y, damage)
} }
fun setTileWire(x: Int, y: Int, tile: Byte) { fun setTileWire(x: Int, y: Int, tile: Byte) {
@@ -216,6 +211,32 @@ constructor(//properties
} }
} }
fun inflctTerrainDamage(x: Int, y: Int, damage: Int) {
val addr = RealEstateUtility.getAbsoluteTileNumber(x, y)
if (terrainDamages[addr] == null) {
terrainDamages[addr] = damage
}
else {
terrainDamages[addr] = terrainDamages[addr]!! + damage
}
}
fun getTerrainDamage(x: Int, y: Int) =
terrainDamages[RealEstateUtility.getAbsoluteTileNumber(x, y)] ?: 0
fun inflctWallDamage(x: Int, y: Int, damage: Int) {
val addr = RealEstateUtility.getAbsoluteTileNumber(x, y)
if (wallDamages[addr] == null) {
wallDamages[addr] = damage
}
else {
wallDamages[addr] = wallDamages[addr]!! + damage
}
}
fun getWallDamage(x: Int, y: Int) =
wallDamages[RealEstateUtility.getAbsoluteTileNumber(x, y)] ?: 0
companion object { companion object {
@Transient val WALL = 0 @Transient val WALL = 0
@@ -224,7 +245,7 @@ constructor(//properties
@Transient val TILES_SUPPORTED = MapLayer.RANGE * PairedMapLayer.RANGE @Transient val TILES_SUPPORTED = MapLayer.RANGE * PairedMapLayer.RANGE
@Transient val SIZEOF: Byte = MapLayer.SIZEOF @Transient val SIZEOF: Byte = MapLayer.SIZEOF
@Transient val LAYERS: Byte = 4 // terrain, wall (terrainDamage + wallDamage), wire @Transient val LAYERS: Byte = 4 // terrain, wall (layerTerrainLowBits + layerWallLowBits), wire
} }
} }

View File

@@ -2,6 +2,7 @@ package net.torvald.terrarum.gameworld
import net.torvald.random.HQRNG import net.torvald.random.HQRNG
import net.torvald.terrarum.Terrarum import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.gameactors.AnyPlayer
import net.torvald.terrarum.gameactors.HistoricalFigure import net.torvald.terrarum.gameactors.HistoricalFigure
import net.torvald.terrarum.gameactors.Player import net.torvald.terrarum.gameactors.Player
import net.torvald.terrarum.gameactors.roundInt import net.torvald.terrarum.gameactors.roundInt
@@ -40,8 +41,7 @@ object WorldSimulator {
private val world = Terrarum.ingame!!.world private val world = Terrarum.ingame!!.world
// TODO future Kotlin feature -- typealias AnyPlayer: HistoricalFigure operator fun invoke(p: AnyPlayer?, delta: Int) {
operator fun invoke(p: HistoricalFigure?, delta: Int) {
if (p != null) { if (p != null) {
updateXFrom = p.hitbox.centeredX.div(FeaturesDrawer.TILE_SIZE).minus(FLUID_UPDATING_SQUARE_RADIUS).roundInt() updateXFrom = p.hitbox.centeredX.div(FeaturesDrawer.TILE_SIZE).minus(FLUID_UPDATING_SQUARE_RADIUS).roundInt()
updateYFrom = p.hitbox.centeredY.div(FeaturesDrawer.TILE_SIZE).minus(FLUID_UPDATING_SQUARE_RADIUS).roundInt() updateYFrom = p.hitbox.centeredY.div(FeaturesDrawer.TILE_SIZE).minus(FLUID_UPDATING_SQUARE_RADIUS).roundInt()

View File

@@ -98,8 +98,6 @@ class WorldTime(initTime: Long = 0L) {
) )
val DAY_NAMES_SHORT = arrayOf("Mon", "Tys", "Mid", "Tor", "Fre", "Lau", "Sun", "Ver") val DAY_NAMES_SHORT = arrayOf("Mon", "Tys", "Mid", "Tor", "Fre", "Lau", "Sun", "Ver")
// FIXME Next to Granite is Felsite
val MONTH_NAMES = arrayOf( val MONTH_NAMES = arrayOf(
"Opal", "Obsidian", "Granite", "Slate", "Felsite", "Hematite", "Opal", "Obsidian", "Granite", "Slate", "Felsite", "Hematite",
"Malachite", "Galena", "Limestone", "Sandstone", "Timber", "Moonstone" "Malachite", "Galena", "Limestone", "Sandstone", "Timber", "Moonstone"

View File

@@ -16,8 +16,8 @@ import net.torvald.terrarum.gameactors.GameDate
* *
* Created by minjaesong on 16-01-24. * Created by minjaesong on 16-01-24.
*/ */
@Deprecated("Are you even reading the name?")
class YeOldeWorldTime { class YeOldeWorldTime {
internal var TIME_T = 0L // TODO use it! Epoch: Year 125, 1st Granite, 0h00:00
internal var seconds: Int // 0 - 59 internal var seconds: Int // 0 - 59
internal var minutes: Int // 0 - 59 internal var minutes: Int // 0 - 59

View File

@@ -11,6 +11,8 @@ import net.torvald.terrarum.gamecontroller.mouseTileY
import net.torvald.terrarum.gameitem.IVKey import net.torvald.terrarum.gameitem.IVKey
import net.torvald.terrarum.gameworld.GameWorld import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.mapdrawer.TilesDrawer import net.torvald.terrarum.mapdrawer.TilesDrawer
import net.torvald.terrarum.mapdrawer.TilesDrawer.wallOverlayColour
import net.torvald.terrarum.tileproperties.Tile
import net.torvald.terrarum.tileproperties.TileCodex import net.torvald.terrarum.tileproperties.TileCodex
import org.newdawn.slick.GameContainer import org.newdawn.slick.GameContainer
import org.newdawn.slick.Image import org.newdawn.slick.Image
@@ -28,31 +30,35 @@ object ItemCodex {
private val itemCodex = HashMap<Int, InventoryItem>() private val itemCodex = HashMap<Int, InventoryItem>()
private val dynamicItemDescription = HashMap<Int, KVHashMap>() private val dynamicItemDescription = HashMap<Int, KVHashMap>()
val ITEM_TILE_MAX = GameWorld.TILES_SUPPORTED - 1 // 4095 val ITEM_TILES = 0..GameWorld.TILES_SUPPORTED - 1
val ITEM_COUNT_MAX = 1048576 val ITEM_WALLS = GameWorld.TILES_SUPPORTED..GameWorld.TILES_SUPPORTED * 2 - 1
val ITEM_DYNAMIC_MAX = ITEM_COUNT_MAX - 1 val ITEM_WIRES = GameWorld.TILES_SUPPORTED * 2..GameWorld.TILES_SUPPORTED * 2 + 255
val ITEM_STATIC_MAX = 32767 val ITEM_STATIC = ITEM_WIRES.endInclusive + 1..32767
val ITEM_DYNAMIC_MIN = ITEM_STATIC_MAX + 1 val ITEM_DYNAMIC = 32768..1048575
val ITEM_STATIC_MIN = ITEM_TILE_MAX + 1 // 4096 val ACTOR_ID_MIN = ITEM_DYNAMIC.endInclusive + 1
private val itemImagePlaceholder = Image("./assets/item_kari_24.tga") private val itemImagePlaceholder = Image("./assets/item_kari_24.tga")
init { init {
// tile items (blocks and walls are the same thing basically) // tile items (blocks and walls are the same thing basically)
for (i in 0..ITEM_TILE_MAX) { for (i in ITEM_TILES + ITEM_WALLS) {
itemCodex[i] = object : InventoryItem() { itemCodex[i] = object : InventoryItem() {
override val id: Int = i override val id: Int = i
override val isUnique: Boolean = false override val isUnique: Boolean = false
override var baseMass: Double = TileCodex[i].density / 1000.0 override var baseMass: Double = TileCodex[i].density / 1000.0
override var baseToolSize: Double? = null override var baseToolSize: Double? = null
override var equipPosition = EquipPosition.HAND_GRIP override var equipPosition = EquipPosition.HAND_GRIP
override var category = "block" override val originalName = TileCodex[i % ITEM_WALLS.first].nameKey
override val originalName = TileCodex[i].nameKey
override var consumable = true override var consumable = true
override var inventoryCategory = Category.BLOCK
init { init {
itemProperties[IVKey.ITEMTYPE] = IVKey.ItemType.BLOCK itemProperties[IVKey.ITEMTYPE] = if (i in ITEM_TILES)
IVKey.ItemType.BLOCK
else
IVKey.ItemType.WALL
} }
override fun primaryUse(gc: GameContainer, delta: Int): Boolean { override fun primaryUse(gc: GameContainer, delta: Int): Boolean {
@@ -74,26 +80,81 @@ object ItemCodex {
// filter passed, do the job // filter passed, do the job
// FIXME this is only useful for Player // FIXME this is only useful for Player
Terrarum.ingame!!.world.setTileTerrain( if (i in ITEM_TILES) {
gc.mouseTileX, Terrarum.ingame!!.world.setTileTerrain(
gc.mouseTileY, gc.mouseTileX,
i gc.mouseTileY,
) i
)
}
else {
Terrarum.ingame!!.world.setTileWall(
gc.mouseTileX,
gc.mouseTileY,
i
)
}
return true return true
} }
} }
} }
// read prop in csv and fill itemCodex // test copper pickaxe
itemCodex[ITEM_STATIC.first] = object : InventoryItem() {
override val id = ITEM_STATIC.first
override val isUnique = false
override val originalName = "Test Pick"
override var baseMass = 10.0
override var baseToolSize: Double? = 10.0
override var consumable = false
override var maxDurability = 200 // this much tiles before breaking
override var durability = maxDurability.toFloat()
override var equipPosition = EquipPosition.HAND_GRIP
override var inventoryCategory = Category.TOOL
init {
itemProperties[IVKey.ITEMTYPE] = IVKey.ItemType.PICK
}
override fun primaryUse(gc: GameContainer, delta: Int): Boolean {
val mousePoint = Point2d(gc.mouseTileX.toDouble(), gc.mouseTileY.toDouble())
// linear search filter (check for intersection with tilewise mouse point and tilewise hitbox)
Terrarum.ingame!!.actorContainer.forEach {
if (it is ActorWithSprite && it.tilewiseHitbox.intersects(mousePoint))
return false
}
// return false if the tile is already there
if (this.id == Terrarum.ingame!!.world.getTileFromTerrain(gc.mouseTileX, gc.mouseTileY))
return false
// filter passed, do the job
Terrarum.ingame!!.world.setTileTerrain(
gc.mouseTileX,
gc.mouseTileY,
Tile.AIR
)
/*Terrarum.ingame!!.world.inflctTerrainDamage(
gc.mouseTileX,
gc.mouseTileY,
<power calculation using ForceMod (ref. Pickaxe Power.xlsx) and other shits>
)*/
return true
}
}
// TODO read prop in Lua and fill itemCodex
// read from save (if applicable) and fill dynamicItemDescription // read from save (if applicable) and fill dynamicItemDescription
} }
operator fun get(code: Int): InventoryItem { operator fun get(code: Int): InventoryItem {
if (code < ITEM_STATIC_MAX) // generic item if (code <= ITEM_STATIC.endInclusive) // generic item
return itemCodex[code]!! // from CSV return itemCodex[code]!!.clone() // from CSV
else if (code < ITEM_DYNAMIC_MAX) { else if (code <= ITEM_DYNAMIC.endInclusive) {
TODO("read from dynamicitem description (JSON)") TODO("read from dynamicitem description (JSON)")
} }
else { else {
@@ -105,8 +166,15 @@ object ItemCodex {
} }
fun getItemImage(code: Int): Image { fun getItemImage(code: Int): Image {
if (code <= ITEM_TILE_MAX) if (code <= ITEM_TILES.endInclusive)
return TilesDrawer.tilesTerrain.getSubImage((code % 16) * 16, code / 16) return TilesDrawer.tilesTerrain.getSubImage((code % 16) * 16, code / 16)
else if (code <= ITEM_WALLS.endInclusive) {
val img = TilesDrawer.tilesTerrain.getSubImage((code % 16) * 16, code / 16)
img.setImageColor(wallOverlayColour.r, wallOverlayColour.g, wallOverlayColour.b)
return img
}
else if (code <= ITEM_WIRES.endInclusive)
return TilesDrawer.tilesWire.getSubImage((code % 16) * 16, code / 16)
else else
return itemImagePlaceholder return itemImagePlaceholder
} }

View File

@@ -0,0 +1,61 @@
package net.torvald.terrarum.itemproperties
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.gameactors.ai.toLua
import net.torvald.terrarum.gamecontroller.mouseTileX
import net.torvald.terrarum.gamecontroller.mouseTileY
import net.torvald.terrarum.gamecontroller.mouseX
import net.torvald.terrarum.gamecontroller.mouseY
import org.luaj.vm2.Globals
import org.luaj.vm2.LuaTable
import org.luaj.vm2.LuaValue
import org.luaj.vm2.lib.ThreeArgFunction
import org.luaj.vm2.lib.ZeroArgFunction
/**
* Created by SKYHi14 on 2017-04-16.
*/
class ItemEffectsLuaAPI(g: Globals) {
init {
g["getMouseTile"] = GetMouseTile()
g["getMousePos"] = GetMousePos()
g["world"] = LuaTable()
g["world"]["strikeEarth"] = StrikeEarth()
g["world"]["strikeWall"] = StrikeWall()
g["actor"] = LuaTable()
}
class GetMouseTile : ZeroArgFunction() {
override fun call(): LuaValue {
return LuaValue.tableOf(arrayOf(Terrarum.appgc.mouseTileX.toLua(), Terrarum.appgc.mouseTileY.toLua()))
}
}
class GetMousePos : ZeroArgFunction() {
override fun call(): LuaValue {
return LuaValue.tableOf(arrayOf(Terrarum.appgc.mouseX.toLua(), Terrarum.appgc.mouseY.toLua()))
}
}
class StrikeEarth : ThreeArgFunction() {
override fun call(x: LuaValue, y: LuaValue, power: LuaValue): LuaValue {
Terrarum.ingame!!.world.inflctTerrainDamage(x.checkint(), y.checkint(), power.checkint())
return LuaValue.NONE
}
}
class StrikeWall : ThreeArgFunction() {
override fun call(x: LuaValue, y: LuaValue, power: LuaValue): LuaValue {
Terrarum.ingame!!.world.inflctWallDamage(x.checkint(), y.checkint(), power.checkint())
return LuaValue.NONE
}
}
}

View File

@@ -106,7 +106,7 @@ object Lang {
} }
operator fun get(key: String): String { operator fun get(key: String): String {
fun fallback(): String = langpack["${key}_$FALLBACK_LANG_CODE"] ?: "ERRNULL:$key" fun fallback(): String = langpack["${key}_$FALLBACK_LANG_CODE"] ?: "$$key"
val ret = langpack["${key}_${Terrarum.gameLocale}"] val ret = langpack["${key}_${Terrarum.gameLocale}"]

View File

@@ -401,10 +401,10 @@ object TilesDrawer {
*/ */
fun getNearbyTilesInfo(x: Int, y: Int, mode: Int, mark: Int?): Int { fun getNearbyTilesInfo(x: Int, y: Int, mode: Int, mark: Int?): Int {
val nearbyTiles = IntArray(4) val nearbyTiles = IntArray(4)
nearbyTiles[NEARBY_TILE_KEY_LEFT] = world.getTileFrom(mode, x - 1, y) ?: 4096 nearbyTiles[NEARBY_TILE_KEY_LEFT] = world.getTileFrom(mode, x - 1, y) ?: Tile.NULL
nearbyTiles[NEARBY_TILE_KEY_RIGHT] = world.getTileFrom(mode, x + 1, y) ?: 4096 nearbyTiles[NEARBY_TILE_KEY_RIGHT] = world.getTileFrom(mode, x + 1, y) ?: Tile.NULL
nearbyTiles[NEARBY_TILE_KEY_UP] = world.getTileFrom(mode, x , y - 1) ?: 4906 nearbyTiles[NEARBY_TILE_KEY_UP] = world.getTileFrom(mode, x , y - 1) ?: 4906
nearbyTiles[NEARBY_TILE_KEY_DOWN] = world.getTileFrom(mode, x , y + 1) ?: 4096 nearbyTiles[NEARBY_TILE_KEY_DOWN] = world.getTileFrom(mode, x , y + 1) ?: Tile.NULL
// try for // try for
var ret = 0 var ret = 0
@@ -419,10 +419,10 @@ object TilesDrawer {
fun getNearbyTilesInfoNonSolid(x: Int, y: Int, mode: Int): Int { fun getNearbyTilesInfoNonSolid(x: Int, y: Int, mode: Int): Int {
val nearbyTiles = IntArray(4) val nearbyTiles = IntArray(4)
nearbyTiles[NEARBY_TILE_KEY_LEFT] = world.getTileFrom(mode, x - 1, y) ?: 4096 nearbyTiles[NEARBY_TILE_KEY_LEFT] = world.getTileFrom(mode, x - 1, y) ?: Tile.NULL
nearbyTiles[NEARBY_TILE_KEY_RIGHT] = world.getTileFrom(mode, x + 1, y) ?: 4096 nearbyTiles[NEARBY_TILE_KEY_RIGHT] = world.getTileFrom(mode, x + 1, y) ?: Tile.NULL
nearbyTiles[NEARBY_TILE_KEY_UP] = world.getTileFrom(mode, x , y - 1) ?: 4906 nearbyTiles[NEARBY_TILE_KEY_UP] = world.getTileFrom(mode, x , y - 1) ?: 4906
nearbyTiles[NEARBY_TILE_KEY_DOWN] = world.getTileFrom(mode, x , y + 1) ?: 4096 nearbyTiles[NEARBY_TILE_KEY_DOWN] = world.getTileFrom(mode, x , y + 1) ?: Tile.NULL
// try for // try for
var ret = 0 var ret = 0
@@ -443,10 +443,10 @@ object TilesDrawer {
fun getNearbyTilesInfoWallSticker(x: Int, y: Int): Int { fun getNearbyTilesInfoWallSticker(x: Int, y: Int): Int {
val nearbyTiles = IntArray(4) val nearbyTiles = IntArray(4)
val NEARBY_TILE_KEY_BACK = NEARBY_TILE_KEY_UP val NEARBY_TILE_KEY_BACK = NEARBY_TILE_KEY_UP
nearbyTiles[NEARBY_TILE_KEY_LEFT] = world.getTileFrom(TERRAIN, x - 1, y) ?: 4096 nearbyTiles[NEARBY_TILE_KEY_LEFT] = world.getTileFrom(TERRAIN, x - 1, y) ?: Tile.NULL
nearbyTiles[NEARBY_TILE_KEY_RIGHT] = world.getTileFrom(TERRAIN, x + 1, y) ?: 4096 nearbyTiles[NEARBY_TILE_KEY_RIGHT] = world.getTileFrom(TERRAIN, x + 1, y) ?: Tile.NULL
nearbyTiles[NEARBY_TILE_KEY_DOWN] = world.getTileFrom(TERRAIN, x , y + 1) ?: 4096 nearbyTiles[NEARBY_TILE_KEY_DOWN] = world.getTileFrom(TERRAIN, x , y + 1) ?: Tile.NULL
nearbyTiles[NEARBY_TILE_KEY_BACK] = world.getTileFrom(WALL, x , y) ?: 4096 nearbyTiles[NEARBY_TILE_KEY_BACK] = world.getTileFrom(WALL, x , y) ?: Tile.NULL
try { try {
if (TileCodex[nearbyTiles[NEARBY_TILE_KEY_DOWN]].isSolid) if (TileCodex[nearbyTiles[NEARBY_TILE_KEY_DOWN]].isSolid)
@@ -476,8 +476,8 @@ object TilesDrawer {
fun getNearbyTilesInfoPlatform(x: Int, y: Int): Int { fun getNearbyTilesInfoPlatform(x: Int, y: Int): Int {
val nearbyTiles = IntArray(4) val nearbyTiles = IntArray(4)
nearbyTiles[NEARBY_TILE_KEY_LEFT] = world.getTileFrom(TERRAIN, x - 1, y) ?: 4096 nearbyTiles[NEARBY_TILE_KEY_LEFT] = world.getTileFrom(TERRAIN, x - 1, y) ?: Tile.NULL
nearbyTiles[NEARBY_TILE_KEY_RIGHT] = world.getTileFrom(TERRAIN, x + 1, y) ?: 4096 nearbyTiles[NEARBY_TILE_KEY_RIGHT] = world.getTileFrom(TERRAIN, x + 1, y) ?: Tile.NULL
if ((TileCodex[nearbyTiles[NEARBY_TILE_KEY_LEFT]].isSolid && if ((TileCodex[nearbyTiles[NEARBY_TILE_KEY_LEFT]].isSolid &&
TileCodex[nearbyTiles[NEARBY_TILE_KEY_RIGHT]].isSolid) || TileCodex[nearbyTiles[NEARBY_TILE_KEY_RIGHT]].isSolid) ||

View File

@@ -2,21 +2,22 @@ package net.torvald.terrarum.realestate
import net.torvald.terrarum.Terrarum import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.gameactors.faction.FactionCodex import net.torvald.terrarum.gameactors.faction.FactionCodex
import net.torvald.terrarum.gameworld.TileAddress
/** /**
* Created by minjaesong on 16-03-27. * Created by minjaesong on 16-03-27.
*/ */
object RealEstateUtility { object RealEstateUtility {
fun getAbsoluteTileNumber(x: Int, y: Int): Long = fun getAbsoluteTileNumber(x: Int, y: Int): TileAddress =
(Terrarum.ingame!!.world.width * y).toLong() + x (Terrarum.ingame!!.world.width * y).toLong() + x
fun resolveAbsoluteTileNumber(t: Long): Pair<Int, Int> = fun resolveAbsoluteTileNumber(t: TileAddress): Pair<Int, Int> =
Pair((t % Terrarum.ingame!!.world.width).toInt(), (t / Terrarum.ingame!!.world.width).toInt()) Pair((t % Terrarum.ingame!!.world.width).toInt(), (t / Terrarum.ingame!!.world.width).toInt())
/** /**
* Get owner ID as an Actor/Faction * Get owner ID as an Actor/Faction
*/ */
fun resolveOwner(id: Long): Any = fun resolveOwner(id: TileAddress): Any =
if (id < 0x80000000L) if (id < 0x80000000L)
Terrarum.ingame!!.getActorByID(id.toInt()) Terrarum.ingame!!.getActorByID(id.toInt())
else else

View File

@@ -150,5 +150,5 @@ object Tile {
val LAVA_15 = TileCodex.idDamageToIndex(254, 14) val LAVA_15 = TileCodex.idDamageToIndex(254, 14)
val LAVA = TileCodex.idDamageToIndex(254, 15) val LAVA = TileCodex.idDamageToIndex(254, 15)
val NULL = 4096 val NULL = -1
} }

View File

@@ -18,8 +18,10 @@ object TileCodex {
const val TILE_UNIQUE_MAX = MapLayer.RANGE * PairedMapLayer.RANGE const val TILE_UNIQUE_MAX = MapLayer.RANGE * PairedMapLayer.RANGE
private val nullProp = TileProp()
init { init {
tileProps = Array<TileProp>(TILE_UNIQUE_MAX + 1, { i -> TileProp() }) tileProps = Array<TileProp>(TILE_UNIQUE_MAX * 2, { i -> TileProp() })
for (i in tileProps.indices) { for (i in tileProps.indices) {
tileProps[i] = TileProp() tileProps[i] = TileProp()
@@ -31,8 +33,15 @@ object TileCodex {
println("[TileCodex] Building tile properties table") println("[TileCodex] Building tile properties table")
records.forEach { setProp( records.forEach {
tileProps[idDamageToIndex(intVal(it, "id"), intVal(it, "dmg"))], it) if (intVal(it, "dmg") == -1) {
setProp(nullProp, it)
}
else {
setProp(
tileProps[idDamageToIndex(intVal(it, "id"), intVal(it, "dmg"))], it
)
}
} }
} }
catch (e: IOException) { catch (e: IOException) {
@@ -53,14 +62,16 @@ object TileCodex {
} }
operator fun get(rawIndex: Int?): TileProp { operator fun get(rawIndex: Int?): TileProp {
if (rawIndex == null || rawIndex == Tile.NULL) {
return nullProp
}
try { try {
tileProps[rawIndex ?: Tile.NULL].id return tileProps[rawIndex]
} }
catch (e: NullPointerException) { catch (e: NullPointerException) {
throw NullPointerException("Tile prop with raw id $rawIndex does not exist.") throw NullPointerException("Tile prop with raw id $rawIndex does not exist.")
} }
return tileProps[rawIndex ?: Tile.NULL]
} }
private fun setProp(prop: TileProp, record: CSVRecord) { private fun setProp(prop: TileProp, record: CSVRecord) {

View File

@@ -135,7 +135,7 @@ object TilePropCSV {
"255"; "13";"TILE_WATER" ; "27282445"; "100";"1000";"watr"; "1"; "0"; "0"; "0"; "N/A"; "N/A"; "0"; "0"; "16"; "0";"16" "255"; "13";"TILE_WATER" ; "27282445"; "100";"1000";"watr"; "1"; "0"; "0"; "0"; "N/A"; "N/A"; "0"; "0"; "16"; "0";"16"
"255"; "14";"TILE_WATER" ; "27282445"; "100";"1000";"watr"; "1"; "0"; "0"; "0"; "N/A"; "N/A"; "0"; "0"; "16"; "0";"16" "255"; "14";"TILE_WATER" ; "27282445"; "100";"1000";"watr"; "1"; "0"; "0"; "0"; "N/A"; "N/A"; "0"; "0"; "16"; "0";"16"
"255"; "15";"TILE_WATER" ; "27282445"; "100";"1000";"watr"; "1"; "0"; "0"; "0"; "N/A"; "N/A"; "0"; "0"; "16"; "0";"16" "255"; "15";"TILE_WATER" ; "27282445"; "100";"1000";"watr"; "1"; "0"; "0"; "0"; "N/A"; "N/A"; "0"; "0"; "16"; "0";"16"
"256"; "0";"TILE_NULL" ;"1073741823"; "-1";"2600";"null"; "0"; "0"; "1"; "0"; "N/A"; "N/A"; "0"; "0"; "N/A"; "0";"16" "0"; "-1";"TILE_NULL" ;"1073741823"; "-1";"2600";"null"; "0"; "0"; "1"; "0"; "N/A"; "N/A"; "0"; "0"; "N/A"; "0";"16"
## Notes ## ## Notes ##

View File

@@ -1,6 +1,9 @@
package net.torvald.terrarum.ui package net.torvald.terrarum.ui
import net.torvald.point.Point2d
import net.torvald.terrarum.Millisec import net.torvald.terrarum.Millisec
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.gameactors.roundInt
import org.newdawn.slick.GameContainer import org.newdawn.slick.GameContainer
import org.newdawn.slick.Graphics import org.newdawn.slick.Graphics
import org.newdawn.slick.Input import org.newdawn.slick.Input
@@ -57,21 +60,86 @@ interface UICanvas {
fun doOpeningFade(handler: UIHandler?, openCloseTime: Int) { fun doOpeningFade(handler: UIHandler?, openCloseTime: Int) {
handler!!.opacity = handler.openCloseCounter.toFloat() / openCloseTime handler!!.opacity = handler.openCloseCounter.toFloat() / openCloseTime
} }
fun doClosingFade(handler: UIHandler?, openCloseTime: Int) { fun doClosingFade(handler: UIHandler?, openCloseTime: Int) {
handler!!.opacity = (openCloseTime - handler.openCloseCounter.toFloat()) / openCloseTime handler!!.opacity = (openCloseTime - handler.openCloseCounter.toFloat()) / openCloseTime
} }
fun endOpeningFade(handler: UIHandler?) { fun endOpeningFade(handler: UIHandler?) {
handler!!.opacity = 1f handler!!.opacity = 1f
} }
fun endClosingFade(handler: UIHandler?) { fun endClosingFade(handler: UIHandler?) {
handler!!.opacity = 0f handler!!.opacity = 0f
} }
// TODO add drawer slide in/out (quadratic)
fun doOpeningPopOut(handler: UIHandler?, openCloseTime: Int, position: Position) {
when (position) {
Position.LEFT -> handler!!.posX = Movement.fastPullOut(
handler.openCloseCounter.toFloat() / openCloseTime,
-handler.UI.width.toFloat(),
0f
).roundInt()
Position.TOP -> handler!!.posY = Movement.fastPullOut(
handler.openCloseCounter.toFloat() / openCloseTime,
-handler.UI.height.toFloat(),
0f
).roundInt()
Position.RIGHT -> handler!!.posX = Movement.fastPullOut(
handler.openCloseCounter.toFloat() / openCloseTime,
Terrarum.WIDTH.toFloat(),
Terrarum.WIDTH - handler.UI.width.toFloat()
).roundInt()
Position.BOTTOM -> handler!!.posY = Movement.fastPullOut(
handler.openCloseCounter.toFloat() / openCloseTime,
Terrarum.HEIGHT.toFloat(),
Terrarum.HEIGHT - handler.UI.height.toFloat()
).roundInt()
}
}
fun doClosingPopOut(handler: UIHandler?, openCloseTime: Int, position: Position) {
when (position) {
Position.LEFT -> handler!!.posX = Movement.fastPullOut(
handler.openCloseCounter.toFloat() / openCloseTime,
0f,
-handler.UI.width.toFloat()
).roundInt()
Position.TOP -> handler!!.posY = Movement.fastPullOut(
handler.openCloseCounter.toFloat() / openCloseTime,
0f,
-handler.UI.height.toFloat()
).roundInt()
Position.RIGHT -> handler!!.posX = Movement.fastPullOut(
handler.openCloseCounter.toFloat() / openCloseTime,
Terrarum.WIDTH - handler.UI.width.toFloat(),
Terrarum.WIDTH.toFloat()
).roundInt()
Position.BOTTOM -> handler!!.posY = Movement.fastPullOut(
handler.openCloseCounter.toFloat() / openCloseTime,
Terrarum.HEIGHT - handler.UI.height.toFloat(),
Terrarum.HEIGHT.toFloat()
).roundInt()
}
}
fun endOpeningPopOut(handler: UIHandler?, position: Position) {
when (position) {
Position.LEFT -> handler!!.posX = 0
Position.TOP -> handler!!.posY = 0
Position.RIGHT -> handler!!.posX = Terrarum.WIDTH - handler.UI.width
Position.BOTTOM -> handler!!.posY = Terrarum.HEIGHT - handler.UI.height
}
}
fun endClosingPopOut(handler: UIHandler?, position: Position) {
when (position) {
Position.LEFT -> handler!!.posX = -handler.UI.width
Position.TOP -> handler!!.posY = -handler.UI.height
Position.RIGHT -> handler!!.posX = Terrarum.WIDTH
Position.BOTTOM -> handler!!.posY = Terrarum.HEIGHT
}
}
// TODO add blackboard take in/out (sinusoidal) // TODO add blackboard take in/out (sinusoidal)
enum class Position {
LEFT, RIGHT, TOP, BOTTOM
}
} }
} }

View File

@@ -158,7 +158,7 @@ class UIInventory(
// filter items // filter items
inventory?.forEach { inventory?.forEach {
if (it.item.category == filter || filter == "__all__") if (it.item.inventoryCategory == filter || filter == "__all__")
inventorySortList.add(it) inventorySortList.add(it)
} }
@@ -295,27 +295,20 @@ class UIInventory(
} }
override fun doOpening(gc: GameContainer, delta: Int) { override fun doOpening(gc: GameContainer, delta: Int) {
handler!!.posX = Movement.fastPullOut( UICanvas.doOpeningPopOut(handler, openCloseTime, UICanvas.Companion.Position.LEFT)
handler!!.openCloseCounter.toFloat() / openCloseTime,
-width.toFloat(),
0f
).roundInt()
} }
override fun doClosing(gc: GameContainer, delta: Int) { override fun doClosing(gc: GameContainer, delta: Int) {
handler!!.posX = Movement.fastPullOut( UICanvas.doClosingPopOut(handler, openCloseTime, UICanvas.Companion.Position.LEFT)
handler!!.openCloseCounter.toFloat() / openCloseTime,
0f,
-width.toFloat()
).roundInt()
} }
override fun endOpening(gc: GameContainer, delta: Int) { override fun endOpening(gc: GameContainer, delta: Int) {
handler!!.posX = 0 UICanvas.endOpeningPopOut(handler, UICanvas.Companion.Position.LEFT)
} }
override fun endClosing(gc: GameContainer, delta: Int) { override fun endClosing(gc: GameContainer, delta: Int) {
handler!!.posX = -width UICanvas.endClosingPopOut(handler, UICanvas.Companion.Position.LEFT)
} }
override fun keyPressed(key: Int, c: Char) { override fun keyPressed(key: Int, c: Char) {

View File

@@ -94,7 +94,7 @@ internal class Filesystem(globals: Globals, computer: TerrarumComputer) {
path.dropMount() path.dropMount()
return VDUtil.getFile(disk, path)?.file return VDUtil.getFile(disk, path)
} }
/** /**
@@ -201,7 +201,7 @@ internal class Filesystem(globals: Globals, computer: TerrarumComputer) {
if (file!!.contents is EntryFile) if (file!!.contents is EntryFile)
return LuaValue.valueOf(file.contents.getSizePure().toInt()) return LuaValue.valueOf(file.contents.getSizePure().toInt())
else if (file.contents is EntryDirectory) else if (file.contents is EntryDirectory)
return LuaValue.valueOf(file.contents.entries.size) return LuaValue.valueOf(file.contents.entryCount)
} }
catch (e: KotlinNullPointerException) { catch (e: KotlinNullPointerException) {
} }
@@ -279,12 +279,12 @@ internal class Filesystem(globals: Globals, computer: TerrarumComputer) {
val file = VDUtil.getFile(disk1, pathFrom)!! val file = VDUtil.getFile(disk1, pathFrom)!!
try { try {
VDUtil.addFile(disk2, pathTo.getParent(), file.file) VDUtil.addFile(disk2, pathTo.getParent(), file)
} }
catch (e: FileNotFoundException) { catch (e: FileNotFoundException) {
// roll back delete on disk2 // roll back delete on disk2
if (oldFile != null) { if (oldFile != null) {
VDUtil.addFile(disk2, oldFile.parent.entryID, oldFile.file) VDUtil.addFile(disk2, oldFile.parentEntryID, oldFile)
throw FileNotFoundException("No such destination") throw FileNotFoundException("No such destination")
} }
} }

View File

@@ -41,7 +41,7 @@ object VDUtil {
unsanitisedHierarchy.removeAt(0) unsanitisedHierarchy.removeAt(0)
// removes tail slash // removes tail slash
if (unsanitisedHierarchy.size > 0 && if (unsanitisedHierarchy.size > 0 &&
unsanitisedHierarchy[unsanitisedHierarchy.lastIndex].isEmpty()) unsanitisedHierarchy[unsanitisedHierarchy.lastIndex].isEmpty())
unsanitisedHierarchy.removeAt(unsanitisedHierarchy.lastIndex) unsanitisedHierarchy.removeAt(unsanitisedHierarchy.lastIndex)
unsanitisedHierarchy.forEach { unsanitisedHierarchy.forEach {
@@ -202,7 +202,7 @@ object VDUtil {
val calculatedCRC = diskEntry.hashCode() val calculatedCRC = diskEntry.hashCode()
val crcMsg = "CRC failed: expected ${entryCRC.toHex()}, got ${calculatedCRC.toHex()}\n" + val crcMsg = "CRC failed: expected ${entryCRC.toHex()}, got ${calculatedCRC.toHex()}\n" +
"at file \"${diskEntry.getFilenameString(charset)}\" (entry ID ${diskEntry.entryID})" "at file \"${diskEntry.getFilenameString(charset)}\" (entry ID ${diskEntry.entryID})"
if (calculatedCRC != entryCRC) { if (calculatedCRC != entryCRC) {
if (crcWarnLevel == Level.SEVERE) if (crcWarnLevel == Level.SEVERE)
@@ -243,7 +243,7 @@ object VDUtil {
throw IllegalArgumentException("The entry is not directory") throw IllegalArgumentException("The entry is not directory")
val entriesList = ArrayList<DiskEntry>() val entriesList = ArrayList<DiskEntry>()
dirToSearch.contents.entries.forEach { dirToSearch.contents.forEach {
val entry = disk.entries[it] val entry = disk.entries[it]
if (entry != null) entriesList.add(entry) if (entry != null) entriesList.add(entry)
} }
@@ -267,7 +267,7 @@ object VDUtil {
* Search a entry using path * Search a entry using path
* @return Pair of <The file, Parent file>, or null if not found * @return Pair of <The file, Parent file>, or null if not found
*/ */
fun getFile(disk: VirtualDisk, path: VDPath): EntrySearchResult? { fun getFile(disk: VirtualDisk, path: VDPath): DiskEntry? {
val searchHierarchy = ArrayList<DiskEntry>() val searchHierarchy = ArrayList<DiskEntry>()
fun getCurrentEntry(): DiskEntry = searchHierarchy.last() fun getCurrentEntry(): DiskEntry = searchHierarchy.last()
//var currentDirectory = disk.root //var currentDirectory = disk.root
@@ -276,10 +276,7 @@ object VDUtil {
// path of root // path of root
if (path.hierarchy.size == 0) { if (path.hierarchy.size == 0) {
return EntrySearchResult( return disk.entries[0]!!
disk.entries[0]!!,
disk.entries[0]!!
)
} }
try { try {
@@ -310,10 +307,7 @@ object VDUtil {
} }
// file found // file found
return EntrySearchResult( return searchHierarchy[searchHierarchy.lastIndex]
searchHierarchy[searchHierarchy.lastIndex],
searchHierarchy[searchHierarchy.lastIndex - 1]
)
} }
/** /**
@@ -323,12 +317,12 @@ object VDUtil {
*/ */
private fun DiskEntry.getAsNormalFile(disk: VirtualDisk): EntryFile = private fun DiskEntry.getAsNormalFile(disk: VirtualDisk): EntryFile =
this.contents as? EntryFile ?: this.contents as? EntryFile ?:
if (this.contents is EntryDirectory) if (this.contents is EntryDirectory)
throw RuntimeException("this is directory") throw RuntimeException("this is directory")
else if (this.contents is EntrySymlink) else if (this.contents is EntrySymlink)
disk.entries[this.contents.target]!!.getAsNormalFile(disk) disk.entries[this.contents.target]!!.getAsNormalFile(disk)
else else
throw RuntimeException("Unknown entry type") throw RuntimeException("Unknown entry type")
/** /**
* SYNOPSIS disk.getFile("bin/msh.lua")!!.first.getAsNormalFile(disk) * SYNOPSIS disk.getFile("bin/msh.lua")!!.first.getAsNormalFile(disk)
* *
@@ -336,18 +330,18 @@ object VDUtil {
*/ */
private fun DiskEntry.getAsDirectory(disk: VirtualDisk): EntryDirectory = private fun DiskEntry.getAsDirectory(disk: VirtualDisk): EntryDirectory =
this.contents as? EntryDirectory ?: this.contents as? EntryDirectory ?:
if (this.contents is EntrySymlink) if (this.contents is EntrySymlink)
disk.entries[this.contents.target]!!.getAsDirectory(disk) disk.entries[this.contents.target]!!.getAsDirectory(disk)
else if (this.contents is EntryFile) else if (this.contents is EntryFile)
throw RuntimeException("this is not directory") throw RuntimeException("this is not directory")
else else
throw RuntimeException("Unknown entry type") throw RuntimeException("Unknown entry type")
/** /**
* Search for the file and returns a instance of normal file. * Search for the file and returns a instance of normal file.
*/ */
fun getAsNormalFile(disk: VirtualDisk, path: VDPath) = fun getAsNormalFile(disk: VirtualDisk, path: VDPath) =
getFile(disk, path)!!.file.getAsNormalFile(disk) getFile(disk, path)!!.getAsNormalFile(disk)
/** /**
* Fetch the file and returns a instance of normal file. * Fetch the file and returns a instance of normal file.
*/ */
@@ -357,7 +351,7 @@ object VDUtil {
* Search for the file and returns a instance of directory. * Search for the file and returns a instance of directory.
*/ */
fun getAsDirectory(disk: VirtualDisk, path: VDPath) = fun getAsDirectory(disk: VirtualDisk, path: VDPath) =
getFile(disk, path)!!.file.getAsDirectory(disk) getFile(disk, path)!!.getAsDirectory(disk)
/** /**
* Fetch the file and returns a instance of directory. * Fetch the file and returns a instance of directory.
*/ */
@@ -367,11 +361,8 @@ object VDUtil {
* Deletes file on the disk safely. * Deletes file on the disk safely.
*/ */
fun deleteFile(disk: VirtualDisk, path: VDPath) { fun deleteFile(disk: VirtualDisk, path: VDPath) {
disk.checkReadOnly()
val fileSearchResult = getFile(disk, path)!! val fileSearchResult = getFile(disk, path)!!
return deleteFile(disk, fileSearchResult.entryID)
return deleteFile(disk, fileSearchResult.file.entryID)
} }
/** /**
* Deletes file on the disk safely. * Deletes file on the disk safely.
@@ -386,29 +377,25 @@ object VDUtil {
} }
val parentID = file.parentEntryID val parentID = file.parentEntryID
val parentDir = disk.entries[parentID] val parentDir = getAsDirectory(disk, parentID)
fun rollback() { fun rollback() {
if (!disk.entries.contains(targetID)) { if (!disk.entries.contains(targetID)) {
disk.entries[targetID] = file disk.entries[targetID] = file
} }
if (!(parentDir!!.contents as EntryDirectory).entries.contains(targetID)) { if (!parentDir.contains(targetID)) {
(parentDir.contents as EntryDirectory).entries.add(targetID) parentDir.add(targetID)
} }
} }
if (parentDir == null || parentDir.contents !is EntryDirectory) {
throw FileNotFoundException("No such parent directory")
}
// check if directory "parentID" has "targetID" in the first place // check if directory "parentID" has "targetID" in the first place
else if (!directoryContains(disk, parentID, targetID)) { if (!directoryContains(disk, parentID, targetID)) {
throw FileNotFoundException("No such file to delete") throw FileNotFoundException("No such file to delete")
} }
else if (targetID == 0) { else if (targetID == 0) {
throw IOException("Cannot delete root file system") throw IOException("Cannot delete root file system")
} }
else if (file.contents is EntryDirectory && file.contents.entries.size > 0) { else if (file.contents is EntryDirectory && file.contents.entryCount > 0) {
//throw IOException("Cannot delete directory that contains something")
deleteDirRecurse(disk, targetID) deleteDirRecurse(disk, targetID)
} }
else { else {
@@ -416,7 +403,7 @@ object VDUtil {
// delete file record // delete file record
disk.entries.remove(targetID) disk.entries.remove(targetID)
// unlist file from parent directly // unlist file from parent directly
(disk.entries[parentID]!!.contents as EntryDirectory).entries.remove(targetID) parentDir.remove(targetID)
} }
catch (e: Exception) { catch (e: Exception) {
rollback() rollback()
@@ -428,7 +415,7 @@ object VDUtil {
* Changes the name of the entry. * Changes the name of the entry.
*/ */
fun renameFile(disk: VirtualDisk, path: VDPath, newName: String, charset: Charset) { fun renameFile(disk: VirtualDisk, path: VDPath, newName: String, charset: Charset) {
val file = getFile(disk, path)?.file val file = getFile(disk, path)
if (file != null) { if (file != null) {
file.filename = newName.sanitisePath().toEntryName(DiskEntry.NAME_LENGTH, charset) file.filename = newName.sanitisePath().toEntryName(DiskEntry.NAME_LENGTH, charset)
@@ -451,23 +438,12 @@ object VDUtil {
} }
} }
/** /**
* Add file to the specified directory. ParentID of the file will be overwritten. * Add file to the specified directory.
* The file will get new EntryID and its ParentID will be overwritten.
*/ */
fun addFile(disk: VirtualDisk, parentPath: VDPath, file: DiskEntry) { fun addFile(disk: VirtualDisk, parentPath: VDPath, file: DiskEntry) {
disk.checkReadOnly() val targetDirID = getFile(disk, parentPath)!!.entryID
disk.checkCapacity(file.serialisedSize) return addFile(disk, targetDirID, file)
try {
val parentID = getFile(disk, parentPath)!!.file.entryID
// add record to the directory
getAsDirectory(disk, parentPath).entries.add(file.entryID)
// add entry on the disk
disk.entries[file.entryID] = file
file.parentEntryID = parentID
}
catch (e: KotlinNullPointerException) {
throw FileNotFoundException("No such directory")
}
} }
/** /**
* Add file to the specified directory. ParentID of the file will be overwritten. * Add file to the specified directory. ParentID of the file will be overwritten.
@@ -477,10 +453,13 @@ object VDUtil {
disk.checkCapacity(file.serialisedSize) disk.checkCapacity(file.serialisedSize)
try { try {
// generate new ID for the file
file.entryID = disk.generateUniqueID()
// add record to the directory // add record to the directory
getAsDirectory(disk, directoryID).entries.add(file.entryID) getAsDirectory(disk, directoryID).add(file.entryID)
// add entry on the disk // add entry on the disk
disk.entries[file.entryID] = file disk.entries[file.entryID] = file
// make this boy recognise his new parent
file.parentEntryID = directoryID file.parentEntryID = directoryID
} }
catch (e: KotlinNullPointerException) { catch (e: KotlinNullPointerException) {
@@ -491,28 +470,8 @@ object VDUtil {
* Add subdirectory to the specified directory. * Add subdirectory to the specified directory.
*/ */
fun addDir(disk: VirtualDisk, parentPath: VDPath, name: ByteArray) { fun addDir(disk: VirtualDisk, parentPath: VDPath, name: ByteArray) {
disk.checkReadOnly() val parentID = getFile(disk, parentPath)!!.entryID
disk.checkCapacity(EntryDirectory.NEW_ENTRY_SIZE) return addDir(disk, parentID, name)
val newID = disk.generateUniqueID()
try {
val parentID = getFile(disk, parentPath)!!.file.entryID
// add record to the directory
getAsDirectory(disk, parentPath).entries.add(newID)
// add entry on the disk
disk.entries[newID] = DiskEntry(
newID,
parentID,
name,
currentUnixtime,
currentUnixtime,
EntryDirectory()
)
}
catch (e: KotlinNullPointerException) {
throw FileNotFoundException("No such directory")
}
} }
/** /**
* Add file to the specified directory. * Add file to the specified directory.
@@ -525,7 +484,7 @@ object VDUtil {
try { try {
// add record to the directory // add record to the directory
getAsDirectory(disk, directoryID).entries.add(newID) getAsDirectory(disk, directoryID).add(newID)
// add entry on the disk // add entry on the disk
disk.entries[newID] = DiskEntry( disk.entries[newID] = DiskEntry(
newID, newID,
@@ -553,7 +512,7 @@ object VDUtil {
} }
// recurse // recurse
else { else {
entry.contents.entries.forEach { entry.contents.forEach {
entriesToDelete.add(entry.entryID) entriesToDelete.add(entry.entryID)
recurse1(disk.entries[it]) recurse1(disk.entries[it])
} }
@@ -562,7 +521,7 @@ object VDUtil {
val entry = disk.entries[directoryID] val entry = disk.entries[directoryID]
if (entry != null && entry.contents is EntryDirectory) { if (entry != null && entry.contents is EntryDirectory) {
entry.contents.entries.forEach { entry.contents.forEach {
entriesToDelete.add(directoryID) entriesToDelete.add(directoryID)
recurse1(disk.entries[it]) recurse1(disk.entries[it])
} }
@@ -630,12 +589,10 @@ object VDUtil {
fun moveFile(disk1: VirtualDisk, fromPath: VDPath, disk2: VirtualDisk, toPath: VDPath) { fun moveFile(disk1: VirtualDisk, fromPath: VDPath, disk2: VirtualDisk, toPath: VDPath) {
val file = getFile(disk1, fromPath) val file = getFile(disk1, fromPath)
if (file != null) { // checking readOnly is redundant here
if (file.file.contents is EntryDirectory) {
throw IOException("Cannot move directory")
}
disk2.checkCapacity(file.file.contents.getSizeEntry()) if (file != null) {
disk2.checkCapacity(file.contents.getSizeEntry())
try { try {
deleteFile(disk2, toPath) deleteFile(disk2, toPath)
@@ -644,11 +601,11 @@ object VDUtil {
deleteFile(disk1, fromPath) // any uncaught no_from_file will be caught here deleteFile(disk1, fromPath) // any uncaught no_from_file will be caught here
try { try {
addFile(disk2, toPath.getParent(), file.file) addFile(disk2, toPath.getParent(), file)
} }
catch (e: FileNotFoundException) { catch (e: FileNotFoundException) {
// roll back delete on disk1 // roll back delete on disk1
addFile(disk1, file.parent.entryID, file.file) addFile(disk1, file.parentEntryID, file)
throw FileNotFoundException("No such destination") throw FileNotFoundException("No such destination")
} }
} }
@@ -740,7 +697,6 @@ object VDUtil {
val path1 = this.replace('\\', '/') val path1 = this.replace('\\', '/')
return path1 return path1
} }
data class EntrySearchResult(val file: DiskEntry, val parent: DiskEntry)
fun resolveIfSymlink(disk: VirtualDisk, indexNumber: EntryID, recurse: Boolean = false): DiskEntry { fun resolveIfSymlink(disk: VirtualDisk, indexNumber: EntryID, recurse: Boolean = false): DiskEntry {
var entry: DiskEntry? = disk.entries[indexNumber] var entry: DiskEntry? = disk.entries[indexNumber]
@@ -769,7 +725,7 @@ object VDUtil {
throw FileNotFoundException("Not a directory") throw FileNotFoundException("Not a directory")
} }
else { else {
return dir.contents.entries.contains(targetID) return dir.contents.contains(targetID)
} }
} }
@@ -784,11 +740,16 @@ object VDUtil {
return disk.entries.filter { disk.entries[it.value.parentEntryID] == null }.keys.toList() return disk.entries.filter { disk.entries[it.value.parentEntryID] == null }.keys.toList()
} }
/**
* Searches for null-pointing entries (phantoms) within every directory.
*
* @return List of search results, which is Pair(directory that contains null pointer, null pointer)
*/
fun gcSearchPhantomBaby(disk: VirtualDisk): List<Pair<EntryID, EntryID>> { fun gcSearchPhantomBaby(disk: VirtualDisk): List<Pair<EntryID, EntryID>> {
// Pair<DirectoryID, ID of phantom in the directory> // Pair<DirectoryID, ID of phantom in the directory>
val phantoms = ArrayList<Pair<EntryID, EntryID>>() val phantoms = ArrayList<Pair<EntryID, EntryID>>()
disk.entries.filter { it.value.contents is EntryDirectory }.values.forEach { directory -> disk.entries.filter { it.value.contents is EntryDirectory }.values.forEach { directory ->
(directory.contents as EntryDirectory).entries.forEach { dirEntryID -> (directory.contents as EntryDirectory).forEach { dirEntryID ->
if (disk.entries[dirEntryID] == null) { if (disk.entries[dirEntryID] == null) {
phantoms.add(Pair(directory.entryID, dirEntryID)) phantoms.add(Pair(directory.entryID, dirEntryID))
} }
@@ -811,7 +772,7 @@ object VDUtil {
fun gcDumpAll(disk: VirtualDisk) { fun gcDumpAll(disk: VirtualDisk) {
try { try {
gcSearchPhantomBaby(disk).forEach { gcSearchPhantomBaby(disk).forEach {
getAsDirectory(disk, it.first).entries.remove(it.second) getAsDirectory(disk, it.first).remove(it.second)
} }
gcSearchOrphan(disk).forEach { gcSearchOrphan(disk).forEach {
disk.entries.remove(it) disk.entries.remove(it)

View File

@@ -1,5 +1,6 @@
package net.torvald.terrarum.virtualcomputer.tvd package net.torvald.terrarum.virtualcomputer.tvd
import java.io.IOException
import java.nio.charset.Charset import java.nio.charset.Charset
import java.util.* import java.util.*
import java.util.function.Consumer import java.util.function.Consumer
@@ -182,10 +183,30 @@ class EntryFile(var bytes: ByteArray64) : DiskEntryContent {
return buffer return buffer
} }
} }
class EntryDirectory(val entries: ArrayList<EntryID> = ArrayList<EntryID>()) : DiskEntryContent { class EntryDirectory(private val entries: ArrayList<EntryID> = ArrayList<EntryID>()) : DiskEntryContent {
override fun getSizePure() = entries.size * 4L override fun getSizePure() = entries.size * 4L
override fun getSizeEntry() = getSizePure() + 2 override fun getSizeEntry() = getSizePure() + 2
private fun checkCapacity(toAdd: Int = 1) {
if (entries.size + toAdd > 65535)
throw IOException("Directory entries limit exceeded.")
}
fun add(entryID: EntryID) {
checkCapacity()
entries.add(entryID)
}
fun remove(entryID: EntryID) {
entries.removeAt(entryID)
}
fun contains(entryID: EntryID) = entries.contains(entryID)
fun forEach(consumer: (EntryID) -> Unit) = entries.forEach(consumer)
val entryCount: Int
get() = entries.size
override fun serialize(): AppendableByteBuffer { override fun serialize(): AppendableByteBuffer {
val buffer = AppendableByteBuffer(getSizeEntry()) val buffer = AppendableByteBuffer(getSizeEntry())

Binary file not shown.

Binary file not shown.

Binary file not shown.