still wip modularisation, game somehow boots

This commit is contained in:
minjaesong
2018-06-21 17:33:22 +09:00
parent f0a6f8b9c2
commit a6ea2b4e18
266 changed files with 2409 additions and 1122 deletions

View File

@@ -0,0 +1,106 @@
package net.torvald.terrarum.modulebasegame
import net.torvald.point.Point2d
import net.torvald.terrarum.ModMgr
import net.torvald.terrarum.ModuleEntryPoint
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.blockproperties.BlockCodex
import net.torvald.terrarum.itemproperties.GameItem
import net.torvald.terrarum.itemproperties.ItemCodex
import net.torvald.terrarum.itemproperties.Material
import net.torvald.terrarum.modulebasegame.gameactors.ActorWithPhysics
/**
* Created by minjaesong on 2018-06-21.
*/
class EntryPoint : ModuleEntryPoint() {
override fun invoke() {
ModMgr.GameBlockLoader.invoke("basegame")
ModMgr.GameItemLoader.invoke("basegame")
ModMgr.GameLanguageLoader.invoke("basegame")
/////////////////////////////////
// load customised item loader //
/////////////////////////////////
println("[ItemCodex] recording item ID ")
// blocks.csvs are loaded by ModMgr beforehand
// block items (blocks and walls are the same thing basically)
for (i in ItemCodex.ITEM_TILES + ItemCodex.ITEM_WALLS) {
ItemCodex.itemCodex[i] = object : GameItem() {
override val originalID = i
override var dynamicID = i
override val isUnique: Boolean = false
override var baseMass: Double = BlockCodex[i].density / 1000.0
override var baseToolSize: Double? = null
override var equipPosition = EquipPosition.HAND_GRIP
override val originalName = BlockCodex[i % ItemCodex.ITEM_WALLS.first].nameKey
override var stackable = true
override var inventoryCategory = if (i in ItemCodex.ITEM_TILES) Category.BLOCK else Category.WALL
override var isDynamic = false
override val material = Material(0,0,0,0,0,0,0,0,0,0.0)
init {
print("$originalID ")
}
override fun primaryUse(delta: Float): Boolean {
return false
// TODO base punch attack
}
override fun secondaryUse(delta: Float): Boolean {
val ingame = Terrarum.ingame!! as Ingame
val mousePoint = Point2d(Terrarum.mouseTileX.toDouble(), Terrarum.mouseTileY.toDouble())
// check for collision with actors (BLOCK only)
if (this.inventoryCategory == Category.BLOCK) {
ingame.actorContainer.forEach {
if (it is ActorWithPhysics && it.hIntTilewiseHitbox.intersects(mousePoint))
return false
}
}
// return false if the tile is already there
if (this.inventoryCategory == Category.BLOCK &&
this.dynamicID == ingame.world.getTileFromTerrain(Terrarum.mouseTileX, Terrarum.mouseTileY) ||
this.inventoryCategory == Category.WALL &&
this.dynamicID - ItemCodex.ITEM_WALLS.start == ingame.world.getTileFromWall(Terrarum.mouseTileX, Terrarum.mouseTileY) ||
this.inventoryCategory == Category.WIRE &&
this.dynamicID - ItemCodex.ITEM_WIRES.start == ingame.world.getTileFromWire(Terrarum.mouseTileX, Terrarum.mouseTileY)
)
return false
// filter passed, do the job
// FIXME this is only useful for Player
if (i in ItemCodex.ITEM_TILES) {
ingame.world.setTileTerrain(
Terrarum.mouseTileX,
Terrarum.mouseTileY,
i
)
}
else {
ingame.world.setTileWall(
Terrarum.mouseTileX,
Terrarum.mouseTileY,
i
)
}
return true
}
}
}
println("Welcome back!")
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,50 @@
package net.torvald.terrarum.modulebasegame.console
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.console.ConsoleCommand
import net.torvald.terrarum.console.Echo
import net.torvald.terrarum.console.EchoError
import net.torvald.terrarum.modulebasegame.Ingame
import net.torvald.terrarum.modulebasegame.debuggerapp.ActorValueTracker
import java.util.*
/**
* Created by minjaesong on 2016-12-29.
*/
internal object AVTracker : ConsoleCommand {
private val jPanelInstances = ArrayList<ActorValueTracker>()
override fun execute(args: Array<String>) {
if (args.size < 2) {
jPanelInstances.add(ActorValueTracker((Terrarum.ingame!! as Ingame).player))
}
else {
try {
val actorID = args[1].toInt()
if (Terrarum.ingame!!.theGameHasActor(actorID)) {
jPanelInstances.add(ActorValueTracker(Terrarum.ingame!!.getActorByID(actorID)))
}
else {
throw IllegalArgumentException()
}
}
catch (e: NumberFormatException) {
EchoError("Illegal actor ID input")
return
}
catch (e1: IllegalArgumentException) {
EchoError("No such actor with specified ID")
return
}
}
}
override fun printUsage() {
Echo("Pops up new window that provides real-time information about the actor's actor value")
}
fun update() {
jPanelInstances.forEach { it.update() }
}
}

View File

@@ -0,0 +1,30 @@
package net.torvald.terrarum.modulebasegame.console
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.console.ConsoleCommand
import net.torvald.terrarum.console.Echo
import net.torvald.terrarum.debuggerapp.ActorsLister
import net.torvald.terrarum.modulebasegame.Ingame
import java.util.*
/**
* Created by minjaesong on 2016-12-29.
*/
internal object ActorsList : ConsoleCommand {
private val jPanelInstances = ArrayList<ActorsLister>()
override fun execute(args: Array<String>) {
jPanelInstances.add(ActorsLister(
(Terrarum.ingame!! as Ingame).actorContainer,
(Terrarum.ingame!! as Ingame).actorContainerInactive)
)
}
override fun printUsage() {
Echo("Pops up new window that displays the list of actors currently in the game")
}
fun update() {
jPanelInstances.forEach { it.update() }
}
}

View File

@@ -0,0 +1,32 @@
package net.torvald.terrarum.modulebasegame.console
import net.torvald.terrarum.console.ConsoleCommand
import net.torvald.terrarum.console.Echo
import java.io.IOException
import java.nio.file.FileSystems
import java.nio.file.Files
/**
* Created by minjaesong on 2016-02-10.
*/
internal object CatStdout : ConsoleCommand {
override fun execute(args: Array<String>) {
if (args.size == 1) {
printUsage()
return
}
try {
Files.lines(FileSystems.getDefault().getPath(args[1])).forEach({ Echo(it) })
}
catch (e: IOException) {
Echo("CatStdout: could not read file -- IOException")
}
}
override fun printUsage() {
Echo("usage: cat 'path/to/text/file")
}
}

View File

@@ -0,0 +1,15 @@
package net.torvald.terrarum.modulebasegame.console
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.console.ConsoleCommand
import net.torvald.terrarum.modulebasegame.Ingame
object CheatWarnTest : ConsoleCommand {
override fun execute(args: Array<String>) {
(Terrarum.ingame as? Ingame)?.uiCheatMotherfuckerNootNoot?.setAsOpen()
}
override fun printUsage() {
}
}

View File

@@ -0,0 +1,49 @@
package net.torvald.terrarum.modulebasegame.console
import net.torvald.terrarum.ccO
import net.torvald.terrarum.console.CommandDict
import net.torvald.terrarum.console.ConsoleCommand
import net.torvald.terrarum.console.Echo
import net.torvald.terrarum.langpack.Lang
import java.util.Formatter
/**
* Created by minjaesong on 2016-01-16.
*/
internal object CodexEdictis : ConsoleCommand {
override fun execute(args: Array<String>) {
if (args.size == 1) {
printList()
}
else {
try {
val commandObj = CommandDict[args[1].toLowerCase()]
commandObj.printUsage()
}
catch (e: NullPointerException) {
val sb = StringBuilder()
val formatter = Formatter(sb)
Echo("Codex: " + formatter.format(Lang["DEV_MESSAGE_CONSOLE_COMMAND_UNKNOWN"], args[1]).toString())
}
}
}
override fun printUsage() {
Echo("Usage: codex (command)")
Echo("shows how to use 'command'")
Echo("leave blank to get list of available commands")
}
private fun printList() {
Echo(Lang["DEV_MESSAGE_CONSOLE_AVAILABLE_COMMANDS"])
CommandDict.dict.forEach { name, cmd ->
Echo("$ccO" + name)
cmd.printUsage()
}
}
}

View File

@@ -0,0 +1,40 @@
package net.torvald.terrarum.modulebasegame.console
import net.torvald.terrarum.utils.JsonWriter
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.console.ConsoleCommand
import net.torvald.terrarum.console.Echo
import net.torvald.terrarum.modulebasegame.Ingame
import java.io.IOException
/**
* Created by minjaesong on 2016-02-10.
*/
internal object ExportAV : ConsoleCommand {
override fun execute(args: Array<String>) {
if (args.size == 2) {
try {
JsonWriter.writeToFile(
(Terrarum.ingame!! as Ingame).player.actorValue,
Terrarum.defaultDir + "/Exports/" + args[1] + ".json")
Echo("ExportAV: exported to " + args[1] + ".json")
}
catch (e: IOException) {
Echo("ExportAV: IOException raised.")
e.printStackTrace()
}
}
else {
printUsage()
}
}
override fun printUsage() {
Echo("Export ActorValue as JSON format.")
Echo("Usage: exportav (id) filename-without-extension")
Echo("blank ID for player")
}
}

View File

@@ -0,0 +1,27 @@
package net.torvald.terrarum.modulebasegame.console
import net.torvald.terrarum.console.ConsoleCommand
import net.torvald.terrarum.console.Echo
import net.torvald.terrarum.serialise.WriteLayerData
/**
* Created by minjaesong on 2017-07-18.
*/
object ExportLayerData : ConsoleCommand {
override fun execute(args: Array<String>) {
if (args.size < 2) {
printUsage()
return
}
val saveDirectoryName = args[1]
WriteLayerData(saveDirectoryName)
Echo("Layer data exported to $saveDirectoryName/${WriteLayerData.META_FILENAME}")
}
override fun printUsage() {
Echo("Usage: exportlayer savename")
}
}

View File

@@ -0,0 +1,119 @@
package net.torvald.terrarum.modulebasegame.console
import net.torvald.colourutil.Col4096
import net.torvald.terrarum.utils.RasterWriter
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.blockproperties.Block
import net.torvald.terrarum.console.ConsoleCommand
import net.torvald.terrarum.console.Echo
import net.torvald.terrarum.console.EchoError
import net.torvald.terrarum.modulebasegame.Ingame
import java.io.*
import java.util.HashMap
/**
* Created by minjaesong on 2016-01-17.
*/
internal object ExportMap : ConsoleCommand {
//private var mapData: ByteArray? = null
// private var mapDataPointer = 0
private val colorTable = HashMap<Int, Col4096>()
init {
colorTable.put(Block.AIR, Col4096(0xCEF))
colorTable.put(Block.STONE, Col4096(0x888))
colorTable.put(Block.DIRT, Col4096(0x753))
colorTable.put(Block.GRASS, Col4096(0x472))
colorTable.put(Block.ORE_COPPER, Col4096(0x6A8))
colorTable.put(Block.ORE_IRON, Col4096(0xC75))
colorTable.put(Block.ORE_GOLD, Col4096(0xA87))
colorTable.put(Block.ORE_ILMENITE, Col4096(0x8AB))
colorTable.put(Block.ORE_AURICHALCUM, Col4096(0xD92))
colorTable.put(Block.ORE_SILVER, Col4096(0xDDD))
colorTable.put(Block.RAW_DIAMOND, Col4096(0x2BF))
colorTable.put(Block.RAW_RUBY, Col4096(0xB10))
colorTable.put(Block.RAW_EMERALD, Col4096(0x0B1))
colorTable.put(Block.RAW_SAPPHIRE, Col4096(0x01B))
colorTable.put(Block.RAW_TOPAZ, Col4096(0xC70))
colorTable.put(Block.RAW_AMETHYST, Col4096(0x70C))
colorTable.put(Block.WATER, Col4096(0x038))
colorTable.put(Block.LAVA, Col4096(0xF50))
colorTable.put(Block.SAND, Col4096(0xDDB))
colorTable.put(Block.SAND_WHITE, Col4096(0xFFD))
colorTable.put(Block.SAND_RED, Col4096(0xA32))
colorTable.put(Block.SAND_DESERT, Col4096(0xEDB))
colorTable.put(Block.SAND_BLACK, Col4096(0x444))
colorTable.put(Block.SAND_GREEN, Col4096(0x9A6))
colorTable.put(Block.GRAVEL, Col4096(0x664))
colorTable.put(Block.GRAVEL_GREY, Col4096(0x999))
colorTable.put(Block.ICE_NATURAL, Col4096(0x9AB))
colorTable.put(Block.ICE_MAGICAL, Col4096(0x7AC))
colorTable.put(Block.ICE_FRAGILE, Col4096(0x6AF))
colorTable.put(Block.SNOW, Col4096(0xCDE))
}
override fun execute(args: Array<String>) {
val world = (Terrarum.ingame!! as Ingame).world
if (args.size == 2) {
var mapData = ByteArray(world.width * world.height * 3)
var mapDataPointer = 0
for (tile in world.terrainIterator()) {
val colArray = (colorTable as Map<Int, Col4096>)
.getOrElse(tile, { Col4096(0xFFF) }).toByteArray()
for (i in 0..2) {
mapData[mapDataPointer + i] = colArray[i]
}
mapDataPointer += 3
}
val dir = Terrarum.defaultDir + "/Exports/"
val dirAsFile = File(dir)
if (!dirAsFile.exists()) {
dirAsFile.mkdir()
}
try {
RasterWriter.writePNG_RGB(
world.width, world.height, mapData, dir + args[1] + ".png")
Echo("ExportMap: exported to " + args[1] + ".png")
}
catch (e: IOException) {
EchoError("ExportMap: IOException raised.")
e.printStackTrace()
}
// mapData = null
// mapDataPointer = 0
// Free up some memory
System.gc()
}
else {
printUsage()
}
}
override fun printUsage() {
Echo("Usage: export <name>")
Echo("Exports current map into echo image.")
Echo("The image can be found at %appdata%/terrarum/Exports")
}
}

View File

@@ -0,0 +1,18 @@
package net.torvald.terrarum.modulebasegame.console
import net.torvald.terrarum.console.ConsoleCommand
import net.torvald.terrarum.console.Echo
/**
* Created by minjaesong on 2016-01-18.
*/
internal object ForceGC : ConsoleCommand {
override fun execute(args: Array<String>) {
System.gc()
Echo("Invoked System.gc")
}
override fun printUsage() {
Echo("Invoke garbage collection of JVM.")
}
}

View File

@@ -0,0 +1,120 @@
package net.torvald.terrarum.modulebasegame.console
import net.torvald.terrarum.*
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.console.ConsoleCommand
import net.torvald.terrarum.console.Echo
import net.torvald.terrarum.console.EchoError
import net.torvald.terrarum.modulebasegame.Ingame
/**
* Created by minjaesong on 2016-01-19.
*/
internal object GetAV : ConsoleCommand {
override fun execute(args: Array<String>) {
try {
val ingame = Terrarum.ingame!! as Ingame
if (args.size == 1 && ingame.player != null) {
// print all actorvalue of player
val av = ingame.player.actorValue
val keyset = av.keySet
Echo("$ccW== ActorValue list for ${ccY}player $ccW==")
println("[GetAV] == ActorValue list for 'player' ==")
keyset.forEach { elem ->
Echo("$ccM$elem $ccW= $ccG${av[elem as String]}")
println("[GetAV] $elem = ${av[elem]}")
}
}
else if (args.size != 3 && args.size != 2) {
printUsage()
}
else if (args.size == 2) {
// check if args[1] is number or not
if (!args[1].isNum()) { // args[1] is ActorValue name
Echo("${ccW}player.$ccM${args[1]} $ccW= " +
ccG +
ingame.player.actorValue[args[1]] +
" $ccO" +
ingame.player.actorValue[args[1]]!!.javaClass.simpleName
)
println("[GetAV] player.${args[1]} = " +
ingame.player.actorValue[args[1]] +
" " +
ingame.player.actorValue[args[1]]!!.javaClass.simpleName
)
}
else {
// args[1] is actor ID
val actor = ingame.getActorByID(args[1].toInt())
val av = actor.actorValue
val keyset = av.keySet
Echo("$ccW== ActorValue list for $ccY$actor $ccW==")
println("[GetAV] == ActorValue list for '$actor' ==")
if (keyset.isEmpty()) {
Echo("$ccK(nothing)")
println("[GetAV] (nothing)")
}
else {
keyset.forEach { elem ->
Echo("$ccM$elem $ccW= $ccG${av[elem as String]}")
println("[GetAV] $elem = ${av[elem]}")
}
}
}
}
else if (args.size == 3) {
val id = args[1].toInt()
val av = args[2]
Echo("$ccW$id.$ccM$av $ccW= $ccG" +
ingame.getActorByID(id).actorValue[av] +
" $ccO" +
ingame.getActorByID(id).actorValue[av]!!.javaClass.simpleName
)
println("$id.$av = " +
ingame.getActorByID(id).actorValue[av] +
" " +
ingame.getActorByID(id).actorValue[av]!!.javaClass.simpleName
)
}
}
catch (e: NullPointerException) {
if (args.size == 2) {
EchoError(args[1] + ": actor value does not exist.")
System.err.println("[GetAV] ${args[1]}: actor value does not exist.")
}
else if (args.size == 3) {
EchoError(args[2] + ": actor value does not exist.")
System.err.println("[GetAV] ${args[2]}: actor value does not exist.")
}
else {
throw NullPointerException()
}
}
catch (e1: IllegalArgumentException) {
EchoError("${args[1]}: no actor with this ID.")
System.err.println("[GetAV] ${args[1]}: no actor with this ID.")
}
}
fun String.isNum(): Boolean {
try {
this.toInt()
return true
}
catch (e: NumberFormatException) {
return false
}
}
override fun printUsage() {
Echo("${ccW}Get desired ActorValue of specific target.")
Echo("${ccW}Usage: ${ccY}getav $ccG(id) <av>")
Echo("${ccW}blank ID for player")
}
}

View File

@@ -0,0 +1,120 @@
package net.torvald.terrarum.modulebasegame.console
import net.torvald.terrarum.langpack.Lang
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.console.ConsoleCommand
import net.torvald.terrarum.console.Echo
import net.torvald.terrarum.console.EchoError
import net.torvald.terrarum.gameactors.Factionable
import net.torvald.terrarum.modulebasegame.Ingame
import net.torvald.terrarum.modulebasegame.gameactors.Player
import net.torvald.terrarumsansbitmap.gdx.GameFontBase
/**
* Created by minjaesong on 2016-02-17.
*/
internal object GetFactioning : ConsoleCommand {
val ccW = GameFontBase.toColorCode(0xFFFF)
val ccY = GameFontBase.toColorCode(0xFE8F)
val ccM = GameFontBase.toColorCode(0xEAFF)
val ccG = GameFontBase.toColorCode(0x8F8F)
val ccK = GameFontBase.toColorCode(0x888F)
private val PRINT_INDENTATION = "$ccK --> $ccW"
override fun execute(args: Array<String>) {
val error = Error()
fun printOutFactioning(id: Int) {
val a = Terrarum.ingame!!.getActorByID(id)
if (a is Factionable) {
Echo("$ccW== Faction assignment for $ccY${if (id == Player.PLAYER_REF_ID) "player" else id.toString()} $ccW==")
println("[GetFactioning] == Faction assignment for '${if (id == Player.PLAYER_REF_ID) "player" else id.toString()}' ==")
// get all factioning data of player
val factionSet = a.faction
if (factionSet.isEmpty()) {
Echo("The actor has empty faction set.")
println("[GetFactioning] The actor has empty faction set.")
return
}
val count = factionSet.size
Echo("$ccG${count.toString()} $ccW${Lang.pluralise(" faction", count)} assigned.")
println("[GetFactioning] ${count.toString()} ${Lang.pluralise(" faction", count)} assigned.")
for (faction in factionSet) {
Echo("${ccW}faction $ccM${faction.factionName}")
println("[GetFactioning] faction '${faction.factionName}'")
Echo("$ccY Amicable")
println("[GetFactioning] Amicable")
faction.factionAmicable.forEach { s ->
Echo(PRINT_INDENTATION + s)
println("[GetFactioning] --> $s")
}
Echo("$ccY Explicit neutral")
println("[GetFactioning] Explicit neutral")
faction.factionNeutral.forEach { s ->
Echo(PRINT_INDENTATION + s)
println("[GetFactioning] --> $s")
}
Echo("$ccY Hostile")
println("[GetFactioning] Hostile")
faction.factionHostile.forEach { s ->
Echo(PRINT_INDENTATION + s)
println("[GetFactioning] --> $s")
}
Echo("$ccY Fearful")
println("[GetFactioning] Fearful")
faction.factionFearful.forEach { s ->
Echo(PRINT_INDENTATION + s)
println("[GetFactioning] --> $s")
}
}
}
else {
EchoError("The actor is not factionable.")
System.err.println("[GetFactioning] The actor is not factionable.")
}
}
if (args.size == 1) {
printOutFactioning(Player.PLAYER_REF_ID)
}
else {
if (!args[1].isNum()) {
EchoError("Invalid actor ID input.")
System.err.println("[GetFactioning] Invalid actor ID input.")
return
}
try {
val actorID = args[1].toInt()
printOutFactioning(actorID)
}
catch (e: IllegalArgumentException) {
EchoError("${args[1]}: no actor with this ID.")
System.err.println("[GetFactioning] ${args[1]}: no actor with this ID.")
}
}
}
fun String.isNum(): Boolean {
try {
this.toInt()
return true
}
catch (e: NumberFormatException) {
return false
}
}
override fun printUsage() {
}
}

View File

@@ -0,0 +1,25 @@
package net.torvald.terrarum.modulebasegame.console
import net.torvald.terrarum.console.ConsoleCommand
import net.torvald.terrarum.console.Echo
import net.torvald.terrarum.langpack.Lang
/**
* Created by minjaesong on 2016-01-22.
*/
internal object GetLocale : ConsoleCommand {
override fun execute(args: Array<String>) {
Echo(
"Locale: "
+ Lang["MENU_LANGUAGE_THIS"]
+ " ("
+ Lang["MENU_LANGUAGE_THIS_EN"]
+ ")")
}
override fun printUsage() {
Echo("Usage: getlocale")
Echo("Get name of locale currently using.")
}
}

View File

@@ -0,0 +1,21 @@
package net.torvald.terrarum.modulebasegame.console
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.console.ConsoleCommand
import net.torvald.terrarum.console.Echo
import net.torvald.terrarum.modulebasegame.Ingame
/**
* Created by minjaesong on 2016-03-20.
*/
internal object GetTime : ConsoleCommand {
override fun execute(args: Array<String>) {
val worldTime = (Terrarum.ingame!! as Ingame).world.time
Echo(worldTime.getFormattedTime())
}
override fun printUsage() {
Echo("Print current world time in convenient form")
}
}

View File

@@ -0,0 +1,49 @@
package net.torvald.terrarum.modulebasegame.console
import net.torvald.terrarum.Terrarum
import com.google.gson.Gson
import net.torvald.terrarum.console.ConsoleCommand
import net.torvald.terrarum.console.Echo
import net.torvald.terrarum.modulebasegame.Ingame
import java.io.BufferedWriter
import java.io.FileWriter
import java.io.IOException
/**
* Created by minjaesong on 2016-02-10.
*/
internal object GsonTest : ConsoleCommand {
override fun execute(args: Array<String>) {
if (args.size == 2) {
val avelem = Gson().toJsonTree((Terrarum.ingame!! as Ingame).player)
val jsonString = avelem.toString()
val bufferedWriter: BufferedWriter
val writer: FileWriter
try {
writer = FileWriter(Terrarum.defaultDir + "/Exports/" + args[1] + ".json")
bufferedWriter = BufferedWriter(writer)
bufferedWriter.write(jsonString)
bufferedWriter.close()
Echo("GsonTest: exported to " + args[1] + ".json")
}
catch (e: IOException) {
Echo("GsonTest: IOException raised.")
e.printStackTrace()
}
}
else {
printUsage()
}
}
override fun printUsage() {
Echo("Usage: gsontest filename-without-extension")
}
}

View File

@@ -0,0 +1,27 @@
package net.torvald.terrarum.modulebasegame.console
import net.torvald.terrarum.console.ConsoleCommand
import net.torvald.terrarum.console.Echo
import net.torvald.terrarum.langpack.Lang
/**
* Created by minjaesong on 2016-03-22.
*/
internal object Help : ConsoleCommand {
override fun execute(args: Array<String>) {
if (args.size == 1) {
for (i in 1..6) Echo(Lang["HELP_OTF_MAIN_$i"])
}
else if (args[1].toLowerCase() == "slow") {
for (i in 1..4) Echo(Lang["HELP_OTF_SLOW_$i"])
}
else {
for (i in 1..6) Echo(Lang["HELP_OTF_MAIN_$i"])
}
}
override fun printUsage() {
Echo("Prints some utility functions assigned to function row of the keyboard.")
}
}

View File

@@ -0,0 +1,35 @@
package net.torvald.terrarum.modulebasegame.console
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.console.ConsoleCommand
import net.torvald.terrarum.console.Echo
import net.torvald.terrarum.modulebasegame.Ingame
import net.torvald.terrarum.serialise.ReadLayerData
import net.torvald.terrarum.worlddrawer.FeaturesDrawer
import java.io.FileInputStream
/**
* Created by minjaesong on 2017-07-18.
*/
object ImportLayerData : ConsoleCommand {
override fun execute(args: Array<String>) {
if (args.size < 2) {
ExportLayerData.printUsage()
return
}
//val fis = GZIPInputStream(FileInputStream(args[1])) // this gzip is kaput
val fis = FileInputStream(args[1])
(Terrarum.ingame!! as Ingame).world = ReadLayerData(fis)
(Terrarum.ingame!! as Ingame).player.setPosition(
(Terrarum.ingame!! as Ingame).world.spawnY * FeaturesDrawer.TILE_SIZE.toDouble(),
(Terrarum.ingame!! as Ingame).world.spawnX * FeaturesDrawer.TILE_SIZE.toDouble()
)
fis.close()
Echo("Successfully loaded ${args[1]}")
}
override fun printUsage() {
Echo("Usage: importlayer path/to/layer.data")
}
}

View File

@@ -0,0 +1,79 @@
package net.torvald.terrarum.modulebasegame.console
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.console.ConsoleCommand
import net.torvald.terrarum.console.Echo
import net.torvald.terrarum.console.EchoError
import net.torvald.terrarum.modulebasegame.gameactors.Player
import net.torvald.terrarum.modulebasegame.gameactors.Pocketed
import net.torvald.terrarum.itemproperties.ItemCodex
import net.torvald.terrarum.modulebasegame.Ingame
/**
* Created by minjaesong on 2016-12-12.
*/
internal object Inventory : ConsoleCommand {
private var target: Pocketed? = (Terrarum.ingame!! as Ingame).player
override fun execute(args: Array<String>) {
if (args.size == 1) {
printUsage()
}
else {
when (args[1]) {
"list" -> listInventory()
"add" -> if (args.size > 3) addItem(args[2].toInt(), args[3].toInt())
else addItem(args[2].toInt())
"target" -> setTarget(args[2].toInt())
"equip" -> equipItem(args[2].toInt())
else -> printUsage()
}
}
}
private fun listInventory() {
if (target != null) {
if (target!!.inventory.getTotalUniqueCount() == 0) {
Echo("(inventory empty)")
}
else {
target!!.inventory.forEach { val (item, amount) = it
if (amount == 0) {
EchoError("Unexpected zero-amounted item: ID ${item.dynamicID}")
}
Echo("ID $item${if (amount > 1) " ($amount)" else ""}")
}
}
}
}
private fun setTarget(actorRefId: Int = Player.PLAYER_REF_ID) {
val actor = Terrarum.ingame!!.getActorByID(actorRefId)
if (actor !is Pocketed) {
EchoError("Cannot edit inventory of incompatible actor: $actor")
}
else {
target = actor
}
}
private fun addItem(refId: Int, amount: Int = 1) {
if (target != null) {
target!!.addItem(ItemCodex[refId], amount)
}
}
private fun equipItem(refId: Int) {
if (target != null) {
val item = ItemCodex[refId]
target!!.equipItem(item)
}
}
override fun printUsage() {
Echo("Usage: inventory command arguments")
Echo("Available commands:")
Echo("list | assign slot | add itemid [amount] | target [actorid] | equip itemid")
}
}

View File

@@ -0,0 +1,23 @@
package net.torvald.terrarum.modulebasegame.console
import net.torvald.terrarum.console.ConsoleCommand
import net.torvald.terrarum.console.Echo
import net.torvald.terrarum.swingapp.IMStringReader
/**
* Created by minjaesong on 2017-02-05.
*/
internal object JavaIMTest : ConsoleCommand {
override fun execute(args: Array<String>) {
IMStringReader(
{ Echo("[JavaIMTest -> IMStringReader] $it") }, // send input to Echo
"JavaIMTest"
)
}
override fun printUsage() {
Echo("Tests Swing input window to get non-English text input")
}
}

View File

@@ -0,0 +1,34 @@
package net.torvald.terrarum.modulebasegame.console
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.console.ConsoleCommand
import net.torvald.terrarum.console.Echo
import net.torvald.terrarum.console.EchoError
import net.torvald.terrarum.langpack.Lang
/**
* Created by minjaesong on 2017-01-31.
*/
internal object KillActor : ConsoleCommand {
override fun execute(args: Array<String>) {
if (args.size == 2) {
try {
val actorid = args[1].toInt()
Terrarum.ingame!!.removeActor(actorid)
}
catch (e: NumberFormatException) {
EchoError("Wrong number input.")
}
catch (e1: RuntimeException) {
EchoError(e1.message ?: Lang["ERROR_GENERIC_TEXT"])
}
}
else {
printUsage()
}
}
override fun printUsage() {
Echo("Usage: kill actorid")
}
}

View File

@@ -0,0 +1,21 @@
package net.torvald.terrarum.modulebasegame.console
import net.torvald.terrarum.console.ConsoleCommand
import net.torvald.terrarum.console.Echo
import net.torvald.terrarum.langpack.Lang
/**
* Created by minjaesong on 2016-07-11.
*/
internal object LangTest : ConsoleCommand {
override fun printUsage() {
Echo("Prints out string in the current lang pack by STRING_ID provided")
}
override fun execute(args: Array<String>) {
if (args.size < 2)
printUsage()
else
Echo(Lang[args[1].toUpperCase()])
}
}

View File

@@ -0,0 +1,20 @@
package net.torvald.terrarum.modulebasegame.console
import net.torvald.random.HQRNG
import net.torvald.terrarum.console.ConsoleCommand
import net.torvald.terrarum.console.Echo
object MoneyDisp : ConsoleCommand {
override fun execute(args: Array<String>) {
if (args.size == 2) {
Echo("¤${0x3000.toChar()}${args[1]}")
}
else {
Echo("¤${0x3000.toChar()}${HQRNG().nextInt(100000)}")
}
}
override fun printUsage() {
Echo("Usage: money [amount] — Prints given or random amount of money")
}
}

View File

@@ -0,0 +1,46 @@
package net.torvald.terrarum.modulebasegame.console
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.audio.Music
import net.torvald.terrarum.console.ConsoleCommand
import net.torvald.terrarum.console.Echo
/**
* Created by minjaesong on 2016-08-02.
*/
internal object MusicTest : ConsoleCommand {
var music: Music? = null
/**
* Args 0: command given
* Args 1: first argument
*
* e.g. in ```setav mass 74```, zeroth args will be ```setav```.
*/
override fun execute(args: Array<String>) {
if (args.size < 2) {
printUsage()
return
}
if (args[1] == "stop") {
music!!.stop()
return
}
val type = args[1].substringAfter('.').toUpperCase()
/*AudioLoader.getStreamingAudio(
type,
File("./assets/sounds/test/${args[1]}").absoluteFile.toURI().toURL()
).playAsMusic(1f, 1f, false)*/
music = Gdx.audio.newMusic(Gdx.files.internal("./assets/sounds/test/${args[1]}"))
music!!.play()
}
override fun printUsage() {
Echo("Usage: musictest filename/in/res/sounds/test")
Echo("musictest stop to stop playback")
}
}

View File

@@ -0,0 +1,18 @@
package net.torvald.terrarum.modulebasegame.console
import net.torvald.terrarum.console.ConsoleCommand
import net.torvald.terrarum.console.Echo
/**
* Created by minjaesong on 2016-07-04.
*/
internal object PrintRandomTips : ConsoleCommand {
override fun execute(args: Array<String>) {
Echo("Nope.")
//Echo(Lang["GAME_TIPS_${Random().nextInt(Lang.TIPS_COUNT) + 1}"])
}
override fun printUsage() {
Echo("Prints random tips for game.")
}
}

View File

@@ -0,0 +1,23 @@
package net.torvald.terrarum.modulebasegame.console
import net.torvald.terrarum.*
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.console.ConsoleCommand
import net.torvald.terrarum.console.Echo
import net.torvald.terrarum.modulebasegame.Ingame
/**
* Created by minjaesong on 2016-06-16.
*/
internal object Seed : ConsoleCommand {
override fun execute(args: Array<String>) {
Echo("Map$ccW: $ccG${(Terrarum.ingame!! as Ingame).world.generatorSeed}")
println("[seed] Map$ccW: $ccG${(Terrarum.ingame!! as Ingame).world.generatorSeed}")
// TODO display randomiser seed
}
override fun printUsage() {
Echo("prints out the generator seed of the current game.")
}
}

View File

@@ -0,0 +1,106 @@
package net.torvald.terrarum.modulebasegame.console
import net.torvald.terrarum.*
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.console.ConsoleCommand
import net.torvald.terrarum.console.Echo
import net.torvald.terrarum.console.EchoError
import net.torvald.terrarum.modulebasegame.Ingame
/**
* Created by minjaesong on 2016-01-15.
*/
internal object SetAV : ConsoleCommand {
override fun printUsage() {
Echo("${ccW}Set actor value of specific target to desired value.")
Echo("${ccW}Usage: ${ccY}setav ${ccG}(id) <av> <val>")
Echo("${ccW}blank ID for player. Data type will be inferred automatically.")
Echo("${ccR}Contaminated (e.g. double -> string) ActorValue will crash the game,")
Echo("${ccR}so make sure it will not happen before you issue the command!")
Echo("${ccW}Use ${ccG}__true ${ccW}and ${ccG}__false ${ccW}for boolean value.")
}
override fun execute(args: Array<String>) {
fun parseAVInput(arg: String): Any {
var inputval: Any
try {
inputval = Integer(arg) // try for integer
}
catch (e: NumberFormatException) {
try {
inputval = arg.toDouble() // try for double
}
catch (ee: NumberFormatException) {
if (arg.equals("__true", ignoreCase = true)) {
inputval = true
}
else if (arg.equals("__false", ignoreCase = true)) {
inputval = false
}
else {
inputval = arg // string if not number
}
}
}
return inputval
}
// setav <id, or blank for player> <av> <val>
if (args.size != 4 && args.size != 3) {
printUsage()
}
else if (args.size == 3) {
val newValue = parseAVInput(args[2])
// check if av is number
if (args[1].isNum()) {
EchoError("Illegal ActorValue ${args[1]}: ActorValue cannot be a number.")
System.err.println("[SetAV] Illegal ActorValue ${args[1]}: ActorValue cannot be a number.")
return
}
(Terrarum.ingame!! as Ingame).player.actorValue[args[1]] = newValue
Echo("${ccW}Set $ccM${args[1]} ${ccW}for ${ccY}player ${ccW}to $ccG$newValue")
println("[SetAV] set ActorValue '${args[1]}' for player to '$newValue'.")
}
else if (args.size == 4) {
try {
val id = args[1].toInt()
val newValue = parseAVInput(args[3])
val actor = Terrarum.ingame!!.getActorByID(id)
// check if av is number
if (args[2].isNum()) {
EchoError("Illegal ActorValue ${args[2]}: ActorValue cannot be a number.")
System.err.println("[SetAV] Illegal ActorValue ${args[2]}: ActorValue cannot be a number.")
return
}
actor.actorValue[args[2]] = newValue
Echo("${ccW}Set $ccM${args[2]} ${ccW}for $ccY$id ${ccW}to $ccG$newValue")
println("[SetAV] set ActorValue '${args[2]}' for $actor to '$newValue'.")
}
catch (e: IllegalArgumentException) {
if (args.size == 4) {
EchoError("${args[1]}: no actor with this ID.")
System.err.println("[SetAV] ${args[1]}: no actor with this ID.")
}
}
}
}
fun String.isNum(): Boolean {
try {
this.toInt()
return true
}
catch (e: NumberFormatException) {
return false
}
}
}

View File

@@ -0,0 +1,32 @@
package net.torvald.terrarum.modulebasegame.console
import net.torvald.terrarum.langpack.Lang
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.console.ConsoleCommand
import net.torvald.terrarum.modulebasegame.Ingame
/**
* Created by minjaesong on 2016-01-23.
*/
internal object SetBulletin : ConsoleCommand {
override fun execute(args: Array<String>) {
val testMsg = arrayOf(
Lang["ERROR_SAVE_CORRUPTED"],
Lang["MENU_LABEL_CONTINUE_QUESTION"]
)
send(testMsg)
}
override fun printUsage() {
}
/**
* Actually send notifinator
* @param message real message
*/
fun send(message: Array<String>) {
(Terrarum.ingame!! as Ingame).sendNotification(message)
println("sent notifinator")
}
}

View File

@@ -0,0 +1,39 @@
package net.torvald.terrarum.modulebasegame.console
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.console.ConsoleCommand
import net.torvald.terrarum.console.Echo
import net.torvald.terrarum.console.EchoError
import net.torvald.terrarum.modulebasegame.Ingame
import net.torvald.terrarum.modulebasegame.gameactors.ActorWithPhysics
/**
* Created by minjaesong on 2017-01-20.
*/
internal object SetScale : ConsoleCommand {
override fun execute(args: Array<String>) {
if (args.size == 2 || args.size == 3) {
try {
val targetID = if (args.size == 3) args[1].toInt() else (Terrarum.ingame!! as Ingame).player.referenceID
val scale = args[if (args.size == 3) 2 else 1].toDouble()
val target = Terrarum.ingame!!.getActorByID(targetID!!)
if (target !is ActorWithPhysics) {
EchoError("Target is not ActorWithPhysics")
}
else {
target.scale = scale
}
}
catch (e: NumberFormatException) {
EchoError("Wrong number input")
}
}
else printUsage()
}
override fun printUsage() {
Echo("Usage: setscale scale | setscale actorID scale")
}
}

View File

@@ -0,0 +1,37 @@
package net.torvald.terrarum.modulebasegame.console
import net.torvald.terrarum.modulebasegame.gameworld.WorldTime
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.console.ConsoleCommand
import net.torvald.terrarum.console.Echo
import net.torvald.terrarum.modulebasegame.Ingame
/**
* Created by minjaesong on 2016-03-20.
*/
internal object SetTime : ConsoleCommand {
override fun execute(args: Array<String>) {
val world = (Terrarum.ingame!! as Ingame).world
if (args.size == 2) {
val timeToSet = WorldTime.parseTime(args[1])
world.time.setTimeOfToday(timeToSet)
Echo("Set time to ${world.time.todaySeconds} " +
"(${world.time.hours}h${formatMin(world.time.minutes)})")
}
else {
printUsage()
}
}
private fun formatMin(min: Int): String {
return if (min < 10) "0${min.toString()}" else min.toString()
}
override fun printUsage() {
Echo("usage: settime <39201-in sec or 13h32-in hour>")
}
}

View File

@@ -0,0 +1,34 @@
package net.torvald.terrarum.modulebasegame.console
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.console.ConsoleCommand
import net.torvald.terrarum.console.Echo
import net.torvald.terrarum.modulebasegame.Ingame
/**
* Created by minjaesong on 2016-03-20.
*/
internal object SetTimeDelta : ConsoleCommand {
val HARD_LIMIT = 60
override fun execute(args: Array<String>) {
val world = (Terrarum.ingame!! as Ingame).world
if (args.size == 2) {
world.time.timeDelta = args[1].toInt()
if (world.time.timeDelta == 0)
Echo("時間よ止まれ!ザ・ワルド!!")
else
Echo("Set time delta to ${world.time.timeDelta}")
}
else {
printUsage()
}
}
override fun printUsage() {
Echo("usage: settimedelta <int>")
}
}

View File

@@ -0,0 +1,52 @@
package net.torvald.terrarum.modulebasegame.console
import net.torvald.terrarum.modulebasegame.gameactors.PhysTestBall
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.console.ConsoleCommand
import net.torvald.terrarum.console.Echo
import net.torvald.terrarum.modulebasegame.Ingame
import org.dyn4j.geometry.Vector2
/**
* Created by minjaesong on 2016-03-05.
*/
internal object SpawnPhysTestBall : ConsoleCommand {
@Throws(Exception::class)
override fun execute(args: Array<String>) {
val world = (Terrarum.ingame!! as Ingame).world
val mouseX = Terrarum.mouseX
val mouseY = Terrarum.mouseY
if (args.size >= 3) {
val elasticity = args[1].toDouble()
val xvel = args[2].toDouble()
val yvel = if (args.size >= 4) args[3].toDouble() else 0.0
val ball = PhysTestBall(world)
ball.setPosition(mouseX, mouseY)
ball.elasticity = elasticity
ball.applyForce(Vector2(xvel, yvel))
Terrarum.ingame!!.addNewActor(ball)
}
else if (args.size == 2) {
val elasticity = args[1].toDouble()
val ball = PhysTestBall(world)
ball.setPosition(mouseX, mouseY)
ball.elasticity = elasticity
Terrarum.ingame!!.addNewActor(ball)
}
else {
printUsage()
}
}
override fun printUsage() {
Echo("usage: spawnball elasticity [x velocity] [y velocity]")
}
}

View File

@@ -0,0 +1,26 @@
package net.torvald.terrarum.modulebasegame.console
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.console.ConsoleCommand
import net.torvald.terrarum.console.Echo
import net.torvald.terrarum.modulebasegame.Ingame
import net.torvald.terrarum.modulebasegame.gameactors.PhysTestLuarLander
/**
* Created by minjaesong on 2018-01-18.
*/
internal object SpawnPhysTestLunarLander : ConsoleCommand {
override fun execute(args: Array<String>) {
val mouseX = Terrarum.mouseX
val mouseY = Terrarum.mouseY
val lander = PhysTestLuarLander((Terrarum.ingame!! as Ingame).world)
lander.setPosition(mouseX, mouseY)
Terrarum.ingame!!.addNewActor(lander)
}
override fun printUsage() {
Echo("control it with arrow keys")
}
}

View File

@@ -0,0 +1,26 @@
package net.torvald.terrarum.modulebasegame.console
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.console.ConsoleCommand
import net.torvald.terrarum.console.Echo
import net.torvald.terrarum.modulebasegame.gameactors.DecodeTapestry
import java.io.File
/**
* Created by minjaesong on 2017-01-14.
*/
internal object SpawnTapestry : ConsoleCommand {
override fun execute(args: Array<String>) {
if (args.size < 2) {
printUsage()
return
}
val tapestry = DecodeTapestry(File(args[1]))
Terrarum.ingame!!.addNewActor(tapestry)
}
override fun printUsage() {
Echo("Usage: spawntapestry <tapestry_file>")
}
}

View File

@@ -0,0 +1,23 @@
package net.torvald.terrarum.modulebasegame.console
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.console.ConsoleCommand
import net.torvald.terrarum.console.Echo
import net.torvald.terrarum.modulebasegame.Ingame
import net.torvald.terrarum.modulebasegame.gameactors.FixtureTikiTorch
/**
* Created by minjaesong on 2016-12-17.
*/
internal object SpawnTikiTorch : ConsoleCommand {
override fun execute(args: Array<String>) {
val torch = FixtureTikiTorch((Terrarum.ingame!! as Ingame).world)
torch.setPosition(Terrarum.mouseX, Terrarum.mouseY)
Terrarum.ingame!!.addNewActor(torch)
}
override fun printUsage() {
Echo("Usage: spawntorch")
}
}

View File

@@ -0,0 +1,115 @@
package net.torvald.terrarum.modulebasegame.console
import net.torvald.terrarum.worlddrawer.FeaturesDrawer
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.console.ConsoleCommand
import net.torvald.terrarum.console.Echo
import net.torvald.terrarum.console.EchoError
import net.torvald.terrarum.modulebasegame.Ingame
import net.torvald.terrarum.modulebasegame.gameactors.ActorWithPhysics
/**
* Created by minjaesong on 2016-01-24.
*/
internal object Teleport : ConsoleCommand {
override fun execute(args: Array<String>) {
if (args.size == 3) {
val x: Int
val y: Int
try {
x = args[1].toInt() * FeaturesDrawer.TILE_SIZE + FeaturesDrawer.TILE_SIZE / 2
y = args[2].toInt() * FeaturesDrawer.TILE_SIZE + FeaturesDrawer.TILE_SIZE / 2
}
catch (e: NumberFormatException) {
EchoError("Teleport: wrong number input.")
return
}
(Terrarum.ingame!! as Ingame).player.setPosition(x.toDouble(), y.toDouble())
}
else if (args.size == 4) {
if (args[2].toLowerCase() != "to") {
EchoError("missing 'to' on teleport command")
return
}
val fromActor: ActorWithPhysics
val targetActor: ActorWithPhysics
try {
val fromActorID = args[1].toInt()
val targetActorID = if (args[3].toLowerCase() == "player")
(Terrarum.ingame!! as Ingame).player.referenceID!!
else
args[3].toInt()
// if from == target, ignore the action
if (fromActorID == targetActorID) return
if (Terrarum.ingame!!.getActorByID(fromActorID) !is ActorWithPhysics ||
Terrarum.ingame!!.getActorByID(targetActorID) !is ActorWithPhysics) {
throw IllegalArgumentException()
}
else {
fromActor = Terrarum.ingame!!.getActorByID(fromActorID) as ActorWithPhysics
targetActor = Terrarum.ingame!!.getActorByID(targetActorID) as ActorWithPhysics
}
}
catch (e: NumberFormatException) {
EchoError("Teleport: illegal number input")
return
}
catch (e1: IllegalArgumentException) {
EchoError("Teleport: operation not possible on specified actor(s)")
return
}
fromActor.setPosition(
targetActor.feetPosVector.x,
targetActor.feetPosVector.y
)
}
else if (args.size == 5) {
if (args[2].toLowerCase() != "to") {
EchoError("missing 'to' on teleport command")
return
}
val actor: ActorWithPhysics
val x: Int
val y: Int
try {
x = args[3].toInt() * FeaturesDrawer.TILE_SIZE + FeaturesDrawer.TILE_SIZE / 2
y = args[4].toInt() * FeaturesDrawer.TILE_SIZE + FeaturesDrawer.TILE_SIZE / 2
val actorID = args[1].toInt()
if (Terrarum.ingame!!.getActorByID(actorID) !is ActorWithPhysics) {
throw IllegalArgumentException()
}
else {
actor = Terrarum.ingame!!.getActorByID(actorID) as ActorWithPhysics
}
}
catch (e: NumberFormatException) {
EchoError("Teleport: illegal number input")
return
}
catch (e1: IllegalArgumentException) {
EchoError("Teleport: operation not possible on specified actor")
return
}
actor.setPosition(x.toDouble(), y.toDouble())
}
else {
printUsage()
}
}
override fun printUsage() {
Echo("Usage: teleport x-tile y-tile")
Echo(" teleport actorid to x-tile y-tile")
Echo(" teleport actorid to actorid")
Echo(" teleport actorid to player")
}
}

View File

@@ -0,0 +1,22 @@
package net.torvald.terrarum.modulebasegame.console
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.console.ConsoleCommand
import net.torvald.terrarum.console.Echo
import net.torvald.terrarum.modulebasegame.Ingame
/**
* Created by minjaesong on 2016-01-19.
*/
internal object ToggleNoClip : ConsoleCommand {
override fun execute(args: Array<String>) {
val status = (Terrarum.ingame!! as Ingame).player.isNoClip()
(Terrarum.ingame!! as Ingame).player.setNoClip(!status)
Echo("Set no-clip status to " + (!status).toString())
}
override fun printUsage() {
Echo("toggle no-clip status of player")
}
}

View File

@@ -0,0 +1,44 @@
package net.torvald.terrarum.modulebasegame.console
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.console.ConsoleCommand
import net.torvald.terrarum.console.Echo
/**
* Created by minjaesong on 2016-01-25.
*/
internal object Zoom : ConsoleCommand {
override fun execute(args: Array<String>) {
if (args.size == 2) {
var zoom: Float
try {
zoom = args[1].toFloat()
}
catch (e: NumberFormatException) {
Echo("Wrong number input.")
return
}
if (zoom < Terrarum.ingame!!.ZOOM_MINIMUM) {
zoom = Terrarum.ingame!!.ZOOM_MINIMUM
}
else if (zoom > Terrarum.ingame!!.ZOOM_MAXIMUM) {
zoom = Terrarum.ingame!!.ZOOM_MAXIMUM
}
Terrarum.ingame!!.screenZoom = zoom
System.gc()
Echo("Set screen zoom to " + zoom.toString())
}
else {
printUsage()
}
}
override fun printUsage() {
Echo("Usage: zoom [zoom]")
}
}

View File

@@ -0,0 +1,209 @@
package net.torvald.terrarum.modulebasegame.debuggerapp
import net.torvald.terrarum.*
import net.torvald.terrarum.gameactors.ActorValue
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.console.Echo
import net.torvald.terrarum.modulebasegame.console.SetAV
import net.torvald.terrarum.gameactors.Actor
import net.torvald.terrarum.modulebasegame.Ingame
import net.torvald.terrarum.modulebasegame.gameactors.ActorWithPhysics
import net.torvald.terrarum.worlddrawer.FeaturesDrawer
import java.awt.BorderLayout
import java.awt.GridLayout
import java.awt.event.MouseAdapter
import java.awt.event.MouseEvent
import javax.swing.*
/**
* Created by minjaesong on 2016-12-29.
*/
class ActorValueTracker constructor() : JFrame() {
constructor(actor: Actor?) : this() {
setTrackingActor(actor)
}
private val avInfoArea = JTextArea()
private val avInfoScroller = JScrollPane(avInfoArea)
private val avPosArea = JTextArea()
private val avPosScroller = JScrollPane(avPosArea)
private var actor: ActorWithPhysics? = null
private var actorValue: ActorValue? = null
private val modavInputKey = JTextField()
private val modavInputValue = JTextField()
private val buttonAddAV = JButton("Add/Mod")
private val buttonDelAV = JButton("Delete")
//private val selectedActorLabel = JLabel("Selected actor: ")
private val actorIDField = JTextField()
private val buttonChangeActor = JButton("Change")
init {
title = "Actor value tracker"
defaultCloseOperation = JFrame.DISPOSE_ON_CLOSE
val divPanel = JPanel()
divPanel.layout = BorderLayout(0, 2)
avInfoArea.highlighter = null // prevent text-drag-crash
avPosArea.highlighter = null // prevent text-drag-crash
avInfoScroller.horizontalScrollBarPolicy = JScrollPane.HORIZONTAL_SCROLLBAR_NEVER
avPosScroller.horizontalScrollBarPolicy = JScrollPane.HORIZONTAL_SCROLLBAR_NEVER
if (actor != null) {
actorIDField.text = "${actor!!.referenceID}"
}
// button listener for buttons
buttonAddAV.addMouseListener(object : MouseAdapter() {
override fun mousePressed(e: MouseEvent?) {
if (actor != null && modavInputKey.text.isNotBlank() && modavInputValue.text.isNotBlank()) {
SetAV.execute((
"setav;" +
"${actor!!.referenceID};" +
"${modavInputKey.text};" +
"${modavInputValue.text}"
).split(';').toTypedArray())
}
}
})
buttonDelAV.addMouseListener(object : MouseAdapter() {
override fun mousePressed(e: MouseEvent?) {
if (actorValue != null && modavInputKey.text.isNotBlank()) {
actorValue!!.remove(modavInputKey.text)
Echo("${ccW}Removed ${ccM}${modavInputKey.text} ${ccW}of ${ccY}${actor!!.referenceID}")
println("[ActorValueTracker] Removed ActorValue '${modavInputKey.text}' of $actor")
}
}
})
buttonChangeActor.addMouseListener(object : MouseAdapter() {
override fun mousePressed(e: MouseEvent?) {
if (actorIDField.text.toLowerCase() == "player") {
actor = (Terrarum.ingame!! as Ingame).player
actorValue = actor!!.actorValue
}
else if (actorIDField.text.isNotBlank()) {
actor = Terrarum.ingame!!.getActorByID(actorIDField.text.toInt()) as ActorWithPhysics
actorValue = actor!!.actorValue
}
}
})
// panel elements
val actorNameBar = JPanel()
actorNameBar.layout = BorderLayout(2, 0)
actorNameBar.add(JLabel("RefID: "), BorderLayout.LINE_START)
actorNameBar.add(actorIDField, BorderLayout.CENTER)
actorNameBar.add(buttonChangeActor, BorderLayout.LINE_END)
val posAndAV = JPanel()
posAndAV.layout = BorderLayout()
posAndAV.add(avPosScroller, BorderLayout.PAGE_START)
posAndAV.add(avInfoScroller, BorderLayout.CENTER)
val toolbox = JPanel()
toolbox.layout = BorderLayout()
val toolpanel = JPanel()
toolpanel.layout = GridLayout(1, 0)
toolpanel.add(buttonAddAV)
toolpanel.add(buttonDelAV)
val modpanelLabels = JPanel()
modpanelLabels.layout = BorderLayout(4, 0)
modpanelLabels.add(JLabel("Key"), BorderLayout.PAGE_START)
modpanelLabels.add(JLabel("Value"), BorderLayout.CENTER)
val modpanelFields = JPanel()
modpanelFields.layout = BorderLayout(4, 2)
modpanelFields.add(modavInputKey, BorderLayout.PAGE_START)
modpanelFields.add(modavInputValue, BorderLayout.CENTER)
val modpanel = JPanel()
modpanel.layout = BorderLayout(4, 2)
modpanel.add(modpanelLabels, BorderLayout.LINE_START)
modpanel.add(modpanelFields, BorderLayout.CENTER)
modpanel.add(JLabel(
"<html>Messed-up type or careless delete will crash the game.<br>" +
"Prepend two underscores for boolean literals.</html>"
), BorderLayout.PAGE_END)
toolbox.add(toolpanel, BorderLayout.PAGE_START)
toolbox.add(modpanel, BorderLayout.CENTER)
divPanel.add(actorNameBar, BorderLayout.PAGE_START)
divPanel.add(posAndAV, BorderLayout.CENTER)
divPanel.add(toolbox, BorderLayout.PAGE_END)
this.add(divPanel)
this.setSize(300, 600)
this.isVisible = true
}
fun setTrackingActor(actor: Actor?) {
this.actorValue = actor?.actorValue
this.title = "AVTracker — $actor"
if (actor is ActorWithPhysics) {
this.actor = actor
}
update()
}
fun update() {
val sb = StringBuilder()
if (actor != null) {
sb.append("toString: ${actor!!}\n")
sb.append("X: ${actor!!.hitbox.canonicalX} (${(actor!!.hitbox.canonicalX / FeaturesDrawer.TILE_SIZE).toInt()})\n")
sb.append("Y: ${actor!!.hitbox.canonicalY} (${(actor!!.hitbox.canonicalY / FeaturesDrawer.TILE_SIZE).toInt()})")
avPosArea.text = "$sb"
sb.setLength(0) // clear stringbuffer
}
if (actorValue != null) {
for (key in actorValue!!.keySet) {
val value = actorValue!![key.toString()]!!
val type = value.javaClass.simpleName
sb.append("$key = $value ($type)\n")
}
if (sb.isNotEmpty()) {
sb.deleteCharAt(sb.length - 1) // delete trailing \n
}
avInfoArea.text = "$sb"
}
else {
avInfoArea.text = ""
}
}
}
/*
+--------------------------------+
| Actor: [5333533 ] [Change] | LBL TFL BTN
+--------------------------------+
| X: 65532.655654747 (4095) | TAR
| Y: 3050.4935465 (190) |
| ... |
+--------------------------------+
| [ Add/Mod ] [ Delete ] | BTN BTN
+--------------------------------+
| Key [ ] | LBL TFL
| Value [ ] | LBL TFL
+--------------------------------+
*/

View File

@@ -0,0 +1,111 @@
package net.torvald.terrarum.modulebasegame.gameactors
/**
* See [res/raw/Creature_raw_doc.md] for information about raw.
*
* Created by minjaesong on 2016-04-02.
*/
object AVKey {
const val BUFF = "buff"
/** pixels per frame
* walking/running speed
*/
const val SPEED = "speed"
const val SPEEDBUFF = "$SPEED$BUFF"
/** pixels per frame squared
* acceleration of the movement (e.g. running, flying, driving, etc.)
*/
const val ACCEL = "accel"
const val ACCELBUFF = "$ACCEL$BUFF"
/** internal value used by ActorHumanoid to implement friction in walkfunction */
const val SCALE = "scale"
const val SCALEBUFF = "$SCALE$BUFF" // aka PHYSIQUE
/** pixels */
const val BASEHEIGHT = "baseheight"
/** kilogrammes */
const val BASEMASS = "basemass"
/** pixels per frame */
const val JUMPPOWER = "jumppower"
const val JUMPPOWERBUFF = "$JUMPPOWER$BUFF"
/** Int
* "Default" value of 1 000
*/
const val STRENGTH = "strength"
const val STRENGTHBUFF = "$STRENGTH$BUFF"
const val ENCUMBRANCE = "encumbrance"
/** 30-bit RGB (Int)
* 0000 0010000000 0010000000 0010000000
* ^ Red ^ Green ^ Blue
*/
const val LUMR = "luminosityred"
const val LUMG = "luminositygreen"
const val LUMB = "luminosityblue"
const val LUMA = "luminosityuv"
const val DRAGCOEFF = "dragcoeff"
const val FALLDAMPENMULT = "falldampenmult"
/** String
* e.g. Jarppi
*/
const val NAME = "name"
/** String
* e.g. Duudsoni
*/
const val RACENAME = "racename"
/** String
* e.g. Duudsonit
*/
const val RACENAMEPLURAL = "racenameplural"
/** killogrammes
* will affect attack strength, speed and inventory label
* (see "Attack momentum calculator.numbers")
* e.g. Hatchet (tiny)
*/
const val TOOLSIZE = "toolsize"
/** Boolean
* whether the player can talk with it
*/
const val INTELLIGENT = "intelligent"
/** (unit TBA)
* base defence point of the species
*/
const val BASEDEFENCE = "basedefence"
/** (unit TBA)
* current defence point of worn armour(s)
*/
const val ARMOURDEFENCE = "armourdefence"
const val ARMOURDEFENCEBUFF = "$ARMOURDEFENCE$BUFF"
const val MAGICREGENRATE = "magicregenrate"
const val MAGICREGENRATEBUFF = "$MAGICREGENRATE$BUFF"
/** Double
*
*/
const val ACTION_INTERVAL = "actioninterval"
/** String
* UUID for certain fixtures
*/
const val UUID = "uuid"
const val __PLAYER_QUICKSLOTSEL = "__quickslotselection"
/** 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"
const val HEALTH = "health"
const val MAGIC = "magic"
}

View File

@@ -0,0 +1,648 @@
package net.torvald.terrarum.modulebasegame.gameactors
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.graphics.Color
import com.jme3.math.FastMath
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.bipolarClamp
import net.torvald.terrarum.gameactors.Controllable
import net.torvald.terrarum.gameactors.Factionable
import net.torvald.terrarum.gameactors.Hitbox
import net.torvald.terrarum.gameactors.Luminous
import net.torvald.terrarum.gameactors.faction.Faction
import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.itemproperties.GameItem
import net.torvald.terrarum.itemproperties.Material
import net.torvald.terrarum.modulebasegame.Ingame
import net.torvald.terrarum.realestate.LandUtil
import net.torvald.terrarum.modulebasegame.ui.UIInventoryFull
import net.torvald.terrarum.worlddrawer.LightmapRenderer
import org.dyn4j.geometry.Vector2
import java.util.*
/**
* Humanoid actor class to provide same controlling function (such as work, jump)
* Also applies unreal air friction for movement control
*
* Created by minjaesong on 2016-10-24.
*/
open class ActorHumanoid(
world: GameWorld,
birth: GameDate,
death: GameDate? = null,
usePhysics: Boolean = true
) : HistoricalFigure(world, birth, death, usePhysics = usePhysics), Controllable, Pocketed, Factionable, Luminous, LandHolder {
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
/** Must be set by PlayerFactory */
override var faction: HashSet<Faction> = HashSet()
/**
* Absolute tile index. index(x, y) = y * map.width + x
* The arraylist will be saved in JSON format with GSON.
*/
override var houseDesignation: ArrayList<Long>? = ArrayList()
override fun addHouseTile(x: Int, y: Int) {
if (houseDesignation != null) houseDesignation!!.add(LandUtil.getBlockAddr(world, x, y))
}
override fun removeHouseTile(x: Int, y: Int) {
if (houseDesignation != null) houseDesignation!!.remove(LandUtil.getBlockAddr(world, x, y))
}
override fun clearHouseDesignation() {
if (houseDesignation != null) houseDesignation!!.clear()
}
override var color: Color
get() = Color(
(actorValue.getAsFloat(AVKey.LUMR) ?: 0f) / LightmapRenderer.MUL_FLOAT,
(actorValue.getAsFloat(AVKey.LUMG) ?: 0f) / LightmapRenderer.MUL_FLOAT,
(actorValue.getAsFloat(AVKey.LUMB) ?: 0f) / LightmapRenderer.MUL_FLOAT,
(actorValue.getAsFloat(AVKey.LUMA) ?: 0f) / LightmapRenderer.MUL_FLOAT
)
set(value) {
actorValue[AVKey.LUMR] = value.r * LightmapRenderer.MUL_FLOAT
actorValue[AVKey.LUMG] = value.g * LightmapRenderer.MUL_FLOAT
actorValue[AVKey.LUMB] = value.b * LightmapRenderer.MUL_FLOAT
actorValue[AVKey.LUMA] = value.a * LightmapRenderer.MUL_FLOAT
}
/**
* Arguments:
*
* Hitbox(x-offset, y-offset, width, height)
* (Use ArrayList for normal circumstances)
*/
override val lightBoxList: List<Hitbox>
get() = arrayOf(Hitbox(2.0, 2.0, hitbox.width - 3, hitbox.height - 3)).toList() // things are asymmetric!!
// use getter; dimension of the player may change by time.
@Transient val BASE_DENSITY = 980.0
companion object {
//@Transient internal const val ACCEL_MULT_IN_FLIGHT: Double = 0.21
@Transient internal const val WALK_ACCEL_BASE: Double = 0.67
@Transient const val BASE_HEIGHT = 40
// 0.33333 miliseconds
@Transient const val BASE_ACTION_INTERVAL = 1.0 / 3.0
@Transient const val SPRITE_ROW_IDLE = 0
@Transient const val SPRITE_ROW_WALK = 1
}
////////////////////////////////
// MOVEMENT RELATED FUNCTIONS //
////////////////////////////////
var axisX = 0f
var axisY = 0f
var axisRX = 0f
var axisRY = 0f
/** empirical value. */
@Transient private val JUMP_ACCELERATION_MOD = 51.0 / 10000.0 // (170 * (17/MAX_JUMP_LENGTH)^2) / 10000.0
@Transient private val WALK_FRAMES_TO_MAX_ACCEL = 6
@Transient private val LEFT = 1
@Transient private val RIGHT = 2
@Transient private val KEY_NULL = -1
/** how long the jump button has down, in frames */
internal var jumpCounter = 0
internal var jumpAcc = 0.0
/** how long the walk button has down, in frames */
internal var walkCounterX = 0
internal var walkCounterY = 0
@Transient private val MAX_JUMP_LENGTH = 25 // manages "heaviness" of the jump control. Higher = heavier
private var readonly_totalX = 0.0
private var readonly_totalY = 0.0
internal var jumping = false
internal var airJumpingAllowed = false
internal var walkHeading: Int = 0
@Transient private var prevHMoveKey = KEY_NULL
@Transient private var prevVMoveKey = KEY_NULL
internal var noClip = false
@Transient private val AXIS_KEYBOARD = -13372f // leetz
@Transient private val GAMEPAD_JUMP = 7
protected var isUpDown = false
protected var isDownDown = false
protected var isLeftDown = false
protected var isRightDown = false
protected var isJumpDown = false
protected inline val isGamer: Boolean
get() = if (Terrarum.ingame == null) false else this == (Terrarum.ingame!! as Ingame).player
private val nullItem = object : GameItem() {
override var dynamicID: Int = 0
override val originalID = dynamicID
override val isUnique: Boolean = false
override var baseMass: Double = 0.0
override var baseToolSize: Double? = null
override var inventoryCategory = "should_not_be_seen"
override val originalName: String = actorValue.getAsString(AVKey.NAME) ?: "(no name)"
override var stackable = false
override val isDynamic = false
override val material = Material(0,0,0,0,0,0,0,0,0,0.0)
}
override fun update(delta: Float) {
super.update(delta)
if (vehicleRiding is Player)
throw Error("Attempted to 'ride' player object. ($vehicleRiding)")
if (vehicleRiding != null && vehicleRiding == this)
throw Error("Attempted to 'ride' itself. ($vehicleRiding)")
// don't put this into keyPressed; execution order is important!
updateGamerControlBox()
processInput(delta)
updateSprite(delta)
if (noClip) {
//grounded = true
}
// reset control box of AI
if (!isGamer) {
isUpDown = false
isDownDown = false
isLeftDown = false
isRightDown = false
isJumpDown = false
axisX = 0f
axisY = 0f
axisRX = 0f
axisRY = 0f
}
// update inventory items
inventory.forEach {
if (!inventory.itemEquipped.contains(it.item)) { // unequipped
it.item.effectWhileInPocket(delta)
}
else { // equipped
it.item.effectWhenEquipped(delta)
}
}
}
private fun updateGamerControlBox() {
if (isGamer) {
isUpDown = Gdx.input.isKeyPressed(Terrarum.getConfigInt("keyup"))
isLeftDown = Gdx.input.isKeyPressed(Terrarum.getConfigInt("keyleft"))
isDownDown = Gdx.input.isKeyPressed(Terrarum.getConfigInt("keydown"))
isRightDown = Gdx.input.isKeyPressed(Terrarum.getConfigInt("keyright"))
isJumpDown = Gdx.input.isKeyPressed(Terrarum.getConfigInt("keyjump"))
if (Terrarum.controller != null) {
axisX = Terrarum.controller!!.getAxisValue(Terrarum.getConfigInt("joypadlstickx"))
axisY = Terrarum.controller!!.getAxisValue(Terrarum.getConfigInt("joypadlsticky"))
axisRX = Terrarum.controller!!.getAxisValue(Terrarum.getConfigInt("joypadrstickx"))
axisRY = Terrarum.controller!!.getAxisValue(Terrarum.getConfigInt("joypadrsticky"))
// deadzonning
if (Math.abs(axisX) < Terrarum.CONTROLLER_DEADZONE) axisX = 0f
if (Math.abs(axisY) < Terrarum.CONTROLLER_DEADZONE) axisY = 0f
if (Math.abs(axisRX) < Terrarum.CONTROLLER_DEADZONE) axisRX = 0f
if (Math.abs(axisRY) < Terrarum.CONTROLLER_DEADZONE) axisRY = 0f
isJumpDown = Gdx.input.isKeyPressed(Terrarum.getConfigInt("keyjump")) ||
Terrarum.controller!!.isButtonPressed(GAMEPAD_JUMP)
}
}
else {
isUpDown = axisY < 0f
isDownDown = axisY > 0f
isLeftDown = axisX < 0f
isRightDown = axisX > 0f
}
}
private inline val hasController: Boolean
get() = if (isGamer) Terrarum.controller != null
else true
private fun processInput(delta: Float) {
/**
* L-R stop
*/
if (hasController && !isWalkingH) {
if (axisX == 0f) {
walkHStop()
}
}
// ↑F, ↑S
if (isWalkingH && !isLeftDown && !isRightDown) {
walkHStop()
prevHMoveKey = KEY_NULL
}
/**
* U-D stop
*/
if (hasController) {
if (axisY == 0f) {
walkVStop()
}
}
// ↑E
// ↑D
if (isNoClip() && !isUpDown && !isDownDown) {
walkVStop()
prevVMoveKey = KEY_NULL
}
/**
* Left/Right movement
*/
if (hasController) {
if (axisX != 0f) {
walkHorizontal(axisX < 0f, axisX.abs())
}
}
// ↑F, ↓S
if (isRightDown && !isLeftDown) {
walkHorizontal(false, AXIS_KEYBOARD)
prevHMoveKey = Terrarum.getConfigInt("keyright")
} // ↓F, ↑S
else if (isLeftDown && !isRightDown) {
walkHorizontal(true, AXIS_KEYBOARD)
prevHMoveKey = Terrarum.getConfigInt("keyleft")
} // ↓F, ↓S
/*else if (isLeftDown && isRightDown) {
if (prevHMoveKey == KeyMap.getKeyCode(EnumKeyFunc.MOVE_LEFT)) {
walkHorizontal(false, AXIS_KEYBOARD)
prevHMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_RIGHT)
} else if (prevHMoveKey == KeyMap.getKeyCode(EnumKeyFunc.MOVE_RIGHT)) {
walkHorizontal(true, AXIS_KEYBOARD)
prevHMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_LEFT)
}
}*/
/**
* Up/Down movement
*/
if (noClip || COLLISION_TEST_MODE) {
if (hasController) {
if (axisY != 0f) {
walkVertical(axisY < 0, axisY.abs())
}
}
// ↑E, ↓D
if (isDownDown && !isUpDown) {
walkVertical(false, AXIS_KEYBOARD)
prevVMoveKey = Terrarum.getConfigInt("keydown")
} // ↓E, ↑D
else if (isUpDown && !isDownDown) {
walkVertical(true, AXIS_KEYBOARD)
prevVMoveKey = Terrarum.getConfigInt("keyup")
} // ↓E, ↓D
/*else if (isUpDown && isDownDown) {
if (prevVMoveKey == KeyMap.getKeyCode(EnumKeyFunc.MOVE_UP)) {
walkVertical(false, AXIS_KEYBOARD)
prevVMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_DOWN)
} else if (prevVMoveKey == KeyMap.getKeyCode(EnumKeyFunc.MOVE_DOWN)) {
walkVertical(true, AXIS_KEYBOARD)
prevVMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_UP)
}
}*/
}
/**
* Jump control
*/
if (isJumpDown) {
if (!noClip) {
if (airJumpingAllowed ||
(!airJumpingAllowed && walledBottom)) {
jumping = true
}
jump()
}
else {
walkVertical(true, AXIS_KEYBOARD)
}
}
else {
jumping = false
jumpCounter = 0
jumpAcc = 0.0
}
}
override fun keyDown(keycode: Int): Boolean {
// quickslot (quickbar)
val quickbarKeys = Terrarum.getConfigIntArray("keyquickbars")
if (keycode in quickbarKeys) {
actorValue[AVKey.__PLAYER_QUICKSLOTSEL] = quickbarKeys.indexOf(keycode)
}
return true
}
/**
* This code directly controls VELOCITY for walking, called walkX and walkY.
*
* In theory, we must add ACCELERATION to the velocity, but unfortunately it's arduous task
* with this simulation code base.
*
* Reason: we have naïve friction code that is not adaptive at all and to add proper walking code to
* this code base, ACCELERATION must be changed (in other words, we must deal with JERK) accordingly
* to the FRICTION.
*
* So I'm adding walkX/Y and getting the ActorWithPhysics.setNewNextHitbox to use the velocity value of
* walkX/Y + velocity, which is stored in variable moveDelta.
*
* Be warned.
*
* @param left (even if the game is joypad controlled, you must give valid value)
* @param absAxisVal (set AXIS_KEYBOARD if keyboard controlled)
* @author minjaesong
*/
private fun walkHorizontal(left: Boolean, absAxisVal: Float) {
if (avAcceleration.isNaN()) {
throw Error("avAccelation is NaN")
}
if (left && walledLeft || !left && walledRight) return
readonly_totalX =
if (absAxisVal == AXIS_KEYBOARD)
avAcceleration * applyVelo(walkCounterX) * (if (left) -1f else 1f)
else
avAcceleration * applyVelo(walkCounterX) * (if (left) -1f else 1f) * absAxisVal
if (absAxisVal != AXIS_KEYBOARD)
controllerMoveDelta?.x?.let { controllerMoveDelta!!.x = controllerMoveDelta!!.x.plus(readonly_totalX).bipolarClamp(avSpeedCap * absAxisVal) }
else
controllerMoveDelta?.x?.let { controllerMoveDelta!!.x = controllerMoveDelta!!.x.plus(readonly_totalX).bipolarClamp(avSpeedCap) }
if (walkCounterX < 1000000) {
walkCounterX += 1
}
isWalkingH = true
// Heading flag
walkHeading = if (left) LEFT else RIGHT
}
/**
* @param up (even if the game is joypad controlled, you must give valid value)
* *
* @param absAxisVal (set AXIS_KEYBOARD if keyboard controlled)
*/
private fun walkVertical(up: Boolean, absAxisVal: Float) {
if (up && walledTop || !up && walledBottom) return
if (avAcceleration.isNaN()) {
throw Error("avAccelation is NaN")
}
readonly_totalY =
if (absAxisVal == AXIS_KEYBOARD)
avAcceleration * applyVelo(walkCounterY) * (if (up) -1f else 1f)
else
avAcceleration * applyVelo(walkCounterY) * (if (up) -1f else 1f) * absAxisVal
if (absAxisVal != AXIS_KEYBOARD)
controllerMoveDelta?.y?.let { controllerMoveDelta!!.y = controllerMoveDelta!!.y.plus(readonly_totalY).bipolarClamp(avSpeedCap * absAxisVal) }
else
controllerMoveDelta?.y?.let { controllerMoveDelta!!.y = controllerMoveDelta!!.y.plus(readonly_totalY).bipolarClamp(avSpeedCap) }
if (walkCounterY < 1000000) {
walkCounterY += 1
}
isWalkingV = true
}
private fun applyAccel(x: Int): Double {
return if (x < WALK_FRAMES_TO_MAX_ACCEL)
Math.sin(Math.PI * x / WALK_FRAMES_TO_MAX_ACCEL)
else 0.0
}
private fun applyVelo(x: Int): Double {
return if (x < WALK_FRAMES_TO_MAX_ACCEL)
0.5 - 0.5 * Math.cos(Math.PI * x / WALK_FRAMES_TO_MAX_ACCEL)
else 1.0
}
// stops; let the friction kick in by doing nothing to the velocity here
private fun walkHStop() {
walkCounterX = 0
isWalkingH = false
}
// stops; let the friction kick in by doing nothing to the velocity here
private fun walkVStop() {
walkCounterY = 0
isWalkingV = false
}
private fun getJumpAcc(pwr: Double, timedJumpCharge: Double): Double {
return pwr * timedJumpCharge * JUMP_ACCELERATION_MOD * Math.sqrt(scale) // positive value
}
private var oldMAX_JUMP_LENGTH = -1 // init
private var oldJUMPPOWER = -1.0 // init
private var oldJUMPPOWERBUFF = -1.0 // init
private var oldScale = -1.0
private var oldDragCoefficient = -1.0
val jumpAirTime: Double = -1.0
get() {
// compare all the affecting variables
if (oldMAX_JUMP_LENGTH == MAX_JUMP_LENGTH &&
oldJUMPPOWER == actorValue.getAsDouble(AVKey.JUMPPOWER)!! &&
oldJUMPPOWERBUFF == actorValue.getAsDouble(AVKey.JUMPPOWERBUFF) ?: 1.0 &&
oldScale == scale &&
oldDragCoefficient == dragCoefficient) {
return field
}
// if variables are changed, get new value, store it and return it
else {
oldMAX_JUMP_LENGTH = MAX_JUMP_LENGTH
oldJUMPPOWER = actorValue.getAsDouble(AVKey.JUMPPOWER)!!
oldJUMPPOWERBUFF = actorValue.getAsDouble(AVKey.JUMPPOWERBUFF) ?: 1.0
oldScale = scale
oldDragCoefficient = dragCoefficient
var frames = 0
var simYPos = 0.0
var forceVec = Vector2(0.0, 0.0)
var jmpCtr = 0
while (true) {
if (jmpCtr < MAX_JUMP_LENGTH) jmpCtr++
val timedJumpCharge = jumpFunc(MAX_JUMP_LENGTH, jmpCtr)
forceVec.y -= getJumpAcc(jumpPower, timedJumpCharge)
forceVec.y += getDrag(forceVec).y
simYPos += forceVec.y // ignoring all the fluid drag OTHER THAN THE AIR
if ((simYPos >= 0.0 && frames > 0) || frames >= 1000) break
frames++
}
field = frames * (1.0 / Terrarum.TARGET_FPS)
// fixme: looks good but return value is wrong -- 2.25 seconds? when I jump it barely goes past 1 sec
return field
}
}
private val jumpPower: Double
get() = actorValue.getAsDouble(AVKey.JUMPPOWER)!! * (actorValue.getAsDouble(AVKey.JUMPPOWERBUFF) ?: 1.0)
private fun jumpFunc(len: Int, counter: Int): Double {
// linear time mode
val init = (len + 1) / 2.0
var timedJumpCharge = init - init / len * counter
if (timedJumpCharge < 0) timedJumpCharge = 0.0
return timedJumpCharge
}
/**
* See ./work_files/Jump power by pressing time.gcx
*
* TODO linear function (play Super Mario Bros. and you'll get what I'm talking about) -- SCRATCH THAT!
*/
private fun jump() {
if (jumping) {// && jumpable) {
// increment jump counter
if (jumpCounter < MAX_JUMP_LENGTH) jumpCounter += 1
val timedJumpCharge = jumpFunc(MAX_JUMP_LENGTH, jumpCounter)
jumpAcc = getJumpAcc(jumpPower, timedJumpCharge)
controllerMoveDelta?.y?.let { controllerMoveDelta!!.y -= jumpAcc } // feed negative value to the vector
// do not think of resetting this to zero when counter hit the ceiling; that's HOW NOT
// newtonian physics work, stupid myself :(
}
// not sure we need this...
/*else if (!jumpable) {
jumpable = true // this is kind of like "semaphore", we toggle it now
grounded = false // just in case...
}*/
// release "jump key" of AIs
if (jumpCounter >= MAX_JUMP_LENGTH && !isGamer) {
isJumpDown = false
jumping = false
jumpCounter = 0
jumpAcc = 0.0
}
}
override fun onActorValueChange(key: String, value: Any?) {
// quickslot implementation
if (key == AVKey.__PLAYER_QUICKSLOTSEL && value != null) {
// ONLY FOR HAND_GRIPs!!
val quickBarItem = inventory.getQuickBar(actorValue.getAsInt(key)!!)?.item
if (quickBarItem != null && quickBarItem.equipPosition == GameItem.EquipPosition.HAND_GRIP) {
equipItem(quickBarItem)
}
// force update inventory UI
try {
((Terrarum.ingame!! as Ingame).uiInventoryPlayer as UIInventoryFull).rebuildList()
}
catch (LateInitMyArse: kotlin.UninitializedPropertyAccessException) { }
}
}
fun isNoClip(): Boolean {
return noClip
}
fun setNoClip(b: Boolean) {
noClip = b
if (b) {
externalForce.zero()
controllerMoveDelta?.zero()
}
}
fun Float.abs() = FastMath.abs(this)
private fun updateSprite(delta: Float) {
sprite?.update(delta)
spriteGlow?.update(delta)
//println("$this\tsprite current frame: ${sprite!!.currentFrame}")
if (walledBottom) {
// set anim row
if (controllerMoveDelta?.x != 0.0) {
sprite?.switchRow(SPRITE_ROW_WALK)
spriteGlow?.switchRow(SPRITE_ROW_WALK)
}
// flipping the sprite
if (walkHeading == LEFT) {
sprite?.flip(true, false)
spriteGlow?.flip(true, false)
}
else {
sprite?.flip(false, false)
spriteGlow?.flip(false, false)
}
}
else {
sprite?.switchRow(SPRITE_ROW_IDLE)
spriteGlow?.switchRow(SPRITE_ROW_IDLE)
}
}
}

View File

@@ -0,0 +1,294 @@
package net.torvald.terrarum.modulebasegame.gameactors
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.blockproperties.BlockCodex
import net.torvald.terrarum.gameactors.Actor
import net.torvald.terrarum.itemproperties.GameItem
import net.torvald.terrarum.itemproperties.ItemCodex
import net.torvald.terrarum.itemproperties.ItemCodex.ITEM_DYNAMIC
import net.torvald.terrarum.itemproperties.ItemCodex.ITEM_WALLS
import net.torvald.terrarum.itemproperties.ItemID
import net.torvald.terrarum.modulebasegame.Ingame
import java.util.*
import java.util.concurrent.locks.Lock
import java.util.concurrent.locks.ReentrantLock
/**
* Created by minjaesong on 2016-03-15.
*/
class ActorInventory(val actor: Pocketed, var maxCapacity: Int, var capacityMode: Int) {
companion object {
@Transient val CAPACITY_MODE_NO_ENCUMBER = 0
@Transient val CAPACITY_MODE_COUNT = 1
@Transient val CAPACITY_MODE_WEIGHT = 2
}
/**
* List of all equipped items (tools, armours, rings, necklaces, etc.)
*/
val itemEquipped = Array<GameItem?>(GameItem.EquipPosition.INDEX_MAX, { null })
/**
* Sorted by referenceID.
*/
val itemList = ArrayList<InventoryPair>()
val quickBar = Array<ItemID?>(10, { null }) // 0: Slot 1, 9: Slot 10
var currency = 0 // unified currency for whole civs; Dwarf Fortress approach seems too complicated
init {
}
fun add(itemID: ItemID, count: Int = 1) = add(ItemCodex[itemID], count)
fun add(item: GameItem, count: Int = 1) {
println("[ActorInventory] add $item, $count")
// not wall-able walls
if (item.inventoryCategory == GameItem.Category.WALL &&
!BlockCodex[item.dynamicID - ITEM_WALLS.start].isWallable) {
throw IllegalArgumentException("Wall ID ${item.dynamicID - ITEM_WALLS.start} is not wall-able.")
}
// other invalid values
if (count == 0)
throw IllegalArgumentException("Item count is zero.")
if (count < 0)
throw IllegalArgumentException("Item count is negative number. If you intended removing items, use remove()\n" +
"These commands are NOT INTERCHANGEABLE; they handle things differently according to the context.")
if (item.originalID == Player.PLAYER_REF_ID || item.originalID == 0x51621D) // do not delete this magic
throw IllegalArgumentException("Attempted to put human player into the inventory.")
if (((Terrarum.ingame as? Ingame)?.gameFullyLoaded ?: false) &&
(item.originalID == (Terrarum.ingame as? Ingame)?.player?.referenceID))
throw IllegalArgumentException("Attempted to put active player into the inventory.")
if ((!item.stackable || item.dynamicID in ITEM_DYNAMIC) && count > 1)
throw IllegalArgumentException("Attempting to adding stack of item but the item is not stackable; item: $item, count: $count")
// If we already have the item, increment the amount
// If not, add item with specified amount
val existingItem = getByDynamicID(item.dynamicID)
// if the item already exists
if (existingItem != null) {
// increment count
existingItem.amount += count
}
// new item
else {
itemList.add(InventoryPair(item, count))
}
insertionSortLastElem(itemList)
}
fun remove(itemID: ItemID, count: Int) = remove(ItemCodex[itemID], count)
/** Will check existence of the item using its Dynamic ID; careful with command order!
* e.g. re-assign after this operation */
fun remove(item: GameItem, count: Int = 1) {
println("[ActorInventory] remove $item, $count")
if (count == 0)
throw IllegalArgumentException("Item count is zero.")
if (count < 0)
throw IllegalArgumentException("Item count is negative number. If you intended adding items, use add()" +
"These commands are NOT INTERCHANGEABLE; they handle things differently according to the context.")
val existingItem = getByDynamicID(item.dynamicID)
if (existingItem != null) { // if the item already exists
val newCount = existingItem.amount - count
if (newCount < 0) {
throw Error("Tried to remove $count of $item, but the inventory only contains ${existingItem.amount} of them.")
}
else if (newCount > 0) {
// decrement count
existingItem.amount = newCount
}
else {
// unequip, if applicable
actor.unequipItem(existingItem.item)
// depleted item; remove entry from inventory
itemList.remove(existingItem)
}
}
else {
throw Error("Tried to remove $item, but the inventory does not have it.")
}
}
fun setQuickBar(slot: Int, dynamicID: ItemID?) {
quickBar[slot] = dynamicID
}
fun getQuickBar(slot: Int): InventoryPair? = getByDynamicID(quickBar[slot])
/**
* HashMap<GameItem, Amounts>
*/
inline fun forEach(consumer: (InventoryPair) -> Unit) = itemList.forEach(consumer)
/**
* Get capacity of inventory
* @return
*/
val capacity: Double
get() = if (capacityMode == CAPACITY_MODE_NO_ENCUMBER)
maxCapacity.toDouble()
else if (capacityMode == CAPACITY_MODE_WEIGHT)
getTotalWeight()
else
getTotalCount().toDouble()
fun getTotalWeight(): Double = itemList.map { it.item.mass * it.amount }.sum()
/**
* Real amount
*/
fun getTotalCount(): Int = itemList.map { it.amount }.sum()
/**
* Unique amount, multiple items are calculated as one
*/
fun getTotalUniqueCount(): Int = itemList.size
/**
* Check whether the itemList contains too many items
* @return
*/
val isEncumbered: Boolean
get() = if (capacityMode == CAPACITY_MODE_NO_ENCUMBER)
false
else if (capacityMode == CAPACITY_MODE_WEIGHT)
maxCapacity < capacity
else
false
fun consumeItem(actor: Actor, item: GameItem) {
if (item.stackable && !item.isDynamic) {
remove(item, 1)
}
else {
val newItem: GameItem
// unpack newly-made dynamic item (e.g. any weapon, floppy disk)
if (item.isDynamic && item.originalID == item.dynamicID) {
itemEquipped[item.equipPosition] = null
remove(item, 1)
newItem = item.clone()
newItem.generateUniqueDynamicID(this)
newItem.stackable = false
add(newItem)
itemEquipped[newItem.equipPosition] = getByDynamicID(newItem.dynamicID)!!.item // will test if some sketchy code is written. Test fail: kotlinNullpointerException
// FIXME now damage meter (vital) is broken
}
else {
newItem = item
}
// calculate damage value
val baseDamagePerSwing = if (actor is ActorHumanoid)
actor.avStrength / 1000.0
else
1.0 // TODO variable: scale, strength
val swingDmgToFrameDmg = Terrarum.deltaTime.toDouble() / actor.actorValue.getAsDouble(AVKey.ACTION_INTERVAL)!!
// damage the item
newItem.durability -= (baseDamagePerSwing * swingDmgToFrameDmg).toFloat()
if (newItem.durability <= 0)
remove(newItem, 1)
//println("[ActorInventory] consumed; ${item.durability}")
}
}
fun contains(item: GameItem) = contains(item.dynamicID)
fun contains(id: ItemID) =
if (itemList.size == 0)
false
else
itemList.binarySearch(id, DYNAMIC_ID) >= 0
fun getByDynamicID(id: ItemID?): InventoryPair? {
if (itemList.size == 0 || id == null)
return null
val index = itemList.binarySearch(id, DYNAMIC_ID)
if (index < 0)
return null
else
return itemList[index]
}
private fun getByStaticID(id: ItemID): InventoryPair? {
if (itemList.size == 0)
return null
val index = itemList.binarySearch(id, STATIC_ID)
if (index < 0)
return null
else
return itemList[index]
}
private fun insertionSortLastElem(arr: ArrayList<InventoryPair>) {
lock(ReentrantLock()) {
var j = arr.lastIndex - 1
val x = arr.last()
while (j >= 0 && arr[j].item > x.item) {
arr[j + 1] = arr[j]
j -= 1
}
arr[j + 1] = x
}
}
private val STATIC_ID = 41324534
private val DYNAMIC_ID = 181643953
private fun ArrayList<InventoryPair>.binarySearch(ID: ItemID, searchBy: Int): Int {
// code from collections/Collections.kt
var low = 0
var high = this.size - 1
while (low <= high) {
val mid = (low + high).ushr(1) // safe from overflows
val midVal = if (searchBy == STATIC_ID) this.get(mid).item.originalID else this.get(mid).item.dynamicID
if (ID > midVal)
low = mid + 1
else if (ID < midVal)
high = mid - 1
else
return mid // key found
}
return -(low + 1) // key not found
}
inline fun lock(lock: Lock, body: () -> Unit) {
lock.lock()
try {
body()
}
finally {
lock.unlock()
}
}
}
data class InventoryPair(val item: GameItem, var amount: Int)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,18 @@
package net.torvald.terrarum.modulebasegame.gameactors
import net.torvald.terrarum.itemproperties.GameItem
/**
* Created by minjaesong on 2016-01-31.
*/
interface CanBeAnItem {
fun getItemWeight(): Double
fun stopUpdateAndDraw()
fun resumeUpdateAndDraw()
var itemData: GameItem
}

View File

@@ -0,0 +1,25 @@
package net.torvald.terrarum.modulebasegame.gameactors
import net.torvald.terrarum.gameactors.Actor
import net.torvald.terrarum.gameworld.GameWorld
/**
* Created by minjaesong on 2016-02-05.
*/
object CreatureBuilder {
/**
* @Param jsonFileName with extension
*/
operator fun invoke(world: GameWorld, module: String, jsonFileName: String): ActorWithPhysics {
val actor = ActorWithPhysics(world, Actor.RenderOrder.MIDDLE)
InjectCreatureRaw(actor.actorValue, module, jsonFileName)
actor.actorValue[AVKey.__ACTION_TIMER] = 0.0
return actor
}
}

View File

@@ -0,0 +1,178 @@
package net.torvald.terrarum.modulebasegame.gameactors
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.Pixmap
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.gameworld.toUint
import net.torvald.terrarum.modulebasegame.Ingame
import java.io.File
import java.nio.charset.Charset
import java.util.*
object DecodeTapestry {
val colourIndices64 = arrayOf(
0x333.fourBitCol(),
0x600.fourBitCol(),
0xA36.fourBitCol(),
0x636.fourBitCol(),
0x73B.fourBitCol(),
0x427.fourBitCol(),
0x44C.fourBitCol(),
0x038.fourBitCol(),
0x47B.fourBitCol(),
0x466.fourBitCol(),
0x353.fourBitCol(),
0x453.fourBitCol(),
0x763.fourBitCol(),
0xA63.fourBitCol(),
0x742.fourBitCol(),
0x000.fourBitCol(),
0x666.fourBitCol(),
0xA00.fourBitCol(),
0xE2A.fourBitCol(),
0xD2F.fourBitCol(),
0x92F.fourBitCol(),
0x548.fourBitCol(),
0x32F.fourBitCol(),
0x36F.fourBitCol(),
0x588.fourBitCol(),
0x390.fourBitCol(),
0x5F0.fourBitCol(),
0x684.fourBitCol(),
0xBA2.fourBitCol(),
0xE60.fourBitCol(),
0x854.fourBitCol(),
0x533.fourBitCol(),
0x999.fourBitCol(),
0xE00.fourBitCol(),
0xE6A.fourBitCol(),
0xE6F.fourBitCol(),
0x848.fourBitCol(),
0x62F.fourBitCol(),
0x66F.fourBitCol(),
0x4AF.fourBitCol(),
0x5BA.fourBitCol(),
0x8FE.fourBitCol(),
0x7F8.fourBitCol(),
0x9E0.fourBitCol(),
0xFE0.fourBitCol(),
0xEA0.fourBitCol(),
0xC85.fourBitCol(),
0xE55.fourBitCol(),
0xCCC.fourBitCol(),
0xFFF.fourBitCol(),
0xFDE.fourBitCol(),
0xEAF.fourBitCol(),
0xA3B.fourBitCol(),
0x96F.fourBitCol(),
0xAAF.fourBitCol(),
0x7AF.fourBitCol(),
0x3DF.fourBitCol(),
0xBFF.fourBitCol(),
0xBFB.fourBitCol(),
0xAF6.fourBitCol(),
0xFEB.fourBitCol(),
0xFD7.fourBitCol(),
0xE96.fourBitCol(),
0xEBA.fourBitCol()
)
val colourIndices16 = arrayOf(
0x000.fourBitCol(),
0xfff.fourBitCol(),
0x666.fourBitCol(),
0xccc.fourBitCol(),
0xfe0.fourBitCol(),
0xe60.fourBitCol(),
0xe00.fourBitCol(),
0xe2a.fourBitCol(),
0x427.fourBitCol(),
0x32f.fourBitCol(),
0x4af.fourBitCol(),
0x5f0.fourBitCol(),
0x390.fourBitCol(),
0x353.fourBitCol(),
0x533.fourBitCol(),
0xa63.fourBitCol()
)
private fun Int.fourBitCol() = Color(
this.and(0xF00).shl(20) or this.and(0xF00).shl(16) or
this.and(0x0F0).shl(16) or this.and(0x0F0).shl(12) or
this.and(0x00F).shl(12) or this.and(0x00F).shl(8) or
0xFF
)
val MAGIC = "TEAF".toByteArray(charset = Charset.forName("US-ASCII"))
val FORMAT_16 = 1
val FORMAT_64 = 2
operator fun invoke(fileObj: File): TapestryObject {
fun magicMismatch(magic: ByteArray, array: ByteArray): Boolean {
return !Arrays.equals(array.sliceArray(0..magic.lastIndex), magic)
}
val file = fileObj.readBytes()
val magic = file.copyOfRange(0, 4)
if (magicMismatch(MAGIC, magic))
throw RuntimeException("Invalid file -- type mismatch: expected header " +
"${MAGIC[0]} ${MAGIC[1]} ${MAGIC[2]} ${MAGIC[3]}; got " +
"${magic[0]} ${magic[1]} ${magic[2]} ${magic[3]}")
val colourModel = file[4].toUint()
if (colourModel != FORMAT_16 && colourModel != FORMAT_64)
throw RuntimeException("Invalid colour model: $colourModel")
val width = file[7].toUint().shl(8) + file[6].toUint()
val artNameBytes = ArrayList<Byte>()
val authorNameBytes = ArrayList<Byte>()
var readCounter = 8
while (file[readCounter] != 0x00.toByte()) {
artNameBytes.add(file[readCounter])
readCounter++
}
readCounter++ // jump over null terminator
while (file[readCounter] != 0x00.toByte()) {
authorNameBytes.add(file[readCounter])
readCounter++
}
readCounter++ // jump over null terminator
val artName = String(artNameBytes.toByteArray(), charset = Charset.forName("UTF-8"))
val authorName = String(authorNameBytes.toByteArray(), charset = Charset.forName("UTF-8"))
val imageDataSize = file.size - readCounter
val height = imageDataSize / width
val outImageData = Pixmap(width, height, Pixmap.Format.RGBA8888)
val counterOffset = readCounter
while (readCounter < file.size) {
val ofs = readCounter - counterOffset
val palIndex = file[readCounter].toUint()
if (colourModel == FORMAT_16) {
outImageData.setColor(colourIndices16[palIndex])
outImageData.drawPixel(ofs % width, ofs / width)
}
else {
outImageData.setColor(colourIndices64[palIndex])
outImageData.drawPixel(ofs % width, ofs / width)
}
readCounter++
}
return TapestryObject((Terrarum.ingame!! as Ingame).world, outImageData, artName, authorName)
}
}

View File

@@ -0,0 +1,39 @@
package net.torvald.terrarum.modulebasegame.gameactors
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import net.torvald.terrarum.itemproperties.GameItem
import net.torvald.terrarum.itemproperties.ItemCodex
import net.torvald.terrarum.blockproperties.BlockCodex
import net.torvald.terrarum.gameworld.GameWorld
/**
* Created by minjaesong on 2016-03-15.
*/
open class DroppedItem(world: GameWorld, private val item: GameItem) : ActorWithPhysics(world, RenderOrder.MIDTOP) {
init {
if (item.dynamicID >= ItemCodex.ACTORID_MIN)
throw RuntimeException("Attempted to create DroppedItem actor of a real actor; the real actor must be dropped instead.")
isVisible = true
avBaseMass = if (item.dynamicID < BlockCodex.TILE_UNIQUE_MAX)
BlockCodex[item.dynamicID].density / 1000.0
else
ItemCodex[item.dynamicID].mass
scale = ItemCodex[item.dynamicID].scale
}
override fun update(delta: Float) {
super.update(delta)
}
override fun drawGlow(batch: SpriteBatch) {
super.drawGlow(batch)
}
override fun drawBody(batch: SpriteBatch) {
super.drawBody(batch)
}
}

View File

@@ -0,0 +1,30 @@
package net.torvald.terrarum.modulebasegame.gameactors
import net.torvald.terrarum.gameworld.GameWorld
/**
* Created by minjaesong on 2016-06-17.
*/
open class FixtureBase(world: GameWorld, physics: Boolean = true) :
ActorWithPhysics(world, RenderOrder.BEHIND, immobileBody = true, usePhysics = physics) {
/**
* 0: Open
* 1: Blocked
* 2: Platform; can be stood on, press DOWN to go down. Also allows other blocks can be places on top of it (e.g. torch)
* 3: Wall_left; blocks rightward movement
* 4: Wall_right: blocks leftward movement
* 5: Same as 2 but player CANNOT go down
* For example, flag of 4 is good for tables; player can stand on, which means
* downward movement is blocked within the fixtures' AABB.
*/
var collisionFlag: Int = 0
companion object {
val COLLISION_OPEN = 0
val COLLISION_BLOCKED = 1
val COLLISION_PLATFORM = 2
val COLLISION_WALL_LEFT = 3
val COLLISION_WALL_RIGHT = 4
val COLLISION_PLATFORM_NOGODOWN = 5
}
}

View File

@@ -0,0 +1,40 @@
package net.torvald.terrarum.modulebasegame.gameactors
import com.badlogic.gdx.graphics.Color
import net.torvald.terrarum.ModMgr
import net.torvald.terrarum.blockproperties.Block
import net.torvald.terrarum.blockproperties.BlockCodex
import net.torvald.terrarum.gameactors.Hitbox
import net.torvald.terrarum.gameactors.Luminous
import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
import java.util.*
/**
* Created by minjaesong on 2016-06-17.
*/
internal class FixtureTikiTorch(world: GameWorld) : FixtureBase(world), Luminous {
override var color: Color
get() = BlockCodex[Block.TORCH].luminosity
set(value) {
throw UnsupportedOperationException()
}
override val lightBoxList: ArrayList<Hitbox>
init {
density = 1200.0
setHitboxDimension(10, 24, 0, 0)
lightBoxList = ArrayList(1)
lightBoxList.add(Hitbox(3.0, 0.0, 4.0, 3.0))
makeNewSprite(TextureRegionPack(ModMgr.getGdxFile("basegame", "sprites/fixtures/tiki_torch.tga"), 10, 27))
sprite!!.delay = 0.2f
sprite!!.setRowsAndFrames(1, 1)
actorValue[AVKey.BASEMASS] = 1.0
}
}

View File

@@ -0,0 +1,83 @@
package net.torvald.terrarum.modulebasegame.gameactors
import net.torvald.random.HQRNG
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.modulebasegame.Ingame
import net.torvald.terrarum.modulebasegame.gameworld.WorldTime
typealias AnyPlayer = HistoricalFigure
/**
* An actor (NPC) which has life and death,
* though death might not exist if it has achieved immortality :)
*
* NOTE: all canonical NPCs are must be HistoricalFigure!! (double excl mark, bitch)
*
* Created by minjaesong on 2016-10-10.
*/
open class HistoricalFigure(
world: GameWorld,
val born: GameDate,
val dead: GameDate? = null,
realAirFriction: Boolean = false,
usePhysics: Boolean = true
) : ActorWithPhysics(world, RenderOrder.MIDDLE, realAirFriction, usePhysics) {
var historicalFigureIdentifier: Int = generateHistoricalFigureIdentifier()
internal set
private fun generateHistoricalFigureIdentifier(): Int {
fun hasCollision(value: Int) =
try {
(Terrarum.ingame!! as Ingame).historicalFigureIDBucket.contains(value)
}
catch (gameNotInitialisedException: KotlinNullPointerException) {
false
}
var ret: Int
do {
ret = HQRNG().nextInt() // set new ID
} while (hasCollision(ret)) // check for collision
return ret
}
init {
this.actorValue["_bornyear"] = born.year
this.actorValue["_borndays"] = born.yearlyDay
if (dead != null) {
this.actorValue["_deadyear"] = dead.year
this.actorValue["_deaddays"] = dead.yearlyDay
}
}
}
data class GameDate(val year: Int, val yearlyDay: Int) {
operator fun plus(other: GameDate): GameDate {
var newyd = this.yearlyDay + other.yearlyDay
var newy = this.year + other.year
if (newyd > WorldTime.YEAR_DAYS) {
newyd -= WorldTime.YEAR_DAYS
newy += 1
}
return GameDate(newy, newyd)
}
operator fun minus(other: GameDate): GameDate {
var newyd = this.yearlyDay - other.yearlyDay
var newy = this.year - other.year
if (newyd < 0) {
newyd += WorldTime.YEAR_DAYS
newy -= 1
}
return GameDate(newy, newyd)
}
}

View File

@@ -0,0 +1,116 @@
package net.torvald.terrarum.modulebasegame.gameactors
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.gameactors.AIControlled
import net.torvald.terrarum.gameactors.ai.ActorAI
import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.itemproperties.GameItem
import net.torvald.terrarum.itemproperties.Material
/**
* @param ai AI class. Use LuaAIWrapper for Lua script
*
* Created by minjaesong on 2016-01-31.
*/
open class HumanoidNPC(
world: GameWorld,
override val ai: ActorAI, // it's there for written-in-Kotlin, "hard-wired" AIs
born: GameDate,
usePhysics: Boolean = true,
forceAssignRefID: Int? = null
) : ActorHumanoid(world, born, usePhysics = usePhysics), AIControlled, CanBeAnItem {
companion object {
val DEFAULT_COLLISION_TYPE = COLLISION_DYNAMIC
}
init {
collisionType = DEFAULT_COLLISION_TYPE
}
// we're having GameItem data so that this class could be somewhat universal
override var itemData: GameItem = object : GameItem() {
override var dynamicID = referenceID ?: forceAssignRefID!!
override val originalID = dynamicID
override val isUnique = true
override var baseMass: Double
get() = actorValue.getAsDouble(AVKey.BASEMASS)!!
set(value) { actorValue[AVKey.BASEMASS] = value }
override var baseToolSize: Double? = 0.0
override var scale: Double
get() = actorValue.getAsDouble(AVKey.SCALE)!!
set(value) {
actorValue[AVKey.SCALE] = value
}
override var inventoryCategory = "npc"
override val originalName: String = actorValue.getAsString(AVKey.NAME) ?: "NPC"
override var stackable = true
override val isDynamic = false
override val material = Material(0,0,0,0,0,0,0,0,0,0.0)
override fun secondaryUse(delta: Float): Boolean {
try {
// place the actor to the world
this@HumanoidNPC.setPosition(Terrarum.mouseX, Terrarum.mouseY)
Terrarum.ingame!!.addNewActor(this@HumanoidNPC)
// successful
return true
}
catch (e: Exception) {
e.printStackTrace()
return false
}
}
}
override fun getItemWeight(): Double {
return mass
}
override fun stopUpdateAndDraw() {
isUpdate = false
isVisible = false
}
override fun resumeUpdateAndDraw() {
isUpdate = true
isVisible = true
}
override fun update(delta: Float) {
ai.update(this, delta)
super.update(delta)
}
override fun moveLeft(amount: Float) { // hit the buttons on the controller box
axisX = -amount
}
override fun moveRight(amount: Float) { // hit the buttons on the controller box
axisX = amount
}
override fun moveUp(amount: Float) { // hit the buttons on the controller box
axisY = -amount
}
override fun moveDown(amount: Float) { // hit the buttons on the controller box
axisY = amount
}
override fun moveJump(amount: Float) { // hit the buttons on the controller box
isJumpDown = true
}
/** fly toward arbitrary angle WARNING: the map is looped! */
override fun moveTo(bearing: Double) {
// if your NPC should fly, override this
throw UnsupportedOperationException("Humans cannot fly :p")
}
/** fly toward arbitrary coord WARNING: the map is looped! */
override fun moveTo(toX: Double, toY: Double) {
// if your NPC should fly, override this
throw UnsupportedOperationException("Humans cannot fly :p")
}
}

View File

@@ -0,0 +1,136 @@
package net.torvald.terrarum.modulebasegame.gameactors
import net.torvald.terrarum.utils.JsonFetcher
import net.torvald.random.Fudge3
import net.torvald.terrarum.langpack.Lang
import com.google.gson.JsonObject
import net.torvald.terrarum.ModMgr
import net.torvald.terrarum.gameactors.ActorValue
import java.security.SecureRandom
/**
* Created by minjaesong on 2016-03-25.
*/
object InjectCreatureRaw {
private const val JSONMULT = "mult" // one appears in JSON files
/**
* 'Injects' creature raw ActorValue to the ActorValue reference provided.
*
* @param actorValueRef ActorValue object to be injected.
* @param jsonFileName with extension
*/
operator fun invoke(actorValueRef: ActorValue, module: String, jsonFileName: String) {
val jsonObj = JsonFetcher(ModMgr.getPath(module, "creatures/$jsonFileName"))
val elementsInt = arrayOf(AVKey.BASEHEIGHT, AVKey.TOOLSIZE, AVKey.ENCUMBRANCE)
val elementsString = arrayOf(AVKey.RACENAME, AVKey.RACENAMEPLURAL)
val elementsDouble = arrayOf(AVKey.BASEMASS, AVKey.ACCEL)
val elementsDoubleVariable = arrayOf(AVKey.STRENGTH, AVKey.SPEED, AVKey.JUMPPOWER, AVKey.SCALE)
val elementsBoolean = arrayOf(AVKey.INTELLIGENT)
// val elementsMultiplyFromOne = arrayOf()
setAVInts(actorValueRef, elementsInt, jsonObj)
setAVStrings(actorValueRef, elementsString, jsonObj)
setAVDoubles(actorValueRef, elementsDouble, jsonObj)
setAVDoublesVariable(actorValueRef, elementsDoubleVariable, jsonObj)
// setAVMultiplyFromOne(actorValueRef, elementsMultiplyFromOne, jsonObj)
setAVBooleans(actorValueRef, elementsBoolean, jsonObj)
actorValueRef[AVKey.ACCEL] = ActorHumanoid.WALK_ACCEL_BASE
actorValueRef[AVKey.ACCELBUFF] = 1.0
}
/**
* Fetch and set actor values that have 'variable' appended. E.g. strength
* @param avRef
* *
* @param elemSet
* *
* @param jsonObject
*/
private fun setAVDoublesVariable(avRef: ActorValue, elemSet: Array<String>, jsonObject: JsonObject) {
for (s in elemSet) {
val baseValue = jsonObject.get(s).asDouble
// roll fudge dice and get value [-3, 3] as [0, 6]
val varSelected = Fudge3(SecureRandom()).rollForArray()
// get multiplier from json. Assuming percentile
val multiplier = jsonObject.get(s + JSONMULT).asJsonArray.get(varSelected).asInt
val realValue = baseValue * multiplier / 100.0
avRef[s] = realValue
avRef[s + "buff"] = 1.0 // buffed value: use multiplied value as 'base' for all sort of things
}
}
/**
* Fetch and set string actor values
* @param avRef
* *
* @param elemSet
* *
* @param jsonObject
*/
private fun setAVStrings(avRef: ActorValue, elemSet: Array<String>, jsonObject: JsonObject) {
for (s in elemSet) {
val key = jsonObject.get(s).asString
avRef[s] = Lang[key]
}
}
/**
* Fetch and set double actor values
* @param avRef
* *
* @param elemSet
* *
* @param jsonObject
*/
private fun setAVDoubles(avRef: ActorValue, elemSet: Array<String>, jsonObject: JsonObject) {
for (s in elemSet) {
avRef[s] = jsonObject.get(s).asDouble
}
}
/**
* Fetch and set int actor values
* @param avRef
* *
* @param elemSet
* *
* @param jsonObject
*/
private fun setAVInts(avRef: ActorValue, elemSet: Array<String>, jsonObject: JsonObject) {
for (s in elemSet) {
avRef[s] = jsonObject.get(s).asInt
}
}
/**
* Fetch and set actor values that should multiplier be applied to the base value of 1.
* @param avRef
* *
* @param elemSet
* *
* @param jsonObject
*/
private fun setAVMultiplyFromOne(avRef: ActorValue, elemSet: Array<String>, jsonObject: JsonObject) {
for (s in elemSet) {
val baseValue = 1.0
// roll fudge dice and get value [-3, 3] as [0, 6]
val varSelected = Fudge3(SecureRandom()).rollForArray()
// get multiplier from json. Assuming percentile
val multiplier = jsonObject.get(s).asJsonArray.get(varSelected).asInt
val realValue = baseValue * multiplier / 100.0
avRef[s] = realValue
}
}
private fun setAVBooleans(avRef: ActorValue, elemSet: Array<String>, jsonObject: JsonObject) {
for (s in elemSet) {
avRef[s] = jsonObject.get(s).asBoolean
}
}
}

View File

@@ -0,0 +1,19 @@
package net.torvald.terrarum.modulebasegame.gameactors
import java.util.*
/**
* Created by minjaesong on 2016-02-20.
*/
interface LandHolder {
/**
* Absolute tile index. index(x, y) = y * map.width + x
* The arraylist will be saved in JSON format with GSON.
*/
var houseDesignation: ArrayList<Long>?
fun addHouseTile(x: Int, y: Int)
fun removeHouseTile(x: Int, y: Int)
fun clearHouseDesignation()
}

View File

@@ -0,0 +1,85 @@
package net.torvald.terrarum.modulebasegame.gameactors
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.badlogic.gdx.graphics.g2d.TextureRegion
import net.torvald.terrarum.Second
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.modulebasegame.gameactors.ActorWithPhysics.Companion.SI_TO_GAME_ACC
import net.torvald.terrarum.worlddrawer.FeaturesDrawer.TILE_SIZE
import net.torvald.terrarum.blockproperties.Block
import net.torvald.terrarum.blockproperties.BlockCodex
import net.torvald.terrarum.floorInt
import net.torvald.terrarum.gameactors.Actor
import net.torvald.terrarum.gameactors.Hitbox
import net.torvald.terrarum.modulebasegame.Ingame
import org.dyn4j.geometry.Vector2
/**
* Actors with static sprites and very simple physics
*
* Created by minjaesong on 2017-01-20.
*/
open class ParticleBase(renderOrder: Actor.RenderOrder, val despawnUponCollision: Boolean, maxLifeTime: Second? = null) : Runnable {
/** Will NOT actually delete from the CircularArray */
@Volatile var flagDespawn = false
override fun run() = update(Terrarum.deltaTime)
var isNoSubjectToGrav = false
var dragCoefficient = 3.0
private val lifetimeMax = maxLifeTime ?: 5f
private var lifetimeCounter = 0f
open val velocity = Vector2(0.0, 0.0)
open val hitbox = Hitbox(0.0, 0.0, 0.0, 0.0)
open lateinit var body: TextureRegion // you might want to use SpriteAnimation
open var glow: TextureRegion? = null
init {
}
fun update(delta: Float) {
if (!flagDespawn) {
lifetimeCounter += delta
if (despawnUponCollision) {
if (velocity.isZero ||
// simple stuck check
BlockCodex[(Terrarum.ingame!! as Ingame).world.getTileFromTerrain(
hitbox.canonicalX.div(TILE_SIZE).floorInt(),
hitbox.canonicalY.div(TILE_SIZE).floorInt()
) ?: Block.STONE].isSolid) {
flagDespawn = true
}
}
if (lifetimeCounter >= lifetimeMax) {
flagDespawn = true
}
// gravity, winds, etc. (external forces)
if (!isNoSubjectToGrav) {
velocity += (Terrarum.ingame!! as Ingame).world.gravitation / dragCoefficient * SI_TO_GAME_ACC
}
// combine external forces
hitbox.translate(velocity)
}
}
fun drawBody(batch: SpriteBatch) {
if (!flagDespawn) {
batch.draw(body, hitbox.startX.toFloat(), hitbox.startY.toFloat(), hitbox.width.toFloat(), hitbox.height.toFloat())
}
}
fun drawGlow(batch: SpriteBatch) {
if (!flagDespawn && glow != null) {
batch.draw(glow, hitbox.startX.toFloat(), hitbox.startY.toFloat(), hitbox.width.toFloat(), hitbox.height.toFloat())
}
}
}

View File

@@ -0,0 +1,109 @@
package net.torvald.terrarum.modulebasegame.gameactors
import com.badlogic.gdx.graphics.Pixmap
import com.badlogic.gdx.graphics.Texture
import com.badlogic.gdx.graphics.g2d.TextureRegion
import net.torvald.random.HQRNG
import net.torvald.terrarum.ModMgr
import net.torvald.terrarum.Second
import net.torvald.terrarum.gameactors.Actor
/**
* Created by minjaesong on 2017-12-18.
*/
class ParticleMegaRain(posX: Double, posY: Double) : ParticleBase(Actor.RenderOrder.BEHIND, true, 3.2f) {
init {
body = MegaRainGovernor.get()
val w = body.regionWidth.toDouble()
val h = body.regionHeight.toDouble()
hitbox.setFromWidthHeight(
posX - w.times(0.5),
posY - h.times(0.5),
w, h
)
velocity.y = 11.5 * ActorWithPhysics.SI_TO_GAME_VEL
}
}
object MegaRainGovernor {
private var reseedTimer = 0f
var reseedTime: Second = 90f
private val body = Pixmap(ModMgr.getGdxFile("basegame", "weathers/raindrop.tga"))
private lateinit var bodies: Array<TextureRegion>
private var withdrawCounter = 0
init {
seed()
}
private fun seed() {
val w = body.width
val h = body.height
bodies = Array(1024) {
//val pixmap = Pixmap(Terrarum.WIDTH * 2, Terrarum.HEIGHT / 4, Pixmap.Format.RGBA8888)
val pixmap = Pixmap(64, 64, Pixmap.Format.RGBA8888)
val rng = HQRNG()
repeat(rng.nextInt(2) + 3) { // 3 or 4
val rndX = rng.nextInt(pixmap.width - body.width)
val rndY = rng.nextInt(pixmap.height - body.height)
pixmap.drawPixmap(body, rndX, rndY)
}
// return composed (mega)pixmap
val region = TextureRegion(Texture(pixmap))
region.flip(false, true)
/*return*/region
}
// randomise
bodies.shuffle()
}
fun get(): TextureRegion {
if (withdrawCounter >= bodies.size) {
withdrawCounter = 0
//bodies.shuffle() // if pre-rendered random set is sufficiently large, it'd look random enough
}
return bodies[withdrawCounter++]
}
@Deprecated("re-seeding freezes the game a little and large enough randomnesses ought to be good")
fun update(delta: Float) {
if (reseedTimer >= reseedTime) {
seed()
reseedTimer -= reseedTime
}
reseedTimer += delta
}
fun resize() {
seed()
withdrawCounter = 0
reseedTimer = 0f
}
fun Array<TextureRegion>.shuffle() {
for (i in this.size - 1 downTo 1) {
val rndIndex = (Math.random() * (i + 1)).toInt()
val t = this[rndIndex]
this[rndIndex] = this[i]
this[i] = t
}
}
}

View File

@@ -0,0 +1,26 @@
package net.torvald.terrarum.modulebasegame.gameactors
import com.badlogic.gdx.graphics.Texture
import com.badlogic.gdx.graphics.g2d.TextureRegion
import net.torvald.terrarum.ModMgr
import net.torvald.terrarum.gameactors.Actor
/**
* Created by minjaesong on 2017-01-20.
*/
class ParticleTestRain(posX: Double, posY: Double) : ParticleBase(Actor.RenderOrder.BEHIND, true, 6f) {
init {
body = TextureRegion(Texture(ModMgr.getGdxFile("basegame", "weathers/raindrop.tga")))
val w = body.regionWidth.toDouble()
val h = body.regionHeight.toDouble()
hitbox.setFromWidthHeight(
posX - w.times(0.5),
posY - h.times(0.5),
w, h
)
velocity.y = 10.0
}
}

View File

@@ -0,0 +1,49 @@
package net.torvald.terrarum.modulebasegame.gameactors
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.modulebasegame.Ingame
import net.torvald.terrarum.modulebasegame.worldgenerator.RoguelikeRandomiser
/**
* Created by minjaesong on 2016-03-05.
*/
class PhysTestBall(world: GameWorld) : ActorWithPhysics(world, RenderOrder.MIDDLE, immobileBody = true) {
private var color = Color.GOLD
init {
setHitboxDimension(16, 16, 0, 0)
avBaseMass = 10.0
density = 200.0
color = RoguelikeRandomiser.composeColourFrom(RoguelikeRandomiser.POTION_PRIMARY_COLSET)
}
override fun drawBody(batch: SpriteBatch) {
Terrarum.inShapeRenderer {
it.color = color
it.circle(
hitbox.startX.toFloat() - 1f,
hitbox.startY.toFloat() - 1f,
hitbox.width.toFloat()
)
it.circle(
hitbox.startX.toFloat() + (Terrarum.ingame!! as Ingame).world.width * TILE_SIZE - 1f,
hitbox.startY.toFloat() - 1f,
hitbox.width.toFloat()
)
it.circle(
hitbox.startX.toFloat() - (Terrarum.ingame!! as Ingame).world.width * TILE_SIZE - 1f,
hitbox.startY.toFloat() - 1f,
hitbox.width.toFloat()
)
}
//println(moveDelta)
}
}

View File

@@ -0,0 +1,62 @@
package net.torvald.terrarum.modulebasegame.gameactors
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.Input
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.Texture
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import net.torvald.terrarum.ModMgr
import net.torvald.terrarum.gameactors.Controllable
import net.torvald.terrarum.gameactors.Hitbox
import net.torvald.terrarum.gameworld.GameWorld
/**
* Created by minjaesong on 2018-01-17.
*/
class PhysTestLuarLander(world: GameWorld) : ActorWithPhysics(world, RenderOrder.MIDTOP), Controllable {
private val texture = Texture(ModMgr.getGdxFile("basegame", "sprites/phystest_lunarlander.tga"))
override val hitbox: Hitbox
init {
hitbox = Hitbox(0.0, 0.0, 0.0, 0.0)
setHitboxDimension(texture.width, texture.height, 0, 0)
actorValue[AVKey.SPEED] = 8.0
avBaseMass = 18650.0
}
override fun run() {
super.run()
}
override fun update(delta: Float) {
super.update(delta)
if (Gdx.input.isKeyPressed(Input.Keys.UP)) {
controllerMoveDelta!!.y = avSpeedCap
}
}
override fun keyDown(keycode: Int): Boolean {
return true
}
override fun drawGlow(batch: SpriteBatch) {
}
override fun drawBody(batch: SpriteBatch) {
batch.color = Color.WHITE
batch.draw(texture, hitbox.startX.toFloat(), hitbox.endY.toFloat(), hitbox.width.toFloat(), -hitbox.height.toFloat())
}
override fun onActorValueChange(key: String, value: Any?) {
super.onActorValueChange(key, value)
}
override fun dispose() {
super.dispose()
texture.dispose()
}
}

View File

@@ -0,0 +1,31 @@
package net.torvald.terrarum.modulebasegame.gameactors
import net.torvald.terrarum.gameactors.Controllable
/**
* A wrapper to support instant player changing (or possessing other NPCs maybe)
*
* @param actor : here you 'attach' the actor you wish to control
* Created by minjaesong on 2016-10-23.
*/
class PlayableActorDelegate(val actor: ActorHumanoid) {
init {
if (actor !is Controllable)
throw IllegalArgumentException("Player must be 'Controllable'!")
}
fun update(delta: Float) {
//val oldTilewisePos = actor.hIntTilewiseHitbox
actor.update(delta)
// fire lightmap recalculate event upon tilewise pos change
//val newTilewisePos = actor.hIntTilewiseHitbox
//if (oldTilewisePos != newTilewisePos) {
// LightmapRenderer.fireRecalculateEvent()
//}
// not going to work: think about stationery tiki torches, global lights, etc
}
}

View File

@@ -0,0 +1,35 @@
package net.torvald.terrarum.modulebasegame.gameactors
import net.torvald.terrarum.gameworld.GameWorld
/**
* Game player (YOU!)
*
* Created by minjaesong on 2015-12-31.
*/
class Player(world: GameWorld, born: GameDate) : ActorHumanoid(world, born) {
companion object {
@Transient const val PLAYER_REF_ID: Int = 0x91A7E2
}
/**
* Creates new Player instance with empty elements (sprites, actorvalue, etc.).
* **Use PlayerFactory to build player!**
* @throws SlickException
*/
init {
referenceID = PLAYER_REF_ID // forcibly set ID
density = BASE_DENSITY
collisionType = COLLISION_KINEMATIC
}
override fun update(delta: Float) {
super.update(delta)
}
}

View File

@@ -0,0 +1,25 @@
package net.torvald.terrarum.modulebasegame.gameactors
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.gameactors.Actor
import net.torvald.terrarum.modulebasegame.Ingame
/**
* Created by minjaesong on 2016-02-03.
*/
object PlayerBuilder {
operator fun invoke(): Actor {
val p: Actor = Player((Terrarum.ingame!! as Ingame).world, (Terrarum.ingame!! as Ingame).world.time.currentTimeAsGameDate)
InjectCreatureRaw(p.actorValue, "basegame", "CreatureHuman.json")
// attach sprite
// 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
}
}

View File

@@ -0,0 +1,49 @@
package net.torvald.terrarum.modulebasegame.gameactors
import net.torvald.terrarum.ModMgr
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.gameactors.ai.NullAI
import net.torvald.terrarum.modulebasegame.Ingame
import net.torvald.terrarum.worlddrawer.FeaturesDrawer
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
/**
* Created by minjaesong on 2016-03-25.
*/
object PlayerBuilderCynthia {
operator fun invoke(): ActorWithPhysics {
//val p: Player = Player(GameDate(100, 143)) // random value thrown
val p: HumanoidNPC = HumanoidNPC(
(Terrarum.ingame!! as Ingame).world,
NullAI(),
GameDate(100, 143)) // random value thrown
InjectCreatureRaw(p.actorValue, "basegame", "CreatureHuman.json")
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(TextureRegionPack(ModMgr.getGdxFile("basegame", "sprites/test_player_2.tga"), 26, 42))
p.sprite!!.delay = 0.2f
p.sprite!!.setRowsAndFrames(1, 1)
p.setHitboxDimension(15, p.actorValue.getAsInt(AVKey.BASEHEIGHT) ?: ActorHumanoid.BASE_HEIGHT, 9, 0)
p.setPosition(4096.0 * FeaturesDrawer.TILE_SIZE, 300.0 * FeaturesDrawer.TILE_SIZE)
p.referenceID = 321321321
return p
}
}

View File

@@ -0,0 +1,102 @@
package net.torvald.terrarum.modulebasegame.gameactors
import net.torvald.terrarum.ModMgr
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.gameactors.faction.FactionFactory
import net.torvald.terrarum.itemproperties.ItemCodex
import net.torvald.terrarum.blockproperties.Block
import net.torvald.terrarum.modulebasegame.Ingame
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
/**
* Created by minjaesong on 2016-02-03.
*/
object PlayerBuilderSigrid {
operator fun invoke(): Player {
val p = Player((Terrarum.ingame!! as Ingame).world, GameDate(-2147483648, 0)) // XD
p.referenceID = 0x51621D // the only constant of this procedural universe
p.historicalFigureIdentifier = 0x51621D // the only constant of this procedural universe
p.makeNewSprite(TextureRegionPack(ModMgr.getGdxFile("basegame", "sprites/test_player.tga"), 28, 51))
p.sprite!!.delay = 0.2f
p.sprite!!.setRowsAndFrames(1, 1)
p.makeNewSpriteGlow(TextureRegionPack(ModMgr.getGdxFile("basegame", "sprites/test_player_glow.tga"), 28, 51))
p.spriteGlow!!.delay = 0.2f
p.spriteGlow!!.setRowsAndFrames(1, 1)
p.actorValue[AVKey.SCALE] = 1.0
p.actorValue[AVKey.SPEED] = 4.0
p.actorValue[AVKey.SPEEDBUFF] = 1.0
p.actorValue[AVKey.ACCEL] = ActorHumanoid.WALK_ACCEL_BASE
p.actorValue[AVKey.ACCELBUFF] = 1.0
p.actorValue[AVKey.JUMPPOWER] = 13.0
p.actorValue[AVKey.BASEMASS] = 80.0
p.actorValue[AVKey.SCALEBUFF] = 1.0 // Constant 1.0 for player, meant to be used by random mobs
/**
* fixed value, or 'base value', from creature strength of Dwarf Fortress.
* Human race uses 1000. (see CreatureHuman.json)
*/
p.actorValue[AVKey.STRENGTH] = 1414 // this is test character, after all.
p.actorValue[AVKey.ENCUMBRANCE] = 1000
p.actorValue[AVKey.BASEHEIGHT] = 46
p.actorValue[AVKey.NAME] = "Sigrid"
p.actorValue[AVKey.INTELLIGENT] = true
//p.actorValue[AVKey.LUMR] = 0.84
//p.actorValue[AVKey.LUMG] = 0.93
//p.actorValue[AVKey.LUMB] = 1.37
p.actorValue[AVKey.LUMA] = 1.93
p.actorValue[AVKey.BASEDEFENCE] = 141
p.actorValue[AVKey.__PLAYER_QUICKSLOTSEL] = 0
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)
p.inventory = ActorInventory(p, 0, ActorInventory.CAPACITY_MODE_NO_ENCUMBER)
p.faction.add(FactionFactory.create("basegame", "factions/FactionSigrid.json"))
// Test fill up inventory
val blocks = arrayOf(
Block.AIR, Block.DIRT, Block.GLASS_CRUDE,
Block.GRASS, Block.GRAVEL, Block.ICE_MAGICAL, Block.LANTERN,
Block.PLANK_BIRCH, Block.PLANK_BLOODROSE, Block.PLANK_EBONY, Block.PLANK_NORMAL,
Block.SANDSTONE, Block.SANDSTONE_BLACK, Block.SANDSTONE_GREEN,
Block.SANDSTONE_RED, Block.STONE, Block.STONE_BRICKS,
Block.STONE_QUARRIED, Block.STONE_TILE_WHITE, Block.TORCH,
Block.DAYLIGHT_CAPACITOR, Block.ICE_FRAGILE,
Block.ILLUMINATOR_WHITE, Block.ILLUMINATOR_BLACK, Block.ILLUMINATOR_ORANGE,
Block.ILLUMINATOR_GREEN, Block.ILLUMINATOR_CYAN, Block.SUNSTONE
)
val walls = arrayOf(
Block.AIR, Block.DIRT, Block.GLASS_CRUDE,
Block.GRASSWALL, Block.ICE_MAGICAL,
Block.PLANK_BIRCH, Block.PLANK_BLOODROSE, Block.PLANK_EBONY, Block.PLANK_NORMAL,
Block.SANDSTONE, Block.SANDSTONE_BLACK, Block.SANDSTONE_GREEN,
Block.SANDSTONE_RED, Block.STONE, Block.STONE_BRICKS,
Block.STONE_QUARRIED, Block.STONE_TILE_WHITE
)
blocks.forEach { p.addItem(it, 999) }
walls.forEach { p.addItem(it + 4096, 999) }
p.inventory.add(ItemCodex.ITEM_STATIC.first)
return p
}
}

View File

@@ -0,0 +1,38 @@
package net.torvald.terrarum.modulebasegame.gameactors
import net.torvald.terrarum.ModMgr
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.modulebasegame.Ingame
import net.torvald.terrarum.worlddrawer.FeaturesDrawer
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
/**
* Created by minjaesong on 2017-02-10.
*/
object PlayerBuilderTestSubject1 {
operator fun invoke(): Player {
val p: Player = Player((Terrarum.ingame!! as Ingame).world, GameDate(100, 143)) // random value thrown
InjectCreatureRaw(p.actorValue, "basegame", "CreatureHuman.json")
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(TextureRegionPack(ModMgr.getGdxFile("basegame", "sprites/npc_template_anim_prototype.tga"), 48, 52))
p.sprite!!.delay = 0.2f
p.sprite!!.setRowsAndFrames(2, 4)
p.setHitboxDimension(15, p.actorValue.getAsInt(AVKey.BASEHEIGHT) ?: ActorHumanoid.BASE_HEIGHT, 21, 0)
p.setPosition(4096.0 * FeaturesDrawer.TILE_SIZE, 300.0 * FeaturesDrawer.TILE_SIZE)
return p
}
}

View File

@@ -0,0 +1,81 @@
package net.torvald.terrarum.modulebasegame.gameactors
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.gameactors.Actor
import net.torvald.terrarum.itemproperties.GameItem
import net.torvald.terrarum.itemproperties.ItemCodex
/**
* Created by minjaesong on 2016-01-15.
*/
interface Pocketed {
var inventory: ActorInventory
/**
* Equips an item. If the item is not in the inventory, an error will be thrown.
*/
fun unequipItem(item: GameItem?) {
if (item == null) return
if (item.equipPosition == GameItem.EquipPosition.NULL)
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")
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.deltaTime)
}
// no need for equipSlot(Int)
fun unequipSlot(slot: Int) {
if (slot < 0 || slot > GameItem.EquipPosition.INDEX_MAX)
throw IllegalArgumentException("Slot index out of range: $slot")
unequipItem(inventory.itemEquipped[slot])
}
/**
* Equips an item. If the item is not in the inventory, adds the item first.
*/
fun equipItem(item: GameItem) {
if (!inventory.contains(item)) {
println("[Pocketed] Item does not exist; adding one before equipped")
inventory.add(item)
}
if (item.equipPosition >= 0) {
inventory.itemEquipped[item.equipPosition] = item
item.effectWhenEquipped(Terrarum.deltaTime)
}
// else do nothing
}
fun equipped(item: GameItem): Boolean {
return inventory.itemEquipped[item.equipPosition] == item
}
fun addItem(itemID: Int, count: Int = 1) = inventory.add(ItemCodex[itemID], count)
fun addItem(item: GameItem, count: Int = 1) = inventory.add(item, count)
fun removeItem(itemID: Int, count: Int = 1) = inventory.remove(ItemCodex[itemID], count)
fun removeItem(item: GameItem, count: Int = 1) = inventory.remove(item, count)
fun hasItem(item: GameItem) = inventory.contains(item.dynamicID)
fun hasItem(id: Int) = inventory.contains(id)
fun consumePrimary(item: GameItem) {
if (item.primaryUse(Terrarum.deltaTime)) {
inventory.consumeItem(this as Actor, item) // consume on successful
}
}
fun consumeSecondary(item: GameItem) {
if (item.secondaryUse(Terrarum.deltaTime))
inventory.consumeItem(this as Actor, item) // consume on successful
}
}

View File

@@ -0,0 +1,9 @@
package net.torvald.terrarum.modulebasegame.gameactors
/**
* Projectile marker. Used to kill them when they're far away from the player, instead of making them sleep.
*
* Created by minjaesong on 2016-09-05.
*/
interface Projectile {
}

View File

@@ -0,0 +1,18 @@
package net.torvald.terrarum.modulebasegame.gameactors
import net.torvald.terrarum.gameworld.GameWorld
import org.dyn4j.geometry.Vector2
/**
* Created by minjaesong on 2016-08-29.
*/
class ProjectileHoming(
world: GameWorld,
type: Int,
fromPoint: Vector2, // projected coord
toPoint: Vector2 // arriving coord
) : ProjectileSimple(world, type, fromPoint, toPoint) {
}

View File

@@ -0,0 +1,129 @@
package net.torvald.terrarum.modulebasegame.gameactors
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import net.torvald.point.Point2d
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.blockproperties.Block
import net.torvald.terrarum.blockproperties.BlockCodex
import net.torvald.terrarum.gameactors.Hitbox
import net.torvald.terrarum.gameactors.Luminous
import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.modulebasegame.Ingame
import org.dyn4j.geometry.Vector2
import java.util.*
/**
* Simplest projectile.
*
* Created by minjaesong on 2016-08-29.
*/
// TODO simplified, lightweight physics (does not call PhysicsSolver)
open class ProjectileSimple(
world: GameWorld,
private val type: Int,
fromPoint: Vector2, // projected coord
toPoint: Vector2 // arriving coord
) : ActorWithPhysics(world, RenderOrder.MIDTOP), Luminous, Projectile {
val damage: Int
val displayColour: Color
/** scalar part of velocity */
val speed: Int
override var color: Color
get() = (bulletDatabase[type][OFFSET_LUMINOSITY] as Color).cpy()
set(value) {
}
/**
* Arguments:
*
* Hitbox(x-offset, y-offset, width, height)
* (Use ArrayList for normal circumstances)
*/
override val lightBoxList = ArrayList<Hitbox>()
private val lifetimeMax = 2500
private var lifetimeCounter = 0f
private val posPre: Point2d
init {
setPosition(fromPoint.x, fromPoint.y)
posPre = Point2d(fromPoint.x, fromPoint.y)
// lightbox sized 8x8 centered to the bullet
lightBoxList.add(Hitbox(-4.0, -4.0, 8.0, 8.0))
//this.externalForce.set(velocity)
damage = bulletDatabase[type][OFFSET_DAMAGE] as Int
displayColour = bulletDatabase[type][OFFSET_COL] as Color
isNoSubjectToGrav = bulletDatabase[type][OFFSET_NOGRAVITY] as Boolean
speed = bulletDatabase[type][OFFSET_SPEED] as Int
setHitboxDimension(2, 2, 0, 0) // should be following sprite's properties if there IS one
externalForce.set((fromPoint to toPoint).setMagnitude(speed.toDouble()))
collisionType = COLLISION_KINEMATIC
}
override fun update(delta: Float) {
// hit something and despawn
lifetimeCounter += delta
if (walledTop || walledBottom || walledRight || walledLeft || lifetimeCounter >= lifetimeMax ||
// stuck check
BlockCodex[(Terrarum.ingame!! as Ingame).world.getTileFromTerrain(feetPosTile[0], feetPosTile[1]) ?: Block.STONE].isSolid
) {
flagDespawn()
}
posPre.set(centrePosPoint)
super.update(delta)
}
/**
* WARNING! ends and begins Batch
*/
override fun drawBody(batch: SpriteBatch) {
val colourTail = displayColour.cpy() // clone a colour
colourTail.a = 0.16f
/*batch.end()
Terrarum.inShapeRenderer {
// draw trail of solid colour (Terraria style maybe?)
it.lineWidth = 2f * Terrarum.ingame!!.screenZoom
g.drawGradientLine(
hitbox.centeredX.toFloat() * Terrarum.ingame!!.screenZoom,
hitbox.centeredY.toFloat() * Terrarum.ingame!!.screenZoom,
displayColour,
posPre.x.toFloat() * Terrarum.ingame!!.screenZoom,
posPre.y.toFloat() * Terrarum.ingame!!.screenZoom,
colourTail
)
}
batch.begin()*/
}
override fun drawGlow(batch: SpriteBatch) = drawBody(batch)
companion object {
val OFFSET_DAMAGE = 0
val OFFSET_COL = 1 // Color or SpriteAnimation
val OFFSET_NOGRAVITY = 2
val OFFSET_SPEED = 3
val OFFSET_LUMINOSITY = 4
val bulletDatabase = arrayOf(
// damage, display colour, no gravity, speed
arrayOf(7, Color(0xFF5429_FF.toInt()), true, 40, 32),
arrayOf(8, Color(0xFF5429_FF.toInt()), true, 20, 0)
// ...
)
}
}

View File

@@ -0,0 +1,38 @@
package net.torvald.terrarum.modulebasegame.gameactors
import com.badlogic.gdx.graphics.Pixmap
import com.badlogic.gdx.graphics.Texture
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
/**
* Created by minjaesong on 2017-01-07.
*/
class TapestryObject(world: GameWorld, pixmap: Pixmap, val artName: String, val artAuthor: String) : FixtureBase(world, physics = false) {
// physics = false only speeds up for ~2 frames with 50 tapestries
init {
val texture = Texture(pixmap)
pixmap.dispose()
texture.setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest)
val texturePack = TextureRegionPack(texture, texture.width, texture.height)
makeNewSprite(texturePack)
setHitboxDimension(texture.width, texture.height, 0, 0)
setPosition(Terrarum.mouseX, Terrarum.mouseY)
// you CAN'T destroy the image
}
override fun update(delta: Float) {
super.update(delta)
}
override fun drawBody(batch: SpriteBatch) {
super.drawBody(batch)
}
override var tooltipText: String? = "$artName\n$artAuthor"
}

