From 5fe604cf45bf50e835cc645362e69eaec1b9256a Mon Sep 17 00:00:00 2001 From: Song Minjae Date: Tue, 18 Apr 2017 01:14:25 +0900 Subject: [PATCH] pickaxe working as intended --- .../modules/basegame/{ => tiles}/tileprop.csv | 2 +- src/net/torvald/CSVFetcher.kt | 6 +- src/net/torvald/colourutil/ColourTemp.kt | 4 +- src/net/torvald/serialise/WriteMeta.kt | 18 +-- src/net/torvald/terrarum/KVHashMap.kt | 37 ++---- .../terrarum/{ModuleManager.kt => ModMgr.kt} | 4 +- src/net/torvald/terrarum/StateInGame.kt | 2 +- src/net/torvald/terrarum/StateUITest.kt | 4 +- src/net/torvald/terrarum/Terrarum.kt | 2 +- .../torvald/terrarum/UIItemInventoryElem.kt | 2 +- src/net/torvald/terrarum/gameactors/AVKey.kt | 10 ++ .../terrarum/gameactors/ActorHumanoid.kt | 6 +- .../terrarum/gameactors/ActorInventory.kt | 2 +- .../terrarum/gameactors/ActorWithSprite.kt | 1 + .../terrarum/gameactors/CanBeAnItem.kt | 2 +- .../terrarum/gameactors/CreatureBuilder.kt | 3 + .../terrarum/gameactors/DroppedItem.kt | 2 +- .../terrarum/gameactors/HumanoidNPC.kt | 2 +- .../terrarum/gameactors/InjectCreatureRaw.kt | 4 +- .../terrarum/gameactors/PlayerBuilder.kt | 2 + .../gameactors/PlayerBuilderCynthia.kt | 6 +- .../gameactors/PlayerBuilderSigrid.kt | 9 +- .../gameactors/PlayerBuilderTestSubject1.kt | 6 +- .../torvald/terrarum/gameactors/Pocketed.kt | 2 +- .../gameactors/faction/FactionFactory.kt | 4 +- .../terrarum/gamecontroller/GameController.kt | 18 ++- .../torvald/terrarum/gameitem/DynamicItem.kt | 67 ----------- .../torvald/terrarum/gameworld/GameWorld.kt | 55 +++++++-- .../terrarum/itemproperties/Calculate.kt | 26 ++++ .../{gameitem => itemproperties}/IVKey.kt | 2 +- .../InventoryItem.kt | 7 +- .../terrarum/itemproperties/ItemCodex.kt | 35 ++++-- .../itemproperties/ItemEffectsLuaAPI.kt | 4 +- .../terrarum/itemproperties/Material.kt | 4 +- .../torvald/terrarum/mapdrawer/TilesDrawer.kt | 113 ++++++++++-------- .../torvald/terrarum/tileproperties/Tile.kt | 2 +- .../terrarum/tileproperties/TileCodex.kt | 15 ++- .../terrarum/tileproperties/TilePropCSV.kt | 2 +- src/net/torvald/terrarum/ui/UIInventory.kt | 2 +- .../torvald/terrarum/weather/WeatherMixer.kt | 8 +- work_files/Pickaxe Power.xlsx | Bin 40456 -> 40885 bytes 41 files changed, 271 insertions(+), 231 deletions(-) rename assets/modules/basegame/{ => tiles}/tileprop.csv (99%) rename src/net/torvald/terrarum/{ModuleManager.kt => ModMgr.kt} (96%) delete mode 100644 src/net/torvald/terrarum/gameitem/DynamicItem.kt create mode 100644 src/net/torvald/terrarum/itemproperties/Calculate.kt rename src/net/torvald/terrarum/{gameitem => itemproperties}/IVKey.kt (93%) rename src/net/torvald/terrarum/{gameitem => itemproperties}/InventoryItem.kt (96%) diff --git a/assets/modules/basegame/tileprop.csv b/assets/modules/basegame/tiles/tileprop.csv similarity index 99% rename from assets/modules/basegame/tileprop.csv rename to assets/modules/basegame/tiles/tileprop.csv index 7850332f4..02297c4cd 100644 --- a/assets/modules/basegame/tileprop.csv +++ b/assets/modules/basegame/tiles/tileprop.csv @@ -1,4 +1,4 @@ - "id";"dmg";"name" ; "opacity";"strength";"dsty";"mate";"fluid";"solid";"wall"; "lumcolor";"drop";"ddmg";"fall";"dlfn";"vscs";"fv";"friction" + "id";"sid";"name" ; "opacity";"strength";"dsty";"mate";"fluid";"solid";"wall"; "lumcolor";"drop";"ddmg";"fall";"dlfn";"vscs";"fv";"friction" "0"; "0";"TILE_AIR" ; "8396808"; "1"; "1";"null"; "0"; "0"; "0"; "0"; "0"; "0"; "0"; "0"; "N/A"; "0";"4" "1"; "0";"TILE_STONE" ; "33587232"; "48";"2400";"rock"; "0"; "1"; "1"; "0"; "1"; "0"; "0"; "0"; "N/A"; "0";"16" "1"; "1";"TILE_STONE_QUARRIED" ; "33587232"; "48";"2400";"rock"; "0"; "1"; "1"; "0"; "1"; "1"; "0"; "0"; "N/A"; "0";"16" diff --git a/src/net/torvald/CSVFetcher.kt b/src/net/torvald/CSVFetcher.kt index 61f8d34d3..4cb839e12 100644 --- a/src/net/torvald/CSVFetcher.kt +++ b/src/net/torvald/CSVFetcher.kt @@ -1,5 +1,6 @@ package net.torvald +import net.torvald.terrarum.ModMgr import org.apache.commons.csv.CSVFormat import org.apache.commons.csv.CSVParser import org.apache.commons.csv.CSVRecord @@ -16,8 +17,7 @@ object CSVFetcher { private var csvString: StringBuffer? = null - @Throws(IOException::class) - operator fun invoke(csvFilePath: String): List { + fun readFromFile(csvFilePath: String): List { csvString = StringBuffer() // reset buffer every time it called readCSVasString(csvFilePath) @@ -40,6 +40,8 @@ object CSVFetcher { return csvRecordList } + fun readFromModule(module: String, path: String) = readFromFile(ModMgr.getPath(module, path)) + fun readFromString(csv: String): List { val csvParser = CSVParser.parse( csv, diff --git a/src/net/torvald/colourutil/ColourTemp.kt b/src/net/torvald/colourutil/ColourTemp.kt index 299aa7fac..e97bc9a55 100644 --- a/src/net/torvald/colourutil/ColourTemp.kt +++ b/src/net/torvald/colourutil/ColourTemp.kt @@ -5,14 +5,14 @@ import net.torvald.terrarum.weather.toColor import org.newdawn.slick.Color import org.newdawn.slick.Image import net.torvald.colourutil.CIEXYZUtil.toColor -import net.torvald.terrarum.ModuleManager +import net.torvald.terrarum.ModMgr /** * RGB-modeled CCT calculator * Created by minjaesong on 16-07-26. */ object ColourTemp { - private var envOverlayColourmap = Image(ModuleManager.getPath("basegame", "colourmap/black_body_col_1000_40000_K.tga")) + private var envOverlayColourmap = Image(ModMgr.getPath("basegame", "colourmap/black_body_col_1000_40000_K.tga")) private fun colTempToImagePos(K: Int): Int { if (K < 1000 || K >= 40000) throw IllegalArgumentException("K: out of range. ($K)") diff --git a/src/net/torvald/serialise/WriteMeta.kt b/src/net/torvald/serialise/WriteMeta.kt index 4824e6bf5..928bdadb1 100644 --- a/src/net/torvald/serialise/WriteMeta.kt +++ b/src/net/torvald/serialise/WriteMeta.kt @@ -5,7 +5,6 @@ import net.torvald.terrarum.mapgenerator.RoguelikeRandomiser import net.torvald.terrarum.Terrarum import net.torvald.terrarum.itemproperties.ItemCodex import net.torvald.terrarum.itemproperties.MaterialCodex -import net.torvald.terrarum.tileproperties.TilePropCSV import net.torvald.terrarum.tileproperties.TileCodex import org.apache.commons.codec.digest.DigestUtils import java.io.FileInputStream @@ -36,7 +35,7 @@ object WriteMeta { * @param savegameName -- Nullable. If the value is not specified, saveDirectoryName will be used instead. */ fun write(saveDirectoryName: String, savegameName: String?): Boolean { - val hashArray: ArrayList = ArrayList() + /*val hashArray: ArrayList = ArrayList() val savenameAsByteArray: ByteArray = (savegameName ?: saveDirectoryName).toByteArray(Charsets.UTF_8) @@ -75,20 +74,7 @@ object WriteMeta { } catch (e: IOException) { e.printStackTrace() - } + }*/ return false } - - fun toByteArray(long: Long): ByteArray { - return byteArrayOf( - (long.ushr(0x38) and 0xFF).toByte(), - (long.ushr(0x30) and 0xFF).toByte(), - (long.ushr(0x28) and 0xFF).toByte(), - (long.ushr(0x20) and 0xFF).toByte(), - (long.ushr(0x18) and 0xFF).toByte(), - (long.ushr(0x10) and 0xFF).toByte(), - (long.ushr(0x08) and 0xFF).toByte(), - (long and 0xFF).toByte() - ) - } } \ No newline at end of file diff --git a/src/net/torvald/terrarum/KVHashMap.kt b/src/net/torvald/terrarum/KVHashMap.kt index 6a8dc6c1f..f6ea73178 100644 --- a/src/net/torvald/terrarum/KVHashMap.kt +++ b/src/net/torvald/terrarum/KVHashMap.kt @@ -51,60 +51,47 @@ class KVHashMap { fun getAsInt(key: String): Int? { val value = get(key) + if (value == null) return null + if (value is JsonPrimitive) return value.asInt - try { - return value as Int - } - catch (e: ClassCastException) { - return null - } + return value as Int } fun getAsDouble(key: String): Double? { val value = get(key) + if (value == null) return null + if (value is Int) return value.toDouble() else if (value is JsonPrimitive) return value.asDouble - try { - return value as Double - } - catch (e: ClassCastException) { - return null - - } + return value as Double } fun getAsString(key: String): String? { val value = get(key) + if (value == null) return null + if (value is JsonPrimitive) return value.asString - try { - return value as String - } - catch (e: ClassCastException) { - return null - } + return value as String } fun getAsBoolean(key: String): Boolean? { val value = get(key) + if (value == null) return null + if (value is JsonPrimitive) return value.asBoolean - try { - return value as Boolean - } - catch (e: ClassCastException) { - return null - } + return value as Boolean } fun hasKey(key: String) = hashMap.containsKey(key) diff --git a/src/net/torvald/terrarum/ModuleManager.kt b/src/net/torvald/terrarum/ModMgr.kt similarity index 96% rename from src/net/torvald/terrarum/ModuleManager.kt rename to src/net/torvald/terrarum/ModMgr.kt index e69986f64..8e648eb49 100644 --- a/src/net/torvald/terrarum/ModuleManager.kt +++ b/src/net/torvald/terrarum/ModMgr.kt @@ -11,7 +11,7 @@ import java.nio.file.FileSystems * * Created by SKYHi14 on 2017-04-17. */ -object ModuleManager { +object ModMgr { data class ModuleMetadata(val order: Int, val isDir: Boolean, val desc: String, val libraries: Array) { override fun toString() = @@ -35,7 +35,7 @@ object ModuleManager { loadOrder.forEachIndexed { index, it -> val moduleName = it[0] - println("[ModuleManager] Loading module $moduleName") + println("[ModMgr] Loading module $moduleName") val description = it[1] val libs = it[2].split(';').toTypedArray() diff --git a/src/net/torvald/terrarum/StateInGame.kt b/src/net/torvald/terrarum/StateInGame.kt index 16c9e97fc..02d0f566c 100644 --- a/src/net/torvald/terrarum/StateInGame.kt +++ b/src/net/torvald/terrarum/StateInGame.kt @@ -12,7 +12,7 @@ import net.torvald.terrarum.gameactors.physicssolver.CollisionSolver import net.torvald.terrarum.gamecontroller.GameController import net.torvald.terrarum.gamecontroller.Key import net.torvald.terrarum.gamecontroller.KeyToggler -import net.torvald.terrarum.gameitem.InventoryItem +import net.torvald.terrarum.itemproperties.InventoryItem import net.torvald.terrarum.gameworld.GameWorld import net.torvald.terrarum.gameworld.WorldSimulator import net.torvald.terrarum.gameworld.WorldTime diff --git a/src/net/torvald/terrarum/StateUITest.kt b/src/net/torvald/terrarum/StateUITest.kt index c1566d4dc..51f51383f 100644 --- a/src/net/torvald/terrarum/StateUITest.kt +++ b/src/net/torvald/terrarum/StateUITest.kt @@ -1,8 +1,8 @@ package net.torvald.terrarum import net.torvald.terrarum.gameactors.* -import net.torvald.terrarum.gameitem.IVKey -import net.torvald.terrarum.gameitem.InventoryItem +import net.torvald.terrarum.itemproperties.IVKey +import net.torvald.terrarum.itemproperties.InventoryItem import net.torvald.terrarum.itemproperties.ItemCodex import net.torvald.terrarum.tileproperties.Tile import net.torvald.terrarum.ui.* diff --git a/src/net/torvald/terrarum/Terrarum.kt b/src/net/torvald/terrarum/Terrarum.kt index a12fe515a..2b7f86061 100644 --- a/src/net/torvald/terrarum/Terrarum.kt +++ b/src/net/torvald/terrarum/Terrarum.kt @@ -313,7 +313,7 @@ object Terrarum : StateBasedGame(GAME_NAME) { // load modules - ModuleManager + ModMgr gc.graphics.clear() // clean up any 'dust' in the buffer diff --git a/src/net/torvald/terrarum/UIItemInventoryElem.kt b/src/net/torvald/terrarum/UIItemInventoryElem.kt index 8a14773bd..9e0b40438 100644 --- a/src/net/torvald/terrarum/UIItemInventoryElem.kt +++ b/src/net/torvald/terrarum/UIItemInventoryElem.kt @@ -1,7 +1,7 @@ package net.torvald.terrarum import net.torvald.colourutil.CIELabUtil.darkerLab -import net.torvald.terrarum.gameitem.InventoryItem +import net.torvald.terrarum.itemproperties.InventoryItem import net.torvald.terrarum.ui.UICanvas import net.torvald.terrarum.ui.UIInventory import net.torvald.terrarum.ui.UIItem diff --git a/src/net/torvald/terrarum/gameactors/AVKey.kt b/src/net/torvald/terrarum/gameactors/AVKey.kt index 1798d0000..24eb6af11 100644 --- a/src/net/torvald/terrarum/gameactors/AVKey.kt +++ b/src/net/torvald/terrarum/gameactors/AVKey.kt @@ -79,6 +79,11 @@ object AVKey { const val MAGICREGENRATE = "magicregenrate" const val MAGICREGENRATEBUFF = "$MAGICREGENRATE$BUFF" + /** Double + * + */ + const val ACTION_INTERVAL = "actioninterval" + /** String * UUID for certain fixtures @@ -91,4 +96,9 @@ object AVKey { /** SYNOPSIS: __qsitem1 .. __qsitem10 * contains tem ID; they are supposed to be unique. Indices must be ONE-BASED! */ const val __PLAYER_QSPREFIX = "__qsitem" // __qsitem1 .. __qsitem10 (NOT ZERO BASED!) + /** Double + * When using tool/arm/etc. how long action button is held, in milliseconds (Int) + * Or for NPCs, how long it has been waiting for next move + */ + const val __ACTION_TIMER = "__actiontimer" } \ No newline at end of file diff --git a/src/net/torvald/terrarum/gameactors/ActorHumanoid.kt b/src/net/torvald/terrarum/gameactors/ActorHumanoid.kt index d065fcd22..8259aa4d8 100644 --- a/src/net/torvald/terrarum/gameactors/ActorHumanoid.kt +++ b/src/net/torvald/terrarum/gameactors/ActorHumanoid.kt @@ -1,10 +1,11 @@ package net.torvald.terrarum.gameactors import com.jme3.math.FastMath +import net.torvald.terrarum.Millisec import net.torvald.terrarum.Terrarum import net.torvald.terrarum.gameactors.faction.Faction import net.torvald.terrarum.gamecontroller.EnumKeyFunc -import net.torvald.terrarum.gameitem.InventoryItem +import net.torvald.terrarum.itemproperties.InventoryItem import net.torvald.terrarum.realestate.RealEstateUtility import org.newdawn.slick.GameContainer import org.newdawn.slick.Input @@ -22,6 +23,7 @@ open class ActorHumanoid(birth: GameDate, death: GameDate? = null) var vehicleRiding: Controllable? = null // usually player only + /** Must be set by PlayerFactory */ override var inventory: ActorInventory = ActorInventory(this, 2000, ActorInventory.CAPACITY_MODE_WEIGHT) // default constructor @@ -77,6 +79,8 @@ open class ActorHumanoid(birth: GameDate, death: GameDate? = null) @Transient internal const val WALK_ACCEL_BASE: Double = 0.67 @Transient const val BASE_HEIGHT = 40 + // 333.33 miliseconds + @Transient const val BASE_ACTION_INTERVAL = 1000.0 / 3.0 @Transient const val SPRITE_ROW_IDLE = 0 @Transient const val SPRITE_ROW_WALK = 1 diff --git a/src/net/torvald/terrarum/gameactors/ActorInventory.kt b/src/net/torvald/terrarum/gameactors/ActorInventory.kt index 468a88203..d8d23dc57 100644 --- a/src/net/torvald/terrarum/gameactors/ActorInventory.kt +++ b/src/net/torvald/terrarum/gameactors/ActorInventory.kt @@ -1,7 +1,7 @@ package net.torvald.terrarum.gameactors import net.torvald.terrarum.Terrarum -import net.torvald.terrarum.gameitem.InventoryItem +import net.torvald.terrarum.itemproperties.InventoryItem import net.torvald.terrarum.itemproperties.ItemCodex import net.torvald.terrarum.ui.UIInventory import java.util.* diff --git a/src/net/torvald/terrarum/gameactors/ActorWithSprite.kt b/src/net/torvald/terrarum/gameactors/ActorWithSprite.kt index c13de5c39..d3de320dc 100644 --- a/src/net/torvald/terrarum/gameactors/ActorWithSprite.kt +++ b/src/net/torvald/terrarum/gameactors/ActorWithSprite.kt @@ -1329,6 +1329,7 @@ fun Float.roundInt(): Int = Math.round(this) fun Double.abs() = Math.abs(this) fun Double.sqr() = this * this fun Double.sqrt() = Math.sqrt(this) +fun Float.sqrt() = FastMath.sqrt(this) fun Int.abs() = if (this < 0) -this else this fun Double.bipolarClamp(limit: Double) = if (this > 0 && this > limit) limit diff --git a/src/net/torvald/terrarum/gameactors/CanBeAnItem.kt b/src/net/torvald/terrarum/gameactors/CanBeAnItem.kt index de394ac44..c9f4aff30 100644 --- a/src/net/torvald/terrarum/gameactors/CanBeAnItem.kt +++ b/src/net/torvald/terrarum/gameactors/CanBeAnItem.kt @@ -1,6 +1,6 @@ package net.torvald.terrarum.gameactors -import net.torvald.terrarum.gameitem.InventoryItem +import net.torvald.terrarum.itemproperties.InventoryItem /** * Created by minjaesong on 16-01-31. diff --git a/src/net/torvald/terrarum/gameactors/CreatureBuilder.kt b/src/net/torvald/terrarum/gameactors/CreatureBuilder.kt index 8811a7eab..9e8aa5ded 100644 --- a/src/net/torvald/terrarum/gameactors/CreatureBuilder.kt +++ b/src/net/torvald/terrarum/gameactors/CreatureBuilder.kt @@ -22,6 +22,9 @@ object CreatureBuilder { val actor = ActorWithSprite(Actor.RenderOrder.MIDDLE) InjectCreatureRaw(actor.actorValue, module, jsonFileName) + + actor.actorValue[AVKey.__ACTION_TIMER] = 0.0 + return actor } } \ No newline at end of file diff --git a/src/net/torvald/terrarum/gameactors/DroppedItem.kt b/src/net/torvald/terrarum/gameactors/DroppedItem.kt index 0c1a045fc..9e243c595 100644 --- a/src/net/torvald/terrarum/gameactors/DroppedItem.kt +++ b/src/net/torvald/terrarum/gameactors/DroppedItem.kt @@ -1,6 +1,6 @@ package net.torvald.terrarum.gameactors -import net.torvald.terrarum.gameitem.InventoryItem +import net.torvald.terrarum.itemproperties.InventoryItem import net.torvald.terrarum.itemproperties.ItemCodex import net.torvald.terrarum.tileproperties.TileCodex import org.newdawn.slick.GameContainer diff --git a/src/net/torvald/terrarum/gameactors/HumanoidNPC.kt b/src/net/torvald/terrarum/gameactors/HumanoidNPC.kt index 371c4c266..48df62bb9 100644 --- a/src/net/torvald/terrarum/gameactors/HumanoidNPC.kt +++ b/src/net/torvald/terrarum/gameactors/HumanoidNPC.kt @@ -7,7 +7,7 @@ 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 net.torvald.terrarum.itemproperties.InventoryItem import org.luaj.vm2.* import org.luaj.vm2.compiler.LuaC import org.luaj.vm2.lib.* diff --git a/src/net/torvald/terrarum/gameactors/InjectCreatureRaw.kt b/src/net/torvald/terrarum/gameactors/InjectCreatureRaw.kt index 9c45674ea..288a9edb8 100644 --- a/src/net/torvald/terrarum/gameactors/InjectCreatureRaw.kt +++ b/src/net/torvald/terrarum/gameactors/InjectCreatureRaw.kt @@ -5,7 +5,7 @@ import net.torvald.random.Fudge3 import net.torvald.terrarum.langpack.Lang import com.google.gson.JsonObject import net.torvald.terrarum.ActorValue -import net.torvald.terrarum.ModuleManager +import net.torvald.terrarum.ModMgr import net.torvald.terrarum.gameactors.ActorHumanoid import org.newdawn.slick.SlickException import java.io.IOException @@ -25,7 +25,7 @@ object InjectCreatureRaw { * @param jsonFileName with extension */ operator fun invoke(actorValueRef: ActorValue, module: String, jsonFileName: String) { - val jsonObj = JsonFetcher(ModuleManager.getPath(module, "creatures/$jsonFileName")) + val jsonObj = JsonFetcher(ModMgr.getPath(module, "creatures/$jsonFileName")) val elementsInt = arrayOf(AVKey.BASEHEIGHT, AVKey.TOOLSIZE, AVKey.ENCUMBRANCE) val elementsString = arrayOf(AVKey.RACENAME, AVKey.RACENAMEPLURAL) diff --git a/src/net/torvald/terrarum/gameactors/PlayerBuilder.kt b/src/net/torvald/terrarum/gameactors/PlayerBuilder.kt index 348d9e5cb..da8568759 100644 --- a/src/net/torvald/terrarum/gameactors/PlayerBuilder.kt +++ b/src/net/torvald/terrarum/gameactors/PlayerBuilder.kt @@ -19,6 +19,8 @@ object PlayerBuilder { // do etc. p.actorValue[AVKey.__PLAYER_QUICKSLOTSEL] = 0 + p.actorValue[AVKey.__ACTION_TIMER] = 0.0 + p.actorValue[AVKey.ACTION_INTERVAL] = ActorHumanoid.BASE_ACTION_INTERVAL return p } diff --git a/src/net/torvald/terrarum/gameactors/PlayerBuilderCynthia.kt b/src/net/torvald/terrarum/gameactors/PlayerBuilderCynthia.kt index da664ce63..00fd81f15 100644 --- a/src/net/torvald/terrarum/gameactors/PlayerBuilderCynthia.kt +++ b/src/net/torvald/terrarum/gameactors/PlayerBuilderCynthia.kt @@ -1,7 +1,7 @@ package net.torvald.terrarum.gameactors import net.torvald.spriteanimation.SpriteAnimation -import net.torvald.terrarum.ModuleManager +import net.torvald.terrarum.ModMgr import net.torvald.terrarum.gameactors.ActorHumanoid import net.torvald.terrarum.gameactors.ai.LuaAIWrapper import net.torvald.terrarum.mapdrawer.FeaturesDrawer @@ -20,10 +20,12 @@ object PlayerBuilderCynthia { p.actorValue[AVKey.__PLAYER_QUICKSLOTSEL] = 0 + p.actorValue[AVKey.__ACTION_TIMER] = 0.0 + p.actorValue[AVKey.ACTION_INTERVAL] = ActorHumanoid.BASE_ACTION_INTERVAL p.actorValue[AVKey.NAME] = "Cynthia" - p.makeNewSprite(26, 42, ModuleManager.getPath("basegame", "sprites/test_player_2.tga")) + p.makeNewSprite(26, 42, ModMgr.getPath("basegame", "sprites/test_player_2.tga")) p.sprite!!.delay = 200 p.sprite!!.setRowsAndFrames(1, 1) diff --git a/src/net/torvald/terrarum/gameactors/PlayerBuilderSigrid.kt b/src/net/torvald/terrarum/gameactors/PlayerBuilderSigrid.kt index 0201eddfd..0030221a6 100644 --- a/src/net/torvald/terrarum/gameactors/PlayerBuilderSigrid.kt +++ b/src/net/torvald/terrarum/gameactors/PlayerBuilderSigrid.kt @@ -5,7 +5,7 @@ import net.torvald.terrarum.gameactors.faction.Faction import net.torvald.spriteanimation.SpriteAnimation import com.google.gson.JsonObject import net.torvald.terrarum.ActorValue -import net.torvald.terrarum.ModuleManager +import net.torvald.terrarum.ModMgr import net.torvald.terrarum.gameactors.ActorHumanoid import net.torvald.terrarum.gameactors.faction.FactionFactory import net.torvald.terrarum.itemproperties.ItemCodex @@ -28,11 +28,11 @@ object PlayerBuilderSigrid { p.referenceID = 0x51621D // the only constant of this procedural universe - p.makeNewSprite(28, 51, ModuleManager.getPath("basegame", "sprites/test_player.tga")) + p.makeNewSprite(28, 51, ModMgr.getPath("basegame", "sprites/test_player.tga")) p.sprite!!.delay = 200 p.sprite!!.setRowsAndFrames(1, 1) - p.makeNewSpriteGlow(28, 51, ModuleManager.getPath("basegame", "sprites/test_player_glow.tga")) + p.makeNewSpriteGlow(28, 51, ModMgr.getPath("basegame", "sprites/test_player_glow.tga")) p.spriteGlow!!.delay = 200 p.spriteGlow!!.setRowsAndFrames(1, 1) @@ -64,7 +64,8 @@ object PlayerBuilderSigrid { p.actorValue[AVKey.BASEDEFENCE] = 141 p.actorValue[AVKey.__PLAYER_QUICKSLOTSEL] = 0 - //p.actorValue["__selectedtile"] = 147 // test code; replace with .primaryUse(gc, delta) + p.actorValue[AVKey.__ACTION_TIMER] = 0.0 + p.actorValue[AVKey.ACTION_INTERVAL] = ActorHumanoid.BASE_ACTION_INTERVAL p.actorValue["__aimhelper"] = true // TODO when you'll gonna implement it? p.setHitboxDimension(15, p.actorValue.getAsInt(AVKey.BASEHEIGHT)!!, 11, 0) diff --git a/src/net/torvald/terrarum/gameactors/PlayerBuilderTestSubject1.kt b/src/net/torvald/terrarum/gameactors/PlayerBuilderTestSubject1.kt index 09678eeaf..1ab9998fb 100644 --- a/src/net/torvald/terrarum/gameactors/PlayerBuilderTestSubject1.kt +++ b/src/net/torvald/terrarum/gameactors/PlayerBuilderTestSubject1.kt @@ -1,6 +1,6 @@ package net.torvald.terrarum.gameactors -import net.torvald.terrarum.ModuleManager +import net.torvald.terrarum.ModMgr import net.torvald.terrarum.gameactors.ai.LuaAIWrapper import net.torvald.terrarum.mapdrawer.FeaturesDrawer @@ -14,10 +14,12 @@ object PlayerBuilderTestSubject1 { p.actorValue[AVKey.__PLAYER_QUICKSLOTSEL] = 0 + p.actorValue[AVKey.__ACTION_TIMER] = 0.0 + p.actorValue[AVKey.ACTION_INTERVAL] = ActorHumanoid.BASE_ACTION_INTERVAL p.actorValue[AVKey.NAME] = "Test Subject 1" - p.makeNewSprite(48, 52, ModuleManager.getPath("basegame", "sprites/npc_template_anim_prototype.tga")) + p.makeNewSprite(48, 52, ModMgr.getPath("basegame", "sprites/npc_template_anim_prototype.tga")) p.sprite!!.delay = 200 p.sprite!!.setRowsAndFrames(2, 4) diff --git a/src/net/torvald/terrarum/gameactors/Pocketed.kt b/src/net/torvald/terrarum/gameactors/Pocketed.kt index bdd7cdc77..f2c1121c0 100644 --- a/src/net/torvald/terrarum/gameactors/Pocketed.kt +++ b/src/net/torvald/terrarum/gameactors/Pocketed.kt @@ -1,7 +1,7 @@ package net.torvald.terrarum.gameactors import net.torvald.terrarum.Terrarum -import net.torvald.terrarum.gameitem.InventoryItem +import net.torvald.terrarum.itemproperties.InventoryItem import net.torvald.terrarum.itemproperties.ItemCodex /** diff --git a/src/net/torvald/terrarum/gameactors/faction/FactionFactory.kt b/src/net/torvald/terrarum/gameactors/faction/FactionFactory.kt index 556bc24fc..ddf4805a2 100644 --- a/src/net/torvald/terrarum/gameactors/faction/FactionFactory.kt +++ b/src/net/torvald/terrarum/gameactors/faction/FactionFactory.kt @@ -2,7 +2,7 @@ package net.torvald.terrarum.gameactors.faction import net.torvald.JsonFetcher import com.google.gson.JsonObject -import net.torvald.terrarum.ModuleManager +import net.torvald.terrarum.ModMgr import java.io.IOException @@ -16,7 +16,7 @@ object FactionFactory { */ @Throws(IOException::class) fun create(module: String, path: String): Faction { - val jsonObj = JsonFetcher(ModuleManager.getPath(module, path)) + val jsonObj = JsonFetcher(ModMgr.getPath(module, path)) val factionObj = Faction(jsonObj.get("factionname").asString) jsonObj.get("factionamicable").asJsonArray.forEach { factionObj.addFactionAmicable(it.asString) } diff --git a/src/net/torvald/terrarum/gamecontroller/GameController.kt b/src/net/torvald/terrarum/gamecontroller/GameController.kt index 6eea005cd..1c42c91bd 100644 --- a/src/net/torvald/terrarum/gamecontroller/GameController.kt +++ b/src/net/torvald/terrarum/gamecontroller/GameController.kt @@ -4,7 +4,7 @@ import net.torvald.terrarum.mapdrawer.TilesDrawer import net.torvald.terrarum.mapdrawer.FeaturesDrawer import net.torvald.terrarum.Terrarum import net.torvald.terrarum.gameactors.* -import net.torvald.terrarum.gameitem.InventoryItem +import net.torvald.terrarum.itemproperties.InventoryItem import net.torvald.terrarum.mapdrawer.MapCamera import net.torvald.terrarum.tileproperties.Tile import net.torvald.terrarum.tileproperties.TileCodex @@ -78,7 +78,7 @@ object GameController { if (input.isMouseButtonDown(Terrarum.getConfigInt("mouseprimary"))) { ingame.player!!.consumePrimary(itemOnGrip) } - else if (input.isMouseButtonDown(Terrarum.getConfigInt("mousesecondary"))) { + if (input.isMouseButtonDown(Terrarum.getConfigInt("mousesecondary"))) { ingame.player!!.consumeSecondary(itemOnGrip) } } @@ -113,7 +113,21 @@ object GameController { } fun mouseReleased(button: Int, x: Int, y: Int) { + if (Terrarum.ingame != null) { + val ingame = Terrarum.ingame!! + if (ingame.player != null && ingame.canPlayerControl) { + val itemOnGrip = ingame.player!!.inventory.itemEquipped[InventoryItem.EquipPosition.HAND_GRIP] + if (itemOnGrip != null) { + if (button == Terrarum.getConfigInt("mousePrimary")) { + itemOnGrip.endPrimaryUse(Terrarum.appgc, Terrarum.UPDATE_DELTA) + } + if (button == Terrarum.getConfigInt("mouseSecondary")) { + itemOnGrip.endSecondaryUse(Terrarum.appgc, Terrarum.UPDATE_DELTA) + } + } + } + } } fun mouseWheelMoved(change: Int) { diff --git a/src/net/torvald/terrarum/gameitem/DynamicItem.kt b/src/net/torvald/terrarum/gameitem/DynamicItem.kt deleted file mode 100644 index a5583d60e..000000000 --- a/src/net/torvald/terrarum/gameitem/DynamicItem.kt +++ /dev/null @@ -1,67 +0,0 @@ -package net.torvald.terrarum.gameitem - -import net.torvald.random.HQRNG -import net.torvald.terrarum.KVHashMap -import net.torvald.terrarum.itemproperties.ItemCodex -import org.newdawn.slick.GameContainer - -/** - * Items that has some more information (like floppy disk that contains UUID) - * - * @param baseItemID ID of the item that this item is based on - * - * Created by minjaesong on 16-09-08. - */ -@Deprecated("Use InventoryItem's ItemProp") -open abstract class DynamicItem(val baseItemID: Int?, newMass: Double? = null, newScale: Double? = null) - : InventoryItem() { - /* - override val id: Int = generateUniqueDynamicItemID() - - private fun generateUniqueDynamicItemID(): Int { - var ret: Int - do { - ret = HQRNG().nextInt().and(0x7FFFFFFF) // set new ID - } while (ItemCodex.contains(ret) || ret < ItemCodex.ITEM_DYNAMIC_MIN || ret > ItemCodex.ITEM_DYNAMIC_MAX) // check for collision - return ret - } - - /** - * Weight of the item - */ - override var mass: Double - get() = itemInfo.getAsDouble(ItemInfoKey.MASS)!! - set(value) { - itemInfo[ItemInfoKey.MASS] = value - } - /** - * Scale of the item. Real mass: mass * (scale^3) - * - * For static item, it must be 1.0. If you tinkered the item to be bigger, - * it must be re-assigned as Dynamic Item - */ - override var scale: Double - get() = itemInfo.getAsDouble(ItemInfoKey.SCALE) ?: 1.0 - set(value) { - itemInfo[ItemInfoKey.SCALE] = value - } - - val itemInfo = KVHashMap() - - init { - // set mass to the value from item codex using baseItemID - if (baseItemID == null) { - mass = newMass!! - } - else { - mass = newMass ?: ItemCodex[baseItemID].mass - } - - if (baseItemID == null) { - scale = newScale!! - } - else { - scale = newScale ?: ItemCodex[baseItemID].scale - } - }*/ -} \ No newline at end of file diff --git a/src/net/torvald/terrarum/gameworld/GameWorld.kt b/src/net/torvald/terrarum/gameworld/GameWorld.kt index dcc09c852..a052396b3 100644 --- a/src/net/torvald/terrarum/gameworld/GameWorld.kt +++ b/src/net/torvald/terrarum/gameworld/GameWorld.kt @@ -2,11 +2,12 @@ package net.torvald.terrarum.gameworld import net.torvald.terrarum.realestate.RealEstateUtility +import net.torvald.terrarum.tileproperties.TileCodex import org.dyn4j.geometry.Vector2 import org.newdawn.slick.SlickException typealias TileAddress = Long -typealias TileDamage = Int +typealias TileDamage = Float class GameWorld(val width: Int, val height: Int) { @@ -211,31 +212,61 @@ class GameWorld(val width: Int, val height: Int) { } } - fun inflctTerrainDamage(x: Int, y: Int, damage: Int) { + /** + * @return true if block is broken + */ + fun inflctTerrainDamage(x: Int, y: Int, damage: Float): Boolean { val addr = RealEstateUtility.getAbsoluteTileNumber(x, y) - if (terrainDamages[addr] == null) { + if (terrainDamages[addr] == null) { // add new terrainDamages[addr] = damage } - else { + else if (terrainDamages[addr]!! + damage <= 0) { // tile is (somehow) fully healed + terrainDamages.remove(addr) + } + else { // normal situation 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) { + //println("[GameWorld] accumulated damage: ${terrainDamages[addr]}") + + // remove tile from the world + if (terrainDamages[addr]!! >= TileCodex[getTileFromTerrain(x, y)].strength) { + setTileTerrain(x, y, 0) + return true + } + + return false + } + fun getTerrainDamage(x: Int, y: Int): Float = + terrainDamages[RealEstateUtility.getAbsoluteTileNumber(x, y)] ?: 0f + + /** + * @return true if block is broken + */ + fun inflctWallDamage(x: Int, y: Int, damage: Float): Boolean { val addr = RealEstateUtility.getAbsoluteTileNumber(x, y) - if (wallDamages[addr] == null) { + if (wallDamages[addr] == null) { // add new wallDamages[addr] = damage } - else { + else if (wallDamages[addr]!! + damage <= 0) { // tile is (somehow) fully healed + wallDamages.remove(addr) + } + else { // normal situation wallDamages[addr] = wallDamages[addr]!! + damage } + + // remove tile from the world + if (wallDamages[addr]!! >= TileCodex[getTileFromWall(x, y)].strength) { + setTileWall(x, y, 0) + return true + } + + return false } - fun getWallDamage(x: Int, y: Int) = - wallDamages[RealEstateUtility.getAbsoluteTileNumber(x, y)] ?: 0 + fun getWallDamage(x: Int, y: Int): Float = + wallDamages[RealEstateUtility.getAbsoluteTileNumber(x, y)] ?: 0f companion object { diff --git a/src/net/torvald/terrarum/itemproperties/Calculate.kt b/src/net/torvald/terrarum/itemproperties/Calculate.kt new file mode 100644 index 000000000..cca57367b --- /dev/null +++ b/src/net/torvald/terrarum/itemproperties/Calculate.kt @@ -0,0 +1,26 @@ +package net.torvald.terrarum.itemproperties + +import net.torvald.terrarum.gameactors.roundInt +import net.torvald.terrarum.gameactors.sqrt + +/** + * Created by SKYHi14 on 2017-04-17. + */ +object Calculate { + /** + * Pickaxe power per action (swing) + * + * @return arbrtrary unit + * + * See: work_files/Pickaxe Power.xlsx + * + * TODO Newtons as unit? + */ + fun pickaxePower(material: Material): Float { + return 4f * material.forceMod.toFloat().sqrt() + } + + + fun armorwhatever() { TODO() } + fun yogafire() { TODO() } +} \ No newline at end of file diff --git a/src/net/torvald/terrarum/gameitem/IVKey.kt b/src/net/torvald/terrarum/itemproperties/IVKey.kt similarity index 93% rename from src/net/torvald/terrarum/gameitem/IVKey.kt rename to src/net/torvald/terrarum/itemproperties/IVKey.kt index 98129d76e..e0e7a8deb 100644 --- a/src/net/torvald/terrarum/gameitem/IVKey.kt +++ b/src/net/torvald/terrarum/itemproperties/IVKey.kt @@ -1,4 +1,4 @@ -package net.torvald.terrarum.gameitem +package net.torvald.terrarum.itemproperties /** * Created by minjaesong on 16-09-09. diff --git a/src/net/torvald/terrarum/gameitem/InventoryItem.kt b/src/net/torvald/terrarum/itemproperties/InventoryItem.kt similarity index 96% rename from src/net/torvald/terrarum/gameitem/InventoryItem.kt rename to src/net/torvald/terrarum/itemproperties/InventoryItem.kt index 031a9f4c7..6c2e5acf7 100644 --- a/src/net/torvald/terrarum/gameitem/InventoryItem.kt +++ b/src/net/torvald/terrarum/itemproperties/InventoryItem.kt @@ -1,4 +1,4 @@ -package net.torvald.terrarum.gameitem +package net.torvald.terrarum.itemproperties import net.torvald.terrarum.ItemValue import net.torvald.terrarum.gameactors.Pocketed @@ -96,6 +96,8 @@ abstract class InventoryItem : Comparable, Cloneable { */ open var durability: Float = 0f + var using = false + /** * Effects applied continuously while in pocket */ @@ -129,6 +131,9 @@ abstract class InventoryItem : Comparable, Cloneable { */ open fun secondaryUse(gc: GameContainer, delta: Int): Boolean = false + open fun endPrimaryUse(gc: GameContainer, delta: Int): Boolean = false + open fun endSecondaryUse(gc: GameContainer, delta: Int): Boolean = false + /** * Effects applied immediately only once if thrown from pocket */ diff --git a/src/net/torvald/terrarum/itemproperties/ItemCodex.kt b/src/net/torvald/terrarum/itemproperties/ItemCodex.kt index 9b307d333..5cb7bd413 100644 --- a/src/net/torvald/terrarum/itemproperties/ItemCodex.kt +++ b/src/net/torvald/terrarum/itemproperties/ItemCodex.kt @@ -3,12 +3,13 @@ package net.torvald.terrarum.itemproperties import net.torvald.point.Point2d import net.torvald.terrarum.KVHashMap import net.torvald.terrarum.gameactors.CanBeAnItem -import net.torvald.terrarum.gameitem.InventoryItem +import net.torvald.terrarum.itemproperties.InventoryItem import net.torvald.terrarum.Terrarum +import net.torvald.terrarum.gameactors.AVKey import net.torvald.terrarum.gameactors.ActorWithSprite import net.torvald.terrarum.gamecontroller.mouseTileX import net.torvald.terrarum.gamecontroller.mouseTileY -import net.torvald.terrarum.gameitem.IVKey +import net.torvald.terrarum.itemproperties.IVKey import net.torvald.terrarum.gameworld.GameWorld import net.torvald.terrarum.mapdrawer.TilesDrawer import net.torvald.terrarum.mapdrawer.TilesDrawer.wallOverlayColour @@ -108,17 +109,27 @@ object ItemCodex { 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 maxDurability = 606 // this much tiles before breaking override var durability = maxDurability.toFloat() override var equipPosition = EquipPosition.HAND_GRIP override var inventoryCategory = Category.TOOL + private val testmaterial = Material( + 0,0,0,0,0,0,0,0,14,0.0 // quick test material Steel + ) + init { itemProperties[IVKey.ITEMTYPE] = IVKey.ItemType.PICK + name = "Steel pickaxe" } override fun primaryUse(gc: GameContainer, delta: Int): Boolean { val mousePoint = Point2d(gc.mouseTileX.toDouble(), gc.mouseTileY.toDouble()) + val actorvalue = Terrarum.ingame!!.player!!.actorValue + + + using = true + // 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)) @@ -129,19 +140,21 @@ object ItemCodex { if (Tile.AIR == Terrarum.ingame!!.world.getTileFromTerrain(gc.mouseTileX, gc.mouseTileY)) return false + // filter passed, do the job - Terrarum.ingame!!.world.setTileTerrain( + val swingDmgToFrameDmg = delta.toDouble() / actorvalue.getAsDouble(AVKey.ACTION_INTERVAL)!! + + return Terrarum.ingame!!.world.inflctTerrainDamage( gc.mouseTileX, gc.mouseTileY, - Tile.AIR + Calculate.pickaxePower(testmaterial) * swingDmgToFrameDmg.toFloat() ) - /*Terrarum.ingame!!.world.inflctTerrainDamage( - gc.mouseTileX, - gc.mouseTileY, - - )*/ - + } + override fun endPrimaryUse(gc: GameContainer, delta: Int): Boolean { + using = false + // reset action timer to zero + Terrarum.ingame!!.player!!.actorValue[AVKey.__ACTION_TIMER] = 0.0 return true } } diff --git a/src/net/torvald/terrarum/itemproperties/ItemEffectsLuaAPI.kt b/src/net/torvald/terrarum/itemproperties/ItemEffectsLuaAPI.kt index 33572eafc..455ff5a35 100644 --- a/src/net/torvald/terrarum/itemproperties/ItemEffectsLuaAPI.kt +++ b/src/net/torvald/terrarum/itemproperties/ItemEffectsLuaAPI.kt @@ -47,13 +47,13 @@ class ItemEffectsLuaAPI(g: Globals) { class StrikeEarth : ThreeArgFunction() { override fun call(x: LuaValue, y: LuaValue, power: LuaValue): LuaValue { - Terrarum.ingame!!.world.inflctTerrainDamage(x.checkint(), y.checkint(), power.checkint()) + Terrarum.ingame!!.world.inflctTerrainDamage(x.checkint(), y.checkint(), power.checkdouble().toFloat()) 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()) + Terrarum.ingame!!.world.inflctWallDamage(x.checkint(), y.checkint(), power.checkdouble().toFloat()) return LuaValue.NONE } } diff --git a/src/net/torvald/terrarum/itemproperties/Material.kt b/src/net/torvald/terrarum/itemproperties/Material.kt index bfff25ac2..e9ee890ca 100644 --- a/src/net/torvald/terrarum/itemproperties/Material.kt +++ b/src/net/torvald/terrarum/itemproperties/Material.kt @@ -30,7 +30,7 @@ data class Material ( var thermalConductivity: Int, // pascal (N/m^2); if the item (e.g. sword) receives a force that exceeds this value, the item will be destroyed - // must be a item properties - var weapSharpnessMod: Double, // multiplier + + var forceMod: Int, // arbitrary unit. See Pickaxe_Power.xlsx var armourMod: Double // multiplier ) \ No newline at end of file diff --git a/src/net/torvald/terrarum/mapdrawer/TilesDrawer.kt b/src/net/torvald/terrarum/mapdrawer/TilesDrawer.kt index 4a6355d7a..3b7e3e1d5 100644 --- a/src/net/torvald/terrarum/mapdrawer/TilesDrawer.kt +++ b/src/net/torvald/terrarum/mapdrawer/TilesDrawer.kt @@ -7,12 +7,14 @@ import net.torvald.terrarum.tileproperties.TileCodex import com.jme3.math.FastMath import net.torvald.terrarum.* import net.torvald.terrarum.concurrent.ThreadParallel +import net.torvald.terrarum.gameactors.roundInt import net.torvald.terrarum.mapdrawer.FeaturesDrawer.TILE_SIZE import net.torvald.terrarum.mapdrawer.LightmapRenderer.normaliseToColour import net.torvald.terrarum.mapdrawer.MapCamera.x import net.torvald.terrarum.mapdrawer.MapCamera.y import net.torvald.terrarum.mapdrawer.MapCamera.height import net.torvald.terrarum.mapdrawer.MapCamera.width +import net.torvald.terrarum.realestate.RealEstateUtility import org.lwjgl.opengl.GL11 import org.newdawn.slick.* import java.util.* @@ -25,10 +27,11 @@ object TilesDrawer { private val TILE_SIZE = FeaturesDrawer.TILE_SIZE private val TILE_SIZEF = FeaturesDrawer.TILE_SIZE.toFloat() - var tilesTerrain: SpriteSheet = SpriteSheet(ModuleManager.getPath("basegame", "tiles/terrain.tga"), TILE_SIZE, TILE_SIZE) - private set // Slick has some weird quirks with PNG's transparency. I'm using 32-bit targa here. - var tilesWire: SpriteSheet = SpriteSheet(ModuleManager.getPath("basegame", "tiles/wire.tga"), TILE_SIZE, TILE_SIZE) - private set + val tilesTerrain = SpriteSheet(ModMgr.getPath("basegame", "tiles/terrain.tga"), TILE_SIZE, TILE_SIZE) + // Slick has some weird quirks with PNG's transparency. I'm using 32-bit targa here. + val tilesWire = SpriteSheet(ModMgr.getPath("basegame", "tiles/wire.tga"), TILE_SIZE, TILE_SIZE) + + val breakAnimSteps = 10 val WALL = GameWorld.WALL val TERRAIN = GameWorld.TERRAIN @@ -49,7 +52,7 @@ object TilesDrawer { * It holds different shading rule to discriminate with group 02, index 0 is single tile. * These are the tiles that only connects to itself, will not connect to colour variants */ - var TILES_CONNECT_SELF = arrayOf( + val TILES_CONNECT_SELF = arrayListOf( Tile.ICE_MAGICAL, Tile.GLASS_CRUDE, Tile.GLASS_CLEAN, @@ -98,7 +101,7 @@ object TilesDrawer { * Connectivity group 02 : natural tiles * It holds different shading rule to discriminate with group 01, index 0 is middle tile. */ - var TILES_CONNECT_MUTUAL = arrayOf( + val TILES_CONNECT_MUTUAL = arrayListOf( Tile.STONE, Tile.STONE_QUARRIED, Tile.STONE_TILE_WHITE, @@ -163,7 +166,7 @@ object TilesDrawer { /** * Torches, levers, switches, ... */ - var TILES_WALL_STICKER = arrayOf( + val TILES_WALL_STICKER = arrayListOf( Tile.TORCH, Tile.TORCH_FROST, Tile.TORCH_OFF, @@ -173,7 +176,7 @@ object TilesDrawer { /** * platforms, ... */ - var TILES_WALL_STICKER_CONNECT_SELF = arrayOf( + val TILES_WALL_STICKER_CONNECT_SELF = arrayListOf( Tile.PLATFORM_BIRCH, Tile.PLATFORM_BLOODROSE, Tile.PLATFORM_EBONY, @@ -186,7 +189,7 @@ object TilesDrawer { * will blend colour using colour multiplication * i.e. red hues get lost if you dive into the water */ - var TILES_BLEND_MUL = arrayOf( + val TILES_BLEND_MUL = arrayListOf( Tile.WATER, Tile.WATER_1, Tile.WATER_2, @@ -304,7 +307,7 @@ object TilesDrawer { val noDamageLayer = mode % 3 == WIRE - // draw + // draw a tile, but only when illuminated try { if ((mode == WALL || mode == TERRAIN) && // not an air tile (thisTile ?: 0) != Tile.AIR) { @@ -318,54 +321,70 @@ object TilesDrawer { LightmapRenderer.getHighestRGB(x + 1, y + 1) ?: 0 >= tileDrawLightThreshold || LightmapRenderer.getHighestRGB(x + 1, y - 1) ?: 0 >= tileDrawLightThreshold || LightmapRenderer.getHighestRGB(x - 1, y + 1) ?: 0 >= tileDrawLightThreshold) { - // blackness - if (zeroTileCounter > 0) { - /* unable to do anything */ + // blackness + if (zeroTileCounter > 0) { + /* unable to do anything */ - zeroTileCounter = 0 - } + zeroTileCounter = 0 + } - val nearbyTilesInfo: Int - if (isPlatform(thisTile)) { - nearbyTilesInfo = getNearbyTilesInfoPlatform(x, y) - } - else if (isWallSticker(thisTile)) { - nearbyTilesInfo = getNearbyTilesInfoWallSticker(x, y) - } - else if (isConnectMutual(thisTile)) { - nearbyTilesInfo = getNearbyTilesInfoNonSolid(x, y, mode) - } - else if (isConnectSelf(thisTile)) { - nearbyTilesInfo = getNearbyTilesInfo(x, y, mode, thisTile) - } - else { - nearbyTilesInfo = 0 - } + val nearbyTilesInfo: Int + if (isPlatform(thisTile)) { + nearbyTilesInfo = getNearbyTilesInfoPlatform(x, y) + } + else if (isWallSticker(thisTile)) { + nearbyTilesInfo = getNearbyTilesInfoWallSticker(x, y) + } + else if (isConnectMutual(thisTile)) { + nearbyTilesInfo = getNearbyTilesInfoNonSolid(x, y, mode) + } + else if (isConnectSelf(thisTile)) { + nearbyTilesInfo = getNearbyTilesInfo(x, y, mode, thisTile) + } + else { + nearbyTilesInfo = 0 + } - val thisTileX: Int - if (!noDamageLayer) - thisTileX = PairedMapLayer.RANGE * ((thisTile ?: 0) % PairedMapLayer.RANGE) + nearbyTilesInfo - else - thisTileX = nearbyTilesInfo + val thisTileX = if (!noDamageLayer) + PairedMapLayer.RANGE * ((thisTile ?: 0) % PairedMapLayer.RANGE) + nearbyTilesInfo + else + nearbyTilesInfo - val thisTileY = (thisTile ?: 0) / PairedMapLayer.RANGE + val thisTileY = (thisTile ?: 0) / PairedMapLayer.RANGE - if (drawModeTilesBlendMul) { - if (TilesDrawer.isBlendMul(thisTile)) { - drawTile(mode, x, y, thisTileX, thisTileY) - } - } - else { - // do NOT add "if (!isBlendMul(thisTile))"! - // or else they will not look like they should be when backed with wall + + // draw a tile + if (drawModeTilesBlendMul) { + if (TilesDrawer.isBlendMul(thisTile)) { drawTile(mode, x, y, thisTileX, thisTileY) } + } + else { + // do NOT add "if (!isBlendMul(thisTile))"! + // or else they will not look like they should be when backed with wall + drawTile(mode, x, y, thisTileX, thisTileY) + } + + // draw a breakage + if (mode == TERRAIN || mode == WALL) { + val breakage = if (mode == TERRAIN) world.getTerrainDamage(x, y) else world.getWallDamage(x, y) + val maxHealth = TileCodex[world.getTileFromTerrain(x, y)].strength + val stage = (breakage / maxHealth).times(breakAnimSteps).roundInt() + // actual drawing + if (stage > 0) { + // alpha blending works, but no GL blend func... + drawTile(mode, x, y, 5 + stage, 0) + } + } + + } // end if (is illuminated) + // draw black patch else { - zeroTileCounter++ - //drawTile(mode, x, y, 1, 0) // black patch + zeroTileCounter++ // unused for now + GL11.glColor4f(0f, 0f, 0f, 1f) GL11.glTexCoord2f(0f, 0f) diff --git a/src/net/torvald/terrarum/tileproperties/Tile.kt b/src/net/torvald/terrarum/tileproperties/Tile.kt index 10c891072..06868e25f 100644 --- a/src/net/torvald/terrarum/tileproperties/Tile.kt +++ b/src/net/torvald/terrarum/tileproperties/Tile.kt @@ -7,7 +7,7 @@ import net.torvald.terrarum.Terrarum */ object Tile { - val AIR = 0 + val AIR = 0 // hard coded; this is the standard val STONE = TileCodex.idDamageToIndex(1, 0) val STONE_QUARRIED = TileCodex.idDamageToIndex(1, 1) diff --git a/src/net/torvald/terrarum/tileproperties/TileCodex.kt b/src/net/torvald/terrarum/tileproperties/TileCodex.kt index 1fd53aa76..811b4ebf4 100644 --- a/src/net/torvald/terrarum/tileproperties/TileCodex.kt +++ b/src/net/torvald/terrarum/tileproperties/TileCodex.kt @@ -1,6 +1,7 @@ package net.torvald.terrarum.tileproperties import net.torvald.CSVFetcher +import net.torvald.terrarum.ModMgr import net.torvald.terrarum.gameworld.MapLayer import net.torvald.terrarum.gameworld.PairedMapLayer import org.apache.commons.csv.CSVRecord @@ -14,14 +15,12 @@ object TileCodex { private var tileProps: Array - val CSV_PATH = "/net/torvald/terrarum/tileproperties/tileprop.csv" - const val TILE_UNIQUE_MAX = MapLayer.RANGE * PairedMapLayer.RANGE private val nullProp = TileProp() init { - tileProps = Array(TILE_UNIQUE_MAX * 2, { i -> TileProp() }) + tileProps = Array(TILE_UNIQUE_MAX * 2, { TileProp() }) for (i in tileProps.indices) { tileProps[i] = TileProp() @@ -29,7 +28,7 @@ object TileCodex { try { // todo verify CSV using pre-calculated SHA256 hash - val records = CSVFetcher.readFromString(TilePropCSV()) + val records = CSVFetcher.readFromModule("basegame", "tiles/tileprop.csv") println("[TileCodex] Building tile properties table") @@ -50,15 +49,15 @@ object TileCodex { } - fun get(index: Int, damage: Int): TileProp { + fun get(index: Int, subID: Int): TileProp { try { - tileProps[idDamageToIndex(index, damage)].id + tileProps[idDamageToIndex(index, subID)].id } catch (e: NullPointerException) { - throw NullPointerException("Tile prop with id $index and damage $damage does not exist.") + throw NullPointerException("Tile prop with id $index and subID $subID does not exist.") } - return tileProps[idDamageToIndex(index, damage)] + return tileProps[idDamageToIndex(index, subID)] } operator fun get(rawIndex: Int?): TileProp { diff --git a/src/net/torvald/terrarum/tileproperties/TilePropCSV.kt b/src/net/torvald/terrarum/tileproperties/TilePropCSV.kt index 4e7c65af2..64213ab01 100644 --- a/src/net/torvald/terrarum/tileproperties/TilePropCSV.kt +++ b/src/net/torvald/terrarum/tileproperties/TilePropCSV.kt @@ -3,7 +3,7 @@ package net.torvald.terrarum.tileproperties /** * Created by minjaesong on 16-09-11. */ -object TilePropCSV { +object TilePropCSVFUfufufufufufffnufuuufufu { operator fun invoke() = """ "id";"sid";"name" ; "opacity";"strength";"dsty";"mate";"fluid";"solid";"wall"; "lumcolor";"drop";"ddmg";"fall";"dlfn";"vscs";"fv";"friction" "0"; "0";"TILE_AIR" ; "8396808"; "1"; "1";"null"; "0"; "0"; "0"; "0"; "0"; "0"; "0"; "0"; "N/A"; "0";"4" diff --git a/src/net/torvald/terrarum/ui/UIInventory.kt b/src/net/torvald/terrarum/ui/UIInventory.kt index 701aaf859..6465a3f37 100644 --- a/src/net/torvald/terrarum/ui/UIInventory.kt +++ b/src/net/torvald/terrarum/ui/UIInventory.kt @@ -6,7 +6,7 @@ import net.torvald.terrarum.Terrarum.joypadLabelNinA import net.torvald.terrarum.Terrarum.joypadLabelNinY import net.torvald.terrarum.gameactors.* import net.torvald.terrarum.gameactors.ActorInventory.Companion.CAPACITY_MODE_NO_ENCUMBER -import net.torvald.terrarum.gameitem.InventoryItem +import net.torvald.terrarum.itemproperties.InventoryItem import net.torvald.terrarum.itemproperties.ItemCodex import net.torvald.terrarum.langpack.Lang import org.newdawn.slick.* diff --git a/src/net/torvald/terrarum/weather/WeatherMixer.kt b/src/net/torvald/terrarum/weather/WeatherMixer.kt index be685c2a5..306b66289 100644 --- a/src/net/torvald/terrarum/weather/WeatherMixer.kt +++ b/src/net/torvald/terrarum/weather/WeatherMixer.kt @@ -49,7 +49,7 @@ object WeatherMixer { // read weather descriptions from assets/weather (modular weather) val weatherRawValidList = ArrayList() - val weatherRaws = ModuleManager.getFiles("basegame", "weathers") + val weatherRaws = ModMgr.getFiles("basegame", "weathers") weatherRaws.forEach { if (!it.isDirectory && it.name.endsWith(".json")) weatherRawValidList.add(it) @@ -207,7 +207,7 @@ object WeatherMixer { // parse globalLight if (globalLightInJson.isString) - globalLight = Image(ModuleManager.getPath("basegame", "$pathToImage/${globalLightInJson.asString}")) + globalLight = Image(ModMgr.getPath("basegame", "$pathToImage/${globalLightInJson.asString}")) else if (globalLightInJson.isNumber) { // make 1x1 image with specified colour globalLight = Image(1, 1) @@ -219,7 +219,7 @@ object WeatherMixer { // parse skyboxGradColourMap if (skyboxInJson.isString) - skybox = Image(ModuleManager.getPath("basegame", "$pathToImage/${skyboxInJson.asString}")) + skybox = Image(ModMgr.getPath("basegame", "$pathToImage/${skyboxInJson.asString}")) else if (globalLightInJson.isNumber) { // make 1x2 image with specified colour skybox = Image(1, 2) @@ -231,7 +231,7 @@ object WeatherMixer { // get extra images for (i in extraImagesPath) - extraImages.add(Image(ModuleManager.getPath("basegame", "$pathToImage/${i.asString}"))) + extraImages.add(Image(ModMgr.getPath("basegame", "$pathToImage/${i.asString}"))) // get mix from diff --git a/work_files/Pickaxe Power.xlsx b/work_files/Pickaxe Power.xlsx index 066d521b61618b8873f5ca49b9c91866a8e6f4fc..bc246eca23e8d830172101cd2b9327d11d53f5a6 100644 GIT binary patch delta 16023 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 wqdj-wuP1;ClCVK}d8p8HN&gA_Kjjsbl>h($