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|
|-----|-----------|
|0..4095|Tiles|
|4096..32767|Items (static)|
|4096..8191|Walls|
|8192..8447|Wires|
|8448..32767|Items (static)|
|32768..1048575|Items (dynamic\*)|
|1048576..0x7FFF_FFFF|Actors|
|0x8000_0000..0xFFFF_FFFF (all negative numbers)|Faction|

View File

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

View File

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

View File

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

View File

@@ -3,6 +3,7 @@ package net.torvald.terrarum
import com.google.gson.JsonPrimitive
import java.util.*
import java.util.function.Consumer
import kotlin.collections.HashMap
typealias ActorValue = KVHashMap
typealias ItemValue = KVHashMap
@@ -13,7 +14,15 @@ typealias GameConfig = 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.
@@ -108,4 +117,9 @@ class KVHashMap {
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 baseMass: Double = 12.0
override var baseToolSize: Double? = 8.0
override var category: String = InventoryItem.Category.TOOL
override var maxDurability: Double = 10.0
override var durability: Double = 6.43
override var inventoryCategory: String = InventoryItem.Category.TOOL
override var maxDurability: Int = 143
override var durability: Float = 64f
override var consumable = false
})
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 baseMass: Double = 1.4
override var baseToolSize: Double? = null
override var category: String = InventoryItem.Category.MISC
override var inventoryCategory: String = InventoryItem.Category.MISC
override var consumable = false
})

View File

@@ -106,7 +106,7 @@ class UIItemInventoryElem(
g.lineWidth = 3f
g.drawLine(barOffset, posY + durabilityBarOffY, barOffset + barFullLen, posY + durabilityBarOffY)
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) =
try {
Terrarum.ingame!!.theGameHasActor(value) ||
value < ItemCodex.ITEM_COUNT_MAX ||
value < ItemCodex.ACTOR_ID_MIN ||
value < when (renderOrder) {
RenderOrder.BEHIND -> ItemCodex.ITEM_COUNT_MAX
RenderOrder.BEHIND -> ItemCodex.ACTOR_ID_MIN
RenderOrder.MIDDLE -> 0x10000000
RenderOrder.MIDTOP -> 0x60000000
RenderOrder.FRONT -> 0x70000000

View File

@@ -138,7 +138,7 @@ open class ActorHumanoid(birth: GameDate, death: GameDate? = null)
override val isUnique: Boolean = false
override var baseMass: Double = 0.0
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 var consumable = false
}

View File

@@ -126,7 +126,9 @@ class ActorInventory(val actor: Pocketed, var maxCapacity: Int, var capacityMode
remove(item, 1)
}
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) {
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.")
isVisible = true

View File

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

View File

@@ -1,9 +1,12 @@
package net.torvald.terrarum.gameactors
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.gameactors.ActorHumanoid
import net.torvald.terrarum.gameactors.ai.AILuaAPI
import net.torvald.terrarum.gameactors.ai.ActorAI
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 org.luaj.vm2.*
import org.luaj.vm2.compiler.LuaC
@@ -51,13 +54,22 @@ open class HumanoidNPC(
set(value) {
actorValue[AVKey.SCALE] = value
}
override var category = "npc"
override var inventoryCategory = "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 {
try {
// 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
// TODO place this Actor to the world
}
}
}

View File

