Skip to content

Commit

Permalink
Refactored Inventory into an immutable class
Browse files Browse the repository at this point in the history
  • Loading branch information
Martomate committed Dec 9, 2023
1 parent 4ad8806 commit 76cb35e
Show file tree
Hide file tree
Showing 6 changed files with 56 additions and 64 deletions.
18 changes: 10 additions & 8 deletions game/src/main/scala/hexacraft/game/GameScene.scala
Original file line number Diff line number Diff line change
Expand Up @@ -143,14 +143,16 @@ class GameScene(
setUseMouse(false)
isInPopup = true

val onCloseInventory = () => {
overlays -= inventoryScene
inventoryScene.unload()
inventoryScene = null
isInPopup = false
setUseMouse(true)
}
inventoryScene = InventoryBox(player.inventory, onCloseInventory, blockSpecRegistry)
inventoryScene = InventoryBox(player.inventory, blockSpecRegistry):
case InventoryBox.Event.BoxClosed =>
overlays -= inventoryScene
inventoryScene.unload()
inventoryScene = null
isInPopup = false
setUseMouse(true)
case InventoryBox.Event.InventoryUpdated(inv) =>
player.inventory = inv
toolbar.onInventoryUpdated(inv)

overlays += inventoryScene
case KeyboardKey.Letter('M') =>
Expand Down
53 changes: 26 additions & 27 deletions game/src/main/scala/hexacraft/game/inventory/InventoryBox.scala
Original file line number Diff line number Diff line change
@@ -1,33 +1,30 @@
package hexacraft.game.inventory

import hexacraft.game
import hexacraft.gui.{Event, LocationInfo, RenderContext}
import hexacraft.gui.comp.{Component, GUITransformation}
import hexacraft.infra.window.{KeyAction, KeyboardKey, MouseAction}
import hexacraft.util.Tracker
import hexacraft.world.block.{Block, BlockSpecRegistry}
import hexacraft.world.player.Inventory

import org.joml.{Matrix4f, Vector4f}

object InventoryBox {
def apply(inventory: Inventory, closeScene: () => Unit, specs: BlockSpecRegistry): InventoryBox =
val box = new InventoryBox(
inventory,
closeScene,
makeGuiBlockRenderer(specs),
makeFloatingBlockRenderer(specs)
)
box.updateRendererContent()
box
enum Event:
case BoxClosed
case InventoryUpdated(inventory: Inventory)

def apply(currentInventory: Inventory, specs: BlockSpecRegistry)(eventHandler: Tracker[Event]): InventoryBox =
val gridRenderer = new GuiBlockRenderer(9, 4)(specs)
gridRenderer.setViewMatrix(makeTiltedBlockViewMatrix)

private def makeGuiBlockRenderer(specs: BlockSpecRegistry) =
val renderer = new GuiBlockRenderer(9, 4)(specs)
renderer.setViewMatrix(makeTiltedBlockViewMatrix)
renderer
val floatingBlockRenderer = new GuiBlockRenderer(1, 1)(specs)
floatingBlockRenderer.setViewMatrix(makeTiltedBlockViewMatrix)

private def makeFloatingBlockRenderer(specs: BlockSpecRegistry) =
val renderer = GuiBlockRenderer(1, 1)(specs)
renderer.setViewMatrix(makeTiltedBlockViewMatrix)
renderer
val box = new InventoryBox(currentInventory, gridRenderer, floatingBlockRenderer, eventHandler)
box.updateRendererContent()
box

private def makeTiltedBlockViewMatrix =
new Matrix4f()
Expand All @@ -38,10 +35,10 @@ object InventoryBox {
}

class InventoryBox private (
inventory: Inventory,
closeScene: () => Unit,
private var inventory: Inventory,
gridRenderer: GuiBlockRenderer,
floatingBlockRenderer: GuiBlockRenderer
floatingBlockRenderer: GuiBlockRenderer,
eventHandler: Tracker[InventoryBox.Event]
) extends Component {

private val location: LocationInfo = LocationInfo(-4.5f * 0.2f, -2.5f * 0.2f, 9 * 0.2f, 4 * 0.2f)
Expand All @@ -55,8 +52,6 @@ class InventoryBox private (
/** The block currently being moved */
private var floatingBlock: Option[Block] = None

private val revokeInventoryTracker = inventory.trackChanges(_ => updateRendererContent())

private def updateRendererContent(): Unit =
gridRenderer.updateContent(-4 * 0.2f, -2 * 0.2f, (0 until 9 * 4).map(i => inventory(i)))
if floatingBlock.isEmpty then floatingBlockRenderer.updateContent(0, 0, Seq(Block.Air))
Expand All @@ -76,13 +71,15 @@ class InventoryBox private (
key match
case KeyboardKey.Escape | KeyboardKey.Letter('E') =>
handleFloatingBlock()
closeScene()
eventHandler.notify(InventoryBox.Event.BoxClosed)
case _ =>
case MouseClickEvent(_, MouseAction.Release, _, mousePos) if location.containsPoint(mousePos) =>
hoverIndex match
case Some(hover) =>
val newFloatingBlock = Some(inventory(hover)).filter(_ != Block.Air)
inventory(hover) = floatingBlock.getOrElse(Block.Air)
inventory = inventory.updated(hover, floatingBlock.getOrElse(Block.Air))
eventHandler.notify(InventoryBox.Event.InventoryUpdated(inventory))
updateRendererContent()
floatingBlock = newFloatingBlock
case None =>
handleFloatingBlock()
Expand All @@ -93,8 +90,11 @@ class InventoryBox private (
floatingBlock match
case Some(block) =>
inventory.firstEmptySlot match
case Some(slot) => inventory(slot) = block
case None => // TODO: drop the block because the inventory is full
case Some(slot) =>
inventory = inventory.updated(slot, block)
eventHandler.notify(InventoryBox.Event.InventoryUpdated(inventory))
updateRendererContent()
case None => // TODO: drop the block because the inventory is full
case None =>

override def render(transformation: GUITransformation)(using context: RenderContext): Unit =
Expand All @@ -121,7 +121,6 @@ class InventoryBox private (
then floatingBlockRenderer.render(transformation)

override def unload(): Unit =
revokeInventoryTracker()
gridRenderer.unload()
floatingBlockRenderer.unload()
super.unload()
Expand Down
7 changes: 4 additions & 3 deletions game/src/main/scala/hexacraft/game/inventory/Toolbar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import hexacraft.world.player.Inventory

import org.joml.{Matrix4f, Vector4f}

class Toolbar(location: LocationInfo, inventory: Inventory)(specs: BlockSpecRegistry)
class Toolbar(location: LocationInfo, private var inventory: Inventory)(specs: BlockSpecRegistry)
extends Component
with SubComponents {
private val backgroundColor = new Vector4f(0.4f, 0.4f, 0.4f, 0.75f)
Expand Down Expand Up @@ -37,7 +37,9 @@ class Toolbar(location: LocationInfo, inventory: Inventory)(specs: BlockSpecRegi
def setWindowAspectRatio(aspectRatio: Float): Unit =
guiBlockRenderer.setWindowAspectRatio(aspectRatio)

private val revokeInventoryTracker = inventory.trackChanges(_ => updateRendererContent())
def onInventoryUpdated(inventory: Inventory): Unit =
this.inventory = inventory
updateRendererContent()

private def updateRendererContent(): Unit =
guiBlockRenderer.updateContent(-4 * 0.2f, -0.83f, (0 until 9).map(i => inventory(i)))
Expand All @@ -56,7 +58,6 @@ class Toolbar(location: LocationInfo, inventory: Inventory)(specs: BlockSpecRegi
}

override def unload(): Unit = {
revokeInventoryTracker()
guiBlockRenderer.unload()
super.unload()
}
Expand Down
38 changes: 14 additions & 24 deletions game/src/main/scala/hexacraft/world/player/Inventory.scala
Original file line number Diff line number Diff line change
@@ -1,45 +1,35 @@
package hexacraft.world.player

import com.martomate.nbt.Nbt
import hexacraft.util.{EventDispatcher, RevokeTrackerFn, Tracker}
import hexacraft.world.block.Block

class Inventory(init_slots: Map[Int, Block]) {
private val dispatcher = new EventDispatcher[Unit]
def trackChanges(tracker: Tracker[Unit]): RevokeTrackerFn = dispatcher.track(tracker)

private val slots: Array[Block] = Array.fill(4 * 9)(Block.Air)
for ((idx, block) <- init_slots) slots(idx) = block
import com.martomate.nbt.Nbt

def apply(idx: Int): Block = slots(idx)
case class Inventory(slots: Map[Int, Block]) {
def apply(idx: Int): Block = slots.getOrElse(idx, Block.Air)

def firstEmptySlot: Option[Int] = (0 until 4 * 9).find(i => this(i) == Block.Air)

def update(idx: Int, block: Block): Unit =
slots(idx) = block
dispatcher.notify(())
def updated(idx: Int, block: Block): Inventory = Inventory(slots.updated(idx, block))

def toNBT: Nbt.MapTag =
Nbt.makeMap(
"slots" ->
Nbt
.ListTag(
slots.toSeq.zipWithIndex
.filter((block, _) => block.id != Block.Air.id)
.map((block, idx) => Nbt.makeMap("slot" -> Nbt.ByteTag(idx.toByte), "id" -> Nbt.ByteTag(block.id)))
for
(idx, block) <- slots.toSeq
if block != Block.Air
yield Nbt.makeMap(
"slot" -> Nbt.ByteTag(idx.toByte),
"id" -> Nbt.ByteTag(block.id)
)
)
)

override def equals(obj: Any): Boolean =
obj match
case inv: Inventory =>
inv.slots.sameElements(this.slots)
case _ => false
}

object Inventory {
def default: Inventory =
new Inventory(
Inventory(
Map(
0 -> Block.Dirt,
1 -> Block.Grass,
Expand Down Expand Up @@ -68,6 +58,6 @@ object Inventory {
else None
case _ => None
}
new Inventory(Map.from(slots))
case None => new Inventory(Map.empty)
Inventory(Map.from(slots))
case None => Inventory(Map.empty)
}
2 changes: 1 addition & 1 deletion game/src/main/scala/hexacraft/world/player/Player.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import hexacraft.world.coord.fp.CylCoords
import com.martomate.nbt.Nbt
import org.joml.Vector3d

class Player(val inventory: Inventory) {
class Player(var inventory: Inventory) {
val bounds = new HexBox(0.2f, -1.65f, 0.1f)
val velocity = new Vector3d
val position = new Vector3d
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class PlayerTest extends FunSuite {
playerBefore.rotation.set(0.1, 0.2, -0.3)
playerBefore.flying = true
playerBefore.selectedItemSlot = 7
playerBefore.inventory(3) = Block.Stone
playerBefore.inventory = playerBefore.inventory.updated(3, Block.Stone)

val playerAfter = Player.fromNBT(playerBefore.toNBT)

Expand Down

0 comments on commit 76cb35e

Please sign in to comment.