diff --git a/lib/ocelot-brain b/lib/ocelot-brain index aea345a..f326a45 160000 --- a/lib/ocelot-brain +++ b/lib/ocelot-brain @@ -1 +1 @@ -Subproject commit aea345a223281e4bd8155e3b82fbbbb5b9bd1c2c +Subproject commit f326a45a0ae223904869ae02ad22ca7afdeb8b1f diff --git a/src/main/scala/ocelot/desktop/node/nodes/ComputerNode.scala b/src/main/scala/ocelot/desktop/node/nodes/ComputerNode.scala index 16eafcd..ce90f67 100644 --- a/src/main/scala/ocelot/desktop/node/nodes/ComputerNode.scala +++ b/src/main/scala/ocelot/desktop/node/nodes/ComputerNode.scala @@ -11,12 +11,14 @@ import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry, Cont import ocelot.desktop.ui.widget.slot._ import ocelot.desktop.util.{ResourceManager, TierColor} import org.lwjgl.input.Keyboard -import totoro.ocelot.brain.entity.traits.{Computer, Entity, GenericCPU} -import totoro.ocelot.brain.entity.{CPU, Case, EEPROM, FloppyManaged, GraphicsCard, HDDManaged, Memory} +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 scala.reflect.ClassTag + class ComputerNode(val computer: Case, setup: Boolean = true) extends Node { var lastFilesystemAccess = 0L @@ -32,12 +34,12 @@ class ComputerNode(val computer: Case, setup: Boolean = true) extends Node { setupSlots() if (setup) { - computer.add(new CPU(computer.tier.min(2))) - computer.add(new Memory(computer.tier.min(2) * 2 + 1)) - computer.add(new Memory(computer.tier.min(2) * 2 + 1)) - computer.add(new GraphicsCard(computer.tier.min(1))) - computer.add(Loot.OpenOsFloppy.create()) - computer.add(Loot.OpenOsEEPROM.create()) + 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() OcelotDesktop.workspace.add(computer) } @@ -118,61 +120,135 @@ class ComputerNode(val computer: Case, setup: Boolean = true) extends Node { 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) - for (item <- computer.inventory) { - try { - item match { - case cpu: GenericCPU => - if (cpuSlot.tier >= cpu.cpuTier) - cpuSlot._item = Some(cpu) - else - computer.remove(item) - case mem: Memory => memorySlots - .find(slot => slot._item.isEmpty && slot.tier >= (mem.tier + 1) / 2 - 1) - .get._item = Some(mem) - case hdd: HDDManaged => diskSlots - .find(slot => slot._item.isEmpty && slot.tier >= hdd.tier) - .get._item = Some(hdd) - case eeprom: EEPROM => eepromSlot._item = Some(eeprom) - case floppy: FloppyManaged => floppySlot.get._item = Some(floppy) - case card: Entity => cardSlots - .find(slot => slot._item.isEmpty && slot.tier >= CardRegistry.getTier(card)) - .get._item = Some(card) - case _ => - } - } catch { - case _: NoSuchElementException => computer.remove(item) - } + 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 = { - eepromSlot = new EEPROMSlot(this) - cpuSlot = new CPUSlot(this, computer.tier.min(Tier.Three)) + var slotIndex = 0 - floppySlot = if (computer.tier >= Tier.Three) Some(new FloppySlot(computer)) else None - - memorySlots = Array( - new MemorySlot(this, computer.tier.min(Tier.Three)), - new MemorySlot(this, computer.tier.min(Tier.Three))) - - diskSlots = computer.tier match { - case Tier.One => Array(new DiskSlot(this, Tier.One)) - case Tier.Two => Array(new DiskSlot(this, Tier.Two), new DiskSlot(this, Tier.One)) - case Tier.Three => Array(new DiskSlot(this, Tier.Three), new DiskSlot(this, Tier.Two)) - case _ => Array(new DiskSlot(this, Tier.Three), new DiskSlot(this, Tier.Three)) + def nextSlot(): computer.Slot = { + val result = computer.inventory(slotIndex) + slotIndex += 1 + result } - cardSlots = computer.tier match { - case Tier.One => Array(new CardSlot(this, Tier.One), new CardSlot(this, Tier.One)) - case Tier.Two => Array(new CardSlot(this, Tier.Two), new CardSlot(this, Tier.One)) - case Tier.Three => Array(new CardSlot(this, Tier.Three), new CardSlot(this, Tier.Two), new CardSlot(this, Tier.Two)) - case _ => Array(new CardSlot(this, Tier.Three), new CardSlot(this, Tier.Three), new CardSlot(this, Tier.Three)) + def addSlot[T <: InventorySlot[_]](factory: computer.Slot => T): T = { + val slot = nextSlot() + val widget = factory(slot) + + widget + } + + def addSlots[T <: InventorySlot[_] : ClassTag](factories: (computer.Slot => T)*): Array[T] = { + val array = Array.newBuilder[T] + + for (factory <- factories) { + array += addSlot(factory) + } + + array.result() + } + + 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)) + floppySlot = None + 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(_)) + + 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)) + floppySlot = None + 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)) + } else { + addSlots(new CardSlot(_, Tier.Three), new CardSlot(_, Tier.Three), new CardSlot(_, 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)) + } else { + 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(_)) } } diff --git a/src/main/scala/ocelot/desktop/node/nodes/DiskDriveNode.scala b/src/main/scala/ocelot/desktop/node/nodes/DiskDriveNode.scala index c03df6d..4797b96 100644 --- a/src/main/scala/ocelot/desktop/node/nodes/DiskDriveNode.scala +++ b/src/main/scala/ocelot/desktop/node/nodes/DiskDriveNode.scala @@ -16,7 +16,7 @@ class DiskDriveNode(val diskDrive: FloppyDiskDrive) extends Node { OcelotDesktop.workspace.add(diskDrive) - val slot: FloppySlot = new FloppySlot(diskDrive) + val slot: FloppySlot = new FloppySlot(diskDrive.inventory(0)) slot.item = floppy.getOrElse(Loot.OpenOsFloppy.create()) def this(nbt: NBTTagCompound) { @@ -70,7 +70,7 @@ class DiskDriveNode(val diskDrive: FloppyDiskDrive) extends Node { override val window: Option[DiskDriveWindow] = Some(new DiskDriveWindow(this)) private def floppy: Option[Floppy] = { - diskDrive.inventory.headOption match { + diskDrive.inventory(0).get match { case Some(floppy: Floppy) => Some(floppy) case _ => None } diff --git a/src/main/scala/ocelot/desktop/ui/widget/slot/CPUSlot.scala b/src/main/scala/ocelot/desktop/ui/widget/slot/CPUSlot.scala index 4714a44..6d2ac03 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/slot/CPUSlot.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/slot/CPUSlot.scala @@ -4,11 +4,11 @@ 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} +import totoro.ocelot.brain.entity.traits.{Entity, GenericCPU, Inventory} import totoro.ocelot.brain.entity.{APU, CPU} import totoro.ocelot.brain.util.Tier -class CPUSlot(node: ComputerNode, val tier: Int) extends InventorySlot[GenericCPU with Entity](node.computer) { +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))) diff --git a/src/main/scala/ocelot/desktop/ui/widget/slot/CardSlot.scala b/src/main/scala/ocelot/desktop/ui/widget/slot/CardSlot.scala index 1961736..2060a0e 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/slot/CardSlot.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/slot/CardSlot.scala @@ -6,10 +6,10 @@ 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 +import totoro.ocelot.brain.entity.traits.{Entity, Inventory} import totoro.ocelot.brain.util.Tier -class CardSlot(node: ComputerNode, val tier: Int) extends InventorySlot[Entity](node.computer) { +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: IconDef = new IconDef("icons/Card") diff --git a/src/main/scala/ocelot/desktop/ui/widget/slot/DiskSlot.scala b/src/main/scala/ocelot/desktop/ui/widget/slot/DiskSlot.scala index 548ea2c..b9342c6 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/slot/DiskSlot.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/slot/DiskSlot.scala @@ -4,9 +4,10 @@ import ocelot.desktop.graphics.IconDef import ocelot.desktop.node.nodes.ComputerNode 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(node: ComputerNode, val tier: Int) extends InventorySlot[HDDManaged](node.computer) { +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: IconDef = new IconDef("icons/HDD") diff --git a/src/main/scala/ocelot/desktop/ui/widget/slot/EEPROMSlot.scala b/src/main/scala/ocelot/desktop/ui/widget/slot/EEPROMSlot.scala index b437fe0..d69c3a6 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/slot/EEPROMSlot.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/slot/EEPROMSlot.scala @@ -4,9 +4,10 @@ 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(node: ComputerNode) extends InventorySlot[EEPROM](node.computer) { +class EEPROMSlot(owner: Inventory#Slot) extends InventorySlot[EEPROM](owner) { override def itemIcon: Option[IconDef] = item.map(_ => new IconDef("items/EEPROM")) override def icon: IconDef = new IconDef("icons/EEPROM") diff --git a/src/main/scala/ocelot/desktop/ui/widget/slot/FloppySlot.scala b/src/main/scala/ocelot/desktop/ui/widget/slot/FloppySlot.scala index 6935181..90bbfb9 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/slot/FloppySlot.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/slot/FloppySlot.scala @@ -8,7 +8,7 @@ import totoro.ocelot.brain.entity.traits.{Floppy, Inventory} import totoro.ocelot.brain.loot.Loot import totoro.ocelot.brain.util.DyeColor -class FloppySlot(inventory: Inventory) extends InventorySlot[Floppy](inventory) { +class FloppySlot(owner: Inventory#Slot) extends InventorySlot[Floppy](owner) { private val soundFloppyInsert = new SoundSource(Audio.FloppyInsert) private val soundFloppyEject = new SoundSource(Audio.FloppyEject) diff --git a/src/main/scala/ocelot/desktop/ui/widget/slot/InventorySlot.scala b/src/main/scala/ocelot/desktop/ui/widget/slot/InventorySlot.scala index 22a5cd4..f5dc548 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/slot/InventorySlot.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/slot/InventorySlot.scala @@ -4,14 +4,12 @@ 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](inventory: Inventory) extends SlotWidget[T] { - override def onAdded(item: T): Unit = { - inventory.add(item) - } +abstract class InventorySlot[T <: Entity](val owner: Inventory#Slot) extends SlotWidget[T] { + reloadItem() - override def onRemoved(item: T): Unit = { - inventory.remove(item) - } + 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 { @@ -23,4 +21,6 @@ abstract class InventorySlot[T <: Entity](inventory: Inventory) extends SlotWidg } super.fillRmbMenu(menu) } + + def reloadItem(): Unit = _item = owner.get.map(_.asInstanceOf[T]) } diff --git a/src/main/scala/ocelot/desktop/ui/widget/slot/MemorySlot.scala b/src/main/scala/ocelot/desktop/ui/widget/slot/MemorySlot.scala index ef2c41e..f35e1e4 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/slot/MemorySlot.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/slot/MemorySlot.scala @@ -4,9 +4,10 @@ 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(node: ComputerNode, val tier: Int) extends InventorySlot[Memory](node.computer) { +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: IconDef = new IconDef("icons/Memory")