View File

@@ -0,0 +1,24 @@
package net.torvald.terrarum.modulebasegame.gameactors
import net.torvald.terrarum.Terrarum
/**
* Created by minjaesong on 2016-05-25.
*/
class ThreadActorUpdate(val startIndex: Int, val endIndex: Int) : Runnable {
override fun run() {
for (i in startIndex..endIndex) {
val it = Terrarum.ingame!!.actorContainer[i]
it.update(Terrarum.deltaTime)
if (it is Pocketed) {
it.inventory.forEach { inventoryEntry ->
inventoryEntry.item.effectWhileInPocket(Terrarum.deltaTime)
if (it.equipped(inventoryEntry.item)) {
inventoryEntry.item.effectWhenEquipped(Terrarum.deltaTime)
}
}
}
}
}
}

View File

@@ -0,0 +1,39 @@
package net.torvald.terrarum.modulebasegame.gameactors
import com.badlogic.gdx.graphics.Color
import net.torvald.terrarum.gameactors.Hitbox
import net.torvald.terrarum.gameactors.Luminous
import net.torvald.terrarum.gameworld.GameWorld
/**
* Created by minjaesong on 2016-04-26.
*/
class WeaponSwung(world: GameWorld, val itemID: Int) : ActorWithPhysics(world, RenderOrder.MIDTOP), Luminous {
// just let the solver use AABB; it's cheap but works just enough
/**
* Recommended implementation:
*
override var color: Int
get() = actorValue.getAsInt(AVKey.LUMINOSITY) ?: 0
set(value) {
actorValue[AVKey.LUMINOSITY] = value
}
*/
override var color: Color
get() = throw UnsupportedOperationException()
set(value) {
}
/**
* Arguments:
*
* Hitbox(x-offset, y-offset, width, height)
* (Use ArrayList for normal circumstances)
*/
override val lightBoxList: List<Hitbox>
get() = throw UnsupportedOperationException()
init {
}
}

