inventory and its ui

Former-commit-id: b1a073c5636ac4516e6c9cf41bb97a844057de3f
This commit is contained in:
Song Minjae
2017-03-17 03:28:47 +09:00
parent 3d91023011
commit 2491a03c99
15 changed files with 364 additions and 63 deletions

View File

@@ -0,0 +1,55 @@
package net.torvald.terrarum
import net.torvald.terrarum.gamecontroller.Key
import net.torvald.terrarum.gamecontroller.KeyToggler
import net.torvald.terrarum.gameworld.fmod
import org.newdawn.slick.Color
import org.newdawn.slick.GameContainer
import org.newdawn.slick.Graphics
import org.newdawn.slick.state.BasicGameState
import org.newdawn.slick.state.StateBasedGame
/**
* Created by SKYHi14 on 2017-03-15.
*/
class StateControllerRumbleTest : BasicGameState() {
override fun init(container: GameContainer?, game: StateBasedGame?) {
}
override fun update(container: GameContainer, game: StateBasedGame, delta: Int) {
Terrarum.appgc.setTitle("${GAME_NAME} — Do not pull out the controller!")
KeyToggler.update(container.input)
if (Terrarum.controller != null) {
for (i in 0..minOf(rumblerCount - 1, 9)) {
Terrarum.controller!!.setRumblerStrength(i, if (KeyToggler.isOn(2 + i)) 1f else 0f)
}
}
}
private var rumblerCount = Terrarum.controller?.rumblerCount ?: 0
override fun getID() = Terrarum.STATE_ID_TOOL_RUMBLE_DIAGNOSIS
override fun render(gc: GameContainer, game: StateBasedGame, g: Graphics) {
g.font = Terrarum.fontGame
g.color = Color.white
if (Terrarum.controller != null) {
g.drawString("Controller: ${Terrarum.controller!!.name}", 10f, 10f)
g.drawString("Rumbler count: ${rumblerCount}", 10f, 30f)
g.drawString("Rumblers", 10f, 70f)
for (i in 0..minOf(rumblerCount - 1, 9)) {
g.color = if (KeyToggler.isOn(2 + i)) Color(0x55ff55) else Color(0x808080)
//g.drawString("$i", 10f + i * 16f, 90f)
g.drawString("$i${Terrarum.controller!!.getRumblerName(i)}", 10f, 90f + 20 * i)
}
}
else {
g.drawString("Controller not found.", 10f, 10f)
}
}
}

View File