@@ -15,8 +15,6 @@ import java.security.SecureRandom
*/
object InjectCreatureRaw {
// FIXME strength not injected properly?
const val JSONPATH = "./assets/raw/creatures/"
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.LUMINOSITY] = Color(0x434aff).to10bit()
//p.actorValue[AVKey.LUMINOSITY] = 214127943 // bright purple
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["__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)
@@ -85,6 +86,7 @@ object PlayerBuilderSigrid {
Tile.STONE_QUARRIED, Tile.STONE_TILE_WHITE, Tile.TORCH
)
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.gameitem.InventoryItem
import net.torvald.terrarum.itemproperties.ItemCodex
import java.util.*
/**
* Created by minjaesong on 16-01-15.
@@ -17,10 +16,13 @@ interface Pocketed {
*/
fun unequipItem(item: InventoryItem) {
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))
throw Error("Unequipping the item that does not exist in inventory")
if (!inventory.contains(item)) {
//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
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()
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 liquidity = tile.isFluid.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)
: 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()
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 UUID = "uuid" // some items need UUID to be stored
const val BASE_WEAPON_POWER = "baseweaponpower"
const val BASE_PICK_POWER = "basepickpower"
object ItemType {
const val BLOCK = "tile"

View File

@@ -10,17 +10,12 @@ import org.newdawn.slick.GameContainer
/**
* Created by minjaesong on 16-01-16.
*/
abstract class InventoryItem : Comparable<InventoryItem> {
/**
* 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 class InventoryItem : Comparable<InventoryItem>, Cloneable {
abstract val id: Int
/**
*
* e.g. Key Items (in a Pokémon sense), floppies
*/
abstract val isUnique: Boolean
@@ -47,11 +42,11 @@ abstract class InventoryItem : Comparable<InventoryItem> {
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()
/** Single-use then destroyed (e.g. Tiles) */
/** Single-use then destroyed (e.g. Tiles), aka negation of "stackable" */
abstract var consumable: Boolean
/**
@@ -94,9 +89,12 @@ abstract class InventoryItem : Comparable<InventoryItem> {
/**
* 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
@@ -115,6 +113,9 @@ abstract class InventoryItem : Comparable<InventoryItem> {
* @return true when used successfully, false otherwise
*
* 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
@@ -212,4 +213,12 @@ abstract class InventoryItem : Comparable<InventoryItem> {
const val WALL = "wall"
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
import net.torvald.terrarum.realestate.RealEstateUtility
import org.dyn4j.geometry.Vector2
import org.newdawn.slick.SlickException
class GameWorld
/**
* @param width
* *
* @param height
* *
* @throws SlickException
*/
@Throws(SlickException::class)
constructor(//properties
val width: Int, val height: Int) {
typealias TileAddress = Long
typealias TileDamage = Int
class GameWorld(val width: Int, val height: Int) {
//layers
val layerWall: MapLayer
/**
* Get MapLayer object of terrain
* @return MapLayer terrain layer
*/
val layerTerrain: MapLayer
val layerWire: MapLayer
val wallDamage: PairedMapLayer
val terrainDamage: PairedMapLayer
val layerWallLowBits: PairedMapLayer
val layerTerrainLowBits: PairedMapLayer
val spawnX: 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) );
//physics
/** 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)
layerWall = MapLayer(width, height)
layerWire = MapLayer(width, height)
terrainDamage = PairedMapLayer(width, height)
wallDamage = PairedMapLayer(width, height)
layerTerrainLowBits = PairedMapLayer(width, height)
layerWallLowBits = PairedMapLayer(width, height)
time = WorldTime(
71 * WorldTime.DAY_LENGTH +
@@ -87,11 +82,11 @@ constructor(//properties
* @return byte[][] damage code pair
*/
val damageDataArray: Array<ByteArray>
get() = terrainDamage.dataPair
get() = layerTerrainLowBits.dataPair
fun getTileFromWall(x: Int, y: Int): Int? {
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)
null
else
@@ -100,7 +95,7 @@ constructor(//properties
fun getTileFromTerrain(x: Int, y: Int): Int? {
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)
null
else
@@ -111,12 +106,12 @@ constructor(//properties
return layerWire.getTile(x fmod width, y)
}
fun getWallDamage(x: Int, y: Int): Int? {
return wallDamage.getData(x fmod width, y)
fun getWallLowBits(x: Int, y: Int): Int? {
return layerWallLowBits.getData(x fmod width, y)
}
fun getTerrainDamage(x: Int, y: Int): Int? {
return terrainDamage.getData(x fmod width, y)
fun getTerrainLowBits(x: Int, y: Int): Int? {
return layerTerrainLowBits.getData(x fmod width, y)
}
/**
@@ -145,12 +140,12 @@ constructor(//properties
fun setTileWall(x: Int, y: Int, tile: Byte, damage: Int) {
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) {
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) {
@@ -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 {
@Transient val WALL = 0
@@ -224,7 +245,7 @@ constructor(//properties
@Transient val TILES_SUPPORTED = MapLayer.RANGE * PairedMapLayer.RANGE
@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.terrarum.Terrarum
import net.torvald.terrarum.gameactors.AnyPlayer
import net.torvald.terrarum.gameactors.HistoricalFigure
import net.torvald.terrarum.gameactors.Player
import net.torvald.terrarum.gameactors.roundInt
@@ -40,8 +41,7 @@ object WorldSimulator {
private val world = Terrarum.ingame!!.world
// TODO future Kotlin feature -- typealias AnyPlayer: HistoricalFigure
operator fun invoke(p: HistoricalFigure?, delta: Int) {
operator fun invoke(p: AnyPlayer?, delta: Int) {
if (p != null) {
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()

View File

@@ -98,8 +98,6 @@ class WorldTime(initTime: Long = 0L) {
)
val DAY_NAMES_SHORT = arrayOf("Mon", "Tys", "Mid", "Tor", "Fre", "Lau", "Sun", "Ver")
// FIXME Next to Granite is Felsite
val MONTH_NAMES = arrayOf(
"Opal", "Obsidian", "Granite", "Slate", "Felsite", "Hematite",
"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.
*/
@Deprecated("Are you even reading the name?")
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 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.gameworld.GameWorld
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 org.newdawn.slick.GameContainer
import org.newdawn.slick.Image
@@ -28,31 +30,35 @@ object ItemCodex {
private val itemCodex = HashMap<Int, InventoryItem>()
private val dynamicItemDescription = HashMap<Int, KVHashMap>()
val ITEM_TILE_MAX = GameWorld.TILES_SUPPORTED - 1 // 4095
val ITEM_COUNT_MAX = 1048576
val ITEM_DYNAMIC_MAX = ITEM_COUNT_MAX - 1
val ITEM_STATIC_MAX = 32767
val ITEM_DYNAMIC_MIN = ITEM_STATIC_MAX + 1
val ITEM_STATIC_MIN = ITEM_TILE_MAX + 1 // 4096
val ITEM_TILES = 0..GameWorld.TILES_SUPPORTED - 1
val ITEM_WALLS = GameWorld.TILES_SUPPORTED..GameWorld.TILES_SUPPORTED * 2 - 1
val ITEM_WIRES = GameWorld.TILES_SUPPORTED * 2..GameWorld.TILES_SUPPORTED * 2 + 255
val ITEM_STATIC = ITEM_WIRES.endInclusive + 1..32767
val ITEM_DYNAMIC = 32768..1048575
val ACTOR_ID_MIN = ITEM_DYNAMIC.endInclusive + 1
private val itemImagePlaceholder = Image("./assets/item_kari_24.tga")
init {
// 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() {
override val id: Int = i
override val isUnique: Boolean = false
override var baseMass: Double = TileCodex[i].density / 1000.0
override var baseToolSize: Double? = null
override var equipPosition = EquipPosition.HAND_GRIP
override var category = "block"
override val originalName = TileCodex[i].nameKey
override val originalName = TileCodex[i % ITEM_WALLS.first].nameKey
override var consumable = true
override var inventoryCategory = Category.BLOCK
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 {
@@ -74,26 +80,81 @@ object ItemCodex {
// filter passed, do the job
// FIXME this is only useful for Player
if (i in ITEM_TILES) {
Terrarum.ingame!!.world.setTileTerrain(
gc.mouseTileX,
gc.mouseTileY,
i
)
}
else {
Terrarum.ingame!!.world.setTileWall(
gc.mouseTileX,
gc.mouseTileY,
i
)
}
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
}
operator fun get(code: Int): InventoryItem {
if (code < ITEM_STATIC_MAX) // generic item
return itemCodex[code]!! // from CSV
else if (code < ITEM_DYNAMIC_MAX) {
if (code <= ITEM_STATIC.endInclusive) // generic item
return itemCodex[code]!!.clone() // from CSV
else if (code <= ITEM_DYNAMIC.endInclusive) {
TODO("read from dynamicitem description (JSON)")
}
else {
@@ -105,8 +166,15 @@ object ItemCodex {
}
fun getItemImage(code: Int): Image {
if (code <= ITEM_TILE_MAX)
if (code <= ITEM_TILES.endInclusive)
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
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 {
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}"]

View File

@@ -401,10 +401,10 @@ object TilesDrawer {
*/
fun getNearbyTilesInfo(x: Int, y: Int, mode: Int, mark: Int?): Int {
val nearbyTiles = IntArray(4)
nearbyTiles[NEARBY_TILE_KEY_LEFT] = world.getTileFrom(mode, x - 1, y) ?: 4096
nearbyTiles[NEARBY_TILE_KEY_RIGHT] = 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) ?: Tile.NULL
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
var ret = 0
@@ -419,10 +419,10 @@ object TilesDrawer {
fun getNearbyTilesInfoNonSolid(x: Int, y: Int, mode: Int): Int {
val nearbyTiles = IntArray(4)
nearbyTiles[NEARBY_TILE_KEY_LEFT] = world.getTileFrom(mode, x - 1, y) ?: 4096
nearbyTiles[NEARBY_TILE_KEY_RIGHT] = 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) ?: Tile.NULL
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
var ret = 0
@@ -443,10 +443,10 @@ object TilesDrawer {
fun getNearbyTilesInfoWallSticker(x: Int, y: Int): Int {
val nearbyTiles = IntArray(4)
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_RIGHT] = world.getTileFrom(TERRAIN, x + 1, y) ?: 4096
nearbyTiles[NEARBY_TILE_KEY_DOWN] = world.getTileFrom(TERRAIN, x , y + 1) ?: 4096
nearbyTiles[NEARBY_TILE_KEY_BACK] = world.getTileFrom(WALL, x , 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) ?: Tile.NULL
nearbyTiles[NEARBY_TILE_KEY_DOWN] = world.getTileFrom(TERRAIN, x , y + 1) ?: Tile.NULL
nearbyTiles[NEARBY_TILE_KEY_BACK] = world.getTileFrom(WALL, x , y) ?: Tile.NULL
try {
if (TileCodex[nearbyTiles[NEARBY_TILE_KEY_DOWN]].isSolid)
@@ -476,8 +476,8 @@ object TilesDrawer {
fun getNearbyTilesInfoPlatform(x: Int, y: Int): Int {
val nearbyTiles = IntArray(4)
nearbyTiles[NEARBY_TILE_KEY_LEFT] = world.getTileFrom(TERRAIN, x - 1, y) ?: 4096
nearbyTiles[NEARBY_TILE_KEY_RIGHT] = 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) ?: Tile.NULL
if ((TileCodex[nearbyTiles[NEARBY_TILE_KEY_LEFT]].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.gameactors.faction.FactionCodex
import net.torvald.terrarum.gameworld.TileAddress
/**
* Created by minjaesong on 16-03-27.
*/
object RealEstateUtility {
fun getAbsoluteTileNumber(x: Int, y: Int): Long =
fun getAbsoluteTileNumber(x: Int, y: Int): TileAddress =
(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())
/**
* Get owner ID as an Actor/Faction
*/
fun resolveOwner(id: Long): Any =
fun resolveOwner(id: TileAddress): Any =
if (id < 0x80000000L)
Terrarum.ingame!!.getActorByID(id.toInt())
else

View File

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

View File

@@ -1,6 +1,9 @@
package net.torvald.terrarum.ui
import net.torvald.point.Point2d
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.Graphics
import org.newdawn.slick.Input
@@ -57,21 +60,86 @@ interface UICanvas {
fun doOpeningFade(handler: UIHandler?, openCloseTime: Int) {
handler!!.opacity = handler.openCloseCounter.toFloat() / openCloseTime
}
fun doClosingFade(handler: UIHandler?, openCloseTime: Int) {
handler!!.opacity = (openCloseTime - handler.openCloseCounter.toFloat()) / openCloseTime
}
fun endOpeningFade(handler: UIHandler?) {
handler!!.opacity = 1f
}
fun endClosingFade(handler: UIHandler?) {
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)
enum class Position {
LEFT, RIGHT, TOP, BOTTOM
}
}
}

View File

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

View File

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

View File

@@ -243,7 +243,7 @@ object VDUtil {
throw IllegalArgumentException("The entry is not directory")
val entriesList = ArrayList<DiskEntry>()
dirToSearch.contents.entries.forEach {
dirToSearch.contents.forEach {
val entry = disk.entries[it]
if (entry != null) entriesList.add(entry)
}
@@ -267,7 +267,7 @@ object VDUtil {
* Search a entry using path
* @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>()
fun getCurrentEntry(): DiskEntry = searchHierarchy.last()
//var currentDirectory = disk.root
@@ -276,10 +276,7 @@ object VDUtil {
// path of root
if (path.hierarchy.size == 0) {
return EntrySearchResult(
disk.entries[0]!!,
disk.entries[0]!!
)
return disk.entries[0]!!
}
try {
@@ -310,10 +307,7 @@ object VDUtil {
}
// file found
return EntrySearchResult(
searchHierarchy[searchHierarchy.lastIndex],
searchHierarchy[searchHierarchy.lastIndex - 1]
)
return searchHierarchy[searchHierarchy.lastIndex]
}
/**
@@ -347,7 +341,7 @@ object VDUtil {
* Search for the file and returns a instance of normal file.
*/
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.
*/
@@ -357,7 +351,7 @@ object VDUtil {
* Search for the file and returns a instance of directory.
*/
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.
*/
@@ -367,11 +361,8 @@ object VDUtil {
* Deletes file on the disk safely.
*/
fun deleteFile(disk: VirtualDisk, path: VDPath) {
disk.checkReadOnly()
val fileSearchResult = getFile(disk, path)!!
return deleteFile(disk, fileSearchResult.file.entryID)
return deleteFile(disk, fileSearchResult.entryID)
}
/**
* Deletes file on the disk safely.
@@ -386,29 +377,25 @@ object VDUtil {
}
val parentID = file.parentEntryID
val parentDir = disk.entries[parentID]
val parentDir = getAsDirectory(disk, parentID)
fun rollback() {
if (!disk.entries.contains(targetID)) {
disk.entries[targetID] = file
}
if (!(parentDir!!.contents as EntryDirectory).entries.contains(targetID)) {
(parentDir.contents as EntryDirectory).entries.add(targetID)
if (!parentDir.contains(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
else if (!directoryContains(disk, parentID, targetID)) {
if (!directoryContains(disk, parentID, targetID)) {
throw FileNotFoundException("No such file to delete")
}
else if (targetID == 0) {
throw IOException("Cannot delete root file system")
}
else if (file.contents is EntryDirectory && file.contents.entries.size > 0) {
//throw IOException("Cannot delete directory that contains something")
else if (file.contents is EntryDirectory && file.contents.entryCount > 0) {
deleteDirRecurse(disk, targetID)
}
else {
@@ -416,7 +403,7 @@ object VDUtil {
// delete file record
disk.entries.remove(targetID)
// unlist file from parent directly
(disk.entries[parentID]!!.contents as EntryDirectory).entries.remove(targetID)
parentDir.remove(targetID)
}
catch (e: Exception) {
rollback()
@@ -428,7 +415,7 @@ object VDUtil {
* Changes the name of the entry.
*/
fun renameFile(disk: VirtualDisk, path: VDPath, newName: String, charset: Charset) {
val file = getFile(disk, path)?.file
val file = getFile(disk, path)
if (file != null) {
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) {
disk.checkReadOnly()
disk.checkCapacity(file.serialisedSize)
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")
}
val targetDirID = getFile(disk, parentPath)!!.entryID
return addFile(disk, targetDirID, file)
}
/**
* Add file to the specified directory. ParentID of the file will be overwritten.
@@ -477,10 +453,13 @@ object VDUtil {
disk.checkCapacity(file.serialisedSize)
try {
// generate new ID for the file
file.entryID = disk.generateUniqueID()
// add record to the directory
getAsDirectory(disk, directoryID).entries.add(file.entryID)
getAsDirectory(disk, directoryID).add(file.entryID)
// add entry on the disk
disk.entries[file.entryID] = file
// make this boy recognise his new parent
file.parentEntryID = directoryID
}
catch (e: KotlinNullPointerException) {
@@ -491,28 +470,8 @@ object VDUtil {
* Add subdirectory to the specified directory.
*/
fun addDir(disk: VirtualDisk, parentPath: VDPath, name: ByteArray) {
disk.checkReadOnly()
disk.checkCapacity(EntryDirectory.NEW_ENTRY_SIZE)
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")
}
val parentID = getFile(disk, parentPath)!!.entryID
return addDir(disk, parentID, name)
}
/**
* Add file to the specified directory.
@@ -525,7 +484,7 @@ object VDUtil {
try {
// add record to the directory
getAsDirectory(disk, directoryID).entries.add(newID)
getAsDirectory(disk, directoryID).add(newID)
// add entry on the disk
disk.entries[newID] = DiskEntry(
newID,
@@ -553,7 +512,7 @@ object VDUtil {
}
// recurse
else {
entry.contents.entries.forEach {
entry.contents.forEach {
entriesToDelete.add(entry.entryID)
recurse1(disk.entries[it])
}
@@ -562,7 +521,7 @@ object VDUtil {
val entry = disk.entries[directoryID]
if (entry != null && entry.contents is EntryDirectory) {
entry.contents.entries.forEach {
entry.contents.forEach {
entriesToDelete.add(directoryID)
recurse1(disk.entries[it])
}
@@ -630,12 +589,10 @@ object VDUtil {
fun moveFile(disk1: VirtualDisk, fromPath: VDPath, disk2: VirtualDisk, toPath: VDPath) {
val file = getFile(disk1, fromPath)
if (file != null) {
if (file.file.contents is EntryDirectory) {
throw IOException("Cannot move directory")
}
// checking readOnly is redundant here
disk2.checkCapacity(file.file.contents.getSizeEntry())
if (file != null) {
disk2.checkCapacity(file.contents.getSizeEntry())
try {
deleteFile(disk2, toPath)
@@ -644,11 +601,11 @@ object VDUtil {
deleteFile(disk1, fromPath) // any uncaught no_from_file will be caught here
try {
addFile(disk2, toPath.getParent(), file.file)
addFile(disk2, toPath.getParent(), file)
}
catch (e: FileNotFoundException) {
// roll back delete on disk1
addFile(disk1, file.parent.entryID, file.file)
addFile(disk1, file.parentEntryID, file)
throw FileNotFoundException("No such destination")
}
}
@@ -740,7 +697,6 @@ object VDUtil {
val path1 = this.replace('\\', '/')
return path1
}
data class EntrySearchResult(val file: DiskEntry, val parent: DiskEntry)
fun resolveIfSymlink(disk: VirtualDisk, indexNumber: EntryID, recurse: Boolean = false): DiskEntry {
var entry: DiskEntry? = disk.entries[indexNumber]
@@ -769,7 +725,7 @@ object VDUtil {
throw FileNotFoundException("Not a directory")
}
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()
}
/**
* 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>> {
// Pair<DirectoryID, ID of phantom in the directory>
val phantoms = ArrayList<Pair<EntryID, EntryID>>()
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) {
phantoms.add(Pair(directory.entryID, dirEntryID))
}
@@ -811,7 +772,7 @@ object VDUtil {
fun gcDumpAll(disk: VirtualDisk) {
try {
gcSearchPhantomBaby(disk).forEach {
getAsDirectory(disk, it.first).entries.remove(it.second)
getAsDirectory(disk, it.first).remove(it.second)
}
gcSearchOrphan(disk).forEach {
disk.entries.remove(it)

View File

@@ -1,5 +1,6 @@
package net.torvald.terrarum.virtualcomputer.tvd
import java.io.IOException
import java.nio.charset.Charset
import java.util.*
import java.util.function.Consumer
@@ -182,10 +183,30 @@ class EntryFile(var bytes: ByteArray64) : DiskEntryContent {
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 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 {
val buffer = AppendableByteBuffer(getSizeEntry())

Binary file not shown.

Binary file not shown.

Binary file not shown.