View File

@@ -0,0 +1,55 @@
package net.torvald.terrarum.modulebasegame.gameactors.ai
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.modulebasegame.gameactors.HumanoidNPC
import net.torvald.terrarum.Second
import net.torvald.terrarum.gameactors.Actor
import net.torvald.terrarum.gameactors.ai.ActorAI
import net.torvald.terrarum.modulebasegame.Ingame
/**
* Slime's stupid AI but can adjust his jump power to smack you as fast as possible
* by achieving "allostasis".
*
* Created by minjaesong on 2017-12-10.
*/
class SmarterSlimes : ActorAI {
val memoryCells = IntArray(12, { 0 })
// index 0: most recent memory
// intentionally making it stupid by using less precise INT
// also we're not discrimination different enemies, making it further dumb
// stores "overshoot" amount (learn target) of x position
var maxJumpDist: Double = -1.0
var cooltime: Second = 5f
override fun update(actor: Actor, delta: Float) {
val actor = actor as HumanoidNPC
// sensor: compare(my X pos, nearest enemy's X pos)
maxJumpDist = actor.avSpeedCap * actor.jumpAirTime // speed * air_time
// (to be precise, we need simulation just like jumpAirTime, but oh well; we like it LINEAR)
// TEST: just target player
val playerXPos = (Terrarum.ingame!! as Ingame).player.centrePosPoint.x
val thisXPos = actor.centrePosPoint.x
val xDiff = thisXPos - playerXPos
// extrapolate from memories:
// otherwise linear extp. except the slope is d of 0th and 2nd point
if (xDiff > 0) {
actor.moveLeft()
}
}
}

