mirror of
https://gitlab.com/cc-ru/ocelot/ocelot-desktop.git
synced 2025-12-20 02:59:19 +01:00
Refactor inventory system
Instead of storing the actual Entity, slots now contain an Item, which is a wrapper type that, aside from the Entity itself, provides its name, icon, and context menu entries. For the sake of generality, the value represented by an Item is called its element (so it doesn't have to an Entity). Each Item keeps a reference to its ItemPrototype, which, broadly speaking, encapsulates its essential characteristics. In Minecraft, ItemPrototype would be what you'd see in the creative mode's item selection tabs, and Item is what you'd store in inventories. In Ocelot Desktop, item selector widget shows a list of prototypes, and slots store items. Previously, slots could store any item whatsoever, but since SlotWidget subclasses only showed accepted entries in the selector and there was no other way of inserting an item into the slot, it worked, uhh, fine. However, this no longer proves adequate if I want to allow moving items between slots. Therefore, the slots are required to accept an item if and only if their `accepts` method returns `true` for the item's prototype. We check prototypes instead of the actual items so that we could populate the item selector list with accepted prototypes without having to build concrete items each time. For convenience, `accepts(Item)` is also defined in `SlotWidget` that delegates to `accepts(ItemPrototype)`; the method is marked `final` to prevent overriding. Another major change introduced by this commit concerns the responsibility for slot persistence. Again, previously, the persistence worked as follows. (InventorySlotWidget is an Ocelot Desktop widget; Inventory is ocelot-brain's trait; and Inventory#Slot is a reference to one of the Inventory's slots.) 1. An InventorySlotWidget was associated with its owning Inventory#Slot. Setting an item in the InventorySlotWidget would set it in the owning Inventory#Slot, and clearing the InventorySlotWidget would clear the Inventory#Slot. 2. The InventorySlotWidget didn't care about persistence whatsoever; it was the responsibility of Inventory, which would save its contents to NBT. 3. When a workspace was loaded, the entity in the Inventory#Slot was restored from NBT, and the InventorySlotWidget would initialize its item to the contents of the Inventory#Slot. 4. The item's icon and UI behavior was determined by the concrete InventorySlotWidget class: for instance, CPUSlot would check whether the item was a CPU or an APU, retrieve its tier, and update the icon accordingly. After the logic described in the 4th point had been moved to Item and ItemPrototype, a problem has appeared: an Item instance had be to be restored from the item's element. This commit now moves the burden of saving/loading SlotWidget contents to Ocelot Desktop — more specifically, the SlotGroup and InventorySlotGroup. (Accordingly, an InventorySlotWidget's associated Inventory#Slot is henceforth referred to as its slave slot.) The SlotGroup is a group of slots treated as a single logical inventory. It provides the `save` method that serializes the Items (instead of their element) to NBT and loads them afterwards. The InventorySlotGroup extends the SlotGroup by providing synchronization of InventorySlotWidgets with the slave Inventory#Slots. In addition, the Item recovery logic described in the previous paragraph is implemented there — mostly for migration purposes; however, if a de-sync of the kind "the slave slot has an Entity, but the owner thinks it doesn't" occurs (such as when an item is inserted by ocelot-brain for some reason), we also need to recover the Item to give the InventorySlotWidget. Recoverers and item prototypes are registered in the ItemRegistry. Its `recover(E)` method looks up the ItemRecoverer registered for the runtime classes of the provided element and uses it to build an `Item[E]`. The item prototypes, however, are stored in a tree consisting of group nodes whose children are other group nodes and leaves each containing an ItemPrototype. This is used by the item selector to group several entries together. Finally, the SlotWidget's `transient` boolean property indicates whether the slot is in the middle of updating its contents. This is used to ignore the events fired by an Inventory as a result of inserting an item into an InventorySlotWidget. Once the design solidifies, I'll add documentation to the key classes affected by the refactoring to explain the new behavior.
This commit is contained in:
parent
bfe83a00b3
commit
04f6e647d4
@ -1 +1 @@
|
||||
Subproject commit f326a45a0ae223904869ae02ad22ca7afdeb8b1f
|
||||
Subproject commit 95f04168b920306c066c47308d91b411f95c82a2
|
||||
@ -3,6 +3,8 @@ package ocelot.desktop
|
||||
import li.flor.nativejfilechooser.NativeJFileChooser
|
||||
import ocelot.desktop.audio.{Audio, SoundSource}
|
||||
import ocelot.desktop.ui.UiHandler
|
||||
import ocelot.desktop.ui.widget.inventory.item.traits.PersistableItemPrototype
|
||||
import ocelot.desktop.ui.widget.inventory.item._
|
||||
import ocelot.desktop.ui.widget.{ExitConfirmationDialog, RootWidget, SettingsDialog}
|
||||
import ocelot.desktop.util.FileUtils.getOcelotConfigDirectory
|
||||
import ocelot.desktop.util._
|
||||
@ -11,6 +13,7 @@ import org.apache.logging.log4j.LogManager
|
||||
import totoro.ocelot.brain.Ocelot
|
||||
import totoro.ocelot.brain.event.FileSystemActivityType.Floppy
|
||||
import totoro.ocelot.brain.event._
|
||||
import totoro.ocelot.brain.nbt.persistence.NBTPersistence
|
||||
import totoro.ocelot.brain.nbt.{CompressedStreamTools, NBTTagCompound}
|
||||
import totoro.ocelot.brain.workspace.Workspace
|
||||
|
||||
@ -31,12 +34,13 @@ object OcelotDesktop extends Logging {
|
||||
|
||||
private val random: Random = new Random()
|
||||
|
||||
def withTickLockAcquired(f: () => Unit): Unit = withLockAcquired(tickLock, f)
|
||||
def withTickLockAcquired(f: => Unit): Unit = withLockAcquired(tickLock, f)
|
||||
|
||||
def mainInner(): Unit = {
|
||||
logger.info("Starting up Ocelot Desktop")
|
||||
|
||||
Ocelot.initialize(LogManager.getLogger(Ocelot))
|
||||
setupNbtConstructors()
|
||||
|
||||
val settingsFile = getOcelotConfigDirectory.resolve("ocelot.conf")
|
||||
Settings.load(settingsFile)
|
||||
@ -60,10 +64,10 @@ object OcelotDesktop extends Logging {
|
||||
while (true) {
|
||||
Profiler.startTimeMeasurement("tick")
|
||||
|
||||
withTickLockAcquired(() => {
|
||||
withTickLockAcquired {
|
||||
workspace.update()
|
||||
tpsCounter.tick()
|
||||
})
|
||||
}
|
||||
|
||||
Profiler.endTimeMeasurement("tick")
|
||||
ticker.waitNext()
|
||||
@ -97,26 +101,26 @@ object OcelotDesktop extends Logging {
|
||||
}
|
||||
}
|
||||
|
||||
private def saveWorld(nbt: NBTTagCompound): Unit = withTickLockAcquired(() => {
|
||||
private def saveWorld(nbt: NBTTagCompound): Unit = withTickLockAcquired {
|
||||
val backendNBT = new NBTTagCompound
|
||||
val frontendNBT = new NBTTagCompound
|
||||
workspace.save(backendNBT)
|
||||
root.workspaceView.save(frontendNBT)
|
||||
nbt.setTag("back", backendNBT)
|
||||
nbt.setTag("front", frontendNBT)
|
||||
})
|
||||
}
|
||||
|
||||
private def loadWorld(nbt: NBTTagCompound): Unit = withTickLockAcquired(() => {
|
||||
private def loadWorld(nbt: NBTTagCompound): Unit = withTickLockAcquired {
|
||||
val backendNBT = nbt.getCompoundTag("back")
|
||||
val frontendNBT = nbt.getCompoundTag("front")
|
||||
|
||||
workspace.load(backendNBT)
|
||||
root.workspaceView.load(frontendNBT)
|
||||
})
|
||||
}
|
||||
|
||||
private var savePath: Option[Path] = None
|
||||
private val tmpPath = Files.createTempDirectory("ocelot-save")
|
||||
|
||||
private val tmpPath = Files.createTempDirectory("ocelot-save")
|
||||
def newWorkspace(): Unit = {
|
||||
root.workspaceView.newWorkspace()
|
||||
Settings.get.recentWorkspace = None
|
||||
@ -252,32 +256,76 @@ object OcelotDesktop extends Logging {
|
||||
workspace = new Workspace(tmpPath)
|
||||
}
|
||||
|
||||
def setupNbtConstructors(): Unit = {
|
||||
// FIXME: must construct and load an item's prototype first and use it then to build the item itself
|
||||
val itemConstructor = new PersistableItemPrototype.ItemConstructor
|
||||
val tieredConstructor = new NBTPersistence.TieredConstructor
|
||||
NBTPersistence.registerConstructor(classOf[CPUItemPrototype.CPU#Item].getName, itemConstructor)
|
||||
NBTPersistence.registerConstructor(classOf[CPUItemPrototype.CPU].getName, tieredConstructor)
|
||||
|
||||
NBTPersistence.registerConstructor(classOf[CPUItemPrototype.APU#Item].getName, itemConstructor)
|
||||
NBTPersistence.registerConstructor(classOf[CPUItemPrototype.APU].getName, tieredConstructor)
|
||||
|
||||
NBTPersistence.registerConstructor(classOf[DataCardItemPrototype.Tier1#Item].getName, itemConstructor)
|
||||
NBTPersistence.registerConstructor(classOf[DataCardItemPrototype.Tier2#Item].getName, itemConstructor)
|
||||
NBTPersistence.registerConstructor(classOf[DataCardItemPrototype.Tier3#Item].getName, itemConstructor)
|
||||
|
||||
NBTPersistence.registerConstructor(classOf[EEPROMItemPrototype#Item].getName, itemConstructor)
|
||||
|
||||
NBTPersistence.registerConstructor(classOf[FloppyItemPrototype.Managed#Item].getName, itemConstructor)
|
||||
NBTPersistence.registerConstructor(classOf[FloppyItemPrototype.Unmanaged#Item].getName, itemConstructor)
|
||||
|
||||
NBTPersistence.registerConstructor(classOf[GraphicsCardItemPrototype#Item].getName, itemConstructor)
|
||||
NBTPersistence.registerConstructor(classOf[GraphicsCardItemPrototype].getName, tieredConstructor)
|
||||
|
||||
NBTPersistence.registerConstructor(classOf[HDDItemPrototype.Managed#Item].getName, itemConstructor)
|
||||
NBTPersistence.registerConstructor(classOf[HDDItemPrototype.Managed].getName, tieredConstructor)
|
||||
|
||||
NBTPersistence.registerConstructor(classOf[HDDItemPrototype.Unmanaged#Item].getName, itemConstructor)
|
||||
NBTPersistence.registerConstructor(classOf[HDDItemPrototype.Unmanaged].getName, tieredConstructor)
|
||||
|
||||
NBTPersistence.registerConstructor(classOf[InternetCardItemPrototype#Item].getName, itemConstructor)
|
||||
|
||||
NBTPersistence.registerConstructor(classOf[LinkedCardItemPrototype#Item].getName, itemConstructor)
|
||||
|
||||
NBTPersistence.registerConstructor(classOf[MemoryItemPrototype#Item].getName, itemConstructor)
|
||||
NBTPersistence.registerConstructor(classOf[MemoryItemPrototype].getName, tieredConstructor)
|
||||
|
||||
NBTPersistence.registerConstructor(classOf[NetworkCardItemPrototype#Item].getName, itemConstructor)
|
||||
|
||||
NBTPersistence.registerConstructor(classOf[RedstoneCardItemPrototype.Tier1#Item].getName, itemConstructor)
|
||||
NBTPersistence.registerConstructor(classOf[RedstoneCardItemPrototype.Tier2#Item].getName, itemConstructor)
|
||||
|
||||
NBTPersistence.registerConstructor(classOf[WirelessNetworkCardItemPrototype.Tier1#Item].getName, itemConstructor)
|
||||
NBTPersistence.registerConstructor(classOf[WirelessNetworkCardItemPrototype.Tier2#Item].getName, itemConstructor)
|
||||
}
|
||||
|
||||
private def setupEventHandlers(): Unit = {
|
||||
EventBus.listenTo(classOf[BeepEvent], { case event: BeepEvent =>
|
||||
EventBus.subscribe[BeepEvent] { event =>
|
||||
if (!UiHandler.audioDisabled)
|
||||
Audio.beep(event.frequency, event.duration)
|
||||
else
|
||||
logger.info(s"Beep ${event.frequency} Hz for ${event.duration} ms")
|
||||
})
|
||||
}
|
||||
|
||||
EventBus.listenTo(classOf[BeepPatternEvent], { case event: BeepPatternEvent =>
|
||||
EventBus.subscribe[BeepPatternEvent] { event =>
|
||||
if (!UiHandler.audioDisabled)
|
||||
Audio.beep(event.pattern)
|
||||
else
|
||||
logger.info(s"Beep pattern `${event.pattern}``")
|
||||
})
|
||||
}
|
||||
|
||||
EventBus.listenTo(classOf[MachineCrashEvent], { case event: MachineCrashEvent =>
|
||||
EventBus.subscribe[MachineCrashEvent] { event =>
|
||||
logger.info(s"[EVENT] Machine crash! (address = ${event.address}, ${event.message})")
|
||||
})
|
||||
}
|
||||
|
||||
if (!UiHandler.audioDisabled) {
|
||||
val soundFloppyAccess = Audio.FloppyAccess.map(buffer => new SoundSource(buffer))
|
||||
val soundHDDAccess = Audio.HDDAccess.map(buffer => new SoundSource(buffer))
|
||||
EventBus.listenTo(classOf[FileSystemActivityEvent], { case event: FileSystemActivityEvent =>
|
||||
EventBus.subscribe[FileSystemActivityEvent] { event =>
|
||||
val sound = if (event.activityType == Floppy) soundFloppyAccess else soundHDDAccess
|
||||
sound(random.nextInt(sound.length)).play()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,14 +24,22 @@ class SoundSource(soundBuffer: SoundBuffer, looping: Boolean = false) extends Re
|
||||
|
||||
def getSourceId: Int = sourceId
|
||||
|
||||
def getStatus: Int = if (sourceId == -1) -1 else AL10.alGetSourcei(sourceId, AL10.AL_SOURCE_STATE)
|
||||
def getStatus: Int = ensureInit(-1)(AL10.alGetSourcei(sourceId, AL10.AL_SOURCE_STATE))
|
||||
def isPlaying: Boolean = getStatus == AL10.AL_PLAYING
|
||||
def isPaused: Boolean = getStatus == AL10.AL_PAUSED
|
||||
def isStopped: Boolean = getStatus == AL10.AL_STOPPED
|
||||
|
||||
def play(): Unit = if (!isPlaying) AL10.alSourcePlay(sourceId)
|
||||
def pause(): Unit = if (isPlaying) AL10.alSourcePause(sourceId)
|
||||
def stop(): Unit = if (isPlaying || isPaused) AL10.alSourceStop(sourceId)
|
||||
def play(): Unit = ensureInit() {
|
||||
if (!isPlaying) AL10.alSourcePlay(sourceId)
|
||||
}
|
||||
|
||||
def pause(): Unit = ensureInit() {
|
||||
if (isPlaying) AL10.alSourcePause(sourceId)
|
||||
}
|
||||
|
||||
def stop(): Unit = ensureInit() {
|
||||
if (isPlaying || isPaused) AL10.alSourceStop(sourceId)
|
||||
}
|
||||
|
||||
def setVolume(value: Float): Unit = {
|
||||
if (sourceId != -1) {
|
||||
@ -45,4 +53,10 @@ class SoundSource(soundBuffer: SoundBuffer, looping: Boolean = false) extends Re
|
||||
AL10.alDeleteSources(sourceId)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs `default` if `sourceId == -1`; otherwise, returned the value provided by `ifInit`.
|
||||
*/
|
||||
private def ensureInit[T](default: => T)(ifInit: => T): T = if (sourceId == -1) default else ifInit
|
||||
private def ensureInit()(ifInit: => Unit): Unit = ensureInit(())(ifInit)
|
||||
}
|
||||
|
||||
@ -93,4 +93,10 @@ case class Rect2D(x: Float, y: Float, w: Float, h: Float) {
|
||||
def mapY(f: Float => Float): Rect2D = copy(y = f(y))
|
||||
def mapW(f: Float => Float): Rect2D = copy(w = f(w))
|
||||
def mapH(f: Float => Float): Rect2D = copy(h = f(h))
|
||||
|
||||
def mapTopLeft(f: Vector2D => Vector2D): Rect2D = {
|
||||
val topLeft = f(Vector2D(x, y))
|
||||
|
||||
copy(x = topLeft.x, y = topLeft.y)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
package ocelot.desktop.node
|
||||
|
||||
import ocelot.desktop.node.nodes._
|
||||
import ocelot.desktop.util.TrayInventory
|
||||
import totoro.ocelot.brain.entity.{Cable, Case, FloppyDiskDrive, Relay, Screen}
|
||||
|
||||
import scala.collection.mutable
|
||||
@ -38,7 +37,5 @@ object NodeRegistry {
|
||||
}))
|
||||
|
||||
// TODO: use a proper icon
|
||||
register(NodeType("Tray", "nodes/NewNode", -1, () => {
|
||||
new TrayNode(new TrayInventory)
|
||||
}))
|
||||
register(NodeType("Tray", "nodes/NewNode", -1, () => new TrayNode))
|
||||
}
|
||||
|
||||
@ -4,22 +4,24 @@ import ocelot.desktop.OcelotDesktop
|
||||
import ocelot.desktop.audio.{Audio, SoundSource}
|
||||
import ocelot.desktop.color.Color
|
||||
import ocelot.desktop.graphics.Graphics
|
||||
import ocelot.desktop.node.{EnvironmentNode, Node}
|
||||
import ocelot.desktop.node.EnvironmentNode
|
||||
import ocelot.desktop.ui.event.sources.KeyEvents
|
||||
import ocelot.desktop.ui.event.{ClickEvent, MouseEvent}
|
||||
import ocelot.desktop.ui.widget.WorkspaceView
|
||||
import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry, ContextMenuSubmenu}
|
||||
import ocelot.desktop.ui.widget.slot._
|
||||
import ocelot.desktop.ui.widget.inventory.item._
|
||||
import ocelot.desktop.ui.widget.inventory.slot._
|
||||
import ocelot.desktop.ui.widget.inventory.{InventorySlotGroup, InventorySlotWidget}
|
||||
import ocelot.desktop.util.{ResourceManager, TierColor}
|
||||
import org.lwjgl.input.Keyboard
|
||||
import totoro.ocelot.brain.entity.traits.{Computer, Entity, Floppy, GenericCPU, Inventory}
|
||||
import totoro.ocelot.brain.entity.{CPU, Case, EEPROM, GraphicsCard, HDDManaged, HDDUnmanaged, Memory}
|
||||
import totoro.ocelot.brain.loot.Loot
|
||||
import totoro.ocelot.brain.nbt.NBTTagCompound
|
||||
import totoro.ocelot.brain.util.Tier
|
||||
import totoro.ocelot.brain.entity.Case
|
||||
import totoro.ocelot.brain.entity.traits.{Computer, Entity}
|
||||
import totoro.ocelot.brain.nbt.{NBT, NBTTagCompound}
|
||||
import totoro.ocelot.brain.util.{Persistable, Tier}
|
||||
|
||||
import scala.reflect.ClassTag
|
||||
|
||||
class ComputerNode(val computer: Case, setup: Boolean = true) extends EnvironmentNode {
|
||||
class ComputerNode(val computer: Case, fillDefault: Boolean = true) extends EnvironmentNode {
|
||||
var lastFilesystemAccess = 0L
|
||||
|
||||
var eepromSlot: EEPROMSlot = _
|
||||
@ -29,39 +31,51 @@ class ComputerNode(val computer: Case, setup: Boolean = true) extends Environmen
|
||||
var diskSlots: Array[DiskSlot] = _
|
||||
var floppySlot: Option[FloppySlot] = None
|
||||
|
||||
val slotGroup = new InventorySlotGroup
|
||||
|
||||
val soundComputerRunning = new SoundSource(Audio.ComputerRunning, true)
|
||||
|
||||
setupSlots()
|
||||
|
||||
if (setup) {
|
||||
cpuSlot.owner.put(new CPU(computer.tier.min(2)))
|
||||
memorySlots(0).owner.put(new Memory(computer.tier.min(2) * 2 + 1))
|
||||
memorySlots(1).owner.put(new Memory(computer.tier.min(2) * 2 + 1))
|
||||
cardSlots(0).owner.put(new GraphicsCard(computer.tier.min(1)))
|
||||
floppySlot.map(_.owner).foreach(_.put(Loot.OpenOsFloppy.create()))
|
||||
eepromSlot.owner.put(Loot.OpenOsEEPROM.create())
|
||||
// refitSlots()
|
||||
if (fillDefault) {
|
||||
slotGroup.pushItems(Iterator(
|
||||
new CPUItemPrototype.CPU(computer.tier.min(Tier.Three)).build,
|
||||
new MemoryItemPrototype(computer.tier.min(Tier.Three) * 2 + 1).build,
|
||||
new MemoryItemPrototype(computer.tier.min(Tier.Three) * 2 + 1).build,
|
||||
new GraphicsCardItemPrototype(computer.tier.min(Tier.Two)).build,
|
||||
new FloppyItemPrototype.Managed("openos").build,
|
||||
new EEPROMItemPrototype(Some("lua-bios")).build,
|
||||
))
|
||||
// TODO: investigate other uses of `add` to make sure they don't add something twice
|
||||
OcelotDesktop.workspace.add(computer)
|
||||
}
|
||||
|
||||
refitSlots()
|
||||
|
||||
def this(nbt: NBTTagCompound) {
|
||||
this({
|
||||
val address = nbt.getString("address")
|
||||
OcelotDesktop.workspace.entityByAddress(address).get.asInstanceOf[Case]
|
||||
}, setup = false)
|
||||
}, fillDefault = false)
|
||||
|
||||
load(nbt)
|
||||
}
|
||||
|
||||
override def load(nbt: NBTTagCompound): Unit = {
|
||||
super.load(nbt)
|
||||
|
||||
slotGroup.load(nbt.getTagList(ComputerNode.InventoryTag, NBT.TAG_COMPOUND))
|
||||
}
|
||||
|
||||
override def save(nbt: NBTTagCompound): Unit = {
|
||||
super.save(nbt)
|
||||
|
||||
nbt.setString("address", computer.node.address)
|
||||
nbt.setTag(ComputerNode.InventoryTag, slotGroup.save())
|
||||
}
|
||||
|
||||
override def environment: Computer = computer
|
||||
|
||||
override val icon: String = "nodes/Computer"
|
||||
|
||||
override def iconColor: Color = TierColor.get(computer.tier)
|
||||
|
||||
override protected val canOpen = true
|
||||
@ -114,79 +128,17 @@ class ComputerNode(val computer: Case, setup: Boolean = true) extends Environmen
|
||||
}
|
||||
|
||||
private def changeTier(n: Int): Unit = {
|
||||
val items = slotGroup.iterator.map(_._2).flatMap(_.item).toSeq
|
||||
computer.tier = n
|
||||
setupSlots()
|
||||
refitSlots()
|
||||
slotGroup.pushItems(items.iterator)
|
||||
|
||||
if (currentWindow != null) currentWindow.updateSlots()
|
||||
}
|
||||
|
||||
private def slotAccepts(slot: Inventory#Slot, entity: Entity): Boolean = entity match {
|
||||
case cpu: GenericCPU => cpuSlot.owner.index == slot.index && cpuSlot.tier >= cpu.cpuTier
|
||||
case mem: Memory => memorySlots
|
||||
.exists(memSlot => memSlot.owner.index == slot.index && memSlot.tier >= (mem.tier + 1) / 2 - 1)
|
||||
case hdd: HDDManaged => diskSlots
|
||||
.exists(diskSlot => diskSlot.owner.index == slot.index && diskSlot.tier >= hdd.tier)
|
||||
case hdd: HDDUnmanaged => diskSlots
|
||||
.exists(diskSlot => diskSlot.owner.index == slot.index && diskSlot.tier >= hdd.tier)
|
||||
case _: EEPROM => eepromSlot.owner.index == slot.index
|
||||
case _: Floppy => floppySlot.exists(_.owner.index == slot.index)
|
||||
case card: Entity => cardSlots
|
||||
.exists(cardSlot => cardSlot.owner.index == slot.index && cardSlot.tier >= CardRegistry.getTier(card))
|
||||
}
|
||||
|
||||
private def isSlotValid(slot: Inventory#Slot): Boolean = slot.get.exists(slotAccepts(slot, _))
|
||||
|
||||
private def reloadSlots(): Unit = {
|
||||
cpuSlot.reloadItem()
|
||||
memorySlots.foreach(_.reloadItem())
|
||||
cardSlots.foreach(_.reloadItem())
|
||||
diskSlots.foreach(_.reloadItem())
|
||||
eepromSlot.reloadItem()
|
||||
floppySlot.foreach(_.reloadItem())
|
||||
}
|
||||
|
||||
private def refitSlots(): Unit = {
|
||||
if (computer.inventory.forall(isSlotValid)) {
|
||||
reloadSlots()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
val entities = computer.inventory.entities.toArray
|
||||
computer.inventory.clear()
|
||||
|
||||
cpuSlot._item = None
|
||||
for (slot <- memorySlots) slot._item = None
|
||||
for (slot <- cardSlots) slot._item = None
|
||||
for (slot <- diskSlots) slot._item = None
|
||||
eepromSlot._item = None
|
||||
floppySlot.foreach(_._item = None)
|
||||
|
||||
def findBestSlot[T <: InventorySlot[_]](entity: Entity, candidates: Array[T], tierProvider: T => Option[Int]): Option[T] = {
|
||||
candidates.iterator
|
||||
.filter(_.owner.isEmpty)
|
||||
.filter(slot => slotAccepts(slot.owner, entity))
|
||||
.minByOption(tierProvider(_).getOrElse(Int.MinValue))
|
||||
}
|
||||
|
||||
for (entity <- entities) {
|
||||
val newSlot = entity match {
|
||||
case _: GenericCPU => findBestSlot[CPUSlot](entity, Array(cpuSlot), slot => Some(slot.tier))
|
||||
case _: Memory => findBestSlot[MemorySlot](entity, memorySlots, slot => Some(slot.tier))
|
||||
case _: HDDManaged => findBestSlot[DiskSlot](entity, diskSlots, slot => Some(slot.tier))
|
||||
case _: HDDUnmanaged => findBestSlot[DiskSlot](entity, diskSlots, slot => Some(slot.tier))
|
||||
case _: EEPROM => findBestSlot[EEPROMSlot](entity, Array(eepromSlot), _ => None)
|
||||
case _: Floppy => findBestSlot[FloppySlot](entity, floppySlot.toArray, _ => None)
|
||||
case _: Entity => findBestSlot[CardSlot](entity, cardSlots, slot => Some(slot.tier))
|
||||
}
|
||||
|
||||
newSlot.foreach(_.owner.put(entity))
|
||||
}
|
||||
|
||||
reloadSlots()
|
||||
}
|
||||
|
||||
private def setupSlots(): Unit = {
|
||||
slotGroup.clear()
|
||||
|
||||
var slotIndex = 0
|
||||
|
||||
def nextSlot(): computer.Slot = {
|
||||
@ -195,14 +147,15 @@ class ComputerNode(val computer: Case, setup: Boolean = true) extends Environmen
|
||||
result
|
||||
}
|
||||
|
||||
def addSlot[T <: InventorySlot[_]](factory: computer.Slot => T): T = {
|
||||
def addSlot[T <: InventorySlotWidget[_ <: Entity with Persistable]](factory: (computer.Slot, WorkspaceView) => T): T = {
|
||||
val slot = nextSlot()
|
||||
val widget = factory(slot)
|
||||
val widget = factory(slot, workspaceView)
|
||||
slotGroup.addSlot(widget)
|
||||
|
||||
widget
|
||||
}
|
||||
|
||||
def addSlots[T <: InventorySlot[_] : ClassTag](factories: (computer.Slot => T)*): Array[T] = {
|
||||
def addSlots[T <: InventorySlotWidget[_ <: Entity with Persistable] : ClassTag](factories: ((computer.Slot, WorkspaceView) => T)*): Array[T] = {
|
||||
val array = Array.newBuilder[T]
|
||||
|
||||
for (factory <- factories) {
|
||||
@ -214,41 +167,41 @@ class ComputerNode(val computer: Case, setup: Boolean = true) extends Environmen
|
||||
|
||||
computer.tier match {
|
||||
case Tier.One =>
|
||||
cardSlots = addSlots(new CardSlot(_, Tier.One), new CardSlot(_, Tier.One))
|
||||
memorySlots = addSlots(new MemorySlot(_, Tier.One))
|
||||
diskSlots = addSlots(new DiskSlot(_, Tier.One))
|
||||
cardSlots = addSlots(new CardSlot(_, Tier.One, _), new CardSlot(_, Tier.One, _))
|
||||
memorySlots = addSlots(new MemorySlot(_, Tier.One, _))
|
||||
diskSlots = addSlots(new DiskSlot(_, Tier.One, _))
|
||||
floppySlot = None
|
||||
cpuSlot = addSlot(new CPUSlot(_, this, Tier.One))
|
||||
cpuSlot = addSlot(new CPUSlot(_, this, Tier.One, _))
|
||||
// no idea why on earth the memory slots are split in two here
|
||||
memorySlots :+= addSlot(new MemorySlot(_, Tier.One))
|
||||
eepromSlot = addSlot(new EEPROMSlot(_))
|
||||
memorySlots :+= addSlot(new MemorySlot(_, Tier.One, _))
|
||||
eepromSlot = addSlot(new EEPROMSlot(_, _))
|
||||
|
||||
case Tier.Two =>
|
||||
cardSlots = addSlots(new CardSlot(_, Tier.Two), new CardSlot(_, Tier.One))
|
||||
memorySlots = addSlots(new MemorySlot(_, Tier.Two), new MemorySlot(_, Tier.Two))
|
||||
diskSlots = addSlots(new DiskSlot(_, Tier.Two), new DiskSlot(_, Tier.One))
|
||||
cardSlots = addSlots(new CardSlot(_, Tier.Two, _), new CardSlot(_, Tier.One, _))
|
||||
memorySlots = addSlots(new MemorySlot(_, Tier.Two, _), new MemorySlot(_, Tier.Two, _))
|
||||
diskSlots = addSlots(new DiskSlot(_, Tier.Two, _), new DiskSlot(_, Tier.One, _))
|
||||
floppySlot = None
|
||||
cpuSlot = addSlot(new CPUSlot(_, this, Tier.Two))
|
||||
eepromSlot = addSlot(new EEPROMSlot(_))
|
||||
cpuSlot = addSlot(new CPUSlot(_, this, Tier.Two, _))
|
||||
eepromSlot = addSlot(new EEPROMSlot(_, _))
|
||||
|
||||
case _ =>
|
||||
cardSlots = if (computer.tier == Tier.Three) {
|
||||
addSlots(new CardSlot(_, Tier.Three), new CardSlot(_, Tier.Two), new CardSlot(_, Tier.Two))
|
||||
addSlots(new CardSlot(_, Tier.Three, _), new CardSlot(_, Tier.Two, _), new CardSlot(_, Tier.Two, _))
|
||||
} else {
|
||||
addSlots(new CardSlot(_, Tier.Three), new CardSlot(_, Tier.Three), new CardSlot(_, Tier.Three))
|
||||
addSlots(new CardSlot(_, Tier.Three, _), new CardSlot(_, Tier.Three, _), new CardSlot(_, Tier.Three, _))
|
||||
}
|
||||
|
||||
memorySlots = addSlots(new MemorySlot(_, Tier.Three), new MemorySlot(_, Tier.Three))
|
||||
memorySlots = addSlots(new MemorySlot(_, Tier.Three, _), new MemorySlot(_, Tier.Three, _))
|
||||
|
||||
diskSlots = if (computer.tier == Tier.Three) {
|
||||
addSlots(new DiskSlot(_, Tier.Three), new DiskSlot(_, Tier.Two))
|
||||
addSlots(new DiskSlot(_, Tier.Three, _), new DiskSlot(_, Tier.Two, _))
|
||||
} else {
|
||||
addSlots(new DiskSlot(_, Tier.Three), new DiskSlot(_, Tier.Three))
|
||||
addSlots(new DiskSlot(_, Tier.Three, _), new DiskSlot(_, Tier.Three, _))
|
||||
}
|
||||
|
||||
floppySlot = Some(addSlot(new FloppySlot(_)))
|
||||
cpuSlot = addSlot(new CPUSlot(_, this, Tier.Three))
|
||||
eepromSlot = addSlot(new EEPROMSlot(_))
|
||||
floppySlot = Some(addSlot(new FloppySlot(_, _)))
|
||||
cpuSlot = addSlot(new CPUSlot(_, this, Tier.Three, _))
|
||||
eepromSlot = addSlot(new EEPROMSlot(_, _))
|
||||
}
|
||||
}
|
||||
|
||||
@ -289,3 +242,7 @@ class ComputerNode(val computer: Case, setup: Boolean = true) extends Environmen
|
||||
super.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
object ComputerNode {
|
||||
private final val InventoryTag = "inventory"
|
||||
}
|
||||
|
||||
@ -4,32 +4,43 @@ import ocelot.desktop.OcelotDesktop
|
||||
import ocelot.desktop.color.IntColor
|
||||
import ocelot.desktop.graphics.Graphics
|
||||
import ocelot.desktop.node.EnvironmentNode
|
||||
import ocelot.desktop.ui.widget.slot.FloppySlot
|
||||
import ocelot.desktop.ui.widget.inventory.{InventorySlotGroup, Item}
|
||||
import ocelot.desktop.ui.widget.inventory.item.FloppyItemPrototype
|
||||
import ocelot.desktop.ui.widget.inventory.slot.FloppySlot
|
||||
import totoro.ocelot.brain.entity.FloppyDiskDrive
|
||||
import totoro.ocelot.brain.entity.traits.{Environment, Floppy}
|
||||
import totoro.ocelot.brain.loot.Loot
|
||||
import totoro.ocelot.brain.nbt.NBTTagCompound
|
||||
import totoro.ocelot.brain.util.DyeColor
|
||||
import totoro.ocelot.brain.nbt.{NBT, NBTTagCompound}
|
||||
import totoro.ocelot.brain.util.{DyeColor, Persistable}
|
||||
|
||||
class DiskDriveNode(val diskDrive: FloppyDiskDrive) extends EnvironmentNode {
|
||||
var lastAccess = 0L
|
||||
|
||||
OcelotDesktop.workspace.add(diskDrive)
|
||||
|
||||
val slot: FloppySlot = new FloppySlot(diskDrive.inventory(0))
|
||||
slot.item = floppy.getOrElse(Loot.OpenOsFloppy.create())
|
||||
val slot = new FloppySlot(diskDrive.inventory(0), workspaceView)
|
||||
val slotGroup = new InventorySlotGroup(slot)
|
||||
slot.item = slot.item.orElse(Some(new FloppyItemPrototype.Managed("openos").build))
|
||||
|
||||
def this(nbt: NBTTagCompound) {
|
||||
this({
|
||||
val address = nbt.getString("address")
|
||||
OcelotDesktop.workspace.entityByAddress(address).get.asInstanceOf[FloppyDiskDrive]
|
||||
})
|
||||
|
||||
load(nbt)
|
||||
}
|
||||
|
||||
override def load(nbt: NBTTagCompound): Unit = {
|
||||
super.load(nbt)
|
||||
|
||||
slotGroup.load(nbt.getTagList(DiskDriveNode.InventoryTag, NBT.TAG_COMPOUND))
|
||||
}
|
||||
|
||||
override def save(nbt: NBTTagCompound): Unit = {
|
||||
super.save(nbt)
|
||||
|
||||
nbt.setString("address", diskDrive.node.address)
|
||||
nbt.setTag(DiskDriveNode.InventoryTag, slotGroup.save())
|
||||
}
|
||||
|
||||
override def environment: Environment = diskDrive
|
||||
@ -63,16 +74,14 @@ class DiskDriveNode(val diskDrive: FloppyDiskDrive) extends EnvironmentNode {
|
||||
if (System.currentTimeMillis() - diskDrive.lastDiskAccess < 400 && Math.random() > 0.1)
|
||||
g.sprite("nodes/DiskDriveActivity", position.x + 2, position.y + 2, size.width - 4, size.height - 4)
|
||||
|
||||
if (slot.item.isDefined)
|
||||
g.sprite("nodes/DiskDriveFloppy", position.x + 2, position.y + 2, size.width - 4, size.height - 4, IntColor(colorMap(slot.item.get.color)))
|
||||
slot.item.foreach(item =>
|
||||
g.sprite("nodes/DiskDriveFloppy", position.x + 2, position.y + 2, size.width - 4, size.height - 4, IntColor(colorMap(item.element.color)))
|
||||
)
|
||||
}
|
||||
|
||||
override val window: Option[DiskDriveWindow] = Some(new DiskDriveWindow(this))
|
||||
|
||||
private def floppy: Option[Floppy] = {
|
||||
diskDrive.inventory(0).get match {
|
||||
case Some(floppy: Floppy) => Some(floppy)
|
||||
case _ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object DiskDriveNode {
|
||||
private final val InventoryTag = "inventory"
|
||||
}
|
||||
@ -1,33 +1,34 @@
|
||||
package ocelot.desktop.node.nodes
|
||||
|
||||
import ocelot.desktop.OcelotDesktop
|
||||
import ocelot.desktop.node.Node
|
||||
import ocelot.desktop.ui.widget.slot.EntitySlot
|
||||
import ocelot.desktop.util.TrayInventory
|
||||
import totoro.ocelot.brain.nbt.NBTTagCompound
|
||||
import totoro.ocelot.brain.nbt.persistence.NBTPersistence
|
||||
import ocelot.desktop.ui.widget.inventory.SlotGroup
|
||||
import ocelot.desktop.ui.widget.inventory.slot.AnySlot
|
||||
import ocelot.desktop.util.ExtendedTuple2
|
||||
import totoro.ocelot.brain.nbt.{NBT, NBTTagCompound}
|
||||
|
||||
class TrayNode(trayInventory: TrayInventory) extends Node {
|
||||
val slots: Array[EntitySlot] = (0 until TrayNode.InventorySize)
|
||||
.iterator
|
||||
.map(trayInventory.inventory.apply)
|
||||
.map(new EntitySlot(_))
|
||||
.toArray
|
||||
class TrayNode extends Node {
|
||||
val slotGroup = new SlotGroup()
|
||||
|
||||
slotGroup ++= Iterator.continually(new AnySlot(workspaceView))
|
||||
.zipWithIndex
|
||||
.map(_.flipped)
|
||||
.take(TrayNode.InventorySize)
|
||||
|
||||
def this(nbt: NBTTagCompound) {
|
||||
this({
|
||||
val inventoryNbt = nbt.getCompoundTag(TrayNode.InventoryTag)
|
||||
NBTPersistence.load(inventoryNbt, OcelotDesktop.workspace).asInstanceOf[TrayInventory]
|
||||
})
|
||||
this()
|
||||
load(nbt)
|
||||
}
|
||||
|
||||
override def load(nbt: NBTTagCompound): Unit = {
|
||||
super.load(nbt)
|
||||
|
||||
slotGroup.load(nbt.getTagList(TrayNode.InventoryTag, NBT.TAG_COMPOUND))
|
||||
}
|
||||
|
||||
override def save(nbt: NBTTagCompound): Unit = {
|
||||
super.save(nbt)
|
||||
|
||||
val inventoryNbt = NBTPersistence.save(trayInventory)
|
||||
nbt.setTag(TrayNode.InventoryTag, inventoryNbt)
|
||||
nbt.setTag(TrayNode.InventoryTag, slotGroup.save())
|
||||
}
|
||||
|
||||
// TODO
|
||||
@ -43,9 +44,9 @@ class TrayNode(trayInventory: TrayInventory) extends Node {
|
||||
}
|
||||
|
||||
object TrayNode {
|
||||
private val InventoryTag = "inventory"
|
||||
private final val InventoryTag = "inventory"
|
||||
|
||||
val InventoryWidth: Int = 9
|
||||
val InventoryHeight: Int = 8
|
||||
val InventorySize: Int = InventoryWidth * InventoryHeight
|
||||
final val InventoryWidth: Int = 9
|
||||
final val InventoryHeight: Int = 8
|
||||
final val InventorySize: Int = InventoryWidth * InventoryHeight
|
||||
}
|
||||
@ -17,7 +17,7 @@ class TrayWindow(owner: TrayNode) extends BasicWindow {
|
||||
|
||||
for (column <- 0 until TrayNode.InventoryHeight) {
|
||||
val index = row * TrayNode.InventoryWidth + column
|
||||
children :+= owner.slots(index)
|
||||
children :+= owner.slotGroup(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -208,7 +208,7 @@ object UiHandler extends Logging {
|
||||
if (!audioDisabled)
|
||||
Audio.update()
|
||||
|
||||
OcelotDesktop.withTickLockAcquired(() => {
|
||||
OcelotDesktop.withTickLockAcquired {
|
||||
updateWindowSizeAndPosition()
|
||||
KeyEvents.update()
|
||||
MouseEvents.update()
|
||||
@ -220,7 +220,7 @@ object UiHandler extends Logging {
|
||||
Profiler.startTimeMeasurement("001_draw")
|
||||
draw()
|
||||
Profiler.endTimeMeasurement("001_draw")
|
||||
})
|
||||
}
|
||||
|
||||
Profiler.startTimeMeasurement("002_sleep")
|
||||
ticker.waitNext()
|
||||
|
||||
@ -46,14 +46,14 @@ class RootWidget(setupDefaultWorkspace: Boolean = true) extends Widget {
|
||||
case KeyEvent(KeyEvent.State.Release, Keyboard.KEY_F1, _) =>
|
||||
isDebugViewVisible = !isDebugViewVisible
|
||||
case KeyEvent(KeyEvent.State.Release, Keyboard.KEY_F3, _) =>
|
||||
OcelotDesktop.withTickLockAcquired(() => {
|
||||
OcelotDesktop.withTickLockAcquired {
|
||||
val backendNBT = new NBTTagCompound
|
||||
val frontendNBT = new NBTTagCompound
|
||||
OcelotDesktop.workspace.save(backendNBT)
|
||||
UiHandler.root.workspaceView.save(frontendNBT)
|
||||
OcelotDesktop.workspace.load(backendNBT)
|
||||
UiHandler.root.workspaceView.load(frontendNBT)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
override def draw(g: Graphics): Unit = {
|
||||
|
||||
@ -9,11 +9,12 @@ import ocelot.desktop.node.{EnvironmentNode, Node, NodePort}
|
||||
import ocelot.desktop.ui.event._
|
||||
import ocelot.desktop.ui.event.handlers.{ClickHandler, DragHandler, HoverHandler}
|
||||
import ocelot.desktop.ui.layout.{CopyLayout, Layout}
|
||||
import ocelot.desktop.ui.widget.inventory.{Item, SlotWidget}
|
||||
import ocelot.desktop.ui.widget.window.{NodeSelector, ProfilerWindow, WindowPool}
|
||||
import ocelot.desktop.util.DrawUtils
|
||||
import ocelot.desktop.util.animation.ValueAnimation
|
||||
import org.lwjgl.input.Keyboard
|
||||
import totoro.ocelot.brain.entity.traits.{Environment, SidedEnvironment}
|
||||
import totoro.ocelot.brain.entity.traits.{Entity, Environment, SidedEnvironment}
|
||||
import totoro.ocelot.brain.entity.{Case, Screen}
|
||||
import totoro.ocelot.brain.nbt.ExtendedNBT._
|
||||
import totoro.ocelot.brain.nbt.{NBT, NBTBase, NBTTagCompound}
|
||||
@ -32,6 +33,9 @@ class WorkspaceView extends Widget with DragHandler with ClickHandler with Hover
|
||||
var cameraOffset: Vector2D = Vector2D(0, 0)
|
||||
var newConnection: Option[(EnvironmentNode, NodePort, Vector2D)] = None
|
||||
|
||||
case class DraggedItem(sourceSlot: SlotWidget[_], item: Item[Entity], pos: Vector2D)
|
||||
var draggedItem: Option[DraggedItem] = None
|
||||
|
||||
private var newNodePos = Vector2D(0, 0)
|
||||
private val gridAlpha = new ValueAnimation(0, 1f)
|
||||
private val portsAlpha = new ValueAnimation(0, 7f)
|
||||
@ -432,6 +436,10 @@ class WorkspaceView extends Widget with DragHandler with ClickHandler with Hover
|
||||
g.translate(-position.x, -position.y)
|
||||
}
|
||||
|
||||
private def drawDraggedItem(g: Graphics): Unit = draggedItem foreach { case DraggedItem(sourceSlot, item, pos) =>
|
||||
// TODO
|
||||
}
|
||||
|
||||
override def draw(g: Graphics): Unit = {
|
||||
g.setScissor(bounds.x, bounds.y, bounds.w, bounds.h)
|
||||
|
||||
@ -502,6 +510,8 @@ class WorkspaceView extends Widget with DragHandler with ClickHandler with Hover
|
||||
|
||||
drawChildren(g)
|
||||
|
||||
drawDraggedItem(g)
|
||||
|
||||
g.setScissor()
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,14 @@
|
||||
package ocelot.desktop.ui.widget.inventory
|
||||
|
||||
import ocelot.desktop.graphics.{Graphics, IconDef}
|
||||
|
||||
trait DecoratedSlotWidget[E] extends TieredSlotWidget[E] {
|
||||
def bgIcon: Option[IconDef]
|
||||
|
||||
def tierIcon: Option[IconDef] = slotTier.map("icons/Tier" + _).map(new IconDef(_))
|
||||
|
||||
override protected def drawEmptySlot(g: Graphics): Unit = {
|
||||
bgIcon.foreach(g.sprite(_, iconBounds))
|
||||
tierIcon.foreach(g.sprite(_, iconBounds))
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,154 @@
|
||||
package ocelot.desktop.ui.widget.inventory
|
||||
|
||||
import ocelot.desktop.util.Logging
|
||||
import totoro.ocelot.brain.entity.traits.{Entity, Inventory}
|
||||
import totoro.ocelot.brain.event.{EventBus, InventoryEntityAddedEvent, InventoryEntityRemovedEvent}
|
||||
import totoro.ocelot.brain.nbt.NBTTagList
|
||||
import totoro.ocelot.brain.util.Persistable
|
||||
|
||||
import scala.collection.mutable
|
||||
|
||||
class InventorySlotGroup extends SlotGroup {
|
||||
def this(slots: InventorySlotWidget[_ <: Entity with Persistable]*) {
|
||||
this()
|
||||
|
||||
slots.foreach(addSlot)
|
||||
}
|
||||
|
||||
private val trackedInventories = mutable.Map.empty[Inventory, TrackedInventoryInfo]
|
||||
private val trackedSlots = mutable.Map.empty[Inventory#Slot, InventorySlotWidget[_ <: Entity with Persistable]]
|
||||
|
||||
override def addSlot(index: Int, slot: SlotWidget[_]): Unit = slot match {
|
||||
case slot: InventorySlotWidget[_] =>
|
||||
if (index != slot.slave.index) {
|
||||
throw new IllegalArgumentException(s"tried to add an InventorySlot (index: ${slot.slave.index}) " +
|
||||
s"to a slot it doesn't belong to (index: $index)")
|
||||
}
|
||||
|
||||
addSlot(slot)
|
||||
|
||||
case _ => super.addSlot(index, slot)
|
||||
}
|
||||
|
||||
def addSlot(slot: InventorySlotWidget[_ <: Entity with Persistable]): Unit = {
|
||||
val slave = slot.slave
|
||||
val index = slave.index
|
||||
|
||||
// we have to explicitly removeSlot here in case we've had event listeners set up for the slot
|
||||
removeSlot(index)
|
||||
addEventListener(slave)
|
||||
trackedSlots += slot.slave -> slot
|
||||
super.addSlot(index, slot)
|
||||
}
|
||||
|
||||
override def removeSlot(index: Int): Option[SlotWidget[_]] =
|
||||
super.removeSlot(index).tapEach(unregisterSlot).headOption
|
||||
|
||||
private def unregisterSlot(slot: SlotWidget[_]): Unit = slot match {
|
||||
case slot: InventorySlotWidget[_] =>
|
||||
removeEventListener(slot.slave)
|
||||
trackedSlots.remove(slot.slave)
|
||||
|
||||
case _ =>
|
||||
}
|
||||
|
||||
override def clear(): Unit = {
|
||||
_slots.valuesIterator.foreach(unregisterSlot)
|
||||
|
||||
_slots.clear()
|
||||
}
|
||||
|
||||
private def addEventListener(slot: Inventory#Slot): Unit = {
|
||||
val owner = slot.inventory.owner
|
||||
|
||||
trackedInventories.updateWith(owner) {
|
||||
case Some(info) => Some(info.withRefIncremented)
|
||||
|
||||
case None =>
|
||||
Some(TrackedInventoryInfo(EventBus.subscribe(onEntityAdded), EventBus.subscribe(onEntityRemoved)))
|
||||
}
|
||||
}
|
||||
|
||||
private def removeEventListener(slot: Inventory#Slot): Unit = {
|
||||
val owner = slot.inventory.owner
|
||||
|
||||
trackedInventories.updateWith(owner) {
|
||||
case Some(info@TrackedInventoryInfo(_, _, refCount)) if refCount > 0 => Some(info.withRefDecremented)
|
||||
|
||||
case Some(TrackedInventoryInfo(onAddedSubscription, onRemovedSubscription, _)) =>
|
||||
onAddedSubscription.cancel()
|
||||
onRemovedSubscription.cancel()
|
||||
|
||||
None
|
||||
|
||||
case None => None
|
||||
}
|
||||
}
|
||||
|
||||
private def onEntityAdded(event: InventoryEntityAddedEvent): Unit =
|
||||
trackedSlots.get(event.slot).filterNot(_.transient).foreach(reloadSlot)
|
||||
|
||||
private def onEntityRemoved(event: InventoryEntityRemovedEvent): Unit =
|
||||
trackedSlots.get(event.slot).filterNot(_.transient).foreach(reloadSlot)
|
||||
|
||||
private def reloadSlot(slot: InventorySlotWidget[_ <: Entity]): Unit = {
|
||||
val slave = slot.slave
|
||||
|
||||
(slave.get, slot.item.map(_.element)) match {
|
||||
case (Some(slaveEntity), Some(masterEntity)) if slaveEntity.eq(masterEntity) => // in sync
|
||||
case (None, None) => // in sync
|
||||
|
||||
case (a, b) => // de-sync
|
||||
logger.atDebug().withThrowable(new Exception("A de-sync has occured"))
|
||||
.log(s"Have $a in slave and $b in master: de-sync!")
|
||||
slot.set(InventorySlotGroup.recoverItemFromSlot(slave))
|
||||
}
|
||||
}
|
||||
|
||||
override def load(nbt: NBTTagList): Unit = {
|
||||
val nbtLoadedItems = loadItemsFromNbt(nbt)
|
||||
val recoveredItems = recoverItemsFromInventory(nbtLoadedItems)
|
||||
|
||||
setItems(nbtLoadedItems ++ recoveredItems)
|
||||
}
|
||||
|
||||
override def load(): Unit = setItems(recoverItemsFromInventory(Map.empty))
|
||||
|
||||
private def recoverItemsFromInventory(loadedItems: Map[Int, Item[Entity]]): Map[Int, Item[Entity]] =
|
||||
_slots.iterator
|
||||
.filter { case (index, _) => _slots.contains(index) }
|
||||
.filterNot { case (index, _) => loadedItems.contains(index) }
|
||||
// fall back to trying to recover items from inventory contents
|
||||
.collect {
|
||||
case (_, slot: InventorySlotWidget[_]) => slot.slave
|
||||
}
|
||||
.flatMap(slave => InventorySlotGroup.recoverItemFromSlot(slave).map((slave.index, _)))
|
||||
.toMap
|
||||
|
||||
private case class TrackedInventoryInfo(onAddedSubscription: EventBus.Subscription,
|
||||
onRemovedSubscription: EventBus.Subscription,
|
||||
refCount: Int = 1) {
|
||||
def withRefIncremented: TrackedInventoryInfo =
|
||||
TrackedInventoryInfo(onAddedSubscription, onRemovedSubscription, refCount + 1)
|
||||
|
||||
def withRefDecremented: TrackedInventoryInfo =
|
||||
TrackedInventoryInfo(onAddedSubscription, onRemovedSubscription, (refCount - 1) max 0)
|
||||
}
|
||||
}
|
||||
|
||||
object InventorySlotGroup extends Logging {
|
||||
def recoverItemFromSlot(slot: Inventory#Slot): Option[Item[Entity]] = {
|
||||
slot.get.flatMap(entity => {
|
||||
val item = ItemRegistry.recover(entity)
|
||||
|
||||
if (item.isEmpty) {
|
||||
logger.atWarn().withThrowable(
|
||||
new Exception(s"Inventory ${slot.inventory.owner} stores an Entity $entity (slot index: ${slot.index}) " +
|
||||
s"that cannot be recovered to an Item")
|
||||
).log("Could not recover an item from an Inventory")
|
||||
}
|
||||
|
||||
item
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
package ocelot.desktop.ui.widget.inventory
|
||||
|
||||
import ocelot.desktop.ui.UiHandler
|
||||
import ocelot.desktop.ui.widget.WorkspaceView
|
||||
import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry}
|
||||
import totoro.ocelot.brain.entity.traits.{Entity, Environment, Inventory}
|
||||
import totoro.ocelot.brain.util.Persistable
|
||||
|
||||
import scala.reflect.ClassTag
|
||||
|
||||
abstract class InventorySlotWidget[E <: Entity with Persistable : ClassTag](val slave: Inventory#Slot,
|
||||
workspaceView: WorkspaceView)
|
||||
extends SlotWidget[E](workspaceView) {
|
||||
|
||||
override def onAdded(item: Item[Element]): Unit = slave.put(item.element)
|
||||
|
||||
override def onRemoved(item: Item[Element]): Unit = slave.remove()
|
||||
|
||||
override def fillRmbMenu(menu: ContextMenu): Unit = {
|
||||
item match {
|
||||
case Some(env: Environment) =>
|
||||
// TODO: move to EnvironmentItemPrototype.Instance
|
||||
menu.addEntry(new ContextMenuEntry("Copy address", () => {
|
||||
UiHandler.clipboard = env.node.address
|
||||
}))
|
||||
|
||||
case _ =>
|
||||
}
|
||||
super.fillRmbMenu(menu)
|
||||
}
|
||||
}
|
||||
17
src/main/scala/ocelot/desktop/ui/widget/inventory/Item.scala
Normal file
17
src/main/scala/ocelot/desktop/ui/widget/inventory/Item.scala
Normal file
@ -0,0 +1,17 @@
|
||||
package ocelot.desktop.ui.widget.inventory
|
||||
|
||||
import ocelot.desktop.graphics.IconDef
|
||||
import ocelot.desktop.ui.widget.contextmenu.ContextMenu
|
||||
|
||||
/**
|
||||
* Something that can be put into a [[SlotWidget]].
|
||||
*/
|
||||
trait Item[+E] {
|
||||
def prototype: ItemPrototype
|
||||
|
||||
def icon: IconDef
|
||||
|
||||
def fillRmbMenu(menu: ContextMenu): Unit = {}
|
||||
|
||||
def element: E
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
package ocelot.desktop.ui.widget.inventory
|
||||
|
||||
import ocelot.desktop.graphics.IconDef
|
||||
import ocelot.desktop.ui.widget.inventory
|
||||
|
||||
import scala.reflect.ClassTag
|
||||
|
||||
trait ItemPrototype {
|
||||
type Element
|
||||
type Item <: inventory.Item[Element]
|
||||
|
||||
val elementTag: ClassTag[Element]
|
||||
|
||||
def build: Item
|
||||
|
||||
def name: String
|
||||
|
||||
def icon: IconDef
|
||||
}
|
||||
|
||||
object ItemPrototype {
|
||||
trait WithInstance extends ItemPrototype {
|
||||
proto =>
|
||||
|
||||
override type Item <: Instance
|
||||
|
||||
abstract class Instance(var element: Element) extends inventory.Item[Element] {
|
||||
override final def prototype: ItemPrototype = proto
|
||||
|
||||
override final def icon: IconDef = proto.icon
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
package ocelot.desktop.ui.widget.inventory
|
||||
|
||||
import scala.reflect.ClassTag
|
||||
|
||||
/**
|
||||
* Recovers an [[Item]] by its element.
|
||||
*/
|
||||
trait ItemRecoverer {
|
||||
type Element
|
||||
|
||||
implicit val elementTag: ClassTag[Element]
|
||||
|
||||
def recover(element: Element): Item[Element]
|
||||
}
|
||||
@ -0,0 +1,60 @@
|
||||
package ocelot.desktop.ui.widget.inventory
|
||||
|
||||
import ocelot.desktop.graphics.IconDef
|
||||
|
||||
import scala.collection.mutable
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
|
||||
object ItemRegistry {
|
||||
private val recovererRegistry = mutable.Map.empty[Class[_], ItemRecoverer]
|
||||
private val nodes = ArrayBuffer.empty[Node]
|
||||
|
||||
def register(prototype: ItemPrototype): ItemPrototype = {
|
||||
nodes += Leaf(prototype)
|
||||
|
||||
prototype
|
||||
}
|
||||
|
||||
def register(group: Group): Group = {
|
||||
nodes += group
|
||||
|
||||
group
|
||||
}
|
||||
|
||||
def register(recoverer: ItemRecoverer): Unit =
|
||||
recovererRegistry(recoverer.elementTag.runtimeClass) = recoverer
|
||||
|
||||
def recover[E](element: E): Option[Item[E]] = {
|
||||
recovererRegistry
|
||||
.get(element.getClass)
|
||||
.map(recoverer => {
|
||||
// wild type-casting ¯\_(ツ)_/¯
|
||||
// (should be safe though)
|
||||
recoverer.recover(element.asInstanceOf[recoverer.Element]).asInstanceOf[Item[E]]
|
||||
})
|
||||
}
|
||||
|
||||
def children: Iterator[Node] = nodes.iterator
|
||||
|
||||
sealed trait Node
|
||||
|
||||
abstract case class Group(name: String, icon: Option[IconDef]) extends Node { self =>
|
||||
private val _children: ArrayBuffer[Node] = ArrayBuffer.empty
|
||||
|
||||
final def children: Iterator[Node] = _children.iterator
|
||||
|
||||
def register(prototype: ItemPrototype): ItemPrototype = {
|
||||
_children += Leaf(prototype)
|
||||
|
||||
prototype
|
||||
}
|
||||
|
||||
def register(group: Group): Group = {
|
||||
_children += group
|
||||
|
||||
group
|
||||
}
|
||||
}
|
||||
|
||||
case class Leaf(prototype: ItemPrototype) extends Node
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
package ocelot.desktop.ui.widget.inventory
|
||||
|
||||
import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry, ContextMenuSubmenu}
|
||||
import ocelot.desktop.ui.widget.inventory.ItemRegistry.{Group, Node}
|
||||
|
||||
trait LmbItemSelectionSlot[E] extends SlotWidget[E] {
|
||||
override def fillLmbMenu(menu: ContextMenu): Unit = {
|
||||
def traverse(node: Node): Option[ContextMenuEntry] = node match {
|
||||
case group@Group(name, icon) =>
|
||||
val entries = group.children.flatMap(traverse).toSeq
|
||||
|
||||
Option.when(entries.nonEmpty) {
|
||||
new ContextMenuSubmenu(name, icon) {
|
||||
entries.foreach(addEntry)
|
||||
}
|
||||
}
|
||||
|
||||
case ItemRegistry.Leaf(prototype) =>
|
||||
Option.when(accepts(prototype)) {
|
||||
new ContextMenuEntry(prototype.name, () => set(prototype.build), Some(prototype.icon))
|
||||
}
|
||||
}
|
||||
|
||||
ItemRegistry.children.flatMap(traverse).foreach(menu.addEntry)
|
||||
|
||||
super.fillLmbMenu(menu)
|
||||
}
|
||||
|
||||
override def lmbMenuEnabled: Boolean = true
|
||||
}
|
||||
@ -0,0 +1,133 @@
|
||||
package ocelot.desktop.ui.widget.inventory
|
||||
|
||||
import ocelot.desktop.OcelotDesktop
|
||||
import ocelot.desktop.util.Logging
|
||||
import totoro.ocelot.brain.entity.traits.Entity
|
||||
import totoro.ocelot.brain.nbt.ExtendedNBT.extendNBTTagList
|
||||
import totoro.ocelot.brain.nbt.persistence.NBTPersistence
|
||||
import totoro.ocelot.brain.nbt.{NBTTagCompound, NBTTagList}
|
||||
import totoro.ocelot.brain.util.Persistable
|
||||
|
||||
import scala.collection.mutable
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
|
||||
class SlotGroup extends mutable.Growable[(Int, SlotWidget[_])] with Logging with PartialFunction[Int, SlotWidget[_]] {
|
||||
private final val SlotTag = "slot"
|
||||
private final val ItemTag = "item"
|
||||
|
||||
protected val _slots = mutable.Map.empty[Int, SlotWidget[_]]
|
||||
|
||||
def iterator: Iterator[(Int, SlotWidget[_])] = _slots.iterator
|
||||
|
||||
override def apply(index: Int): SlotWidget[_] = _slots(index)
|
||||
|
||||
override def isDefinedAt(index: Int): Boolean = _slots.isDefinedAt(index)
|
||||
|
||||
def addSlot(index: Int, slot: SlotWidget[_]): Unit = {
|
||||
_slots += index -> slot
|
||||
}
|
||||
|
||||
def removeSlot(index: Int): Option[SlotWidget[_]] = _slots.remove(index)
|
||||
|
||||
override def clear(): Unit = _slots.clear()
|
||||
|
||||
final override def addOne(elem: (Int, SlotWidget[_])): SlotGroup.this.type = {
|
||||
(addSlot _).tupled(elem)
|
||||
|
||||
this
|
||||
}
|
||||
|
||||
/**
|
||||
* Pushes an item into the first (in index order) empty slot that accepts it.
|
||||
*
|
||||
* @return the index of the slot the item was inserted into, or `None` if no slot was found
|
||||
*/
|
||||
def pushItem[E](item: Item[E]): Option[Int] = {
|
||||
val (index, slot) = _slots
|
||||
.iterator
|
||||
.filter(_._2.isEmpty)
|
||||
.filter(_._2.accepts(item))
|
||||
.maxByOption(_._1)
|
||||
.unzip
|
||||
slot.foreach(_.set(item))
|
||||
|
||||
index
|
||||
}
|
||||
|
||||
/**
|
||||
* Pushes items from an iterator consecutively as if by calling [[pushItem]] on each item
|
||||
*
|
||||
* @return the number of items inserted
|
||||
*/
|
||||
def pushItems(items: Iterator[Item[_]]): Int = {
|
||||
val queue = ArrayBuffer.empty[(Int, SlotWidget[_])]
|
||||
queue ++= _slots.iterator.filter(_._2.isEmpty)
|
||||
queue.sortInPlaceBy(_._1)
|
||||
|
||||
var insertedCount = 0
|
||||
|
||||
for (item <- items) {
|
||||
val entryIndex = queue.iterator.indexWhere(_._2.accepts(item))
|
||||
|
||||
if (entryIndex >= 0) {
|
||||
val (_, slot) = queue(entryIndex)
|
||||
slot.set(item)
|
||||
insertedCount += 1
|
||||
}
|
||||
}
|
||||
|
||||
insertedCount
|
||||
}
|
||||
|
||||
def load(nbt: NBTTagList): Unit = {
|
||||
setItems(loadItemsFromNbt(nbt))
|
||||
}
|
||||
|
||||
def load(): Unit = setItems(Map.empty)
|
||||
|
||||
def save(): NBTTagList = {
|
||||
val nbt = new NBTTagList
|
||||
val values = _slots.iterator
|
||||
.flatMap(slot => slot._2.item.map((slot._1, _)))
|
||||
.flatMap {
|
||||
case (index, item: Persistable) =>
|
||||
val slotNbt = new NBTTagCompound
|
||||
slotNbt.setInteger(SlotTag, index)
|
||||
slotNbt.setTag(ItemTag, NBTPersistence.save(item))
|
||||
|
||||
Some(slotNbt)
|
||||
|
||||
case (_, item) =>
|
||||
logger.atError().withLocation().log(s"Item $item is not Persistable and could not be saved.")
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
nbt.append(values)
|
||||
|
||||
nbt
|
||||
}
|
||||
|
||||
protected def loadItemsFromNbt(nbt: NBTTagList): Map[Int, Item[Entity]] =
|
||||
nbt.iterator[NBTTagCompound].flatMap(slotNbt => {
|
||||
val index = slotNbt.getInteger(SlotTag)
|
||||
|
||||
if (_slots.contains(index)) {
|
||||
val item = NBTPersistence
|
||||
.load(slotNbt.getCompoundTag(ItemTag), OcelotDesktop.workspace)
|
||||
.asInstanceOf[Item[Entity]]
|
||||
|
||||
Some((index, item))
|
||||
} else {
|
||||
logger.atError().withLocation().log(s"Slot $index is non-empty in NBT; however, no SlotWidget manages it")
|
||||
|
||||
None
|
||||
}
|
||||
}).toMap
|
||||
|
||||
protected def setItems(items: Map[Int, Item[Entity]]): Unit = {
|
||||
for ((index, slot) <- _slots) {
|
||||
slot.set(items.get(index))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,118 @@
|
||||
package ocelot.desktop.ui.widget.inventory
|
||||
|
||||
import ocelot.desktop.geometry.{Rect2D, Size2D}
|
||||
import ocelot.desktop.graphics.Graphics
|
||||
import ocelot.desktop.ui.event.handlers.ClickHandler
|
||||
import ocelot.desktop.ui.event.{ClickEvent, MouseEvent}
|
||||
import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry}
|
||||
import ocelot.desktop.ui.widget.{Widget, WorkspaceView, inventory}
|
||||
|
||||
import scala.annotation.unused
|
||||
import scala.reflect.{ClassTag, classTag}
|
||||
|
||||
abstract class SlotWidget[E] protected(val workspaceView: WorkspaceView)
|
||||
(implicit val elementTag: ClassTag[E])
|
||||
extends Widget
|
||||
with ClickHandler {
|
||||
|
||||
type Element = E
|
||||
|
||||
def transient: Boolean = _transient
|
||||
|
||||
// true iff the slot's item is being changed
|
||||
protected var _transient: Boolean = false
|
||||
|
||||
def onRemoved(@unused item: Item[Element]): Unit = {}
|
||||
|
||||
def onAdded(@unused item: Item[Element]): Unit = {}
|
||||
|
||||
def fillLmbMenu(menu: ContextMenu): Unit = {}
|
||||
|
||||
def fillRmbMenu(menu: ContextMenu): Unit = {
|
||||
item.foreach(_.fillRmbMenu(menu))
|
||||
|
||||
menu.addEntry(new ContextMenuEntry("Remove", () => item = None))
|
||||
}
|
||||
|
||||
def accepts(prototype: ItemPrototype): Boolean =
|
||||
prototype.elementTag.runtimeClass.isAssignableFrom(elementTag.runtimeClass)
|
||||
|
||||
final def accepts(item: inventory.Item[_]): Boolean = accepts(item.prototype)
|
||||
|
||||
def lmbMenuEnabled: Boolean = false
|
||||
|
||||
def rmbMenuEnabled: Boolean = _item.isDefined
|
||||
|
||||
final var _item: Option[Item[Element]] = None
|
||||
|
||||
final def item_=[I <: Item[Element]](v: Option[I]): Unit = {
|
||||
if (!v.map(_.prototype).forall(accepts)) {
|
||||
throw new IllegalArgumentException(
|
||||
s"tried to put an inadmissible item ${v.get} into a SlotWidget requiring ${classTag[Element].runtimeClass}")
|
||||
}
|
||||
|
||||
_transient = true
|
||||
_item.foreach(onRemoved)
|
||||
_item = v
|
||||
_item.foreach(onAdded)
|
||||
_transient = false
|
||||
}
|
||||
|
||||
final def item_=[I <: Item[Element]](v: I): Unit = item = Some(v)
|
||||
|
||||
final def item: Option[Item[Element]] = _item
|
||||
|
||||
final def set(v: Option[inventory.Item[_]]): Unit = v match {
|
||||
case Some(v: Item[Element@unchecked]) if classTag[Element].runtimeClass.isAssignableFrom(v.element.getClass) =>
|
||||
item = v
|
||||
|
||||
case Some(v) =>
|
||||
throw new IllegalArgumentException(
|
||||
s"tried to set $v to a SlotWidget requiring ${classTag[Element].runtimeClass}")
|
||||
|
||||
case None => item = None
|
||||
}
|
||||
|
||||
final def set(v: inventory.Item[_]): Unit = set(Some(v))
|
||||
|
||||
final def isEmpty: Boolean = _item.isEmpty
|
||||
|
||||
final def nonEmpty: Boolean = _item.nonEmpty
|
||||
|
||||
final override def minimumSize: Size2D = Size2D(36, 36)
|
||||
|
||||
final override def maximumSize: Size2D = minimumSize
|
||||
|
||||
final override def receiveMouseEvents: Boolean = true
|
||||
|
||||
eventHandlers += {
|
||||
case ClickEvent(MouseEvent.Button.Left, _) if lmbMenuEnabled =>
|
||||
val menu = new ContextMenu
|
||||
fillLmbMenu(menu)
|
||||
root.get.contextMenus.open(menu)
|
||||
|
||||
case ClickEvent(MouseEvent.Button.Right, _) if rmbMenuEnabled =>
|
||||
val menu = new ContextMenu
|
||||
fillRmbMenu(menu)
|
||||
root.get.contextMenus.open(menu)
|
||||
}
|
||||
|
||||
protected def iconBounds: Rect2D = bounds.inflate(-2)
|
||||
|
||||
protected def drawSlotBackground(g: Graphics): Unit =
|
||||
g.sprite("EmptySlot", bounds)
|
||||
|
||||
protected def drawEmptySlot(g: Graphics): Unit = {}
|
||||
|
||||
protected def drawOccupiedSlot(g: Graphics, item: Item[Element]): Unit =
|
||||
g.sprite(item.icon, iconBounds)
|
||||
|
||||
final override def draw(g: Graphics): Unit = {
|
||||
drawSlotBackground(g)
|
||||
|
||||
item match {
|
||||
case Some(item) => drawOccupiedSlot(g, item)
|
||||
case None => drawEmptySlot(g)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
package ocelot.desktop.ui.widget.inventory
|
||||
|
||||
import totoro.ocelot.brain.util.Tier
|
||||
|
||||
import scala.language.existentials
|
||||
|
||||
trait TieredSlotWidget[E] extends SlotWidget[E] {
|
||||
val slotTier: Option[Int]
|
||||
|
||||
protected def isTierAdmissible(itemTier: Int): Boolean = slotTier.forall(slotTier => PartialFunction.cond(itemTier) {
|
||||
case _ if itemTier <= slotTier => true
|
||||
case _ if itemTier == Tier.Four && slotTier == Tier.Three => true // creative items fit into T3 slots
|
||||
})
|
||||
}
|
||||
@ -0,0 +1,59 @@
|
||||
package ocelot.desktop.ui.widget.inventory.item
|
||||
|
||||
import ocelot.desktop.graphics.IconDef
|
||||
import ocelot.desktop.ui.widget.inventory.ItemPrototype
|
||||
import ocelot.desktop.ui.widget.inventory.item.traits.{EnvironmentItemPrototype, PersistableItemPrototype, SlotTieredItemPrototype}
|
||||
import totoro.ocelot.brain.entity
|
||||
import totoro.ocelot.brain.entity.traits.{GenericCPU, Tiered}
|
||||
|
||||
import scala.reflect.ClassTag
|
||||
|
||||
sealed abstract class CPUItemPrototype[E <: GenericCPU](implicit override val elementTag: ClassTag[E])
|
||||
extends ItemPrototype.WithInstance
|
||||
with SlotTieredItemPrototype
|
||||
with EnvironmentItemPrototype
|
||||
with PersistableItemPrototype
|
||||
with Tiered {
|
||||
|
||||
override type Element = E
|
||||
|
||||
override def slotTier: Int = tier
|
||||
|
||||
class Instance(element: Element)
|
||||
extends super.Instance(element)
|
||||
with super[EnvironmentItemPrototype].ExtendedInstance
|
||||
with super[PersistableItemPrototype].ExtendedInstance
|
||||
|
||||
// TODO: override fillRmbMenu (set arch)
|
||||
}
|
||||
|
||||
object CPUItemPrototype {
|
||||
class CPU(override var tier: Int) extends CPUItemPrototype[entity.CPU] {
|
||||
override type Item = Instance
|
||||
|
||||
override def name: String = s"CPU ($tierString)"
|
||||
|
||||
override def icon: IconDef = new IconDef(s"items/CPU$tier")
|
||||
|
||||
override def build: Item = new Instance(new entity.CPU(tier))
|
||||
}
|
||||
|
||||
class APU(override var tier: Int) extends CPUItemPrototype[entity.APU] {
|
||||
override type Item = Instance
|
||||
|
||||
override def name: String = s"APU ($tierString)"
|
||||
|
||||
override def icon: IconDef = new IconDef(s"items/APU$tier", animation = APU.Animation)
|
||||
|
||||
// an APU is a tier higher than its base CPU
|
||||
override def slotTier: Int = tier + 1
|
||||
|
||||
override def build: Item = new Instance(new entity.APU(tier))
|
||||
}
|
||||
|
||||
object APU {
|
||||
private val Animation = Some(Array(
|
||||
(0, 3f), (1, 3f), (2, 3f), (3, 3f), (4, 3f), (5, 3f),
|
||||
(4, 3f), (3, 3f), (2, 3f), (1, 3f), (0, 3f)))
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,56 @@
|
||||
package ocelot.desktop.ui.widget.inventory.item
|
||||
|
||||
import ocelot.desktop.graphics.IconDef
|
||||
import ocelot.desktop.ui.widget.inventory.ItemPrototype
|
||||
import ocelot.desktop.ui.widget.inventory.item.traits.{CardItemPrototype, EnvironmentItemPrototype, PersistableItemPrototype, SlotTieredItemPrototype}
|
||||
import totoro.ocelot.brain.entity.DataCard
|
||||
import totoro.ocelot.brain.util.Tier
|
||||
|
||||
import scala.reflect.ClassTag
|
||||
|
||||
sealed abstract class DataCardItemPrototype[E <: DataCard](implicit override val elementTag: ClassTag[E])
|
||||
extends ItemPrototype.WithInstance
|
||||
with SlotTieredItemPrototype
|
||||
with EnvironmentItemPrototype
|
||||
with PersistableItemPrototype
|
||||
with CardItemPrototype {
|
||||
|
||||
override type Element = E
|
||||
|
||||
override def icon: IconDef = new IconDef(s"items/DataCard$slotTier", animation = DataCardItemPrototype.Animation)
|
||||
|
||||
override def name: String = s"Data Card ($tierString)"
|
||||
|
||||
class Instance(element: Element)
|
||||
extends super.Instance(element)
|
||||
with super[EnvironmentItemPrototype].ExtendedInstance
|
||||
with super[PersistableItemPrototype].ExtendedInstance
|
||||
}
|
||||
|
||||
object DataCardItemPrototype {
|
||||
private final val Animation = Some(Array((0, 4f), (1, 4f), (2, 4f), (3, 4f), (4, 4f), (5, 4f), (6, 4f), (7, 4f)))
|
||||
|
||||
class Tier1 extends DataCardItemPrototype[DataCard.Tier1] {
|
||||
override type Item = Instance
|
||||
|
||||
override val slotTier: Int = Tier.One
|
||||
|
||||
override def build: Item = new Instance(new DataCard.Tier1)
|
||||
}
|
||||
|
||||
class Tier2 extends DataCardItemPrototype[DataCard.Tier2] {
|
||||
override type Item = Instance
|
||||
|
||||
override val slotTier: Int = Tier.Two
|
||||
|
||||
override def build: Item = new Instance(new DataCard.Tier2)
|
||||
}
|
||||
|
||||
class Tier3 extends DataCardItemPrototype[DataCard.Tier3] {
|
||||
override type Item = Instance
|
||||
|
||||
override val slotTier: Int = Tier.Three
|
||||
|
||||
override def build: Item = new Instance(new DataCard.Tier3)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,70 @@
|
||||
package ocelot.desktop.ui.widget.inventory.item
|
||||
|
||||
import ocelot.desktop.graphics.IconDef
|
||||
import ocelot.desktop.ui.widget.inventory.ItemPrototype
|
||||
import ocelot.desktop.ui.widget.inventory.item.EEPROMItemPrototype.LootFactories
|
||||
import ocelot.desktop.ui.widget.inventory.item.traits.{EnvironmentItemPrototype, PersistableItemPrototype}
|
||||
import totoro.ocelot.brain.entity.EEPROM
|
||||
import totoro.ocelot.brain.loot.Loot
|
||||
import totoro.ocelot.brain.nbt.NBTTagCompound
|
||||
import totoro.ocelot.brain.workspace.Workspace
|
||||
|
||||
import scala.reflect.{ClassTag, classTag}
|
||||
|
||||
class EEPROMItemPrototype(private var lootFactoryKey: Option[String])
|
||||
extends ItemPrototype.WithInstance
|
||||
with EnvironmentItemPrototype
|
||||
with PersistableItemPrototype {
|
||||
|
||||
require(lootFactoryKey.forall(EEPROMItemPrototype.LootFactories.contains),
|
||||
s"unrecognized loot EEPROM key ${lootFactoryKey.get}")
|
||||
|
||||
def this() {
|
||||
this(None)
|
||||
}
|
||||
|
||||
override type Element = EEPROM
|
||||
override type Item = Instance
|
||||
|
||||
override val elementTag: ClassTag[EEPROM] = classTag[EEPROM]
|
||||
|
||||
private def lootFactory: Option[Loot.EEPROMFactory] = lootFactoryKey.map(EEPROMItemPrototype.LootFactories)
|
||||
|
||||
override def icon: IconDef = new IconDef("items/EEPROM")
|
||||
|
||||
override def name: String = lootFactory.map(_.label).getOrElse("EEPROM")
|
||||
|
||||
override def build: Item =
|
||||
new Instance(lootFactory.map(_.create()).getOrElse(new EEPROM))
|
||||
|
||||
override def load(nbt: NBTTagCompound, workspace: Workspace): Unit = {
|
||||
super.load(nbt, workspace)
|
||||
|
||||
lootFactoryKey = Option.when(nbt.hasKey(EEPROMItemPrototype.LootTag)) {
|
||||
Some(nbt.getString(EEPROMItemPrototype.LootTag)).filter(LootFactories.contains)
|
||||
}.flatten
|
||||
}
|
||||
|
||||
override def save(nbt: NBTTagCompound): Unit = {
|
||||
super.save(nbt)
|
||||
|
||||
lootFactoryKey.foreach(nbt.setString(EEPROMItemPrototype.LootTag, _))
|
||||
}
|
||||
|
||||
class Instance(element: Element)
|
||||
extends super.Instance(element)
|
||||
with super[EnvironmentItemPrototype].ExtendedInstance
|
||||
with super[PersistableItemPrototype].ExtendedInstance
|
||||
|
||||
// TODO: override name in Instance once we add this method to Item (for tooltips)
|
||||
}
|
||||
|
||||
object EEPROMItemPrototype {
|
||||
// the key is used for persistence
|
||||
final val LootFactories = Map(
|
||||
"lua-bios" -> Loot.LuaBiosEEPROM,
|
||||
"advloader" -> Loot.AdvLoaderEEPROM,
|
||||
)
|
||||
|
||||
private final val LootTag = "loot"
|
||||
}
|
||||
@ -0,0 +1,112 @@
|
||||
package ocelot.desktop.ui.widget.inventory.item
|
||||
|
||||
import ocelot.desktop.graphics.IconDef
|
||||
import ocelot.desktop.ui.widget.inventory.item.FloppyItemPrototype.Managed.LootFactories
|
||||
import ocelot.desktop.ui.widget.inventory.item.traits.{EnvironmentItemPrototype, PersistableItemPrototype}
|
||||
import ocelot.desktop.ui.widget.inventory.{Item, ItemPrototype}
|
||||
import totoro.ocelot.brain.entity.traits.Floppy
|
||||
import totoro.ocelot.brain.entity.{FloppyManaged, FloppyUnmanaged}
|
||||
import totoro.ocelot.brain.loot.Loot
|
||||
import totoro.ocelot.brain.nbt.NBTTagCompound
|
||||
import totoro.ocelot.brain.util.DyeColor
|
||||
import totoro.ocelot.brain.workspace.Workspace
|
||||
|
||||
import scala.reflect.ClassTag
|
||||
|
||||
sealed abstract class FloppyItemPrototype[E <: Floppy](var color: DyeColor)
|
||||
(implicit override val elementTag: ClassTag[E])
|
||||
extends ItemPrototype.WithInstance
|
||||
with EnvironmentItemPrototype
|
||||
with PersistableItemPrototype {
|
||||
|
||||
override type Element = E
|
||||
|
||||
override def icon: IconDef = new IconDef(s"items/FloppyDisk_${color.name}")
|
||||
|
||||
override def load(nbt: NBTTagCompound, workspace: Workspace): Unit = {
|
||||
super.load(nbt, workspace)
|
||||
color = DyeColor.byCode(nbt.getInteger(FloppyItemPrototype.ColorTag))
|
||||
}
|
||||
|
||||
override def save(nbt: NBTTagCompound): Unit = {
|
||||
super.save(nbt)
|
||||
nbt.setInteger(FloppyItemPrototype.ColorTag, color.code)
|
||||
}
|
||||
|
||||
class Instance(element: Element)
|
||||
extends super.Instance(element)
|
||||
with super[EnvironmentItemPrototype].ExtendedInstance
|
||||
with super[PersistableItemPrototype].ExtendedInstance
|
||||
|
||||
// TODO: override name in Instance once we add this method to Item (for tooltips)
|
||||
}
|
||||
|
||||
object FloppyItemPrototype {
|
||||
class Managed(initialColor: DyeColor,
|
||||
private var lootFactoryKey: Option[String]) extends FloppyItemPrototype[FloppyManaged](initialColor) {
|
||||
def this() {
|
||||
this(DyeColor.GRAY, None)
|
||||
}
|
||||
|
||||
def this(lootFactoryKey: String) {
|
||||
this(DyeColor.GRAY, Some(lootFactoryKey))
|
||||
|
||||
color = Managed.LootFactories(lootFactoryKey).color
|
||||
}
|
||||
|
||||
require(lootFactoryKey.forall(Managed.LootFactories.contains),
|
||||
s"unrecognized loot floppy key ${lootFactoryKey.get}")
|
||||
|
||||
override type Item = Instance
|
||||
|
||||
private def lootFactory: Option[Loot.FloppyFactory] = lootFactoryKey.map(Managed.LootFactories)
|
||||
|
||||
override def name: String = lootFactory.map(_.name).getOrElse("Floppy Disk")
|
||||
|
||||
override def build: Item =
|
||||
new Instance(lootFactory.map(_.create())
|
||||
.getOrElse(new FloppyManaged(name, color)))
|
||||
|
||||
override def load(nbt: NBTTagCompound, workspace: Workspace): Unit = {
|
||||
super.load(nbt, workspace)
|
||||
|
||||
lootFactoryKey = Option.when(nbt.hasKey(Managed.LootTag)) {
|
||||
Some(nbt.getString(Managed.LootTag)).filter(LootFactories.contains)
|
||||
}.flatten
|
||||
}
|
||||
|
||||
override def save(nbt: NBTTagCompound): Unit = {
|
||||
super.save(nbt)
|
||||
|
||||
lootFactoryKey.foreach(nbt.setString(Managed.LootTag, _))
|
||||
}
|
||||
}
|
||||
|
||||
object Managed {
|
||||
final val LootFactories = Map(
|
||||
"network" -> Loot.NetworkFloppy,
|
||||
"plan9k" -> Loot.Plan9kFloppy,
|
||||
"irc" -> Loot.IrcFloppy,
|
||||
"openloader" -> Loot.OpenLoaderFloppy,
|
||||
"openos" -> Loot.OpenOsFloppy,
|
||||
"oppm" -> Loot.OPPMFloppy,
|
||||
"data" -> Loot.DataFloppy,
|
||||
)
|
||||
|
||||
private final val LootTag = "loot"
|
||||
}
|
||||
|
||||
class Unmanaged(initialColor: DyeColor) extends FloppyItemPrototype[FloppyUnmanaged](initialColor) {
|
||||
def this() {
|
||||
this(DyeColor.GRAY)
|
||||
}
|
||||
|
||||
override type Item = Instance
|
||||
|
||||
override def name: String = "Floppy Disk (unmanaged)"
|
||||
|
||||
override def build: Item = new Instance(new FloppyUnmanaged(name, color))
|
||||
}
|
||||
|
||||
private final val ColorTag = "color"
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
package ocelot.desktop.ui.widget.inventory.item
|
||||
|
||||
import ocelot.desktop.graphics.IconDef
|
||||
import ocelot.desktop.ui.widget.inventory.item.traits.{CardItemPrototype, EnvironmentItemPrototype, PersistableItemPrototype}
|
||||
import ocelot.desktop.ui.widget.inventory.{Item, ItemPrototype}
|
||||
import totoro.ocelot.brain.entity.GraphicsCard
|
||||
import totoro.ocelot.brain.entity.traits.Tiered
|
||||
|
||||
import scala.reflect.{ClassTag, classTag}
|
||||
|
||||
class GraphicsCardItemPrototype(override var tier: Int)
|
||||
extends ItemPrototype.WithInstance
|
||||
with CardItemPrototype
|
||||
with EnvironmentItemPrototype
|
||||
with PersistableItemPrototype
|
||||
with Tiered {
|
||||
|
||||
require((0 to 2).contains(tier), s"Unsupported GPU tier: $tier")
|
||||
|
||||
override type Element = GraphicsCard
|
||||
override type Item = Instance
|
||||
|
||||
override val elementTag: ClassTag[GraphicsCard] = classTag[GraphicsCard]
|
||||
|
||||
override def slotTier: Int = tier
|
||||
|
||||
override def name: String = s"Graphics Card ($tierString)"
|
||||
|
||||
override def icon: IconDef = new IconDef(s"items/GraphicsCard$tier")
|
||||
|
||||
override def build: Item = new Instance(new GraphicsCard(tier))
|
||||
|
||||
class Instance(element: Element)
|
||||
extends super.Instance(element)
|
||||
with super[EnvironmentItemPrototype].ExtendedInstance
|
||||
with super[PersistableItemPrototype].ExtendedInstance
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
package ocelot.desktop.ui.widget.inventory.item
|
||||
|
||||
import ocelot.desktop.graphics.IconDef
|
||||
import ocelot.desktop.ui.widget.inventory.{Item, ItemPrototype}
|
||||
import ocelot.desktop.ui.widget.inventory.item.traits.{EnvironmentItemPrototype, PersistableItemPrototype, SlotTieredItemPrototype}
|
||||
import totoro.ocelot.brain.entity.{HDDManaged, HDDUnmanaged}
|
||||
import totoro.ocelot.brain.entity.traits.{Disk, Tiered}
|
||||
|
||||
import scala.reflect.ClassTag
|
||||
|
||||
sealed abstract class HDDItemPrototype[E <: Disk](implicit override val elementTag: ClassTag[E])
|
||||
extends ItemPrototype.WithInstance
|
||||
with SlotTieredItemPrototype
|
||||
with EnvironmentItemPrototype
|
||||
with PersistableItemPrototype
|
||||
with Tiered {
|
||||
|
||||
override type Element = E
|
||||
|
||||
override def icon: IconDef = new IconDef(s"items/HardDiskDrive$slotTier")
|
||||
|
||||
override def slotTier: Int = tier
|
||||
|
||||
class Instance(element: Element)
|
||||
extends super.Instance(element)
|
||||
with super[EnvironmentItemPrototype].ExtendedInstance
|
||||
with super[PersistableItemPrototype].ExtendedInstance
|
||||
}
|
||||
|
||||
object HDDItemPrototype {
|
||||
class Managed(override var tier: Int) extends HDDItemPrototype[HDDManaged] {
|
||||
override type Item = Instance
|
||||
|
||||
override def name: String = s"HDD ($tierString)"
|
||||
|
||||
override def build: Item = new Instance(new HDDManaged(tier))
|
||||
}
|
||||
|
||||
class Unmanaged(override var tier: Int) extends HDDItemPrototype[HDDUnmanaged] {
|
||||
override type Item = Instance
|
||||
|
||||
override def name: String = s"HDD ($tierString, unmanaged)"
|
||||
|
||||
// TODO: figure out what should be fed to name
|
||||
override def build: Item = new Instance(new HDDUnmanaged(tier, name = null))
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
package ocelot.desktop.ui.widget.inventory.item
|
||||
|
||||
import ocelot.desktop.graphics.IconDef
|
||||
import ocelot.desktop.ui.widget.inventory.{Item, ItemPrototype}
|
||||
import ocelot.desktop.ui.widget.inventory.item.traits.{CardItemPrototype, EnvironmentItemPrototype, PersistableItemPrototype}
|
||||
import totoro.ocelot.brain.entity.InternetCard
|
||||
import totoro.ocelot.brain.util.Tier
|
||||
|
||||
import scala.reflect.{ClassTag, classTag}
|
||||
|
||||
class InternetCardItemPrototype
|
||||
extends ItemPrototype
|
||||
with CardItemPrototype
|
||||
with EnvironmentItemPrototype
|
||||
with PersistableItemPrototype {
|
||||
|
||||
override type Element = InternetCard
|
||||
override type Item = Instance
|
||||
|
||||
override val elementTag: ClassTag[InternetCard] = classTag[InternetCard]
|
||||
|
||||
override val slotTier: Int = Tier.Two
|
||||
|
||||
override def icon: IconDef = new IconDef("items/InternetCard", animation = InternetCardItemPrototype.Animation)
|
||||
|
||||
override val name: String = "Internet Card"
|
||||
|
||||
override def build: Item = new Instance(new InternetCard)
|
||||
|
||||
class Instance(element: Element)
|
||||
extends super.Instance(element)
|
||||
with super[EnvironmentItemPrototype].ExtendedInstance
|
||||
with super[PersistableItemPrototype].ExtendedInstance
|
||||
}
|
||||
|
||||
object InternetCardItemPrototype {
|
||||
private final val Animation =
|
||||
Some(Array((0, 2f), (1, 7f), (0, 5f), (1, 4f), (0, 7f), (1, 2f), (0, 8f), (1, 9f), (0, 6f), (1, 4f)))
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
package ocelot.desktop.ui.widget.inventory.item
|
||||
|
||||
import ocelot.desktop.graphics.IconDef
|
||||
import ocelot.desktop.ui.widget.inventory.{Item, ItemPrototype}
|
||||
import ocelot.desktop.ui.widget.inventory.item.traits.{CardItemPrototype, EnvironmentItemPrototype, PersistableItemPrototype}
|
||||
import totoro.ocelot.brain.entity.LinkedCard
|
||||
import totoro.ocelot.brain.util.Tier
|
||||
|
||||
import scala.reflect.{ClassTag, classTag}
|
||||
|
||||
class LinkedCardItemPrototype
|
||||
extends ItemPrototype
|
||||
with CardItemPrototype
|
||||
with EnvironmentItemPrototype
|
||||
with PersistableItemPrototype {
|
||||
|
||||
override type Element = LinkedCard
|
||||
override type Item = Instance
|
||||
|
||||
override val elementTag: ClassTag[LinkedCard] = classTag[LinkedCard]
|
||||
|
||||
override val slotTier: Int = Tier.Three
|
||||
|
||||
override def icon: IconDef = new IconDef("items/LinkedCard", animation = LinkedCardItemPrototype.Animation)
|
||||
|
||||
override val name: String = "Linked Card"
|
||||
|
||||
override def build: Item = new Instance(new LinkedCard)
|
||||
|
||||
class Instance(element: Element)
|
||||
extends super.Instance(element)
|
||||
with super[EnvironmentItemPrototype].ExtendedInstance
|
||||
with super[PersistableItemPrototype].ExtendedInstance
|
||||
}
|
||||
|
||||
object LinkedCardItemPrototype {
|
||||
private final val Animation = Some(Array((0, 3f), (1, 3f), (2, 3f), (3, 3f), (4, 3f), (5, 3f)))
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
package ocelot.desktop.ui.widget.inventory.item
|
||||
|
||||
import ocelot.desktop.graphics.IconDef
|
||||
import ocelot.desktop.ui.widget.inventory.{Item, ItemPrototype}
|
||||
import ocelot.desktop.ui.widget.inventory.item.traits.{EnvironmentItemPrototype, PersistableItemPrototype, SlotTieredItemPrototype}
|
||||
import totoro.ocelot.brain.entity.Memory
|
||||
import totoro.ocelot.brain.entity.traits.Tiered
|
||||
|
||||
import scala.reflect.{ClassTag, classTag}
|
||||
|
||||
class MemoryItemPrototype(var tier: Int)
|
||||
extends ItemPrototype
|
||||
with SlotTieredItemPrototype
|
||||
with EnvironmentItemPrototype
|
||||
with PersistableItemPrototype
|
||||
with Tiered {
|
||||
|
||||
require((1 to 6).contains(tier), s"Unsupported memory tier: $tier")
|
||||
|
||||
override type Element = Memory
|
||||
override type Item = Instance
|
||||
|
||||
override val elementTag: ClassTag[Memory] = classTag[Memory]
|
||||
|
||||
override def slotTier: Int = tier / 2
|
||||
|
||||
override def icon: IconDef = new IconDef(s"items/Memory$tier")
|
||||
|
||||
override protected def tierString: String = MemoryItemPrototype.Tiers(tier)
|
||||
|
||||
override def name: String = s"RAM ($tierString)"
|
||||
|
||||
override def build: Item = new Instance(new Memory(tier))
|
||||
|
||||
class Instance(element: Element)
|
||||
extends super.Instance(element)
|
||||
with super[EnvironmentItemPrototype].ExtendedInstance
|
||||
with super[PersistableItemPrototype].ExtendedInstance
|
||||
}
|
||||
|
||||
object MemoryItemPrototype {
|
||||
private final val Tiers = Array(
|
||||
"Tier 1", "Tier 1.5",
|
||||
"Tier 2", "Tier 2.5",
|
||||
"Tier 3", "Tier 3.5",
|
||||
)
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
package ocelot.desktop.ui.widget.inventory.item
|
||||
|
||||
import ocelot.desktop.graphics.IconDef
|
||||
import ocelot.desktop.ui.widget.inventory.{Item, ItemPrototype}
|
||||
import ocelot.desktop.ui.widget.inventory.item.traits.{CardItemPrototype, EnvironmentItemPrototype, PersistableItemPrototype}
|
||||
import totoro.ocelot.brain.entity.NetworkCard
|
||||
import totoro.ocelot.brain.util.Tier
|
||||
|
||||
import scala.reflect.{ClassTag, classTag}
|
||||
|
||||
class NetworkCardItemPrototype
|
||||
extends ItemPrototype
|
||||
with CardItemPrototype
|
||||
with EnvironmentItemPrototype
|
||||
with PersistableItemPrototype {
|
||||
|
||||
override type Element = NetworkCard
|
||||
override type Item = Instance
|
||||
|
||||
override val elementTag: ClassTag[NetworkCard] = classTag[NetworkCard]
|
||||
|
||||
override val slotTier: Int = Tier.One
|
||||
|
||||
override def icon: IconDef = new IconDef("items/NetworkCard")
|
||||
|
||||
override val name: String = "Network Card"
|
||||
|
||||
override def build: Item = new Instance(new NetworkCard)
|
||||
|
||||
class Instance(element: Element)
|
||||
extends super.Instance(element)
|
||||
with super[EnvironmentItemPrototype].ExtendedInstance
|
||||
with super[PersistableItemPrototype].ExtendedInstance
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
package ocelot.desktop.ui.widget.inventory.item
|
||||
|
||||
import ocelot.desktop.graphics.IconDef
|
||||
import ocelot.desktop.ui.widget.inventory.{Item, ItemPrototype}
|
||||
import ocelot.desktop.ui.widget.inventory.item.traits.{CardItemPrototype, EnvironmentItemPrototype, PersistableItemPrototype}
|
||||
import totoro.ocelot.brain.entity.Redstone
|
||||
import totoro.ocelot.brain.entity.traits.Environment
|
||||
import totoro.ocelot.brain.util.{Persistable, Tier}
|
||||
|
||||
import scala.reflect.ClassTag
|
||||
|
||||
sealed abstract class RedstoneCardItemPrototype[E <: Environment with Persistable](implicit override val elementTag: ClassTag[E])
|
||||
extends ItemPrototype
|
||||
with CardItemPrototype
|
||||
with EnvironmentItemPrototype
|
||||
with PersistableItemPrototype {
|
||||
|
||||
override type Element = E
|
||||
|
||||
override def icon: IconDef = new IconDef(s"items/RedstoneCard$slotTier")
|
||||
|
||||
override def name: String = s"Redstone Card ($tierString)"
|
||||
|
||||
class Instance(element: Element)
|
||||
extends super.Instance(element)
|
||||
with super[EnvironmentItemPrototype].ExtendedInstance
|
||||
with super[PersistableItemPrototype].ExtendedInstance
|
||||
|
||||
// TODO: override fillRmbMenu in Instance
|
||||
}
|
||||
|
||||
object RedstoneCardItemPrototype {
|
||||
class Tier1 extends RedstoneCardItemPrototype[Redstone.Tier1] {
|
||||
override type Item = Instance
|
||||
|
||||
override val slotTier: Int = Tier.One
|
||||
|
||||
override def build: Item = new Instance(new Redstone.Tier1)
|
||||
}
|
||||
|
||||
class Tier2 extends RedstoneCardItemPrototype[Redstone.Tier2] {
|
||||
override type Item = Instance
|
||||
|
||||
override val slotTier: Int = Tier.Two
|
||||
|
||||
override def build: Item = new Instance(new Redstone.Tier2)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
package ocelot.desktop.ui.widget.inventory.item
|
||||
|
||||
import ocelot.desktop.graphics.IconDef
|
||||
import ocelot.desktop.ui.widget.inventory.ItemPrototype
|
||||
import ocelot.desktop.ui.widget.inventory.item.traits.{CardItemPrototype, EnvironmentItemPrototype, PersistableItemPrototype}
|
||||
import totoro.ocelot.brain.entity.WirelessNetworkCard
|
||||
import totoro.ocelot.brain.util.Tier
|
||||
|
||||
import scala.reflect.ClassTag
|
||||
|
||||
sealed abstract class WirelessNetworkCardItemPrototype[E <: WirelessNetworkCard](implicit override val elementTag: ClassTag[E])
|
||||
extends ItemPrototype
|
||||
with CardItemPrototype
|
||||
with EnvironmentItemPrototype
|
||||
with PersistableItemPrototype {
|
||||
|
||||
override type Element = E
|
||||
|
||||
override def icon: IconDef = new IconDef(s"items/WirelessNetworkCard$slotTier")
|
||||
|
||||
override def name: String = s"Wireless Network Card ($tierString)"
|
||||
|
||||
class Instance(element: Element)
|
||||
extends super.Instance(element)
|
||||
with super[EnvironmentItemPrototype].ExtendedInstance
|
||||
with super[PersistableItemPrototype].ExtendedInstance
|
||||
}
|
||||
|
||||
object WirelessNetworkCardItemPrototype {
|
||||
class Tier1 extends WirelessNetworkCardItemPrototype[WirelessNetworkCard.Tier1] {
|
||||
override type Item = Instance
|
||||
|
||||
override val slotTier: Int = Tier.One
|
||||
|
||||
override def build: Item = new Instance(new WirelessNetworkCard.Tier1)
|
||||
}
|
||||
|
||||
class Tier2 extends WirelessNetworkCardItemPrototype[WirelessNetworkCard.Tier2] {
|
||||
override type Item = Instance
|
||||
|
||||
override val slotTier: Int = Tier.Two
|
||||
|
||||
override def build: Item = new Instance(new WirelessNetworkCard.Tier2)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
package ocelot.desktop.ui.widget.inventory.item.traits
|
||||
|
||||
import ocelot.desktop.ui.widget.inventory.ItemPrototype
|
||||
|
||||
trait CardItemPrototype extends SlotTieredItemPrototype {
|
||||
this: ItemPrototype =>
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
package ocelot.desktop.ui.widget.inventory.item.traits
|
||||
|
||||
import ocelot.desktop.ui.UiHandler
|
||||
import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry}
|
||||
import ocelot.desktop.ui.widget.inventory.ItemPrototype
|
||||
import totoro.ocelot.brain.entity.traits.Environment
|
||||
|
||||
trait EnvironmentItemPrototype extends ItemPrototype.WithInstance {
|
||||
override type Element <: Environment
|
||||
override type Item <: ExtendedInstance
|
||||
|
||||
trait ExtendedInstance extends super.Instance {
|
||||
override def fillRmbMenu(menu: ContextMenu): Unit = {
|
||||
super.fillRmbMenu(menu)
|
||||
|
||||
menu.addEntry(new ContextMenuEntry("Copy address", () => {
|
||||
UiHandler.clipboard = element.node.address
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
package ocelot.desktop.ui.widget.inventory.item.traits
|
||||
|
||||
import ocelot.desktop.ui.widget.inventory.ItemPrototype
|
||||
import totoro.ocelot.brain.nbt.NBTTagCompound
|
||||
import totoro.ocelot.brain.nbt.persistence.NBTPersistence
|
||||
import totoro.ocelot.brain.util.Persistable
|
||||
import totoro.ocelot.brain.workspace.Workspace
|
||||
|
||||
trait PersistableItemPrototype extends ItemPrototype.WithInstance with Persistable {
|
||||
proto =>
|
||||
|
||||
override type Element <: Persistable
|
||||
|
||||
trait ExtendedInstance extends super.Instance with Persistable {
|
||||
override def load(nbt: NBTTagCompound, workspace: Workspace): Unit = {
|
||||
super.load(nbt, workspace)
|
||||
|
||||
val elementNbt = nbt.getCompoundTag(PersistableItemPrototype.ElementTag)
|
||||
element = NBTPersistence.load(elementNbt, workspace).asInstanceOf[Element]
|
||||
}
|
||||
|
||||
override def save(nbt: NBTTagCompound): Unit = {
|
||||
super.save(nbt)
|
||||
|
||||
nbt.setTag(PersistableItemPrototype.ElementTag, NBTPersistence.save(element))
|
||||
nbt.setTag(PersistableItemPrototype.PrototypeTag, NBTPersistence.save(proto))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object PersistableItemPrototype {
|
||||
private final val ElementTag = "element"
|
||||
private final val PrototypeTag = "prototype"
|
||||
|
||||
class ItemConstructor extends NBTPersistence.InstanceConstructor {
|
||||
override def construct(nbt: NBTTagCompound, className: String, workspace: Workspace): Persistable = {
|
||||
val data = nbt.getCompoundTag(NBTPersistence.DataTag)
|
||||
val prototypeNbt = data.getCompoundTag(PrototypeTag)
|
||||
val prototype = NBTPersistence.load(prototypeNbt, workspace).asInstanceOf[ItemPrototype]
|
||||
val clazz = Class.forName(className).asSubclass(classOf[PersistableItemPrototype#ExtendedInstance])
|
||||
val item = clazz.cast(prototype.build)
|
||||
item.load(data, workspace)
|
||||
|
||||
item
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
package ocelot.desktop.ui.widget.inventory.item.traits
|
||||
|
||||
import ocelot.desktop.ui.widget.inventory.ItemPrototype
|
||||
import totoro.ocelot.brain.util.Tier
|
||||
|
||||
trait SlotTieredItemPrototype {
|
||||
this: ItemPrototype =>
|
||||
|
||||
def slotTier: Int
|
||||
|
||||
protected def tierString: String =
|
||||
if (slotTier == Tier.Four) "Creative"
|
||||
else s"Tier $slotTier"
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
package ocelot.desktop.ui.widget.inventory.slot
|
||||
|
||||
import ocelot.desktop.ui.widget.WorkspaceView
|
||||
import ocelot.desktop.ui.widget.inventory.{ItemPrototype, SlotWidget}
|
||||
|
||||
class AnySlot(workspaceView: WorkspaceView) extends SlotWidget[AnyRef](workspaceView) {
|
||||
override def accepts(prototype: ItemPrototype): Boolean = true
|
||||
}
|
||||
@ -0,0 +1,49 @@
|
||||
package ocelot.desktop.ui.widget.inventory.slot
|
||||
|
||||
import ocelot.desktop.graphics.IconDef
|
||||
import ocelot.desktop.node.nodes.ComputerNode
|
||||
import ocelot.desktop.ui.widget.WorkspaceView
|
||||
import ocelot.desktop.ui.widget.inventory.item.CPUItemPrototype
|
||||
import ocelot.desktop.ui.widget.inventory.{DecoratedSlotWidget, InventorySlotWidget, ItemPrototype, LmbItemSelectionSlot}
|
||||
import totoro.ocelot.brain.entity.traits.{Entity, GenericCPU, Inventory}
|
||||
|
||||
class CPUSlot(owner: Inventory#Slot, node: ComputerNode, val tier: Int, workspaceView: WorkspaceView)
|
||||
extends InventorySlotWidget[GenericCPU with Entity](owner, workspaceView)
|
||||
with DecoratedSlotWidget[GenericCPU with Entity]
|
||||
with LmbItemSelectionSlot[GenericCPU with Entity] {
|
||||
|
||||
override val slotTier: Option[Int] = Some(tier)
|
||||
|
||||
override def bgIcon: Option[IconDef] = Some(new IconDef("icons/CPU"))
|
||||
|
||||
override def accepts(prototype: ItemPrototype): Boolean = PartialFunction.cond(prototype) {
|
||||
case cpu: CPUItemPrototype[_] if isTierAdmissible(cpu.slotTier) => true
|
||||
}
|
||||
|
||||
// TODO: move to CPUItemPrototype
|
||||
// override def fillRmbMenu(menu: ContextMenu): Unit = {
|
||||
// if (item.isEmpty) return
|
||||
//
|
||||
// val cpu = item.get
|
||||
//
|
||||
// menu.addEntry(new ContextMenuSubmenu("Set architecture") {
|
||||
// for (arch <- cpu.allArchitectures) {
|
||||
// val name = MachineAPI.getArchitectureName(arch) +
|
||||
// (if (arch == cpu.architecture) " (current)" else "")
|
||||
//
|
||||
// addEntry(new ContextMenuEntry(name, () => {
|
||||
// val machine = node.computer.machine
|
||||
// if (machine.isRunning) {
|
||||
// machine.stop()
|
||||
// cpu.setArchitecture(arch)
|
||||
// machine.start()
|
||||
// } else {
|
||||
// cpu.setArchitecture(arch)
|
||||
// }
|
||||
// }))
|
||||
// }
|
||||
// })
|
||||
//
|
||||
// super.fillRmbMenu(menu)
|
||||
// }
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
package ocelot.desktop.ui.widget.slot
|
||||
package ocelot.desktop.ui.widget.inventory.slot
|
||||
|
||||
import ocelot.desktop.graphics.IconDef
|
||||
import totoro.ocelot.brain.entity.traits.{Entity, Tiered}
|
||||
@ -0,0 +1,36 @@
|
||||
package ocelot.desktop.ui.widget.inventory.slot
|
||||
|
||||
import ocelot.desktop.graphics.IconDef
|
||||
import ocelot.desktop.ui.widget.inventory.item.traits.CardItemPrototype
|
||||
import ocelot.desktop.ui.widget.inventory.{DecoratedSlotWidget, InventorySlotWidget, ItemPrototype, LmbItemSelectionSlot}
|
||||
import ocelot.desktop.ui.widget.{WorkspaceView, inventory}
|
||||
import totoro.ocelot.brain.entity.traits.{Entity, Inventory}
|
||||
import totoro.ocelot.brain.util.Persistable
|
||||
|
||||
class CardSlot(slave: Inventory#Slot, val tier: Int, workspaceView: WorkspaceView)
|
||||
extends InventorySlotWidget[Entity with Persistable](slave, workspaceView)
|
||||
with DecoratedSlotWidget[Entity with Persistable]
|
||||
with LmbItemSelectionSlot[Entity with Persistable] {
|
||||
|
||||
override val slotTier: Option[Int] = Some(tier)
|
||||
|
||||
override def bgIcon: Option[IconDef] = Some(new IconDef("icons/Card"))
|
||||
|
||||
// TODO: move the code below to Item[Redstone]
|
||||
// override def fillRmbMenu(menu: ContextMenu): Unit = {
|
||||
// val pool = UiHandler.root.workspaceView.windowPool
|
||||
// item match {
|
||||
// case Some(card: Redstone.Tier2) =>
|
||||
// menu.addEntry(new ContextMenuEntry("Redstone I/O", () => pool.openWindow(new Redstone1Window(card))))
|
||||
// menu.addEntry(new ContextMenuEntry("Bundled I/O", () => pool.openWindow(new Redstone2Window(card))))
|
||||
// case Some(card: Redstone.Tier1) =>
|
||||
// menu.addEntry(new ContextMenuEntry("Redstone I/O", () => pool.openWindow(new Redstone1Window(card))))
|
||||
// case _ =>
|
||||
// }
|
||||
// super.fillRmbMenu(menu)
|
||||
// }
|
||||
|
||||
override def accepts(prototype: ItemPrototype): Boolean = PartialFunction.cond(prototype) {
|
||||
case card: CardItemPrototype if isTierAdmissible(card.slotTier) => true
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
package ocelot.desktop.ui.widget.inventory.slot
|
||||
|
||||
import ocelot.desktop.graphics.IconDef
|
||||
import ocelot.desktop.ui.widget.WorkspaceView
|
||||
import ocelot.desktop.ui.widget.inventory.item.HDDItemPrototype
|
||||
import ocelot.desktop.ui.widget.inventory.{DecoratedSlotWidget, InventorySlotWidget, ItemPrototype, LmbItemSelectionSlot}
|
||||
import totoro.ocelot.brain.entity.HDDManaged
|
||||
import totoro.ocelot.brain.entity.traits.Inventory
|
||||
|
||||
class DiskSlot(owner: Inventory#Slot, val tier: Int, workspaceView: WorkspaceView)
|
||||
extends InventorySlotWidget[HDDManaged](owner, workspaceView)
|
||||
with DecoratedSlotWidget[HDDManaged]
|
||||
with LmbItemSelectionSlot[HDDManaged] {
|
||||
|
||||
override val slotTier: Option[Int] = Some(tier)
|
||||
|
||||
override def bgIcon: Option[IconDef] = Some(new IconDef("icons/HDD"))
|
||||
|
||||
override def accepts(prototype: ItemPrototype): Boolean = PartialFunction.cond(prototype) {
|
||||
case hdd: HDDItemPrototype[_] if isTierAdmissible(hdd.slotTier) => true
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
package ocelot.desktop.ui.widget.inventory.slot
|
||||
|
||||
import ocelot.desktop.graphics.IconDef
|
||||
import ocelot.desktop.ui.widget.WorkspaceView
|
||||
import ocelot.desktop.ui.widget.inventory.item.EEPROMItemPrototype
|
||||
import ocelot.desktop.ui.widget.inventory.{DecoratedSlotWidget, InventorySlotWidget, ItemPrototype, LmbItemSelectionSlot}
|
||||
import totoro.ocelot.brain.entity.EEPROM
|
||||
import totoro.ocelot.brain.entity.traits.Inventory
|
||||
|
||||
class EEPROMSlot(owner: Inventory#Slot, workspaceView: WorkspaceView)
|
||||
extends InventorySlotWidget[EEPROM](owner, workspaceView)
|
||||
with DecoratedSlotWidget[EEPROM]
|
||||
with LmbItemSelectionSlot[EEPROM] {
|
||||
|
||||
override type Element = EEPROM
|
||||
|
||||
override val slotTier: Option[Int] = None
|
||||
|
||||
override def bgIcon: Option[IconDef] = Some(new IconDef("icons/EEPROM"))
|
||||
|
||||
override def accepts(prototype: ItemPrototype): Boolean = PartialFunction.cond(prototype) {
|
||||
case _: EEPROMItemPrototype => true
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
package ocelot.desktop.ui.widget.inventory.slot
|
||||
|
||||
import ocelot.desktop.audio.{Audio, SoundSource}
|
||||
import ocelot.desktop.graphics.IconDef
|
||||
import ocelot.desktop.ui.widget.WorkspaceView
|
||||
import ocelot.desktop.ui.widget.inventory.item.FloppyItemPrototype
|
||||
import ocelot.desktop.ui.widget.inventory.{DecoratedSlotWidget, InventorySlotWidget, Item, ItemPrototype, LmbItemSelectionSlot}
|
||||
import totoro.ocelot.brain.entity.traits.{Floppy, Inventory}
|
||||
|
||||
class FloppySlot(owner: Inventory#Slot, workspaceView: WorkspaceView)
|
||||
extends InventorySlotWidget[Floppy](owner, workspaceView)
|
||||
with DecoratedSlotWidget[Floppy]
|
||||
with LmbItemSelectionSlot[Floppy] {
|
||||
|
||||
private val soundFloppyInsert = new SoundSource(Audio.FloppyInsert)
|
||||
private val soundFloppyEject = new SoundSource(Audio.FloppyEject)
|
||||
|
||||
override val slotTier: Option[Int] = None
|
||||
|
||||
override def bgIcon: Option[IconDef] = Some(new IconDef("icons/Floppy"))
|
||||
|
||||
override def accepts(prototype: ItemPrototype): Boolean = PartialFunction.cond(prototype) {
|
||||
case _: FloppyItemPrototype[_] => true
|
||||
}
|
||||
|
||||
override def onAdded(item: Item[Element]): Unit = {
|
||||
super.onAdded(item)
|
||||
|
||||
soundFloppyInsert.play()
|
||||
}
|
||||
|
||||
override def onRemoved(item: Item[Element]): Unit = {
|
||||
super.onRemoved(item)
|
||||
soundFloppyEject.play()
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
package ocelot.desktop.ui.widget.inventory.slot
|
||||
|
||||
import ocelot.desktop.graphics.IconDef
|
||||
import ocelot.desktop.ui.widget.WorkspaceView
|
||||
import ocelot.desktop.ui.widget.inventory.item.MemoryItemPrototype
|
||||
import ocelot.desktop.ui.widget.inventory.{DecoratedSlotWidget, InventorySlotWidget, ItemPrototype, LmbItemSelectionSlot}
|
||||
import totoro.ocelot.brain.entity.Memory
|
||||
import totoro.ocelot.brain.entity.traits.Inventory
|
||||
|
||||
class MemorySlot(slave: Inventory#Slot, val tier: Int, workspaceView: WorkspaceView)
|
||||
extends InventorySlotWidget[Memory](slave, workspaceView)
|
||||
with DecoratedSlotWidget[Memory]
|
||||
with LmbItemSelectionSlot[Memory] {
|
||||
|
||||
override val slotTier: Option[Int] = Some(tier)
|
||||
|
||||
override def bgIcon: Option[IconDef] = Some(new IconDef("icons/Memory"))
|
||||
|
||||
override def accepts(prototype: ItemPrototype): Boolean = PartialFunction.cond(prototype) {
|
||||
case memory: MemoryItemPrototype if isTierAdmissible(memory.slotTier) => true
|
||||
}
|
||||
}
|
||||
@ -1,88 +0,0 @@
|
||||
package ocelot.desktop.ui.widget.slot
|
||||
|
||||
import ocelot.desktop.graphics.IconDef
|
||||
import ocelot.desktop.node.nodes.ComputerNode
|
||||
import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry, ContextMenuSubmenu}
|
||||
import totoro.ocelot.brain.entity.machine.MachineAPI
|
||||
import totoro.ocelot.brain.entity.traits.{Entity, GenericCPU, Inventory}
|
||||
import totoro.ocelot.brain.entity.{APU, CPU}
|
||||
import totoro.ocelot.brain.util.Tier
|
||||
|
||||
class CPUSlot(owner: Inventory#Slot, node: ComputerNode, val tier: Int) extends InventorySlot[GenericCPU with Entity](owner) {
|
||||
private val APUAnimation = Some(Array(
|
||||
(0, 3f), (1, 3f), (2, 3f), (3, 3f), (4, 3f), (5, 3f),
|
||||
(4, 3f), (3, 3f), (2, 3f), (1, 3f), (0, 3f)))
|
||||
|
||||
override def itemIcon: Option[IconDef] = item match {
|
||||
case Some(cpu: CPU) => Some(new IconDef("items/CPU" + cpu.tier))
|
||||
case Some(apu: APU) => Some(new IconDef("items/APU" + apu.tier, animation = APUAnimation))
|
||||
case _ => None
|
||||
}
|
||||
|
||||
override def icon: Option[IconDef] = Some(new IconDef("icons/CPU"))
|
||||
override def tierIcon: Option[IconDef] = Some(new IconDef("icons/Tier" + tier))
|
||||
|
||||
override def fillLmbMenu(menu: ContextMenu): Unit = {
|
||||
menu.addEntry(new ContextMenuEntry("CPU (Tier 1)",
|
||||
() => item = new CPU(Tier.One),
|
||||
icon = Some(new IconDef("items/CPU0"))))
|
||||
|
||||
if (tier >= Tier.Two) {
|
||||
menu.addEntry(new ContextMenuEntry("CPU (Tier 2)",
|
||||
() => item = new CPU(Tier.Two),
|
||||
icon = Some(new IconDef("items/CPU1"))))
|
||||
}
|
||||
|
||||
if (tier >= Tier.Three) {
|
||||
menu.addEntry(new ContextMenuEntry("CPU (Tier 3)",
|
||||
() => item = new CPU(Tier.Three),
|
||||
icon = Some(new IconDef("items/CPU2"))))
|
||||
}
|
||||
|
||||
if (tier < Tier.Two) return
|
||||
|
||||
menu.addEntry(new ContextMenuSubmenu("APU (CPU + GPU)", Some(new IconDef("items/GraphicsCard1"))) {
|
||||
addEntry(new ContextMenuEntry("Tier 2",
|
||||
() => item = new APU(Tier.One),
|
||||
icon = Some(new IconDef("items/APU0", animation = APUAnimation))))
|
||||
|
||||
if (tier >= Tier.Three) {
|
||||
addEntry(new ContextMenuEntry("Tier 3",
|
||||
() => item = new APU(Tier.Two),
|
||||
icon = Some(new IconDef("items/APU1", animation = APUAnimation))))
|
||||
|
||||
addEntry(new ContextMenuEntry("Creative",
|
||||
() => item = new APU(Tier.Three),
|
||||
icon = Some(new IconDef("items/APU2", animation = APUAnimation))))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override def fillRmbMenu(menu: ContextMenu): Unit = {
|
||||
if (item.isEmpty) return
|
||||
|
||||
val cpu = item.get
|
||||
|
||||
menu.addEntry(new ContextMenuSubmenu("Set architecture") {
|
||||
for (arch <- cpu.allArchitectures) {
|
||||
val name = MachineAPI.getArchitectureName(arch) +
|
||||
(if (arch == cpu.architecture) " (current)" else "")
|
||||
|
||||
addEntry(new ContextMenuEntry(name, () => {
|
||||
val machine = node.computer.machine
|
||||
if (machine.isRunning) {
|
||||
machine.stop()
|
||||
cpu.setArchitecture(arch)
|
||||
machine.start()
|
||||
} else {
|
||||
cpu.setArchitecture(arch)
|
||||
}
|
||||
}))
|
||||
}
|
||||
})
|
||||
|
||||
super.fillRmbMenu(menu)
|
||||
}
|
||||
|
||||
override def lmbMenuEnabled: Boolean = true
|
||||
}
|
||||
@ -1,62 +0,0 @@
|
||||
package ocelot.desktop.ui.widget.slot
|
||||
|
||||
import ocelot.desktop.graphics.IconDef
|
||||
import ocelot.desktop.ui.UiHandler
|
||||
import ocelot.desktop.ui.widget.card.{Redstone1Window, Redstone2Window}
|
||||
import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry, ContextMenuSubmenu}
|
||||
import totoro.ocelot.brain.entity.Redstone
|
||||
import totoro.ocelot.brain.entity.traits.{Entity, Inventory}
|
||||
import totoro.ocelot.brain.util.Tier
|
||||
|
||||
class CardSlot(owner: Inventory#Slot, val tier: Int) extends InventorySlot[Entity](owner) {
|
||||
override def itemIcon: Option[IconDef] = item.flatMap(it => CardRegistry.getIcon(it))
|
||||
|
||||
override def icon: Option[IconDef] = Some(new IconDef("icons/Card"))
|
||||
override def tierIcon: Option[IconDef] = Some(new IconDef("icons/Tier" + tier))
|
||||
|
||||
override def fillRmbMenu(menu: ContextMenu): Unit = {
|
||||
val pool = UiHandler.root.workspaceView.windowPool
|
||||
item match {
|
||||
case Some(card: Redstone.Tier2) =>
|
||||
menu.addEntry(new ContextMenuEntry("Redstone I/O", () => pool.openWindow(new Redstone1Window(card))))
|
||||
menu.addEntry(new ContextMenuEntry("Bundled I/O", () => pool.openWindow(new Redstone2Window(card))))
|
||||
case Some(card: Redstone.Tier1) =>
|
||||
menu.addEntry(new ContextMenuEntry("Redstone I/O", () => pool.openWindow(new Redstone1Window(card))))
|
||||
case _ =>
|
||||
}
|
||||
super.fillRmbMenu(menu)
|
||||
}
|
||||
|
||||
override def fillLmbMenu(menu: ContextMenu): Unit = {
|
||||
val entries = CardRegistry.entries
|
||||
|
||||
var i = 0
|
||||
while (i < entries.length) {
|
||||
val entry = entries(i)
|
||||
val nextEntry = entries.lift(i + 1)
|
||||
|
||||
if (nextEntry.isDefined && nextEntry.get.name == entry.name) {
|
||||
val groupName = entry.name
|
||||
if (tier >= entry.tier) {
|
||||
menu.addEntry(new ContextMenuSubmenu(groupName, Some(nextEntry.get.icon)) {
|
||||
entries.view.slice(i, entries.length).takeWhile(_.name == groupName).foreach(entry => {
|
||||
val name = if (entry.tier == Tier.Four) "Creative" else "Tier " + (entry.tier + 1)
|
||||
if (tier >= entry.tier) {
|
||||
addEntry(new ContextMenuEntry(name, () => item = entry.factory(), Some(entry.icon)))
|
||||
}
|
||||
i += 1
|
||||
})
|
||||
})
|
||||
}
|
||||
} else {
|
||||
if (tier >= entry.tier) {
|
||||
menu.addEntry(new ContextMenuEntry(entry.name, () => item = entry.factory(), Some(entry.icon)))
|
||||
}
|
||||
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override def lmbMenuEnabled: Boolean = true
|
||||
}
|
||||
@ -1,34 +0,0 @@
|
||||
package ocelot.desktop.ui.widget.slot
|
||||
|
||||
import ocelot.desktop.graphics.IconDef
|
||||
import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry}
|
||||
import totoro.ocelot.brain.entity.HDDManaged
|
||||
import totoro.ocelot.brain.entity.traits.Inventory
|
||||
import totoro.ocelot.brain.util.Tier
|
||||
|
||||
class DiskSlot(owner: Inventory#Slot, val tier: Int) extends InventorySlot[HDDManaged](owner) {
|
||||
override def itemIcon: Option[IconDef] = item.map(disk => new IconDef("items/HardDiskDrive" + disk.tier))
|
||||
|
||||
override def icon: Option[IconDef] = Some(new IconDef("icons/HDD"))
|
||||
override def tierIcon: Option[IconDef] = Some(new IconDef("icons/Tier" + tier))
|
||||
|
||||
override def fillLmbMenu(menu: ContextMenu): Unit = {
|
||||
menu.addEntry(new ContextMenuEntry("HDD (Tier 1)",
|
||||
() => item = new HDDManaged(Tier.One),
|
||||
icon = Some(new IconDef("items/HardDiskDrive0"))))
|
||||
|
||||
if (tier >= Tier.Two) {
|
||||
menu.addEntry(new ContextMenuEntry("HDD (Tier 2)",
|
||||
() => item = new HDDManaged(Tier.Two),
|
||||
icon = Some(new IconDef("items/HardDiskDrive1"))))
|
||||
}
|
||||
|
||||
if (tier >= Tier.Three) {
|
||||
menu.addEntry(new ContextMenuEntry("HDD (Tier 3)",
|
||||
() => item = new HDDManaged(Tier.Three),
|
||||
icon = Some(new IconDef("items/HardDiskDrive2"))))
|
||||
}
|
||||
}
|
||||
|
||||
override def lmbMenuEnabled: Boolean = true
|
||||
}
|
||||
@ -1,28 +0,0 @@
|
||||
package ocelot.desktop.ui.widget.slot
|
||||
|
||||
import ocelot.desktop.graphics.IconDef
|
||||
import ocelot.desktop.node.nodes.ComputerNode
|
||||
import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry}
|
||||
import totoro.ocelot.brain.entity.EEPROM
|
||||
import totoro.ocelot.brain.entity.traits.Inventory
|
||||
import totoro.ocelot.brain.loot.Loot
|
||||
|
||||
class EEPROMSlot(owner: Inventory#Slot) extends InventorySlot[EEPROM](owner) {
|
||||
override def itemIcon: Option[IconDef] = item.map(_ => new IconDef("items/EEPROM"))
|
||||
override def icon: Option[IconDef] = Some(new IconDef("icons/EEPROM"))
|
||||
|
||||
override def fillLmbMenu(menu: ContextMenu): Unit = {
|
||||
menu.addEntry(new ContextMenuEntry("Lua BIOS",
|
||||
() => item = Loot.OpenOsEEPROM.create(),
|
||||
icon = Some(new IconDef("items/EEPROM"))))
|
||||
menu.addEntry(new ContextMenuEntry("AdvLoader",
|
||||
() => item = Loot.AdvLoaderEEPROM.create(),
|
||||
icon = Some(new IconDef("items/EEPROM"))))
|
||||
menu.addEntry(new ContextMenuEntry("Empty",
|
||||
() => item = new EEPROM,
|
||||
icon = Some(new IconDef("items/EEPROM"))))
|
||||
|
||||
}
|
||||
|
||||
override def lmbMenuEnabled: Boolean = true
|
||||
}
|
||||
@ -1,9 +0,0 @@
|
||||
package ocelot.desktop.ui.widget.slot
|
||||
|
||||
import ocelot.desktop.graphics.IconDef
|
||||
import totoro.ocelot.brain.entity.traits.{Entity, Inventory}
|
||||
|
||||
class EntitySlot(owner: Inventory#Slot) extends InventorySlot[Entity](owner) {
|
||||
// TODO
|
||||
override def icon: Option[IconDef] = None
|
||||
}
|
||||
@ -1,48 +0,0 @@
|
||||
package ocelot.desktop.ui.widget.slot
|
||||
|
||||
import ocelot.desktop.audio.{Audio, SoundSource}
|
||||
import ocelot.desktop.graphics.IconDef
|
||||
import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry}
|
||||
import totoro.ocelot.brain.entity.FloppyManaged
|
||||
import totoro.ocelot.brain.entity.traits.{Floppy, Inventory}
|
||||
import totoro.ocelot.brain.loot.Loot
|
||||
import totoro.ocelot.brain.util.DyeColor
|
||||
|
||||
class FloppySlot(owner: Inventory#Slot) extends InventorySlot[Floppy](owner) {
|
||||
private val soundFloppyInsert = new SoundSource(Audio.FloppyInsert)
|
||||
private val soundFloppyEject = new SoundSource(Audio.FloppyEject)
|
||||
|
||||
override def itemIcon: Option[IconDef] = item.map(fl => new IconDef("items/FloppyDisk_" + fl.color.name))
|
||||
|
||||
override def icon: Option[IconDef] = Some(new IconDef("icons/Floppy"))
|
||||
|
||||
override def fillLmbMenu(menu: ContextMenu): Unit = {
|
||||
for (f <- FloppySlot.FloppyFactories) {
|
||||
val floppy = f.create()
|
||||
menu.addEntry(new ContextMenuEntry(floppy.label.getLabel,
|
||||
() => item = floppy,
|
||||
icon = Some(new IconDef("items/FloppyDisk_" + floppy.color.name))))
|
||||
}
|
||||
|
||||
menu.addEntry(new ContextMenuEntry("Empty",
|
||||
() => item = new FloppyManaged("Floppy Disk", DyeColor.GRAY),
|
||||
icon = Some(new IconDef("items/FloppyDisk_dyeGray"))))
|
||||
}
|
||||
|
||||
override def lmbMenuEnabled: Boolean = true
|
||||
|
||||
override def onAdded(item: Floppy): Unit = {
|
||||
super.onAdded(item)
|
||||
soundFloppyInsert.play()
|
||||
}
|
||||
|
||||
override def onRemoved(item: Floppy): Unit = {
|
||||
super.onRemoved(item)
|
||||
soundFloppyEject.play()
|
||||
}
|
||||
}
|
||||
|
||||
object FloppySlot {
|
||||
private val FloppyFactories = Array(Loot.OpenOsFloppy, Loot.Plan9kFloppy, Loot.OPPMFloppy,
|
||||
Loot.OpenLoaderFloppy, Loot.NetworkFloppy, Loot.IrcFloppy, Loot.DataFloppy)
|
||||
}
|
||||
@ -1,26 +0,0 @@
|
||||
package ocelot.desktop.ui.widget.slot
|
||||
|
||||
import ocelot.desktop.ui.UiHandler
|
||||
import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry}
|
||||
import totoro.ocelot.brain.entity.traits.{Entity, Environment, Inventory}
|
||||
|
||||
abstract class InventorySlot[T <: Entity](val owner: Inventory#Slot) extends SlotWidget[T] {
|
||||
reloadItem()
|
||||
|
||||
override def onAdded(item: T): Unit = owner.put(item)
|
||||
|
||||
override def onRemoved(item: T): Unit = owner.remove()
|
||||
|
||||
override def fillRmbMenu(menu: ContextMenu): Unit = {
|
||||
item match {
|
||||
case Some(env: Environment) =>
|
||||
menu.addEntry(new ContextMenuEntry("Copy address", () => {
|
||||
UiHandler.clipboard = env.node.address
|
||||
}))
|
||||
case _ =>
|
||||
}
|
||||
super.fillRmbMenu(menu)
|
||||
}
|
||||
|
||||
def reloadItem(): Unit = _item = owner.get.map(_.asInstanceOf[T])
|
||||
}
|
||||
@ -1,47 +0,0 @@
|
||||
package ocelot.desktop.ui.widget.slot
|
||||
|
||||
import ocelot.desktop.graphics.IconDef
|
||||
import ocelot.desktop.node.nodes.ComputerNode
|
||||
import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry}
|
||||
import totoro.ocelot.brain.entity.Memory
|
||||
import totoro.ocelot.brain.entity.traits.Inventory
|
||||
import totoro.ocelot.brain.util.Tier
|
||||
|
||||
class MemorySlot(owner: Inventory#Slot, val tier: Int) extends InventorySlot[Memory](owner) {
|
||||
override def itemIcon: Option[IconDef] = item.map(mem => new IconDef("items/Memory" + mem.tier))
|
||||
|
||||
override def icon: Option[IconDef] = Some(new IconDef("icons/Memory"))
|
||||
override def tierIcon: Option[IconDef] = Some(new IconDef("icons/Tier" + tier))
|
||||
|
||||
override def fillLmbMenu(menu: ContextMenu): Unit = {
|
||||
menu.addEntry(new ContextMenuEntry("RAM (Tier 1)",
|
||||
() => item = new Memory(Tier.One),
|
||||
icon = Some(new IconDef("items/Memory0"))))
|
||||
|
||||
menu.addEntry(new ContextMenuEntry("RAM (Tier 1.5)",
|
||||
() => item = new Memory(Tier.Two),
|
||||
icon = Some(new IconDef("items/Memory1"))))
|
||||
|
||||
if (tier >= Tier.Two) {
|
||||
menu.addEntry(new ContextMenuEntry("RAM (Tier 2)",
|
||||
() => item = new Memory(Tier.Three),
|
||||
icon = Some(new IconDef("items/Memory2"))))
|
||||
|
||||
menu.addEntry(new ContextMenuEntry("RAM (Tier 2.5)",
|
||||
() => item = new Memory(Tier.Four),
|
||||
icon = Some(new IconDef("items/Memory3"))))
|
||||
}
|
||||
|
||||
if (tier >= Tier.Three) {
|
||||
menu.addEntry(new ContextMenuEntry("RAM (Tier 3)",
|
||||
() => item = new Memory(Tier.Five),
|
||||
icon = Some(new IconDef("items/Memory4"))))
|
||||
|
||||
menu.addEntry(new ContextMenuEntry("RAM (Tier 3.5)",
|
||||
() => item = new Memory(Tier.Six),
|
||||
icon = Some(new IconDef("items/Memory5"))))
|
||||
}
|
||||
}
|
||||
|
||||
override def lmbMenuEnabled: Boolean = true
|
||||
}
|
||||
@ -1,75 +0,0 @@
|
||||
package ocelot.desktop.ui.widget.slot
|
||||
|
||||
import ocelot.desktop.geometry.Size2D
|
||||
import ocelot.desktop.graphics.{Graphics, IconDef}
|
||||
import ocelot.desktop.ui.event.handlers.ClickHandler
|
||||
import ocelot.desktop.ui.event.{ClickEvent, MouseEvent}
|
||||
import ocelot.desktop.ui.widget.Widget
|
||||
import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry}
|
||||
|
||||
abstract class SlotWidget[T] extends Widget with ClickHandler {
|
||||
def icon: Option[IconDef]
|
||||
|
||||
def tierIcon: Option[IconDef] = None
|
||||
|
||||
def itemIcon: Option[IconDef] = None
|
||||
|
||||
def onRemoved(item: T): Unit = {}
|
||||
|
||||
def onAdded(item: T): Unit = {}
|
||||
|
||||
def fillLmbMenu(menu: ContextMenu): Unit = {}
|
||||
|
||||
def fillRmbMenu(menu: ContextMenu): Unit = {
|
||||
menu.addEntry(new ContextMenuEntry("Remove", () => item = None))
|
||||
}
|
||||
|
||||
def lmbMenuEnabled: Boolean = false
|
||||
|
||||
def rmbMenuEnabled: Boolean = _item.isDefined
|
||||
|
||||
final var _item: Option[T] = None
|
||||
|
||||
final def item_=(v: Option[T]): Unit = {
|
||||
if (_item.isDefined)
|
||||
onRemoved(_item.get)
|
||||
|
||||
_item = v
|
||||
|
||||
if (v.isDefined)
|
||||
onAdded(v.get)
|
||||
}
|
||||
|
||||
final def item_=(v: T): Unit = item = Some(v)
|
||||
|
||||
final def item: Option[T] = _item
|
||||
|
||||
final override def minimumSize: Size2D = Size2D(36, 36)
|
||||
final override def maximumSize: Size2D = minimumSize
|
||||
|
||||
final override def receiveMouseEvents: Boolean = true
|
||||
|
||||
eventHandlers += {
|
||||
case ClickEvent(MouseEvent.Button.Left, _) if lmbMenuEnabled =>
|
||||
val menu = new ContextMenu
|
||||
fillLmbMenu(menu)
|
||||
root.get.contextMenus.open(menu)
|
||||
case ClickEvent(MouseEvent.Button.Right, _) if rmbMenuEnabled =>
|
||||
val menu = new ContextMenu
|
||||
fillRmbMenu(menu)
|
||||
root.get.contextMenus.open(menu)
|
||||
}
|
||||
|
||||
final override def draw(g: Graphics): Unit = {
|
||||
g.sprite("EmptySlot", bounds)
|
||||
|
||||
val iconBounds = bounds.inflate(-2)
|
||||
|
||||
itemIcon match {
|
||||
case Some(icon) => g.sprite(icon, iconBounds)
|
||||
case None =>
|
||||
tierIcon.foreach(g.sprite(_, iconBounds))
|
||||
icon.foreach(g.sprite(_, iconBounds))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,7 +0,0 @@
|
||||
package ocelot.desktop.util
|
||||
|
||||
import totoro.ocelot.brain.entity.traits.Inventory
|
||||
|
||||
class TrayInventory extends Inventory {
|
||||
// TODO add something here?
|
||||
}
|
||||
@ -3,13 +3,17 @@ package ocelot.desktop
|
||||
import java.util.concurrent.locks.Lock
|
||||
|
||||
package object util {
|
||||
final def withLockAcquired[T](lock: Lock, f: () => T): T = {
|
||||
final def withLockAcquired[T](lock: Lock, f: => T): T = {
|
||||
lock.lock()
|
||||
|
||||
try {
|
||||
f()
|
||||
f
|
||||
} finally {
|
||||
lock.unlock()
|
||||
}
|
||||
}
|
||||
|
||||
implicit class ExtendedTuple2[A, B](tuple: (A, B)) {
|
||||
def flipped: (B, A) = (tuple._2, tuple._1)
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user