@@ -1,16 +1,14 @@
package net.torvald.terrarum
import net.torvald.terrarum.gameactors.ActorInventory
import net.torvald.terrarum.gameactors.InventoryPair
import net.torvald.terrarum.gameitem.InventoryItem
import net.torvald.terrarum.mapdrawer.MapCamera
import net.torvald.terrarum.ui.UICanvas
import net.torvald.terrarum.ui.UIHandler
import net.torvald.terrarum.ui.UIItemTextButton
import net.torvald.terrarum.ui.UIItemTextButtonList
import org.newdawn.slick.Color
import org.newdawn.slick.GameContainer
import org.newdawn.slick.Graphics
import org.newdawn.slick.Input
import org.newdawn.slick.*
import org.newdawn.slick.state.BasicGameState
import org.newdawn.slick.state.StateBasedGame
@@ -19,11 +17,13 @@ import org.newdawn.slick.state.StateBasedGame
*/
class StateUITest : BasicGameState() {
val ui = UIHandler(SimpleUI())
val ui: UIHandler
val inventory = ActorInventory()
init {
ui = UIHandler(SimpleUI(inventory))
ui.posX = 50
ui.posY = 30
ui.isVisible = true
@@ -31,13 +31,18 @@ class StateUITest : BasicGameState() {
inventory.add(object : InventoryItem() {
override val id: Int = 5656
override var originalName: String = "Test tool"
override var baseMass: Double = 12.0
override var baseToolSize: Double? = 8.0
override var category: String = "tool"
override var maxDurability: Double = 10.0
override var durability: Double = 10.0
})
inventory.getByID(5656)!!.item.name = "Test tool"
inventory.add(object : InventoryItem() {
override val id: Int = 4633
override var originalName: String = "CONTEXT_ITEM_QUEST_NOUN"
override var baseMass: Double = 1.4
override var baseToolSize: Double? = null
override var category: String = "bulk"
@@ -64,12 +69,14 @@ class StateUITest : BasicGameState() {
private class SimpleUI : UICanvas {
private class SimpleUI(val inventory: ActorInventory) : UICanvas {
override var width = 700
override var height = 440 // multiple of 40 (2 * font.lineHeight)
override var height = 480 // multiple of 40 (2 * font.lineHeight)
override var handler: UIHandler? = null
override var openCloseTime: Int = UICanvas.OPENCLOSE_GENERIC
val itemImage = Image("./assets/item_kari_24.tga")
val buttons = UIItemTextButtonList(
this,
arrayOf(
@@ -93,10 +100,48 @@ private class SimpleUI : UICanvas {
kinematic = true
)
val itemStripGutterV = 4
val itemStripGutterH = 48
val itemsStripWidth = width - buttons.width - 2 * itemStripGutterH
val items = Array(height / (UIItemInventoryElem.height + itemStripGutterV), { UIItemInventoryElem(
parentUI = this,
posX = buttons.width + itemStripGutterH,
posY = it * (UIItemInventoryElem.height + itemStripGutterV),
width = itemsStripWidth,
item = null,
amount = UIItemInventoryElem.UNIQUE_ITEM_HAS_NO_AMOUNT,
itemImage = null,
backCol = Color(255, 255, 255, 0x30)
) })
val itemsScrollOffset = 0
var inventorySortList = ArrayList<InventoryPair>()
var rebuildList = true
override fun update(gc: GameContainer, delta: Int) {
Terrarum.gameLocale = "fiFI" // hot swap this to test
Terrarum.gameLocale = "en" // hot swap this to test
buttons.update(gc, delta)
// test fill: just copy the inventory, fuck sorting
if (rebuildList) {
inventorySortList = ArrayList<InventoryPair>()
inventory.forEach { inventorySortList.add(it) }
rebuildList = false
// sort if needed //
inventorySortList.forEachIndexed { index, pair ->
if (index - itemsScrollOffset >= 0 && index < items.size + itemsScrollOffset) {
items[index - itemsScrollOffset].item = pair.item
items[index - itemsScrollOffset].amount = pair.amount
items[index - itemsScrollOffset].itemImage = itemImage
}
}
}
}
override fun render(gc: GameContainer, g: Graphics) {
@@ -104,6 +149,11 @@ private class SimpleUI : UICanvas {
g.fillRect(0f, 0f, width.toFloat(), height.toFloat())
buttons.render(gc, g)
items.forEach {
it.render(gc, g)
}
}
override fun processInput(gc: GameContainer, delta: Int, input: Input) {
@@ -121,7 +171,7 @@ private class SimpleUI : UICanvas {
UICanvas.endOpeningFade(handler)
}
override fun endClosing(gc: GameContainer, delta: Int) {
override fun endClosing(gc: GameContainer, delta: Int) {7
UICanvas.endClosingFade(handler)
}
}

View File

@@ -138,6 +138,7 @@ object Terrarum : StateBasedGame(GAME_NAME) {
val STATE_ID_TEST_UI = 0x105
val STATE_ID_TOOL_NOISEGEN = 0x200
val STATE_ID_TOOL_RUMBLE_DIAGNOSIS = 0x201
var controller: org.lwjgl.input.Controller? = null
private set
@@ -284,9 +285,9 @@ object Terrarum : StateBasedGame(GAME_NAME) {
//addState(StateShaderTest())
//addState(StateNoiseTester())
addState(StateUITest())
//addState(StateControllerRumbleTest())
//ingame = StateInGame()
//addState(ingame)
//ingame = StateInGame(); addState(ingame)
// foolproof

View File

@@ -0,0 +1,101 @@
package net.torvald.terrarum
import net.torvald.terrarum.gameitem.InventoryItem
import net.torvald.terrarum.ui.UICanvas
import net.torvald.terrarum.ui.UIItem
import net.torvald.terrarum.ui.UIItemTextButton
import org.newdawn.slick.Color
import org.newdawn.slick.GameContainer
import org.newdawn.slick.Graphics
import org.newdawn.slick.Image
/**
* @param amount: set to -1 (UIItemInventoryElem.UNIQUE_ITEM_HAS_NO_AMOUNT) for unique item (does not show item count)
*
* Note that the UI will not render if either item or itemImage is null.
*
* Created by SKYHi14 on 2017-03-16.
*/
class UIItemInventoryElem(
parentUI: UICanvas,
override var posX: Int,
override var posY: Int,
override val width: Int,
var item: InventoryItem?,
var amount: Int,
var itemImage: Image?,
val backCol: Color = Color(0,0,0,0),
val backColBlendMode: String = BlendMode.NORMAL
) : UIItem(parentUI) {
companion object {
val height = 48
val UNIQUE_ITEM_HAS_NO_AMOUNT = -1
}
override val height = UIItemInventoryElem.height
private val imgOffset: Float
get() = (this.height - itemImage!!.height).div(2).toFloat() // to snap to the pixel grid
private val textOffsetX = 52f
override fun update(gc: GameContainer, delta: Int) {
if (item != null) {
}
}
override fun render(gc: GameContainer, g: Graphics) {
if (item != null && itemImage != null) {
g.font = Terrarum.fontGame
if (mouseUp) {
BlendMode.resolve(backColBlendMode)
g.color = backCol
g.fillRect(posX.toFloat(), posY.toFloat(), width.toFloat(), height.toFloat())
}
blendNormal()
g.drawImage(itemImage!!, posX + imgOffset, posY + imgOffset)
// if mouse is over, text lights up
g.color = item!!.nameColour * if (mouseUp) Color(0xffffff) else UIItemTextButton.defaultInactiveCol
g.drawString(item!!.name, posX + textOffsetX, posY + 0f)
if (item!!.maxDurability > 0.0) {
// TODO durability gauge
}
}
}
override fun keyPressed(key: Int, c: Char) {
}
override fun keyReleased(key: Int, c: Char) {
}
override fun mouseMoved(oldx: Int, oldy: Int, newx: Int, newy: Int) {
}
override fun mouseDragged(oldx: Int, oldy: Int, newx: Int, newy: Int) {
}
override fun mousePressed(button: Int, x: Int, y: Int) {
}
override fun mouseReleased(button: Int, x: Int, y: Int) {
}
override fun mouseWheelMoved(change: Int) {
}
override fun controllerButtonPressed(controller: Int, button: Int) {
}
override fun controllerButtonReleased(controller: Int, button: Int) {
}
}

View File

@@ -35,11 +35,11 @@ internal object Inventory : ConsoleCommand {
Echo("(inventory empty)")
}
else {
target.inventory.forEach { refId, amount ->
if (amount == 0) {
EchoError("Unexpected zero-amounted item: ID $refId")
target.inventory.forEach {
if (it.amount == 0) {
EchoError("Unexpected zero-amounted item: ID ${it.item.id}")
}
Echo("ID $refId${if (amount > 1) " ($amount)" else ""}")
Echo("ID ${it.item.id}${if (it.amount > 1) " ($it.second)" else ""}")
}
}
}
@@ -62,7 +62,7 @@ internal object Inventory : ConsoleCommand {
val item = ItemCodex[refId]
// if the item does not exist, add it first
if (!target.inventory.contains(item)) {
if (!target.inventory.hasItem(item)) {
target.inventory.add(item)
}

View File

@@ -140,6 +140,7 @@ open class ActorHumanoid(birth: GameDate, death: GameDate? = null)
override var baseMass: Double = 0.0
override var baseToolSize: Double? = null
override var category = "should_not_be_seen"
override val originalName: String = actorValue.getAsString(AVKey.NAME) ?: "(no name)"
}
override fun update(gc: GameContainer, delta: Int) {
@@ -168,12 +169,12 @@ open class ActorHumanoid(birth: GameDate, death: GameDate? = null)
}
// update inventory items
inventory.forEach { item, amount ->
if (!itemEquipped.contains(item)) { // unequipped
item.effectWhileInPocket(gc, delta)
inventory.forEach {
if (!itemEquipped.contains(it.item)) { // unequipped
it.item.effectWhileInPocket(gc, delta)
}
else { // equipped
item.effectWhenEquipped(gc, delta)
it.item.effectWhenEquipped(gc, delta)
}
}
}

View File

@@ -4,6 +4,8 @@ import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.gameitem.InventoryItem
import net.torvald.terrarum.itemproperties.ItemCodex
import java.util.*
import java.util.concurrent.locks.Lock
import java.util.concurrent.locks.ReentrantLock
/**
* Created by minjaesong on 16-03-15.
@@ -22,9 +24,9 @@ class ActorInventory() {
private var capacityMode: Int
/**
* HashMap<ReferenceID, Amounts>
* Sorted by referenceID.
*/
private val itemList: HashMap<InventoryItem, Int> = HashMap()
private val itemList = ArrayList<InventoryPair>()
/**
* Default constructor with no encumbrance.
@@ -62,32 +64,42 @@ class ActorInventory() {
// If we already have the item, increment the amount
// If not, add item with specified amount
itemList.put(item, itemList[item] ?: 0 + count)
val existingItem = getByID(item.id)
if (existingItem != null) { // if the item already exists
val newCount = getByID(item.id)!!.amount + count
itemList.remove(existingItem)
itemList.add(InventoryPair(existingItem.item, newCount))
}
else {
itemList.add(InventoryPair(item, count))
}
insertionSortLastElem(itemList)
}
fun remove(itemID: Int, count: Int = 1) = remove(ItemCodex[itemID], count)
fun remove(item: InventoryItem, count: Int = 1) {
// check if the item does NOT exist
if (itemList[item] == null) {
return
val existingItem = getByID(item.id)
if (existingItem != null) { // if the item already exists
val newCount = getByID(item.id)!!.amount - count
if (newCount < 0) {
throw Error("Tried to remove $count of $item, but the inventory only contains ${getByID(item.id)!!.amount} of them.")
}
else if (newCount > 0) {
add(item, -count)
}
else {
itemList.remove(existingItem)
}
}
else {
// remove the existence of the item if count <= 0
if (itemList[item]!! - count <= 0) {
itemList.remove(item)
}
// else, decrement the item count
else {
itemList.put(item, itemList[item]!! - count)
}
throw Error("Tried to remove $item, but the inventory does not have it.")
}
}
fun contains(item: InventoryItem) = itemList.containsKey(item)
fun contains(itemID: Int) = itemList.containsKey(ItemCodex[itemID])
fun forEach(consumer: (InventoryItem, Int) -> Unit) = itemList.forEach(consumer)
/**
* HashMap<InventoryItem, Amounts>
*/
fun forEach(consumer: (InventoryPair) -> Unit) = itemList.forEach(consumer)
/**
* Get capacity of inventory
@@ -109,26 +121,9 @@ class ActorInventory() {
return capacityMode
}
/**
* Get reference to the itemList
* @return
*/
fun getItemList(): Map<InventoryItem, Int>? {
return itemList
}
/**
* Get clone of the itemList
* @return
*/
@Suppress("UNCHECKED_CAST")
fun getCopyOfItemList(): Map<InventoryItem, Int>? {
return itemList.clone() as Map<InventoryItem, Int>
}
fun getTotalWeight(): Double {
var weight = 0.0
itemList.forEach { item, i -> weight += item.mass * i }
itemList.forEach { weight += it.item.mass * it.amount }
return weight
}
@@ -138,7 +133,7 @@ class ActorInventory() {
*/
fun getTotalCount(): Int {
var count = 0
itemList.forEach { item, i -> count += i }
itemList.forEach { count += it.amount }
return count
}
@@ -158,10 +153,72 @@ class ActorInventory() {
if (getCapacityMode() == CAPACITY_MODE_WEIGHT) {
return capacityByWeight < getTotalWeight()
} else if (getCapacityMode() == CAPACITY_MODE_COUNT) {
return capacityByCount < getTotalWeight()
return capacityByCount < getTotalCount()
} else {
return false
}
}
}
fun hasItem(item: InventoryItem) = hasItem(item.id)
fun hasItem(id: Int) =
if (itemList.size == 0)
false
else
itemList.binarySearch(id) >= 0
fun getByID(id: Int): InventoryPair? {
if (itemList.size == 0)
return null
val index = itemList.binarySearch(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 fun ArrayList<InventoryPair>.binarySearch(ID: 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 = get(mid).item
if (ID > midVal.id)
low = mid + 1
else if (ID < midVal.id)
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: InventoryItem, val amount: Int)

View File

@@ -53,6 +53,7 @@ open class HumanoidNPC(
actorValue[AVKey.SCALE] = value
}
override var category = "npc"
override val originalName: String = actorValue.getAsString(AVKey.NAME) ?: "NPC"
override fun secondaryUse(gc: GameContainer, delta: Int) {
// TODO place this Actor to the world

View File

@@ -1,12 +1,14 @@
package net.torvald.terrarum.gameitem
import net.torvald.terrarum.itemproperties.Material
import net.torvald.terrarum.langpack.Lang
import org.newdawn.slick.Color
import org.newdawn.slick.GameContainer
/**
* Created by minjaesong on 16-01-16.
*/
abstract class InventoryItem {
abstract class InventoryItem : Comparable<InventoryItem> {
/**
* Internal ID of an Item, Long
* 0-4095: Tiles
@@ -16,6 +18,21 @@ abstract class InventoryItem {
*/
abstract val id: Int
abstract protected val originalName: String
private var newName: String = "SET THE NAME!!"
var name: String
set(value) {
newName = value
isCustomName = true
}
get() = if (isCustomName) newName else Lang[originalName]
open var isCustomName = false // true: reads from lang
var nameColour = Color.white
abstract var baseMass: Double
abstract var baseToolSize: Double?
@@ -59,6 +76,12 @@ abstract class InventoryItem {
*/
open var scale: Double = 1.0
/**
* Set to zero if durability not applicable
*/
open var maxDurability: Double = 0.0
open var durability: Double = maxDurability
/**
* Effects applied continuously while in pocket
*/
@@ -107,6 +130,16 @@ abstract class InventoryItem {
if (other == null) return false
return id == (other as InventoryItem).id
}
fun unsetCustomName() {
name = originalName
isCustomName = false
nameColour = Color.white
}
override fun compareTo(other: InventoryItem): Int = (this.id - other.id).sign()
fun Int.sign(): Int = if (this > 0) 1 else if (this < 0) -1 else 0
}
object EquipPosition {

View File

@@ -42,6 +42,7 @@ object ItemCodex {
override var baseToolSize: Double? = null
override var equipPosition = EquipPosition.HAND_GRIP
override var category = "block"
override val originalName = TileCodex[i].nameKey
override fun primaryUse(gc: GameContainer, delta: Int) {
// TODO base punch attack

View File

@@ -24,12 +24,13 @@ class UIItemTextButton(
val highlightCol: Color = Color(0x00f8ff),
val highlightBackCol: Color = Color(0xb0b0b0),
val highlightBackBlendMode: String = BlendMode.MULTIPLY,
val inactiveCol: Color = Color(0xc8c8c8)
val inactiveCol: Color = UIItemTextButton.defaultInactiveCol
) : UIItem(parentUI) {
companion object {
val font = Terrarum.fontGame!!
val height = font.lineHeight * 2
val defaultInactiveCol: Color = Color(0xc8c8c8)
}
private val label: String