View File

@@ -0,0 +1,223 @@
package net.torvald.terrarum.modulebasegame.gameactors.physicssolver
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.modulebasegame.Ingame
import net.torvald.terrarum.modulebasegame.gameactors.ActorWithPhysics
import java.util.*
/**
* Created by minjaesong on 2016-04-22.
*/
object CollisionSolver {
private const val STARTPOINT = 1
private const val ENDPOINT = 2
private const val COLL_LIST_SIZE = 256
private const val COLL_CANDIDATES_SIZE = 128
private const val COLL_FINAL_CANDIDATES_SIZE = 16
private val collListX = ArrayList<CollisionMarkings>(COLL_LIST_SIZE)
private val collListY = ArrayList<CollisionMarkings>(COLL_LIST_SIZE)
private val collCandidateX = ArrayList<Pair<ActorWithPhysics, ActorWithPhysics>>(COLL_CANDIDATES_SIZE)
private val collCandidateY = ArrayList<Pair<ActorWithPhysics, ActorWithPhysics>>(COLL_CANDIDATES_SIZE)
private var collCandidates = ArrayList<Pair<ActorWithPhysics, ActorWithPhysics>>(COLL_FINAL_CANDIDATES_SIZE)
private val collCandidateStack = Stack<CollisionMarkings>()
/**
* to see what's going on here, visit
* [this link](https://www.toptal.com/game/video-game-physics-part-ii-collision-detection-for-solid-objects)
*/
fun process() {
// TODO threading X and Y
// clean up before we go
collListX.clear()
collListY.clear()
collCandidateX.clear()
collCandidateY.clear()
// mark list x
(Terrarum.ingame!! as Ingame).actorContainer.forEach { it ->
if (it is ActorWithPhysics) {
collListX.add(CollisionMarkings(it.hitbox.hitboxStart.x, STARTPOINT, it))
collListX.add(CollisionMarkings(it.hitbox.hitboxEnd.x, ENDPOINT, it))
}
}
// sort list x
collListX.sortBy { it.pos }
// set candidateX
collListX.forEach {
if (it.kind == STARTPOINT) {
collCandidateStack.push(it)
}
else if (it.kind == ENDPOINT) {
val mark_this = it
val mark_other = collCandidateStack.pop()
// make sure actor with lower ID comes first
val collCandidate = if (mark_this.actor < mark_other.actor)
Pair(mark_this.actor, mark_other.actor)
else
Pair(mark_other.actor, mark_this.actor)
// filter out Pair(E, E); Pair(A, B) if Pair(B, A) exists
if (mark_this.actor != mark_other.actor) {
collCandidateX.add(collCandidate)
}
}
}
collCandidateStack.clear()
// mark list y
(Terrarum.ingame!! as Ingame).actorContainer.forEach { it ->
if (it is ActorWithPhysics) {
collListY.add(CollisionMarkings(it.hitbox.hitboxStart.y, STARTPOINT, it))
collListY.add(CollisionMarkings(it.hitbox.hitboxEnd.y, ENDPOINT, it))
}
}
// sort list y
collListY.sortBy { it.pos }
// set candidateY
collListY.forEach {
if (it.kind == STARTPOINT) {
collCandidateStack.push(it)
}
else if (it.kind == ENDPOINT) {
val mark_this = it
val mark_other = collCandidateStack.pop()
val collCandidate: Pair<ActorWithPhysics, ActorWithPhysics>
// make sure actor with lower ID comes first
if (mark_this.actor < mark_other.actor)
collCandidate = Pair(mark_this.actor, mark_other.actor)
else
collCandidate = Pair(mark_other.actor, mark_this.actor)
// filter out Pair(E, E); Pair(A, B) if Pair(B, A) exists
if (mark_this.actor != mark_other.actor) {
collCandidateY.add(collCandidate)
}
}
}
// look for overlaps in candidate X/Y and put them into collCandidates
// overlapping in X and Y means they are actually overlapping physically
collCandidateY.retainAll(collCandidateX) // list Y will have intersection of X and Y now
collCandidates = collCandidateY // renaming. X and Y won't be used anyway.
//collCandidates.forEach { println(it) }
//println("-----------------------")
// solve collision for actors in collCandidates
collCandidates.forEach { solveCollision(it.first, it.second) }
}
private fun pairEqv(a: Pair<Any?, Any?>, b: Pair<Any?, Any?>) =
(a.first == b.first && a.second == b.second) ||
(a.first == b.second && a.second == b.first)
/** Mimics java's original behaviour, with user-defined equals function */
fun ArrayList<Any?>.containsByFunc(other: Any?, equalsFun: (a: Any?, b: Any?) -> Boolean): Boolean {
fun indexOfEqFn(arrayList: ArrayList<Any?>, o: Any?): Int {
if (o == null) {
for (i in 0..size - 1)
if (arrayList[i] == null)
return i
}
else {
for (i in 0..size - 1)
if (equalsFun(o, arrayList[i]))
return i
}
return -1
}
return indexOfEqFn(this, other) >= 0
}
private fun solveCollision(a: ActorWithPhysics, b: ActorWithPhysics) {
// some of the Pair(a, b) are either duplicates or erroneously reported.
// e.g. (A, B), (B, C) and then (A, C);
// in some situation (A, C) will not making any contact with each other
// we are going to filter them
if (a isCollidingWith b) {
// notify collision, but not solve it yet
//println("Collision: $a <-> $b")
// FIXME does work but has duplication
// if they actually makes collision (e.g. player vs ball), solve it
if (a makesCollisionWith b) {
val a_moveDelta = a.externalForce + a.controllerMoveDelta
val b_moveDelta = b.externalForce + b.controllerMoveDelta
val ux_1 = a_moveDelta.x
val ux_2 = b_moveDelta.x
val uy_1 = a_moveDelta.y
val uy_2 = b_moveDelta.y
val m1 = a.mass
val m2 = b.mass
val vx_1 = (ux_2 * (m1 - m2) + 2 * m2 * ux_2) / (m1 + m2)
val vx_2 = (ux_2 * (m2 - m1) + 2 * m1 * ux_1) / (m1 + m2)
val vy_1 = (uy_2 * (m1 - m2) + 2 * m2 * uy_2) / (m1 + m2)
val vy_2 = (uy_2 * (m2 - m1) + 2 * m1 * uy_1) / (m1 + m2)
/*a.veloX = vx_1
a.veloY = vy_1
b.veloX = vx_2
b.veloY = vy_2*/
}
}
}
private infix fun ActorWithPhysics.makesCollisionWith(other: ActorWithPhysics) =
this.collisionType != ActorWithPhysics.COLLISION_NOCOLLIDE &&
other.collisionType != ActorWithPhysics.COLLISION_NOCOLLIDE
private infix fun ActorWithPhysics.isCollidingWith(other: ActorWithPhysics): Boolean {
val ax = this.hitbox.centeredX
val ay = this.hitbox.centeredY
val bx = other.hitbox.centeredX
val by = other.hitbox.centeredY
// will refer 'actor_dist_t' as 't' afterward
val actor_dist_t_sqr = ((ay - by).sqr() + (ax - bx).sqr()) // no sqrt; 'power' is slower than 'times'
val dist_x = (ax - bx).abs() // 'tx'
val dist_y = (ay - by).abs() // 'ty'
val tangent = dist_y / dist_x
var t_ax: Double; var t_ay: Double
if (dist_x > dist_y) {
t_ax = this.hitbox.width / 2
t_ay = t_ax * tangent
}
else {
t_ay = this.hitbox.height / 2
t_ax = t_ay * tangent
}
return (t_ax.sqr() + t_ay.sqr()) < actor_dist_t_sqr
}
fun Double.abs() = if (this < 0) -this else this
fun Double.sqr() = this * this
data class CollisionMarkings(
val pos: Double,
val kind: Int,
val actor: ActorWithPhysics
)
/**
* === Some useful physics knowledge ===
*
* * Momentum = mass × Velocity (p = mv, conserved)
*
* * Force = mass × acceleration (f = ma, conserved)
*
* * F_AB = -F_BA (Lex Tertia, does NOT apply to fictitious force like centrifugal)
*/
}

View File

@@ -0,0 +1,11 @@
package net.torvald.terrarum.modulebasegame.gameactors.physicssolver
/**
* multithreaded version of CollisionSolver#solveCollision
* Created by minjaesong on 2016-04-26.
*/
internal class SolveByUnit : Runnable {
override fun run() {
throw UnsupportedOperationException()
}
}

View File

@@ -0,0 +1,18 @@
package net.torvald.terrarum.modulebasegame.gameactors.physicssolver
import net.torvald.terrarum.modulebasegame.gameactors.ActorWithPhysics
/**
* Created by minjaesong on 2016-05-01.
*/
object VelocitySolver {
fun process() {
}
private fun applyGravity(actor: ActorWithPhysics) {
}
}

View File

@@ -0,0 +1,32 @@
package net.torvald.terrarum.modulebasegame.gameworld
import net.torvald.terrarum.gameactors.ActorID
/**
* The whole world is economically isolated system. Economy will be important to make player keep playing,
* when all the necessary contents are set and implemented to the production.
*
* Design goal: keep the inflation rate low, but not negative (Single market)
* OR, give each faction (establishment) its own economy and watch them prosper/doomed (DF style)
*
* Created by minjaesong on 2017-04-23.
*/
class GameEconomy {
val transactionHistory = TransanctionHistory()
}
class TransanctionHistory {
private val entries = ArrayList<TransanctionHistory>()
/**
* @param to set 0 to indicate the money was lost to void
*/
data class TransactionEntry(val from: ActorID, val to: ActorID, val amount: Long) {
override fun toString() = "$from -> $to; $amount"
}
}

View File

@@ -0,0 +1,305 @@
package net.torvald.terrarum.modulebasegame.gameworld
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.modulebasegame.gameactors.AnyPlayer
import net.torvald.terrarum.roundInt
import net.torvald.terrarum.worlddrawer.BlocksDrawer
import net.torvald.terrarum.worlddrawer.FeaturesDrawer
import net.torvald.terrarum.blockproperties.Block
import net.torvald.terrarum.blockproperties.BlockCodex
import net.torvald.terrarum.gameworld.FluidCodex
import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.modulebasegame.Ingame
/**
* Created by minjaesong on 2016-08-03.
*/
object WorldSimulator {
/**
* In tiles;
* square width/height = field * 2
*/
const val FLUID_UPDATING_SQUARE_RADIUS = 64 // larger value will have dramatic impact on performance
const private val DOUBLE_RADIUS = FLUID_UPDATING_SQUARE_RADIUS * 2
private val fluidMap = Array<ByteArray>(DOUBLE_RADIUS, { ByteArray(DOUBLE_RADIUS) })
private val fluidTypeMap = Array<ByteArray>(DOUBLE_RADIUS, { ByteArray(DOUBLE_RADIUS) })
const val DISPLACE_CAP = 4
const val FLUID_MAX = 16
var updateXFrom = 0
var updateXTo = 0
var updateYFrom = 0
var updateYTo = 0
val colourNone = Color(0x808080FF.toInt())
val colourWater = Color(0x66BBFFFF.toInt())
private val world = (Terrarum.ingame!! as Ingame).world
operator fun invoke(p: AnyPlayer?, delta: Float) {
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()
updateXTo = updateXFrom + DOUBLE_RADIUS
updateYTo = updateYFrom + DOUBLE_RADIUS
}
moveFluids(delta)
displaceFallables(delta)
}
/**
* displace fluids. Note that the code assumes the gravity pulls things downward ONLY,
* which means you'll need to modify the code A LOT if you're going to implement zero- or
* reverse-gravity.
*
* Procedure: CP world fluidmap -> sim on fluidmap -> CP fluidmap world
* TODO multithread
*/
fun moveFluids(delta: Float) {
////////////////////
// build fluidmap //
////////////////////
purgeFluidMap()
worldToFluidMap(world)
/////////////////////////////////////////////////////////////
// displace fluids. Record displacements into the fluidMap //
/////////////////////////////////////////////////////////////
for (y in updateYFrom..updateYTo) {
for (x in updateXFrom..updateXTo) {
val tile = world.getTileFromTerrain(x, y) ?: Block.STONE
val tileBottom = world.getTileFromTerrain(x, y + 1) ?: Block.STONE
val tileLeft = world.getTileFromTerrain(x - 1, y) ?: Block.STONE
val tileRight = world.getTileFromTerrain(x + 1, y) ?: Block.STONE
if (tile.isFluid()) {
// move down if not obstructed
/*if (!tileBottom.isSolid()) {
val drainage = drain(world, x, y, DISPLACE_CAP)
pour(world, x, y + 1, drainage)
}
// left and right both open
else if (!tileLeft.isSolid() && !tileRight.isSolid()) {
// half-breaker
val moreToTheRight = HQRNG().nextBoolean()
val displacement = drain(world, x, y, DISPLACE_CAP)
if (displacement.isEven()) {
pour(world, x - 1, y, displacement shr 1)
pour(world, x + 1, y, displacement shr 1)
}
else {
pour(world, x - 1, y, (displacement shr 1) + if (moreToTheRight) 0 else 1)
pour(world, x + 1, y, (displacement shr 1) + if (moreToTheRight) 1 else 0)
}
}
// left open
else if (!tileLeft.isSolid()) {
val displacement = drain(world, x, y, DISPLACE_CAP)
pour(world, x - 1, y, displacement)
}
// right open
else if (!tileRight.isSolid()) {
val displacement = drain(world, x, y, DISPLACE_CAP)
pour(world, x + 1, y, displacement)
}
// nowhere open; do default (fill top)
else {
pour(world, x, y - 1, DISPLACE_CAP)
}*/
if (!tileBottom.isSolid()) {
pour(x, y + 1, drain(x, y, FLUID_MAX))
}
}
}
}
/////////////////////////////////////////////////////
// replace fluids in the map according to fluidMap //
/////////////////////////////////////////////////////
fluidMapToWorld(world)
}
/**
* displace fallable tiles. It is scanned bottom-left first. To achieve the sens ofreal
* falling, each tiles are displaced by ONLY ONE TILE below.
*/
fun displaceFallables(delta: Float) {
for (y in updateYFrom..updateYTo) {
for (x in updateXFrom..updateXTo) {
val tile = world.getTileFromTerrain(x, y) ?: Block.STONE
val tileBelow = world.getTileFromTerrain(x, y + 1) ?: Block.STONE
if (tile.isFallable()) {
// displace fluid. This statement must precede isSolid()
if (tileBelow.isFluid()) {
// remove tileThis to create air pocket
world.setTileTerrain(x, y, Block.AIR)
pour(x, y, drain(x, y, tileBelow.fluidLevel().toInt()))
// place our tile
world.setTileTerrain(x, y + 1, tile)
}
else if (!tileBelow.isSolid()) {
world.setTileTerrain(x, y, Block.AIR)
world.setTileTerrain(x, y + 1, tile)
}
}
}
}
}
fun disperseHeat(delta: Float) {
}
fun drawFluidMapDebug(batch: SpriteBatch) {
batch.color = colourWater
for (y in 0..fluidMap.size - 1) {
for (x in 0..fluidMap[0].size - 1) {
val data = fluidMap[y][x]
if (BlocksDrawer.tileInCamera(x + updateXFrom, y + updateYFrom)) {
if (data == 0.toByte())
batch.color = colourNone
else
batch.color = colourWater
Terrarum.fontSmallNumbers.draw(batch,
data.toString(),
updateXFrom.plus(x).times(FeaturesDrawer.TILE_SIZE).toFloat()
+ if (data < 10) 4f else 0f,
updateYFrom.plus(y).times(FeaturesDrawer.TILE_SIZE) + 4f
)
}
//if (data > 0) println(data)
}
}
}
private fun purgeFluidMap() {
for (y in 1..DOUBLE_RADIUS) {
for (x in 1..DOUBLE_RADIUS) {
fluidMap[y - 1][x - 1] = 0
fluidTypeMap[y - 1][x - 1] = 0
}
}
}
private fun worldToFluidMap(world: GameWorld) {
for (y in updateYFrom..updateYTo) {
for (x in updateXFrom..updateXTo) {
val tile = world.getTileFromTerrain(x, y) ?: Block.STONE
if (tile.isFluid()) {
fluidMap[y - updateYFrom][x - updateXFrom] = tile.fluidLevel().toByte()
fluidTypeMap[y - updateYFrom][x - updateXFrom] = tile.fluidType().toByte()
}
}
}
}
private fun fluidMapToWorld(world: GameWorld) {
for (y in 0..fluidMap.size - 1) {
for (x in 0..fluidMap[0].size - 1) {
placeFluid(world, updateXFrom + x, updateYFrom + y
, FluidCodex.FLUID_WATER, fluidMap[y][x] - 1
)
// FIXME test code: deals with water only!
}
}
}
fun Int.isFluid() = BlockCodex[this].isFluid
fun Int.isSolid() = this.fluidLevel() == FLUID_MAX || BlockCodex[this].isSolid
//fun Int.viscosity() = BlockCodex[this].
fun Int.fluidLevel() = if (!this.isFluid()) 0 else (this % FLUID_MAX).plus(1)
fun Int.fluidType() = (this / 16) // 0 - 255, 255 being water, 254 being lava
fun Int.isEven() = (this and 0x01) == 0
fun Int.isFallable() = BlockCodex[this].isFallable
private fun placeFluid(world: GameWorld, x: Int, y: Int, fluidType: Byte, amount: Int) {
if (world.layerTerrain.isInBound(x, y)) {
if (amount > 0 && !world.getTileFromTerrain(x, y)!!.isSolid()) {
world.setTileTerrain(x, y, fluidType, amount - 1)
}
else if (amount == 0 && world.getTileFromTerrain(x, y)!!.isFluid()) {
world.setTileTerrain(x, y, Block.AIR)
}
}
}
/**
* @param x and y: world tile coord
* @return amount of fluid actually drained.
* (intended drainage - this) will give you how much fluid is not yet drained.
* TODO add fluidType support
*/
private fun drain(x: Int, y: Int, amount: Int): Int {
val displacement = Math.min(fluidMap[y - updateYFrom][x - updateXFrom].toInt(), amount)
fluidMap[y - updateYFrom][x - updateXFrom] =
(fluidMap[y - updateYFrom][x - updateXFrom] - displacement).toByte()
return displacement
}
/**
* @param x and y: world tile coord
* TODO add fluidType support
*/
private fun pour(x: Int, y: Int, amount: Int) {
/**
* @param x and y: world tile coord
* @return spillage
* TODO add fluidType support
*/
fun pourInternal(worldXpos: Int, worldYPos: Int, volume: Int): Int {
var spil = 0
val addrX = worldXpos - updateXFrom
val addrY = worldYPos - updateYFrom
if (addrX >= 0 && addrY >= 0 && addrX < DOUBLE_RADIUS && addrY < DOUBLE_RADIUS) {
fluidMap[addrY][addrX] = (fluidMap[addrY][addrX] + volume).toByte()
if (fluidMap[addrY][addrX] > FLUID_MAX) {
spil = fluidMap[addrY][addrX] - FLUID_MAX
fluidMap[addrY][addrX] = FLUID_MAX.toByte()
}
}
return spil
}
// pour the fluid
var spillage = pourInternal(x, y, amount)
if (spillage <= 0) return
// deal with the spillage
val tileUp = world.getTileFromTerrain(x - updateXFrom, y - updateYFrom - 1)
val tileDown = world.getTileFromTerrain(x - updateXFrom, y - updateYFrom + 1)
// try to fill downward
if (tileDown != null && !tileDown.isSolid()) {
spillage = pourInternal(x, y + 1, spillage)
}
// else, try to fill upward. if there is no space, just discard
if (spillage >= 0 && tileUp != null && !tileUp.isSolid()) {
pourInternal(x, y - 1, spillage)
}
}
}

View File

@@ -0,0 +1,187 @@
package net.torvald.terrarum.modulebasegame.gameworld
import net.torvald.terrarum.modulebasegame.gameactors.GameDate
/**
* The World Calendar implementation of Dwarven Calendar, except:
* - the year begins with Mondag instead of Sundag (which is ISO standard)
* - the first month is Opal instead of Granite (to reduce confusion)
*
*
* Please also see:
* https://en.wikipedia.org/wiki/World_Calendar
* http://dwarffortresswiki.org/index.php/DF2014:Calendar
*
* And there is no AM/PM concept, 22-hour clock is forced; no leap years.
* (AM 12 is still 00h in this system, again, to reduce confusion)
*
*
* Calendar
*
* |Mo|Ty|Mi|To|Fr|La|Su|Ve|
* |--|--|--|--|--|--|--|--|
* | 1| 2| 3| 4| 5| 6| 7| |
* | 8| 9|10|11|12|13|14| |
* |15|16|17|18|19|20|21| |
* |22|23|24|25|26|27|28| |
* |29|30|31| 1| 2| 3| 4| |
* | 5| 6| 7| 8| 9|10|11| |
* |12|13|14|15|16|17|18| |
* |19|20|21|22|23|24|25| |
* |26|27|28|29|30| 1| 2| |
* | 3| 4| 5| 6| 7| 8| 9| |
* |10|11|12|13|14|15|16| |
* |17|18|19|20|21|22|23| |
* |24|25|26|27|28|29|30|31|
*
* Verddag only appears on the last day of the year (31st Moonstone)
*
* (Check please:)
* - Equinox/Solstice always occur on 21st day of the month
*
*
* Created by minjaesong on 2016-01-24.
*/
class WorldTime(initTime: Long = 0L) {
var TIME_T = 0L // Epoch: Year 125, 1st Opal, 0h00:00 (Mondag) // 125-01-01
private set
init {
TIME_T = initTime
}
inline val seconds: Int // 0 - 59
get() = TIME_T.toPositiveInt() % MINUTE_SEC
inline val minutes: Int // 0 - 59
get() = TIME_T.div(MINUTE_SEC).abs().toInt() % HOUR_MIN
inline val hours: Int // 0 - 21
get() = TIME_T.div(HOUR_SEC).abs().toInt() % HOURS_PER_DAY
inline val yearlyDays: Int // 0 - 364
get() = (TIME_T.toPositiveInt().div(DAY_LENGTH) % YEAR_DAYS)
inline val days: Int // 1 - 31
get() = quarterlyDays + 1 -
if (quarterlyMonthOffset == 0) 0
else if (quarterlyMonthOffset == 1) 31
else 61
inline val months: Int // 1 - 12
get() = if (yearlyDays == YEAR_DAYS - 1) 12 else
quarter * 3 + 1 +
if (quarterlyDays < 31) 0
else if (quarterlyDays < 61) 1
else 2
inline val years: Int
get() = TIME_T.div(YEAR_DAYS * DAY_LENGTH).abs().toInt() + EPOCH_YEAR
inline val quarter: Int // 0 - 3
get() = if (yearlyDays == YEAR_DAYS - 1) 3 else yearlyDays / QUARTER_LENGTH
inline val quarterlyDays: Int // 0 - 90(91)
get() = if (yearlyDays == YEAR_DAYS - 1) 91 else (yearlyDays % QUARTER_LENGTH)
inline val quarterlyMonthOffset: Int // 0 - 2
get() = months.minus(1) % 3
inline val dayOfWeek: Int //0: Mondag-The first day of weekday (0 - 7)
get() = if (yearlyDays == YEAR_DAYS - 1) 7 else yearlyDays % 7
var timeDelta: Int = 1
set(value) {
field = if (value < 0) 0 else value
}
inline val moonPhase: Double
get() = (TIME_T.plus(1700000L) % LUNAR_CYCLE).toDouble() / LUNAR_CYCLE
@Transient private var realMillisec: Double = 0.0
@Transient private val REAL_SEC_TO_GAME_SECS = 60
val DAY_NAMES = arrayOf(//daynames are taken from Nynorsk (å -> o)
"Mondag", "Tysdag", "Midtveke" //middle-week
, "Torsdag", "Fredag", "Laurdag", "Sundag", "Verddag" //From Norsk word 'verd'
)
val DAY_NAMES_SHORT = arrayOf("Mon", "Tys", "Mid", "Tor", "Fre", "Lau", "Sun", "Ver")
val MONTH_NAMES = arrayOf(
"Opal", "Obsidian", "Granite", "Slate", "Felsite", "Hematite",
"Malachite", "Galena", "Limestone", "Sandstone", "Timber", "Moonstone"
)
val MONTH_NAMES_SHORT = arrayOf("Opal", "Obsi", "Gran", "Slat", "Fels", "Hema",
"Mala", "Gale", "Lime", "Sand", "Timb", "Moon")
inline val currentTimeAsGameDate: GameDate
get() = GameDate(years, yearlyDays)
companion object {
/** Each day is 22-hour long */
val DAY_LENGTH = 79200 //must be the multiple of 3600
val HOUR_SEC: Int = 3600
val MINUTE_SEC: Int = 60
val HOUR_MIN: Int = 60
val GAME_MIN_TO_REAL_SEC: Float = 60f
val HOURS_PER_DAY = DAY_LENGTH / HOUR_SEC
val YEAR_DAYS: Int = 365
val QUARTER_LENGTH = 91 // as per The World Calendar
val EPOCH_YEAR = 125
fun parseTime(s: String): Int =
if (s.length >= 4 && s.contains('h')) {
s.toLowerCase().substringBefore('h').toInt() * HOUR_SEC +
s.toLowerCase().substringAfter('h').toInt() * MINUTE_SEC
}
else if (s.endsWith("h", true)) {
s.toLowerCase().substring(0, s.length - 1).toInt() * HOUR_SEC
}
else {
s.toInt()
}
val LUNAR_CYCLE: Int = 2342643// 29 days, 12 hours, 44 minutes, and 3 seconds in-game calendar
}
fun update(delta: Float) {
//time
realMillisec += delta * 1000.0
if (realMillisec >= 1000.0 / REAL_SEC_TO_GAME_SECS) {
realMillisec -= 1000.0 / REAL_SEC_TO_GAME_SECS
TIME_T += timeDelta
}
}
val todaySeconds: Int
get() = TIME_T.toPositiveInt() % DAY_LENGTH
fun setTimeOfToday(t: Int) {
TIME_T = TIME_T - todaySeconds + t
}
fun addTime(t: Int) {
TIME_T += t
}
val dayName: String
get() = DAY_NAMES[dayOfWeek]
inline fun Long.toPositiveInt() = this.and(0x7FFFFFFF).toInt()
inline fun Long.abs() = Math.abs(this)
/** Format: "%A, %d %B %Y %X" */
fun getFormattedTime() = "${getDayNameShort()}, " +
"$days " +
"${getMonthNameShort()} " +
"$years " +
"${String.format("%02d", hours)}:" +
"${String.format("%02d", minutes)}:" +
"${String.format("%02d", seconds)}"
fun getDayNameFull() = DAY_NAMES[dayOfWeek]
fun getDayNameShort() = DAY_NAMES_SHORT[dayOfWeek]
fun getMonthNameFull() = MONTH_NAMES[months - 1]
fun getMonthNameShort() = MONTH_NAMES_SHORT[months - 1]
override fun toString() = getFormattedTime()
}

View File

@@ -0,0 +1,199 @@
package net.torvald.terrarum.modulebasegame.gameworld
import net.torvald.terrarum.modulebasegame.gameactors.GameDate
/**
* The World Calendar implementation of Dwarven Calendar (we're talking about DF!)
*
* Please see:
* https://en.wikipedia.org/wiki/World_Calendar
* http://dwarffortresswiki.org/index.php/DF2014:Calendar
*
* Normal format for day is
* Tysdag 12th Granite
*
* And there is no AM/PM concept, 22-hour clock is forced.
*
* Created by minjaesong on 2016-01-24.
*/
@Deprecated("Are you even reading the name?")
class YeOldeWorldTime {
internal var seconds: Int // 0 - 59
internal var minutes: Int // 0 - 59
internal var hours: Int // 0 - 21
// days on the year
internal var yearlyDays: Int //NOT a calendar day
internal var days: Int // 1 - 31
internal var months: Int // 1 - 12
internal var years: Int // 1+
internal var dayOfWeek: Int //0: Mondag-The first day of weekday (0 - 7)
internal var timeDelta = 1
@Transient private var realMillisec: Int
val DAY_NAMES = arrayOf(//daynames are taken from Nynorsk (å -> o)
"Mondag", "Tysdag", "Midvikdag" //From Islenska Miðvikudagur
, "Torsdag", "Fredag", "Laurdag", "Sundag", "Verddag" //From Norsk word 'verd'
)
val DAY_NAMES_SHORT = arrayOf("Mon", "Tys", "Mid", "Tor", "Fre", "Lau", "Sun", "Ver")
val MONTH_NAMES = arrayOf(
"Opal", "Obsidian", "Granite", "Slate", "Felsite", "Hematite",
"Malachite", "Galena", "Limestone", "Sandstone", "Timber", "Moonstone"
)
val MONTH_NAMES_SHORT = arrayOf("Opal", "Obsi", "Gran", "Slat", "Fels", "Hema",
"Mala", "Gale", "Lime", "Sand", "Timb", "Moon")
val currentTimeAsGameDate: GameDate
get() = GameDate(years, yearlyDays)
@Transient val REAL_SEC_IN_MILLI = 1000
companion object {
/** Each day is 22-hour long */
val DAY_LENGTH = 79200 //must be the multiple of 3600
val HOUR_SEC: Int = 3600
val MINUTE_SEC: Int = 60
val HOUR_MIN: Int = 60
val GAME_MIN_TO_REAL_SEC: Float = 60f
val YEAR_DAYS: Int = 365
fun parseTime(s: String): Int =
if (s.length >= 4 && s.contains('h')) {
s.toLowerCase().substringBefore('h').toInt() * WorldTime.HOUR_SEC +
s.toLowerCase().substringAfter('h').toInt() * WorldTime.MINUTE_SEC
}
else if (s.endsWith("h", true)) {
s.toLowerCase().substring(0, s.length - 1).toInt() * WorldTime.HOUR_SEC
}
else {
s.toInt()
}
}
init {
// The day when the new world ever is being made.
// If we use Multiverse system (which replaces Terraria's "hack"
// as a reward rather than a cheat), time of current world's time is
// copied to the new world's. (it's Multi-nation rather than Multiverse)
seconds = 0
minutes = 30
hours = 8
yearlyDays = 73
days = 12
months = 3
years = 125
dayOfWeek = 1 // Tysdag
realMillisec = 0
}
fun update(delta: Int) {
val oldsec = seconds
//time
realMillisec += delta * timeDelta
val newsec = Math.round(GAME_MIN_TO_REAL_SEC / REAL_SEC_IN_MILLI.toFloat() * realMillisec.toFloat())
seconds = newsec
if (realMillisec >= REAL_SEC_IN_MILLI)
realMillisec -= REAL_SEC_IN_MILLI
kickVariables()
}
/**
* How much time has passed today, in seconds.
* 0 == 6 AM
* @return
*/
val elapsedSeconds: Int
get() = (HOUR_SEC * hours + MINUTE_SEC * minutes + seconds) % DAY_LENGTH
/** Sets time of this day. */
fun setTime(t: Int) {
days += t / DAY_LENGTH
hours = t / HOUR_SEC
minutes = (t - HOUR_SEC * hours) / MINUTE_SEC
seconds = t - minutes * MINUTE_SEC
yearlyDays += t / DAY_LENGTH
}
fun addTime(t: Int) {
setTime(elapsedSeconds + t)
}
fun setTimeDelta(d: Int) {
timeDelta = if (d < 0) 0 else d
}
val dayName: String
get() = DAY_NAMES[dayOfWeek]
private fun kickVariables() {
if (seconds >= MINUTE_SEC) {
seconds = 0
minutes += 1
}
if (minutes >= HOUR_MIN) {
minutes = 0
hours += 1
}
if (hours >= DAY_LENGTH / HOUR_SEC) {
hours = 0
days += 1
yearlyDays += 1
dayOfWeek += 1
}
//calendar (the world calendar)
if (dayOfWeek == 7) {
dayOfWeek = 0
}
if (months == 12 && days == 31) {
dayOfWeek = 7
}
if (months == 12 && days == 32) {
days = 1
months = 1
years++
}
else if ((months == 1 || months == 4 || months == 7 || months == 10) && days > 31) {
days = 1
months++
}
else if (days > 30) {
days = 1
months++
}
if (months > 12) {
months = 1
years++
yearlyDays = 1
}
}
/** Format: "%A %d %B %Y %X" */
fun getFormattedTime() = "${getDayNameFull()} " +
"$days " +
"${getMonthNameFull()} " +
"$years " +
"${String.format("%02d", hours)}:" +
"${String.format("%02d", minutes)}:" +
"${String.format("%02d", seconds)}"
fun getDayNameFull() = DAY_NAMES[dayOfWeek]
fun getDayNameShort() = DAY_NAMES_SHORT[dayOfWeek]
fun getMonthNameFull() = MONTH_NAMES[months - 1]
fun getMonthNameShort() = MONTH_NAMES_SHORT[months - 1]
}

View File

@@ -0,0 +1,78 @@
package net.torvald.terrarum.modulebasegame.imagefont
import com.badlogic.gdx.graphics.g2d.Batch
import com.badlogic.gdx.graphics.g2d.BitmapFont
import com.badlogic.gdx.graphics.g2d.GlyphLayout
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
/**
* Created by minjaesong on 2017-03-24.
*/
class NewRunes : BitmapFont() {
private val runeSize = 12
// hard-coded encode map
private fun codeToEnc(code: Int): Int? = if (code in 0x21..0x3f)
code - 0x20
else if (code in 0x3001..0x300f)
code - 0x3000 + 0x20
else if (code in 0x3131..0x3163)
code - 0x3130 + 0x30
else
null
private val runes = TextureRegionPack("./assets/graphics/fonts/newrunes.tga", runeSize, runeSize)
var scale = 1
var linegap = 8
fun getWidth(str: String) = runeSize * str.length
override fun draw(batch: Batch, str: CharSequence, x: Float, y: Float): GlyphLayout? {
str.forEachIndexed { index, c ->
val encodePoint = codeToEnc(c.toInt())
if (encodePoint != null) {
batch.draw(
runes.get(encodePoint % 16, encodePoint / 16),
x + runeSize * index * scale.toFloat(),
y * scale.toFloat()
)
}
}
return null
}
override fun usesIntegerPositions() = true
override fun ownsTexture() = true
override fun getXHeight() = runeSize.toFloat()
override fun getCapHeight() = runeSize.toFloat()
override fun getLineHeight() = (runeSize + linegap) * scale.toFloat()
}
/*
How runes are made:
The new runes are based on Hangul writing system. The runes had two main goals:
- Implement the principle of its design (specifically, how this letter is altered from its base shape)
- {KIYEOK, KHIEUKH}, {TIKEUT, THIEUTH, NIEUN}, {PIEUP, PHIEUPH, MIEUM}, {CIEUC, CHIEUCH, SIOS} sets
are similar in shape
- Aspirated sounds keep similar shape to their base
- Vowels are not random; they have rules
- In non-assembled writing, IEUNG only appears as "-ng" phoneme, so the shape is based on
old Hangul YESIEUNG, which actually had "-ng" sound
- "Doensori" are realised by prepending SIOS, much like older Korean orthography
- Good enough obfuscation
Notes:
- In some letters (e.g. NIEUN-HIEUH), ligatures may applied
- EU appear as non-assembled shape; U-shape instead of dash
*/

View File

@@ -0,0 +1,47 @@
package net.torvald.terrarum.modulebasegame.imagefont
import com.badlogic.gdx.graphics.g2d.Batch
import com.badlogic.gdx.graphics.g2d.BitmapFont
import com.badlogic.gdx.graphics.g2d.GlyphLayout
import net.torvald.terrarum.ModMgr
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
/**
* Created by minjaesong on 2017-06-21.
*/
object Watch7SegMain : BitmapFont() {
val charMapping = ('0'..'9')
internal val W = 11
internal val H = 18
internal val fontSheet = TextureRegionPack(ModMgr.getGdxFile("basegame", "fonts/7segnum.tga"), W, H)
init {
setOwnsTexture(true)
}
override fun draw(batch: Batch, str: CharSequence, x: Float, y: Float): GlyphLayout? {
str.forEachIndexed { index, c ->
batch.draw(
if (c != ' ' && c in charMapping)
fontSheet.get(1 + (c - '0'), 0)
else
fontSheet.get(0, 0)
, x + W * index, y)
}
return null
}
override fun getLineHeight() = H.toFloat()
override fun getCapHeight() = getLineHeight()
override fun getXHeight() = getLineHeight()
override fun dispose() {
fontSheet.dispose()
}
}

View File

@@ -0,0 +1,42 @@
package net.torvald.terrarum.modulebasegame.imagefont
import com.badlogic.gdx.graphics.g2d.Batch
import com.badlogic.gdx.graphics.g2d.BitmapFont
import com.badlogic.gdx.graphics.g2d.GlyphLayout
import net.torvald.terrarum.ModMgr
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
/**
* Created by minjaesong on 2017-06-21.
*/
object Watch7SegSmall : BitmapFont() {
val charMapping = (' '..'9')
internal val W = 9
internal val H = 12
internal val fontSheet = TextureRegionPack(ModMgr.getGdxFile("basegame", "fonts/7seg_small.tga"), W, H)
init {
setOwnsTexture(true)
}
override fun draw(batch: Batch, str: CharSequence, x: Float, y: Float): GlyphLayout? {
str.forEachIndexed { index, c ->
if (c in charMapping) {
batch.draw(
fontSheet.get((c - ' ') % 16, (c - ' ') / 16),
x + W * index, y
)
}
}
return null
}
override fun getLineHeight() = H.toFloat()
override fun getCapHeight() = getLineHeight()
override fun getXHeight() = getLineHeight()
}

View File

@@ -0,0 +1,46 @@
package net.torvald.terrarum.modulebasegame.imagefont
import com.badlogic.gdx.graphics.g2d.Batch
import com.badlogic.gdx.graphics.g2d.BitmapFont
import com.badlogic.gdx.graphics.g2d.GlyphLayout
import net.torvald.terrarum.ModMgr
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
/**
* Created by minjaesong on 2017-06-21.
*/
object WatchDotAlph : BitmapFont() {
val charMapping = ('A'..'Z')
internal val W = 12
internal val H = 10
internal val fontSheet = TextureRegionPack(ModMgr.getGdxFile("basegame", "fonts/watch_dotalph.tga"), W, H)
init {
setOwnsTexture(true)
}
override fun draw(batch: Batch, str: CharSequence, x: Float, y: Float): GlyphLayout? {
str.forEachIndexed { index, c ->
batch.draw(
if (c != ' ' && c in charMapping)
fontSheet.get(1 + (c - 'A'), 0)
else
fontSheet.get(0, 0)
, x + W * index, y)
}
return null
}
override fun getLineHeight() = H.toFloat()
override fun getCapHeight() = getLineHeight()
override fun getXHeight() = getLineHeight()
override fun dispose() {
fontSheet.dispose()
}
}

View File

@@ -3,12 +3,13 @@ package net.torvald.terrarum.modulebasegame.items
import net.torvald.point.Point2d
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.blockproperties.Block
import net.torvald.terrarum.gameactors.AVKey
import net.torvald.terrarum.gameactors.ActorWithPhysics
import net.torvald.terrarum.modulebasegame.gameactors.AVKey
import net.torvald.terrarum.modulebasegame.gameactors.ActorWithPhysics
import net.torvald.terrarum.itemproperties.Calculate
import net.torvald.terrarum.itemproperties.GameItem
import net.torvald.terrarum.itemproperties.ItemID
import net.torvald.terrarum.itemproperties.Material
import net.torvald.terrarum.modulebasegame.Ingame
/**
* Created by minjaesong on 2017-07-17.
@@ -37,7 +38,7 @@ class PickaxeGeneric(override val originalID: ItemID) : GameItem() {
val mouseTileY = Terrarum.mouseTileY
val mousePoint = Point2d(mouseTileX.toDouble(), mouseTileY.toDouble())
val actorvalue = Terrarum.ingame!!.player.actorValue
val actorvalue = (Terrarum.ingame!! as Ingame).player.actorValue
using = true
@@ -49,15 +50,15 @@ class PickaxeGeneric(override val originalID: ItemID) : GameItem() {
})
// return false if here's no tile
if (Block.AIR == Terrarum.ingame!!.world.getTileFromTerrain(mouseTileX, mouseTileY))
if (Block.AIR == (Terrarum.ingame!! as Ingame).world.getTileFromTerrain(mouseTileX, mouseTileY))
return false
// filter passed, do the job
val swingDmgToFrameDmg = delta.toDouble() / actorvalue.getAsDouble(AVKey.ACTION_INTERVAL)!!
Terrarum.ingame!!.world.inflictTerrainDamage(
(Terrarum.ingame!! as Ingame).world.inflictTerrainDamage(
mouseTileX, mouseTileY,
Calculate.pickaxePower(Terrarum.ingame!!.player, material) * swingDmgToFrameDmg
Calculate.pickaxePower((Terrarum.ingame!! as Ingame).player, material) * swingDmgToFrameDmg
)
return true
@@ -66,7 +67,7 @@ class PickaxeGeneric(override val originalID: ItemID) : GameItem() {
override fun endPrimaryUse(delta: Float): Boolean {
using = false
// reset action timer to zero
Terrarum.ingame!!.player.actorValue.set(AVKey.__ACTION_TIMER, 0.0)
(Terrarum.ingame!! as Ingame).player.actorValue.set(AVKey.__ACTION_TIMER, 0.0)
return true
}
}

View File

@@ -0,0 +1,147 @@
package net.torvald.terrarum.modulebasegame.magiccontroller
import java.util.*
/**
* YE OLDE MAGIC IDEA No.0
*
* Provides MDL interpretation, pre-compilation and stores state of the interpreter
*
* Created by minjaesong on 2016-07-30.
*/
class MDLInterpreterState {
val stack = MagicArrayStack(20)
fun interpret(line: String) {
}
fun execute(property: MagicWords, power: MagicWords? = null, arg: Int? = null) {
}
enum class MagicWords {
// properties
ELDR, IS, STORMR, HREYFING, LAEKNING, GLEYPI, TJON,
//fire, ice, storm, kinesis, heal, absorb, harm
// reserved words
LAEKNINGHRADI, HREYFINGHRADI, LAEKNINGAUKI, HREYFINGAUKI, STOEKKAUKI, HEILSASTIG,
// heal rate,movement speed, healratemult,movespeedmult, jump boost, health point
// adjectives (power)
// operators
ITA, TOGA, PLUS, MINUS, SINNUM, DEILING, LEIFASTOFN, AFRIT, STAFLISKIPTI, HENNA, NA
// push, pop, +, -, *, /, %, dup, swap, drop, fetch
}
class MagicArrayStack {
/**
* Number of elements in the stack
*/
var depth: Int = 0
private set
var size: Int
get() = data.size
set(newSize) {
if (newSize > depth) inflate(newSize - data.size)
else deflate(data.size - newSize)
}
private lateinit var data: Array<Int?>
constructor(stackSize: Int) {
data = Array(stackSize, { null })
}
constructor(arr: Array<Int?>) {
data = arr.copyOf()
depth = size
}
fun push(v: Int) {
if (depth >= data.size) throw StackOverflowError()
data[depth++] = v
}
fun pop(): Int {
if (depth == 0) throw EmptyStackException()
return data[--depth]!!
}
fun peek(): Int? {
if (depth == 0) return null
return data[depth - 1]
}
fun dup() {
if (depth == 0) throw EmptyStackException()
if (depth == data.size) throw StackOverflowError()
push(peek()!!)
}
fun swap() {
if (depth < 2) throw UnsupportedOperationException("Stack is empty or has only one element.")
val up = pop()
val dn = pop()
push(up)
push(dn)
}
fun drop() {
if (depth == 0) throw EmptyStackException()
--depth
}
fun defineFromArray(arr: Array<Int?>) { data = arr.copyOf() }
/**
* Increase the stack size by a factor.
*/
fun inflate(sizeToAdd: Int) {
if (sizeToAdd < 0) throw UnsupportedOperationException("$sizeToAdd: Cannot deflate the stack with this function. Use deflate(int) instead.")
size += sizeToAdd
val oldStack = this.asArray()
data = Array(size, { if (it < oldStack.size) oldStack[it] else null })
}
/**
* Decrease the stack size by a factor. Overflowing data will be removed.
*/
fun deflate(sizeToTake: Int) {
if (size - sizeToTake < 1) throw UnsupportedOperationException("$sizeToTake: Cannot deflate the stack to the size of zero or negative.")
size -= sizeToTake
val oldStack = this.asArray()
data = Array(size, { oldStack[it] })
if (depth > data.size) depth = data.size
}
/**
* Convert stack as array. Index zero is the bottommost element.
* @return array of data, with array size equivalent to the stack depth.
*/
fun asArray() = data.copyOfRange(0, depth - 1)
fun equalTo(other: MagicArrayStack) = (this.asArray() == other.asArray())
fun plus() { data[depth - 2] = data[depth - 2]!! + (pop().toInt()) }
fun minus() { data[depth - 2] = data[depth - 2]!! - (pop().toInt()) }
fun times() { data[depth - 2] = data[depth - 2]!! * (pop().toInt()) }
fun div() { data[depth - 2] = data[depth - 2]!! / (pop().toInt()) }
fun mod() { data[depth - 2] = data[depth - 2]!! % (pop().toInt()) }
}
}

View File

@@ -0,0 +1,109 @@
package net.torvald.terrarum.modulebasegame.magiccontroller
import net.torvald.terrarum.modulebasegame.gameactors.AVKey
import net.torvald.terrarum.gameactors.Actor
/**
* "Data Type" describing magical force
*
* Created by minjaesong on 2018-06-03.
*/
class TheMagicLanguage(vm: TheMagicMachine) {
open class MagicException : Exception()
class MagicPortReadError : MagicException()
class MagicPortWriteError : MagicException()
/**
* A vessel contains magical power to be process.
*
* Negative numbers are tsraoatrsed as if it's negative power (still has >0 amount)
*/
class MagicAccumulator {
private var power = 0.0
fun pourIn(value: Double) {
power += value // positive powers and negative powers will cancel eath other
}
fun pourOut(value: Double) {
power -= value
}
fun pourOutInto(other: MagicAccumulator, value: Double) {
if (power >= 0) {
// pour out positive power without inversion; result is positive power
if (value >= 0) {
val value = minOf(power, value)
other.pourIn(value)
power -= value
}
// pour out positive power with inversion; result is negative power
else {
val value = minOf(-power, value)
other.pourIn(value)
power += value
}
}
else {
// pour out negative power without inversion; result is negative power
if (value < 0) {
val value = minOf(power, value)
other.pourIn(-value)
}
// pour out negative power with inversion; result is positive power
else {
val value = minOf(-power, value)
other.pourIn(-value)
}
}
}
fun dumpAllInto(other: MagicAccumulator) {
pourOutInto(other, this.power)
}
fun empty() {
// release residual power as heat or something
power = 0.0
}
fun readForPortWrite(): Double {
val r = power
power = 0.0
return r
}
}
interface MagicOutputPort {
fun read(a: MagicAccumulator): Double?
fun write(a: MagicAccumulator)
}
class HealthPort(val output1: Actor) : MagicOutputPort {
override fun read(a: MagicAccumulator): Double {
val value = output1.actorValue.getAsDouble(AVKey.HEALTH) ?: throw MagicPortReadError()
a.pourIn(value)
return value
}
override fun write(a: MagicAccumulator) {
val value = output1.actorValue.getAsDouble(AVKey.HEALTH) ?: throw MagicPortReadError()
output1.actorValue[AVKey.HEALTH] = value + a.readForPortWrite()
}
}
fun opCombine(a: MagicAccumulator, b: MagicAccumulator, c: MagicAccumulator) {
b.dumpAllInto(a)
c.dumpAllInto(a)
}
fun opRelease(akku: MagicAccumulator, port: MagicOutputPort) {
port.write(akku)
}
fun opSiphon(akku: MagicAccumulator, port: MagicOutputPort) {
port.read(akku)
}
}

View File

@@ -0,0 +1,18 @@
package net.torvald.terrarum.modulebasegame.magiccontroller
/**
* Created by minjaesong on 2018-06-03.
*/
class TheMagicMachine(skill: MagicianSkillDefinition) {
val akkuPack = Array(skill.numberOfAccumulator, { TheMagicLanguage.MagicAccumulator() })
}
data class MagicianSkillDefinition(
var numberOfAccumulator: Int,
var useableMagicPower: Double,
var runeComprehensionLevel: Int
)

View File

@@ -0,0 +1,35 @@
package net.torvald.terrarum.modulebasegame.ui
import net.torvald.terrarum.modulebasegame.gameactors.ActorHumanoid
import net.torvald.terrarum.itemproperties.GameItem
/**
* Created by minjaesong on 2017-04-21.
*/
object AmmoMeterProxy {
operator fun invoke(actor: ActorHumanoid, meter: UIVitalMetre) {
val currentItem = actor.inventory.itemEquipped[GameItem.EquipPosition.HAND_GRIP]
if (currentItem == null) {
meter.vitalGetterMax = { null }
meter.vitalGetterVal = { null }
}
else {
meter.vitalGetterVal = {
if (currentItem.stackable && currentItem.maxDurability == GameItem.DURABILITY_NA) {
actor.inventory.getByDynamicID(currentItem.dynamicID)!!.amount.toFloat()
}
else
currentItem.durability
}
meter.vitalGetterMax = {
if (currentItem.stackable && currentItem.maxDurability == GameItem.DURABILITY_NA)
500f
else
currentItem.maxDurability.toFloat()
}
}
}
}

View File

@@ -0,0 +1,42 @@
package net.torvald.terrarum.modulebasegame.ui
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.Texture
import com.badlogic.gdx.graphics.g2d.TextureRegion
import net.torvald.terrarum.gameactors.ai.toInt
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
/**
* Make item slot image with number on bottom-right
*
* Created by minjaesong on 2016-07-20.
*/
object ItemSlotImageBuilder {
// FIXME it leaks mem waaaaagh
val colourBlack = Color(0x404040_FF)
val colourWhite = Color(0xC0C0C0_FF.toInt())
val slotImage = TextureRegionPack(Gdx.files.internal("./assets/graphics/gui/quickbar/item_slots_atlas.tga"), 38, 38) // must have same w/h as slotLarge
private val imageDict = HashMap<Long, Texture>()
fun produce(isBlack: Boolean, number: Int = 10): TextureRegion {
return slotImage.get(number, 0 or isBlack.toInt().shl(1))
}
fun produceLarge(isBlack: Boolean, number: Int = 10): TextureRegion {
return slotImage.get(number, 1 or isBlack.toInt().shl(1))
}
fun dispose() {
slotImage.dispose()
}
}

View File

@@ -0,0 +1,79 @@
package net.torvald.terrarum.modulebasegame.ui
import com.badlogic.gdx.graphics.Camera
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.blendNormal
import net.torvald.terrarum.Second
import net.torvald.terrarum.ui.UICanvas
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
/**
* Created by minjaesong on 2016-01-27.
*/
class MessageWindow(override var width: Int, isBlackVariant: Boolean) : UICanvas() {
private val segment = if (isBlackVariant) SEGMENT_BLACK else SEGMENT_WHITE
var messagesList = arrayOf("", "")
override var height: Int = 0
private var fontCol: Color = if (!isBlackVariant) Color.BLACK else Color.WHITE
private val GLYPH_HEIGHT = Terrarum.fontGame.lineHeight
override var openCloseTime: Second = OPEN_CLOSE_TIME
private val LRmargin = 0f // there's "base value" of 8 px for LR (width of segment tile)
fun setMessage(messagesList: Array<String>) {
this.messagesList = messagesList
}
override fun updateUI(delta: Float) {
}
override fun renderUI(batch: SpriteBatch, camera: Camera) {
blendNormal()
val textWidth = messagesList.map { Terrarum.fontGame.getWidth(it) }.sorted()[1]
batch.color = Color.WHITE
batch.draw(segment.get(1, 0), segment.tileW.toFloat(), 0f, 2 * LRmargin + textWidth, segment.tileH.toFloat())
batch.draw(segment.get(0, 0), 0f, 0f)
batch.draw(segment.get(2, 0), 2 * LRmargin + textWidth, 0f)
messagesList.forEachIndexed { index, s ->
Terrarum.fontGame.draw(batch, s, segment.tileW + LRmargin, (segment.tileH - Terrarum.fontGame.lineHeight) / 2f)
}
}
override fun doOpening(delta: Float) {
}
override fun doClosing(delta: Float) {
}
override fun endOpening(delta: Float) {
}
override fun endClosing(delta: Float) {
}
override fun dispose() {
}
companion object {
// private int messagesShowingIndex = 0;
val MESSAGES_DISPLAY = 2
val OPEN_CLOSE_TIME = 0.16f
// will be disposed by Terrarum (application main instance)
val SEGMENT_BLACK = TextureRegionPack("assets/graphics/gui/message_black.tga", 8, 56)
val SEGMENT_WHITE = TextureRegionPack("assets/graphics/gui/message_white.tga", 8, 56)
}
}

View File

@@ -0,0 +1,71 @@
package net.torvald.terrarum.modulebasegame.ui
import com.badlogic.gdx.graphics.Camera
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.Second
import net.torvald.terrarum.ui.UICanvas
/**
* Created by minjaesong on 2016-01-23.
*/
class Notification : UICanvas() {
private val SHOWUP_MAX = 15000
override var width: Int = 500
internal var msgUI = MessageWindow(width, true)
override var height: Int = msgUI.height
private val visibleTime = Math.min(
Terrarum.getConfigInt("notificationshowuptime"),
SHOWUP_MAX
)
private var displayTimer = 0f
internal var message: Array<String> = Array(MessageWindow.MESSAGES_DISPLAY, { "" })
override var openCloseTime: Second = MessageWindow.OPEN_CLOSE_TIME
override fun updateUI(delta: Float) {
if (handler.isOpened)
displayTimer += delta
if (displayTimer >= visibleTime) {
handler.setAsClose()
displayTimer = 0f
}
}
override fun renderUI(batch: SpriteBatch, camera: Camera) {
msgUI.render(batch, camera)
}
override fun doOpening(delta: Float) {
doOpeningFade(this, openCloseTime)
}
override fun doClosing(delta: Float) {
doClosingFade(this, openCloseTime)
}
override fun endOpening(delta: Float) {
endOpeningFade(this)
}
override fun endClosing(delta: Float) {
endClosingFade(this)
}
fun sendNotification(message: Array<String>) {
this.message = message
msgUI.setMessage(this.message)
handler.openCloseCounter = 0f
handler.opacity = 0f
handler.setAsOpen()
}
override fun dispose() {
}
}

View File

@@ -0,0 +1,35 @@
package net.torvald.terrarum.modulebasegame.ui
import com.badlogic.gdx.graphics.Camera
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import net.torvald.terrarum.ui.UICanvas
/**
* Created by minjaesong on 2017-03-13.
*/
class NullUI : UICanvas() {
override var width: Int = 0
override var height: Int = 0
override var openCloseTime = 0f
override fun updateUI(delta: Float) {
}
override fun renderUI(batch: SpriteBatch, camera: Camera) {
}
override fun doOpening(delta: Float) {
}
override fun doClosing(delta: Float) {
}
override fun endOpening(delta: Float) {
}
override fun endClosing(delta: Float) {
}
override fun dispose() {
}
}

View File

@@ -0,0 +1,157 @@
package net.torvald.terrarum.modulebasegame.ui
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.graphics.Camera
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import net.torvald.terrarum.*
import net.torvald.terrarum.modulebasegame.gameactors.ActorHumanoid
import net.torvald.terrarum.Second
import net.torvald.terrarum.abs
import net.torvald.terrarum.modulebasegame.Ingame
import net.torvald.terrarum.roundInt
import net.torvald.terrarum.modulebasegame.imagefont.Watch7SegSmall
import net.torvald.terrarum.ui.UICanvas
import net.torvald.terrarum.worlddrawer.LightmapRenderer
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
/**
* Created by minjaesong on 2017-06-10.
*/
class UIBasicNotifier(private val player: ActorHumanoid?) : UICanvas() {
override var width = 116
override var height = 24
override var openCloseTime: Second = 0f
private var ELuptimer = 10f // to make the light turned off by default
private val ELuptime = 4f
private var ELon = false
private var atlas = TextureRegionPack(ModMgr.getPath("basegame", "gui/basic_meter_atlas.tga"), width, height)
private var font = Watch7SegSmall
override fun updateUI(delta: Float) {
if (ELon) {
ELuptimer += delta
}
if (mouseUp || Gdx.input.isKeyPressed(Terrarum.getConfigInt("keyinteract"))) {
ELuptimer = 0f
ELon = true
}
if (ELuptimer >= ELuptime) {
ELon = false
}
}
private val temperature: Int
get() {
if (player != null) {
val playerTilePos = player.hIntTilewiseHitbox
val tempCelsius = -273f + ((Terrarum.ingame as? Ingame)?.world?.getTemperature(playerTilePos.centeredX.toInt(), playerTilePos.centeredY.toInt()) ?: 288f)
return if (Terrarum.getConfigBoolean("useamericanunit")) {
tempCelsius.times(1.8f).plus(32f).roundInt()
}
else {
tempCelsius.roundInt()
}
}
else {
return 888
}
}
private val mailCount: Int
get() = 0
private val lcdLitCol = Color(0x141414_ff)
fun getTempStr(): String {
val sb = StringBuilder()
if (temperature < 100) {
if (temperature < 0)
sb.append("-")
else
sb.append(" ")
if (temperature.abs() < 10)
sb.append(" ")
}
sb.append(temperature.abs())
if (Terrarum.getConfigBoolean("useamericanunit")) {
sb.append('#') // fahrenheit subscript
}
else {
sb.append('"') // celsius superscript
}
return sb.toString()
}
fun getMailStr(): String {
val sb = StringBuilder()
if (mailCount < 10)
sb.append(" ")
sb.append(mailCount)
return sb.toString()
}
override fun renderUI(batch: SpriteBatch, camera: Camera) {
// light overlay or EL
if (ELon) {
blendNormal()
batch.draw(atlas.get(0, 2), 0f, 0f)
}
else {
val lightLevel: Color
if (player != null) {
val playerPos = player.hIntTilewiseHitbox
lightLevel = (LightmapRenderer.getLight(playerPos.centeredX.toInt(), playerPos.centeredY.toInt()) ?:
(Terrarum.ingame!! as Ingame).world.globalLight
)
}
else {
lightLevel = (Terrarum.ingame!! as Ingame).world.globalLight
}
// backplate
batch.color = Color(lightLevel.r, lightLevel.g, lightLevel.b, 1f)
batch.draw(atlas.get(0, 0), 0f, 0f)
}
// LCD back
blendNormal()
batch.draw(atlas.get(0, 3), 0f, 0f)
// LCD contents
batch.color = lcdLitCol
font.draw(batch, getTempStr(), 21f, 5f)
font.draw(batch, getMailStr(), 93f, 5f)
}
override fun doOpening(delta: Float) {
}
override fun doClosing(delta: Float) {
}
override fun endOpening(delta: Float) {
}
override fun endClosing(delta: Float) {
}
override fun dispose() {
atlas.dispose()
}
}

View File

@@ -0,0 +1,63 @@
package net.torvald.terrarum.modulebasegame.ui
import com.badlogic.gdx.graphics.Camera
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.fillRect
import net.torvald.terrarum.Second
import net.torvald.terrarum.langpack.Lang
import net.torvald.terrarum.ui.UICanvas
/**
* Created by minjaesong on 2017-12-06.
*/
class UICheatDetected : UICanvas() {
override var width: Int
get() = Terrarum.WIDTH
set(value) { throw UnsupportedOperationException() }
override var height: Int
get() = Terrarum.HEIGHT
set(value) { throw UnsupportedOperationException() }
override var openCloseTime: Second = 0f
private val backgroundCol = Color(0x181818C0)
override fun renderUI(batch: SpriteBatch, camera: Camera) {
Terrarum.ingame?.consoleHandler?.setAsClose()
Terrarum.ingame?.consoleHandler?.isVisible = false
batch.color = backgroundCol
batch.fillRect(0f, 0f, width.toFloat(), height.toFloat())
batch.color = Color.WHITE
val txt = Lang["ERROR_GENERIC_CHEATING"]
val txtW = Terrarum.fontGame.getWidth(txt)
val txtH = Terrarum.fontGame.lineHeight.toInt()
Terrarum.fontGame.draw(batch, txt, width.minus(txtW).ushr(1).toFloat(), height.minus(txtH).ushr(1).toFloat())
}
override fun updateUI(delta: Float) {
}
override fun doOpening(delta: Float) {
}
override fun doClosing(delta: Float) {
}
override fun endOpening(delta: Float) {
}
override fun endClosing(delta: Float) {
}
override fun dispose() {
}
}

View File

@@ -0,0 +1,415 @@
package net.torvald.terrarum.modulebasegame.ui
/**
* Created by minjaesong on 2017-03-13.
*/
/*class UIInventory(
var actor: Pocketed?,
override var width: Int,
override var height: Int,
var categoryWidth: Int,
toggleKeyLiteral: Int? = null, toggleButtonLiteral: Int? = null,
// UI positions itself? (you must g.flush() yourself after the g.translate(Int, Int))
customPositioning: Boolean = false, // mainly used by vital meter
doNotWarnConstant: Boolean = false
) : UICanvas(toggleKeyLiteral, toggleButtonLiteral, customPositioning, doNotWarnConstant) {
val inventory: ActorInventory?
get() = actor?.inventory
//val actorValue: ActorValue
// get() = (actor as Actor).actorValue
override var openCloseTime: Second = 0.12f
val catButtonsToCatIdent = HashMap<String, String>()
val backgroundColour = Color(0x242424_80)
val defaultTextColour = Color(0xeaeaea_ff.toInt())
init {
catButtonsToCatIdent.put("GAME_INVENTORY_WEAPONS", GameItem.Category.WEAPON)
catButtonsToCatIdent.put("CONTEXT_ITEM_TOOL_PLURAL", GameItem.Category.TOOL)
catButtonsToCatIdent.put("CONTEXT_ITEM_ARMOR", GameItem.Category.ARMOUR)
catButtonsToCatIdent.put("GAME_INVENTORY_INGREDIENTS", GameItem.Category.GENERIC)
catButtonsToCatIdent.put("GAME_INVENTORY_POTIONS", GameItem.Category.POTION)
catButtonsToCatIdent.put("CONTEXT_ITEM_MAGIC", GameItem.Category.MAGIC)
catButtonsToCatIdent.put("GAME_INVENTORY_BLOCKS", GameItem.Category.BLOCK)
catButtonsToCatIdent.put("GAME_INVENTORY_WALLS", GameItem.Category.WALL)
catButtonsToCatIdent.put("GAME_GENRE_MISC", GameItem.Category.MISC)
// special filter
catButtonsToCatIdent.put("MENU_LABEL_ALL", "__all__")
}
val itemStripGutterV = 6
val itemStripGutterH = 8
val itemInterColGutter = 8
val controlHelpHeight = Terrarum.fontGame.lineHeight.toInt()
val pageButtonExtraGap = 32
val pageButtonRealWidth = pageButtonExtraGap + itemStripGutterH
private val catButtons = UIItemTextButtonList(
this,
arrayOf(
"MENU_LABEL_ALL",
"GAME_INVENTORY_BLOCKS",
"GAME_INVENTORY_WALLS",
"CONTEXT_ITEM_TOOL_PLURAL",
"GAME_INVENTORY_WEAPONS",
"CONTEXT_ITEM_ARMOR",
"GAME_INVENTORY_INGREDIENTS",
"GAME_INVENTORY_POTIONS",
"CONTEXT_ITEM_MAGIC",
"GAME_GENRE_MISC"
//"GAME_INVENTORY_FAVORITES",
),
posX = 0,
posY = 0,
width = categoryWidth,
height = height - controlHelpHeight,
verticalGutter = itemStripGutterH,
readFromLang = true,
textAreaWidth = 100,
defaultSelection = 0,
iconSpriteSheet = TextureRegionPack("./assets/graphics/gui/inventory/category.tga", 20, 20),
iconSpriteSheetIndices = intArrayOf(9,6,7,1,0,2,3,4,5,8),
iconCol = defaultTextColour,
highlightBackCol = Color(0xb8b8b8_ff.toInt()),
highlightBackBlendMode = BlendMode.MULTIPLY,
backgroundCol = Color(0), // will use custom background colour!
backgroundBlendMode = BlendMode.NORMAL,
kinematic = true,
inactiveCol = defaultTextColour
)
val itemsStripWidth = ((width - catButtons.width) - (2 * itemStripGutterH + itemInterColGutter)) / 2 - pageButtonExtraGap
private val items = Array(
((height - controlHelpHeight) / (UIItemInventoryElem.height + itemStripGutterV)) * 2, {
UIItemInventoryElem(
parentUI = this,
posX = pageButtonExtraGap + catButtons.width + if (it % 2 == 0) itemStripGutterH else (itemStripGutterH + itemsStripWidth + itemInterColGutter),
posY = itemStripGutterH + it / 2 * (UIItemInventoryElem.height + itemStripGutterV),
width = itemsStripWidth,
item = null,
amount = UIItemInventoryElem.UNIQUE_ITEM_HAS_NO_AMOUNT,
itemImage = null,
mouseoverBackCol = Color(0x282828_ff),
mouseoverBackBlendMode = BlendMode.SCREEN,
backCol = Color(0xd4d4d4_ff.toInt()),
backBlendMode = BlendMode.MULTIPLY,
drawBackOnNull = true,
inactiveTextCol = defaultTextColour
) })
private val scrollImageButtonAtlas = TextureRegionPack(
Gdx.files.internal("assets/graphics/gui/inventory/page_arrow_button.tga"),
40, 54
)
private val scrollLeftButton = UIItemImageButton(this,
scrollImageButtonAtlas.get(0, 0),
posX = categoryWidth,
posY = 0,//(height - controlHelpHeight - scrollImageButtonAtlas.tileH) / 2,
width = scrollImageButtonAtlas.tileW,
height = height - controlHelpHeight,
highlightable = false
)
private val scrollRightButton = UIItemImageButton(this,
scrollImageButtonAtlas.get(1, 0),
posX = width - scrollImageButtonAtlas.tileW,
posY = 0,//(height - controlHelpHeight - scrollImageButtonAtlas.tileH) / 2,
width = scrollImageButtonAtlas.tileW,
height = height - controlHelpHeight,
highlightable = false
)
var itemPage = 0
var itemPageCount = 1 // TODO total size of current category / items.size
var inventorySortList = ArrayList<InventoryPair>()
private var rebuildList = true
private val SP = "${0x3000.toChar()}${0x3000.toChar()}"
val listControlHelp: String
get() = if (Terrarum.environment == RunningEnvironment.PC)
"${0xe006.toChar()} ${Lang["GAME_INVENTORY_USE"]}$SP" +
"${0xe011.toChar()}..${0xe010.toChar()} ${Lang["GAME_INVENTORY_REGISTER"]}$SP" +
"${0xe034.toChar()} ${Lang["GAME_INVENTORY_DROP"]}"
else
"$joypadLabelNinY ${Lang["GAME_INVENTORY_USE"]}$SP" +
"${0xe011.toChar()}${0xe010.toChar()} ${Lang["GAME_INVENTORY_REGISTER"]}$SP" +
"$joypadLabelNinA ${Lang["GAME_INVENTORY_DROP"]}"
val listControlClose: String
get() = if (Terrarum.environment == RunningEnvironment.PC)
"${0xe037.toChar()} ${Lang["GAME_ACTION_CLOSE"]}"
else
"${0xe069.toChar()} ${Lang["GAME_ACTION_CLOSE"]}"
private var oldCatSelect: Int? = null
private var encumbrancePerc = 0f
private var isEncumbered = false
private val seekLeft: Int; get() = Terrarum.getConfigInt("keyleft") // getter used to support in-game keybind changing
private val seekRight: Int; get() = Terrarum.getConfigInt("keyright") // getter used to support in-game keybind changing
private val seekUp: Int; get() = Terrarum.getConfigInt("keyup") // getter used to support in-game keybind changing
private val seekDown: Int; get() = Terrarum.getConfigInt("keydown") // getter used to support in-game keybind changing
init {
// assign actions to the buttons
scrollLeftButton.clickOnceListener = { mouseX, mouseY, button -> // click once action doesn't work ?!
if (button == Input.Buttons.LEFT) {
itemPage = (itemPage - 1) fmod itemPageCount
}
}
scrollRightButton.clickOnceListener = { mouseX, mouseY, button ->
if (button == Input.Buttons.LEFT) {
itemPage = (itemPage + 1) fmod itemPageCount
}
}
addItem(scrollLeftButton)
addItem(scrollRightButton)
}
override fun updateUI(delta: Float) {
catButtons.update(delta)
scrollLeftButton.update(delta)
scrollRightButton.update(delta)
if (actor != null && inventory != null) {
// monitor and check if category selection has been changed
// OR UI is being opened from closed state
if (oldCatSelect != catButtons.selectedIndex ||
!rebuildList && handler.openFired) {
rebuildList = true
}
// reset item page to start
if (oldCatSelect != catButtons.selectedIndex) {
itemPage = 0
}
if (rebuildList) {
shutUpAndRebuild()
}
}
oldCatSelect = catButtons.selectedIndex
}
private val weightBarWidth = 60f
override fun renderUI(batch: SpriteBatch, camera: Camera) {
// background
blendNormal()
batch.color = backgroundColour
batch.fillRect(0f, 0f, width.toFloat(), height.toFloat())
// cat bar background
blendMul()
batch.color = Color(0xcccccc_ff.toInt())
batch.fillRect(0f, 0f, catButtons.width.toFloat(), height.toFloat())
catButtons.render(batch, camera)
// left/right page mover
scrollLeftButton.render(batch, camera)
scrollRightButton.render(batch, camera)
items.forEach {
it.render(batch, camera)
}
// texts
blendNormal()
batch.color = defaultTextColour
// W - close
Terrarum.fontGame.draw(batch, listControlClose, 4f, height - controlHelpHeight.toFloat())
// MouseL - Use ; 1.9 - Register ; T - Drop
Terrarum.fontGame.draw(batch, listControlHelp, catButtons.width + 4f, height - controlHelpHeight.toFloat())
// encumbrance
if (inventory != null) {
val encumbranceText = Lang["GAME_INVENTORY_ENCUMBRANCE"]
Terrarum.fontGame.draw(batch,
encumbranceText,
width - 9 - Terrarum.fontGame.getWidth(encumbranceText) - weightBarWidth,
height - controlHelpHeight.toFloat()
)
// encumbrance bar background
blendMul()
batch.color = Color(0xa0a0a0_ff.toInt())
batch.fillRect(
width - 3 - weightBarWidth,
height - controlHelpHeight + 3f,
weightBarWidth,
controlHelpHeight - 6f
)
// encumbrance bar
blendNormal()
batch.color = if (isEncumbered) Color(0xff0000_cc.toInt()) else Color(0x00ff00_cc.toInt())
batch.fillRect(
width - 3 - weightBarWidth,
height - controlHelpHeight + 3f,
if (actor?.inventory?.capacityMode == CAPACITY_MODE_NO_ENCUMBER)
1f
else // make sure 1px is always be seen
minOf(weightBarWidth, maxOf(1f, weightBarWidth * encumbrancePerc)),
controlHelpHeight - 5f
)
}
}
/** Persuade the UI to rebuild its item list */
fun rebuildList() {
rebuildList = true
}
fun shutUpAndRebuild() {
if (catButtons.selectedButton != null) {
val filter = catButtonsToCatIdent[catButtons.selectedButton!!.labelText]
// encumbrance
encumbrancePerc = inventory!!.capacity.toFloat() / inventory!!.maxCapacity
isEncumbered = inventory!!.isEncumbered
inventorySortList = ArrayList<InventoryPair>()
// filter items
inventory?.forEach {
if (it.item.inventoryCategory == filter || filter == "__all__")
inventorySortList.add(it)
}
rebuildList = false
// sort if needed
// test sort by name
inventorySortList.sortBy { it.item.name }
// map sortList to item list
for (k in 0 until items.size) {
// we have an item
try {
val sortListItem = inventorySortList[k + itemPage * items.size]
items[k].item = sortListItem.item
items[k].amount = sortListItem.amount
items[k].itemImage = ItemCodex.getItemImage(sortListItem.item)
// set quickslot number
for (qs in 1..UIQuickBar.SLOT_COUNT) {
if (sortListItem.item == actor?.inventory?.getQuickBar(qs - 1)?.item) {
items[k].quickslot = qs % 10 // 10 -> 0, 1..9 -> 1..9
break
}
else
items[k].quickslot = null
}
// set equippedslot number
for (eq in 0..actor!!.inventory.itemEquipped.size - 1) {
if (eq < actor!!.inventory.itemEquipped.size) {
if (actor!!.inventory.itemEquipped[eq] == items[k].item) {
items[k].equippedSlot = eq
break
}
else
items[k].equippedSlot = null
}
}
}
// we do not have an item, empty the slot
catch (e: IndexOutOfBoundsException) {
items[k].item = null
items[k].amount = 0
items[k].itemImage = null
items[k].quickslot = null
}
}
itemPageCount = maxOf(1, 1 + (inventorySortList.size.minus(1) / items.size))
}
}
////////////
// Inputs //
////////////
override fun doOpening(delta: Float) {
UICanvas.doOpeningPopOut(this, openCloseTime, UICanvas.Companion.Position.LEFT)
}
override fun doClosing(delta: Float) {
UICanvas.doClosingPopOut(this, openCloseTime, UICanvas.Companion.Position.LEFT)
}
override fun endOpening(delta: Float) {
UICanvas.endOpeningPopOut(this, UICanvas.Companion.Position.LEFT)
}
override fun endClosing(delta: Float) {
UICanvas.endClosingPopOut(this, UICanvas.Companion.Position.LEFT)
}
override fun keyDown(keycode: Int): Boolean {
super.keyDown(keycode)
items.forEach { if (it.mouseUp) it.keyDown(keycode) }
shutUpAndRebuild()
return true
}
override fun keyUp(keycode: Int): Boolean {
super.keyUp(keycode)
items.forEach { if (it.mouseUp) it.keyUp(keycode) }
shutUpAndRebuild()
return true
}
override fun touchDown(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean {
super.touchDown(screenX, screenY, pointer, button)
items.forEach { if (it.mouseUp) it.touchDown(screenX, screenY, pointer, button) }
return true
}
override fun touchUp(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean {
items.forEach { if (it.mouseUp) it.touchUp(screenX, screenY, pointer, button) }
return true
}
override fun dispose() {
catButtons.dispose()
items.forEach { it.dispose() }
scrollImageButtonAtlas.dispose()
}
}
*/

View File

@@ -0,0 +1,290 @@
package net.torvald.terrarum.modulebasegame.ui
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.graphics.Camera
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.GL20
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.badlogic.gdx.graphics.glutils.ShapeRenderer
import net.torvald.terrarum.*
import net.torvald.terrarum.modulebasegame.gameactors.ActorWithPhysics
import net.torvald.terrarum.modulebasegame.gameactors.ActorInventory.Companion.CAPACITY_MODE_NO_ENCUMBER
import net.torvald.terrarum.modulebasegame.gameactors.Pocketed
import net.torvald.terrarum.Second
import net.torvald.terrarum.langpack.Lang
import net.torvald.terrarum.modulebasegame.Ingame
import net.torvald.terrarum.ui.UICanvas
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
/**
* Created by minjaesong on 2017-10-21.
*/
class UIInventoryFull(
var actor: Pocketed?,
toggleKeyLiteral: Int? = null, toggleButtonLiteral: Int? = null,
// UI positions itself? (you must g.flush() yourself after the g.translate(Int, Int))
customPositioning: Boolean = false, // mainly used by vital meter
doNotWarnConstant: Boolean = false
) : UICanvas(toggleKeyLiteral, toggleButtonLiteral, customPositioning, doNotWarnConstant) {
override var width: Int = Terrarum.WIDTH
override var height: Int = Terrarum.HEIGHT
val internalWidth: Int = 630
val internalHeight: Int = 558 // grad_begin..grad_end..contents..grad_begin..grad_end
internal val catIcons: TextureRegionPack = TextureRegionPack("./assets/graphics/gui/inventory/category.tga", 20, 20)
internal val catArrangement: IntArray = intArrayOf(9,6,7,1,0,2,3,4,5,8)
private val SP = "${0x3000.toChar()}${0x3000.toChar()}"
val listControlHelp: String
get() = if (Terrarum.environment == RunningEnvironment.PC)
"${0xe031.toChar()} ${Lang["GAME_ACTION_CLOSE"]}$SP" +
"${0xe006.toChar()} ${Lang["GAME_INVENTORY_USE"]}$SP" +
"${0xe011.toChar()}..${0xe010.toChar()} ${Lang["GAME_INVENTORY_REGISTER"]}$SP" +
"${0xe034.toChar()} ${Lang["GAME_INVENTORY_DROP"]}"
else
"${0xe069.toChar()} ${Lang["GAME_ACTION_CLOSE"]}$SP" +
"${Terrarum.joypadLabelNinY} ${Lang["GAME_INVENTORY_USE"]}$SP" +
"${0xe011.toChar()}${0xe010.toChar()} ${Lang["GAME_INVENTORY_REGISTER"]}$SP" +
"${Terrarum.joypadLabelNinA} ${Lang["GAME_INVENTORY_DROP"]}"
val controlHelpHeight = Terrarum.fontGame.lineHeight
private var encumbrancePerc = 0f
private var isEncumbered = false
val catBarWidth = 330
val catBar = UIItemInventoryCatBar(
this,
(Terrarum.WIDTH - catBarWidth) / 2,
66 + (Terrarum.HEIGHT - internalHeight) / 2,
catBarWidth
)
val catSelection: Int
get() = catBar.selectedIndex
val catSelectedIcon: Int
get() = catBar.selectedIcon
override var openCloseTime: Second = 0.0f
private val itemList: UIItemInventoryDynamicList? =
if (actor != null) {
UIItemInventoryDynamicList(
this,
actor!!.inventory,
0 + (Terrarum.WIDTH - internalWidth) / 2,
109 + (Terrarum.HEIGHT - internalHeight) / 2
)
}
else null
private val equipped: UIItemInventoryEquippedView? =
if (actor != null) {
UIItemInventoryEquippedView(
this,
actor!!.inventory,
actor as ActorWithPhysics,
internalWidth - UIItemInventoryEquippedView.width + (Terrarum.WIDTH - internalWidth) / 2,
109 + (Terrarum.HEIGHT - internalHeight) / 2
)
}
else null
init {
addItem(catBar)
itemList?.let { addItem(it) }
equipped?.let { addItem(it) }
catBar.selectionChangeListener = { old, new -> rebuildList() }
rebuildList()
}
private var offsetX = ((Terrarum.WIDTH - internalWidth) / 2).toFloat()
private var offsetY = ((Terrarum.HEIGHT - internalHeight) / 2).toFloat()
override fun updateUI(delta: Float) {
if (handler.openFired) {
rebuildList()
}
catBar.update(delta)
itemList?.update(delta)
equipped?.update(delta)
}
private val gradStartCol = Color(0x404040_60)
private val gradEndCol = Color(0x000000_70)
private val shapeRenderer = ShapeRenderer()
private val gradHeight = 48f
private val weightBarWidth = 60f
override fun renderUI(batch: SpriteBatch, camera: Camera) {
val xEnd = (Terrarum.WIDTH + internalWidth).div(2).toFloat()
val yEnd = (Terrarum.HEIGHT + internalHeight).div(2).toFloat()
// background fill
batch.end()
Gdx.gl.glEnable(GL20.GL_BLEND) // ending the batch disables blend
val gradTopStart = (Terrarum.HEIGHT - internalHeight).div(2).toFloat()
val gradBottomEnd = Terrarum.HEIGHT - gradTopStart
shapeRenderer.inUse {
shapeRenderer.rect(0f, gradTopStart, Terrarum.WIDTH.toFloat(), gradHeight, gradStartCol, gradStartCol, gradEndCol, gradEndCol)
shapeRenderer.rect(0f, gradBottomEnd, Terrarum.WIDTH.toFloat(), -gradHeight, gradStartCol, gradStartCol, gradEndCol, gradEndCol)
shapeRenderer.rect(0f, gradTopStart + gradHeight, Terrarum.WIDTH.toFloat(), internalHeight - (2 * gradHeight), gradEndCol, gradEndCol, gradEndCol, gradEndCol)
shapeRenderer.rect(0f, 0f, Terrarum.WIDTH.toFloat(), gradTopStart, gradStartCol, gradStartCol, gradStartCol, gradStartCol)
shapeRenderer.rect(0f, Terrarum.HEIGHT.toFloat(), Terrarum.WIDTH.toFloat(), -(Terrarum.HEIGHT.toFloat() - gradBottomEnd), gradStartCol, gradStartCol, gradStartCol, gradStartCol)
}
batch.begin()
// UI items
catBar.render(batch, camera)
itemList?.render(batch, camera)
equipped?.render(batch, camera)
// control hints
blendNormal(batch)
batch.color = Color.WHITE
Terrarum.fontGame.draw(batch, listControlHelp, offsetX, offsetY + internalHeight)
// encumbrance meter
if (actor != null) {
val encumbranceText = Lang["GAME_INVENTORY_ENCUMBRANCE"]
Terrarum.fontGame.draw(batch,
encumbranceText,
xEnd - 9 - Terrarum.fontGame.getWidth(encumbranceText) - weightBarWidth,
yEnd
)
// encumbrance bar background
blendMul()
batch.color = Color(0xa0a0a0_ff.toInt())
batch.fillRect(
xEnd - 3 - weightBarWidth,
yEnd + 3f,
weightBarWidth,
controlHelpHeight - 6f
)
// encumbrance bar
blendNormal()
batch.color = if (isEncumbered) Color(0xff0000_cc.toInt()) else Color(0x00ff00_cc.toInt())
batch.fillRect(
xEnd - 3 - weightBarWidth,
yEnd + 3f,
if (actor?.inventory?.capacityMode == CAPACITY_MODE_NO_ENCUMBER)
1f
else // make sure 1px is always be seen
minOf(weightBarWidth, maxOf(1f, weightBarWidth * encumbrancePerc)),
controlHelpHeight - 5f
)
}
}
fun rebuildList() {
itemList?.rebuild()
equipped?.rebuild()
actor?.let {
encumbrancePerc = actor!!.inventory.capacity.toFloat() / actor!!.inventory.maxCapacity
isEncumbered = actor!!.inventory.isEncumbered
}
}
override fun dispose() {
catBar.dispose()
itemList?.dispose()
equipped?.dispose()
}
override fun doOpening(delta: Float) {
(Terrarum.ingame as? Ingame)?.setTooltipMessage(null)
}
override fun doClosing(delta: Float) {
(Terrarum.ingame as? Ingame)?.setTooltipMessage(null)
}
override fun endOpening(delta: Float) {
}
override fun endClosing(delta: Float) {
(Terrarum.ingame as? Ingame)?.setTooltipMessage(null) // required!!
}
override fun resize(width: Int, height: Int) {
super.resize(width, height)
offsetX = ((Terrarum.WIDTH - internalWidth) / 2).toFloat()
offsetY = ((Terrarum.HEIGHT - internalHeight) / 2).toFloat()
}
override fun keyDown(keycode: Int): Boolean {
return super.keyDown(keycode)
}
override fun keyTyped(character: Char): Boolean {
return super.keyTyped(character)
}
override fun touchDown(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean {
return super.touchDown(screenX, screenY, pointer, button)
}
override fun keyUp(keycode: Int): Boolean {
return super.keyUp(keycode)
}
override fun mouseMoved(screenX: Int, screenY: Int): Boolean {
return super.mouseMoved(screenX, screenY)
}
override fun touchDragged(screenX: Int, screenY: Int, pointer: Int): Boolean {
return super.touchDragged(screenX, screenY, pointer)
}
override fun touchUp(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean {
return super.touchUp(screenX, screenY, pointer, button)
}
override fun scrolled(amount: Int): Boolean {
return super.scrolled(amount)
}
}

View File

@@ -0,0 +1,26 @@
package net.torvald.terrarum.modulebasegame.ui
import com.badlogic.gdx.graphics.Camera
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.badlogic.gdx.graphics.g2d.TextureRegion
import net.torvald.terrarum.itemproperties.GameItem
import net.torvald.terrarum.ui.UIItem
/**
* Cross section of two inventory cell types
*
* Created by minjaesong on 2017-10-22.
*/
abstract class UIItemInventoryCellBase(
parentUI: UIInventoryFull,
override var posX: Int,
override var posY: Int,
open var item: GameItem?,
open var amount: Int,
open var itemImage: TextureRegion?,
open var quickslot: Int? = null,
open var equippedSlot: Int? = null
) : UIItem(parentUI) {
abstract override fun update(delta: Float)
abstract override fun render(batch: SpriteBatch, camera: Camera)
}

View File

@@ -0,0 +1,370 @@
package net.torvald.terrarum.modulebasegame.ui
import com.badlogic.gdx.graphics.Camera
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import net.torvald.terrarum.BlendMode
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.UIItemInventoryElem
import net.torvald.terrarum.UIItemInventoryElemSimple
import net.torvald.terrarum.modulebasegame.gameactors.ActorInventory
import net.torvald.terrarum.modulebasegame.gameactors.InventoryPair
import net.torvald.terrarum.ceilInt
import net.torvald.terrarum.gameworld.fmod
import net.torvald.terrarum.itemproperties.GameItem
import net.torvald.terrarum.itemproperties.ItemCodex
import net.torvald.terrarum.modulebasegame.Ingame
import net.torvald.terrarum.ui.UIItem
import net.torvald.terrarum.ui.UIItemImageButton
import java.util.ArrayList
/**
* Display either extended or compact list
*
* Note: everything is pretty much fixed size.
*
* Dimension of the whole area: 496x384
* Number of grids: 9x7
* Number of lists: 2x7
*
* Created by minjaesong on 2017-10-21.
*/
class UIItemInventoryDynamicList(
parentUI: UIInventoryFull,
val inventory: ActorInventory,
override var posX: Int,
override var posY: Int
) : UIItem(parentUI) {
override val width = 496
override val height = 384
private val catArrangement = parentUI.catArrangement
val catIconsMeaning = listOf( // sortedBy: catArrangement
GameItem.Category.WEAPON,
GameItem.Category.TOOL,
GameItem.Category.ARMOUR,
GameItem.Category.GENERIC,
GameItem.Category.POTION,
GameItem.Category.MAGIC,
GameItem.Category.BLOCK,
GameItem.Category.WALL,
GameItem.Category.MISC,
"__all__"
)
private val inventoryUI = parentUI
private val selection: Int
get() = inventoryUI.catSelection
private val selectedIcon: Int
get() = inventoryUI.catSelectedIcon
private val compactViewCat = setOf(3, 4, 6, 7, 9) // ingredients, potions, blocks, walls, all (spritesheet order)
var itemPage = 0
var itemPageCount = 1 // TODO total size of current category / items.size
private set
var inventorySortList = ArrayList<InventoryPair>()
private var rebuildList = true
val defaultTextColour = Color(0xeaeaea_ff.toInt())
private val listGap = 8
private val itemList = Array<UIItemInventoryCellBase>(
7 * 2, {
UIItemInventoryElem(
parentUI = inventoryUI,
posX = this.posX + (244 + listGap) * (it % 2),
posY = this.posY + (UIItemInventoryElem.height + listGap) * (it / 2),
width = 244,
item = null,
amount = UIItemInventoryElem.UNIQUE_ITEM_HAS_NO_AMOUNT,
itemImage = null,
mouseoverBackCol = Color(0x282828_ff),
mouseoverBackBlendMode = BlendMode.SCREEN,
backCol = Color(0x404040_88),
backBlendMode = BlendMode.NORMAL,
drawBackOnNull = true,
inactiveTextCol = defaultTextColour
) })
private val itemGrid = Array<UIItemInventoryCellBase>(
7 * 9, {
UIItemInventoryElemSimple(
parentUI = inventoryUI,
posX = this.posX + (UIItemInventoryElemSimple.height + listGap) * (it % 9),
posY = this.posY + (UIItemInventoryElemSimple.height + listGap) * (it / 9),
item = null,
amount = UIItemInventoryElem.UNIQUE_ITEM_HAS_NO_AMOUNT,
itemImage = null,
mouseoverBackCol = Color(0x282828_ff),
mouseoverBackBlendMode = BlendMode.SCREEN,
backCol = Color(0x404040_88),
backBlendMode = BlendMode.NORMAL,
drawBackOnNull = true,
inactiveTextCol = defaultTextColour
)
}
)
private var items: Array<UIItemInventoryCellBase>
= if (catArrangement[selection] in compactViewCat) itemGrid else itemList // this is INIT code
var isCompactMode = (catArrangement[selection] in compactViewCat) // this is INIT code
set(value) {
items = if (value) itemGrid else itemList
rebuild()
field = value
}
private val iconPosX = posX - 12 - parentUI.catIcons.tileW + 2
private fun getIconPosY(index: Int) =
posY - 2 + (4 + UIItemInventoryElem.height - (parentUI as UIInventoryFull).catIcons.tileH) * index
/** Long/compact mode buttons */
private val gridModeButtons = Array<UIItemImageButton>(2, { index ->
UIItemImageButton(
parentUI,
parentUI.catIcons.get(index + 14, 0),
activeBackCol = Color(0),
activeBackBlendMode = BlendMode.NORMAL,
posX = iconPosX,
posY = getIconPosY(index),
highlightable = true
)
})
private val scrollUpButton = UIItemImageButton(
parentUI,
parentUI.catIcons.get(18, 0),
activeBackCol = Color(0),
activeBackBlendMode = BlendMode.NORMAL,
posX = iconPosX,
posY = getIconPosY(2),
highlightable = false
)
private val scrollDownButton = UIItemImageButton(
parentUI,
parentUI.catIcons.get(19, 0),
activeBackCol = Color(0),
activeBackBlendMode = BlendMode.NORMAL,
posX = iconPosX,
posY = getIconPosY(3),
highlightable = false
)
init {
// initially highlight grid mode buttons
gridModeButtons[if (isCompactMode) 1 else 0].highlighted = true
gridModeButtons[0].touchDownListener = { _, _, _, _ ->
isCompactMode = false
gridModeButtons[0].highlighted = true
gridModeButtons[1].highlighted = false
itemPage = 0
rebuild()
}
gridModeButtons[1].touchDownListener = { _, _, _, _ ->
isCompactMode = true
gridModeButtons[0].highlighted = false
gridModeButtons[1].highlighted = true
itemPage = 0
rebuild()
}
scrollUpButton.clickOnceListener = { _, _, _ ->
itemPage = (itemPage - 1).fmod(itemPageCount)
scrollUpButton.highlighted = false
rebuild()
}
scrollDownButton.clickOnceListener = { _, _, _ ->
itemPage = (itemPage + 1).fmod(itemPageCount)
scrollDownButton.highlighted = false
rebuild()
}
// if (is.mouseUp) handled by this.touchDown()
}
private val upDownButtonGapToDots = 7 // apparent gap may vary depend on the texture itself
override fun render(batch: SpriteBatch, camera: Camera) {
fun getScrollDotYHeight(i: Int) = scrollUpButton.posY + 10 + upDownButtonGapToDots + 10 * i
scrollDownButton.posY = getScrollDotYHeight(itemPageCount) + upDownButtonGapToDots
items.forEach { it.render(batch, camera) }
gridModeButtons.forEach { it.render(batch, camera) }
scrollUpButton.render(batch, camera)
scrollDownButton.render(batch, camera)
// draw scroll dots
for (i in 0 until itemPageCount) {
val colour = if (i == itemPage) Color.WHITE else Color(0xffffff7f.toInt())
batch.color = colour
batch.draw(
(parentUI as UIInventoryFull).catIcons.get(20,0),
scrollUpButton.posX.toFloat(),
getScrollDotYHeight(i).toFloat()
)
}
super.render(batch, camera)
}
override fun update(delta: Float) {
super.update(delta)
var tooltipSet = false
items.forEach {
it.update(delta)
// set tooltip accordingly
if (isCompactMode && it.mouseUp && !tooltipSet) {
(Terrarum.ingame as? Ingame)?.setTooltipMessage(it.item?.name)
tooltipSet = true
}
}
if (!tooltipSet) {
(Terrarum.ingame as? Ingame)?.setTooltipMessage(null)
}
gridModeButtons.forEach { it.update(delta) }
scrollUpButton.update(delta)
scrollDownButton.update(delta)
}
internal fun rebuild() {
//println("Rebuilt inventory")
//println("rebuild: actual itempage: $itemPage")
val filter = catIconsMeaning[selectedIcon]
inventorySortList = ArrayList<InventoryPair>()
// filter items
inventory.forEach {
if (it.item.inventoryCategory == filter || filter == "__all__")
inventorySortList.add(it)
}
// sort if needed
// test sort by name
inventorySortList.sortBy { it.item.name }
// map sortList to item list
for (k in 0 until items.size) {
// we have an item
try {
val sortListItem = inventorySortList[k + itemPage * items.size]
items[k].item = sortListItem.item
items[k].amount = sortListItem.amount
items[k].itemImage = ItemCodex.getItemImage(sortListItem.item)
// set quickslot number
for (qs in 1..UIQuickBar.SLOT_COUNT) {
if (sortListItem.item == inventory.getQuickBar(qs - 1)?.item) {
items[k].quickslot = qs % 10 // 10 -> 0, 1..9 -> 1..9
break
}
else
items[k].quickslot = null
}
// set equippedslot number
for (eq in 0 until inventory.itemEquipped.size) {
if (eq < inventory.itemEquipped.size) {
if (inventory.itemEquipped[eq] == items[k].item) {
items[k].equippedSlot = eq
break
}
else
items[k].equippedSlot = null
}
}
}
// we do not have an item, empty the slot
catch (e: IndexOutOfBoundsException) {
items[k].item = null
items[k].amount = 0
items[k].itemImage = null
items[k].quickslot = null
items[k].equippedSlot = null
}
}
itemPageCount = (inventorySortList.size.toFloat() / items.size.toFloat()).ceilInt()
rebuildList = false
}
override fun dispose() {
itemList.forEach { it.dispose() }
itemGrid.forEach { it.dispose() }
gridModeButtons.forEach { it.dispose() }
scrollUpButton.dispose()
scrollDownButton.dispose()
}
override fun touchDown(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean {
super.touchDown(screenX, screenY, pointer, button)
items.forEach { if (it.mouseUp) it.touchDown(screenX, screenY, pointer, button) }
gridModeButtons.forEach { if (it.mouseUp) it.touchDown(screenX, screenY, pointer, button) }
if (scrollUpButton.mouseUp) scrollUpButton.touchDown(screenX, screenY, pointer, button)
if (scrollDownButton.mouseUp) scrollDownButton.touchDown(screenX, screenY, pointer, button)
return true
}
override fun touchUp(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean {
items.forEach { if (it.mouseUp) it.touchUp(screenX, screenY, pointer, button) }
return true
}
override fun keyDown(keycode: Int): Boolean {
super.keyDown(keycode)
items.forEach { if (it.mouseUp) it.keyDown(keycode) }
rebuild()
return true
}
override fun keyUp(keycode: Int): Boolean {
super.keyUp(keycode)
items.forEach { if (it.mouseUp) it.keyUp(keycode) }
rebuild()
return true
}
}

View File

@@ -0,0 +1,162 @@
package net.torvald.terrarum.modulebasegame.ui
import com.badlogic.gdx.graphics.Camera
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import net.torvald.terrarum.*
import net.torvald.terrarum.modulebasegame.gameactors.ActorInventory
import net.torvald.terrarum.modulebasegame.gameactors.ActorWithPhysics
import net.torvald.terrarum.itemproperties.GameItem
import net.torvald.terrarum.itemproperties.ItemCodex
import net.torvald.terrarum.ui.UIItem
/**
* Created by minjaesong on 2017-10-28.
*/
class UIItemInventoryEquippedView(
parentUI: UIInventoryFull,
val inventory: ActorInventory,
val theActor: ActorWithPhysics,
override var posX: Int,
override var posY: Int
) : UIItem(parentUI) {
override val width = 104
override val height = 384
companion object {
val width = 104
val height = 384
}
private val listGap = 8
var itemPage = 0
var itemPageCount = 1 // TODO total size of current category / itemGrid.size
lateinit var inventorySortList: Array<GameItem?>
private var rebuildList = true
val spriteViewBackCol: Color; get() = Color(0x404040_88.toInt())//Color(0xd4d4d4_ff.toInt())
private val itemGrid = Array<UIItemInventoryCellBase>(
2 * 5, {
UIItemInventoryElemSimple(
parentUI = parentUI,
posX = this.posX + (UIItemInventoryElemSimple.height + listGap) * ((it + 4) % 2),
posY = this.posY + (UIItemInventoryElemSimple.height + listGap) * ((it + 4) / 2),
item = null,
amount = UIItemInventoryElem.UNIQUE_ITEM_HAS_NO_AMOUNT,
itemImage = null,
mouseoverBackCol = Color(0x282828_ff),
mouseoverBackBlendMode = BlendMode.SCREEN,
backCol = Color(0x404040_88),
backBlendMode = BlendMode.NORMAL,
drawBackOnNull = true
)
}
)
override fun update(delta: Float) {
itemGrid.forEach { it.update(delta) }
}
override fun render(batch: SpriteBatch, camera: Camera) {
// sprite background
blendNormal()
batch.color = spriteViewBackCol
batch.fillRect(
posX.toFloat(), posY.toFloat(),
width.toFloat(), width.toFloat()
)
// sprite
val sprite = theActor.sprite
sprite?.let {
blendNormal(batch)
it.render(
batch,
posX + (width - it.cellWidth).div(2).toFloat(),
posY + (width - it.cellHeight).div(2).toFloat()
) }
itemGrid.forEach { it.render(batch, camera) }
}
internal fun rebuild() {
inventorySortList = inventory.itemEquipped.clone()
rebuildList = false
// sort by equip position
// fill the grid from fastest index, make no gap in-between of slots
var listPushCnt = 0
for (k in 0 until itemGrid.size) {
val it = inventorySortList[k]
if (it != null) {
val itemRecord = inventory.getByDynamicID(it.dynamicID)!!
itemGrid[listPushCnt].item = it
itemGrid[listPushCnt].amount = itemRecord.amount
itemGrid[listPushCnt].itemImage = ItemCodex.getItemImage(it)
itemGrid[listPushCnt].quickslot = null // don't need to be displayed
itemGrid[listPushCnt].equippedSlot = null // don't need to be displayed
listPushCnt++
}
}
// empty out un-filled grids from previous garbage
for (m in listPushCnt until itemGrid.size) {
itemGrid[m].item = null
itemGrid[m].amount = 0
itemGrid[m].itemImage = null
itemGrid[m].quickslot = null
itemGrid[m].equippedSlot = null
}
}
override fun dispose() {
itemGrid.forEach { it.dispose() }
}
override fun touchDown(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean {
super.touchDown(screenX, screenY, pointer, button)
itemGrid.forEach { if (it.mouseUp) it.touchDown(screenX, screenY, pointer, button) }
return true
}
override fun touchUp(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean {
itemGrid.forEach { if (it.mouseUp) it.touchUp(screenX, screenY, pointer, button) }
return true
}
override fun keyDown(keycode: Int): Boolean {
super.keyDown(keycode)
itemGrid.forEach { if (it.mouseUp) it.keyDown(keycode) }
rebuild()
return true
}
override fun keyUp(keycode: Int): Boolean {
super.keyUp(keycode)
itemGrid.forEach { if (it.mouseUp) it.keyUp(keycode) }
rebuild()
return true
}
}

View File

@@ -0,0 +1,86 @@
package net.torvald.terrarum.modulebasegame.ui
import com.badlogic.gdx.graphics.Camera
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import net.torvald.terrarum.ModMgr
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.blendNormal
import net.torvald.terrarum.floor
import net.torvald.terrarum.ui.UICanvas
import net.torvald.terrarum.ui.UIItem
class UIItemModuleInfoCell(
parent: UICanvas,
var moduleName: String,
override val width: Int,
override var posX: Int,
override var posY: Int
) : UIItem(parent) {
override val height: Int = Terrarum.fontGame.lineHeight.toInt() * 2
private val numberAreaWidth = Terrarum.fontSmallNumbers.W * 3 + 4
override fun render(batch: SpriteBatch, camera: Camera) {
blendNormal()
if (ModMgr.moduleInfo.containsKey(moduleName)) {
val modInfo = ModMgr.moduleInfo[moduleName]!!
// print load order index
batch.color = Color(0xccccccff.toInt())
var strlen = Terrarum.fontSmallNumbers.getWidth(modInfo.order.toString())
Terrarum.fontSmallNumbers.draw(batch,
modInfo.order.toString(),
posX + (numberAreaWidth - strlen).div(2f).floor(),
posY + (height - Terrarum.fontSmallNumbers.H).div(2f).floor()
)
// print module name
batch.color = Color.WHITE
Terrarum.fontGame.draw(batch,
"${modInfo.properName} (${modInfo.version})",
posX + numberAreaWidth.toFloat(),
posY.toFloat()
)
// print author name
strlen = Terrarum.fontGame.getWidth(modInfo.author)
Terrarum.fontGame.draw(batch,
modInfo.author,
posX + width - strlen.toFloat(),
posY.toFloat()
)
// print description
Terrarum.fontGame.draw(batch,
modInfo.description,
posX + numberAreaWidth.toFloat(),
posY + Terrarum.fontGame.lineHeight
)
// print releasedate
strlen = Terrarum.fontGame.getWidth(modInfo.releaseDate)
Terrarum.fontGame.draw(batch,
modInfo.releaseDate,
posX + width - strlen.toFloat(),
posY + Terrarum.fontGame.lineHeight
)
}
else {
batch.color = Color(0xff8080_ff.toInt())
val str = "InternalError: no such module: '$moduleName'"
val strlen = Terrarum.fontSmallNumbers.getWidth(str)
Terrarum.fontSmallNumbers.draw(batch,
str,
posX + (width - numberAreaWidth - strlen).div(2f).floor() + numberAreaWidth,
posY + (height - Terrarum.fontSmallNumbers.H).div(2f).floor()
)
}
}
override fun dispose() {
}
}

Some files were not shown because too many files have changed in this diff Show More