Unify item factory and recoverer registries

This commit is contained in:
Fingercomp
2023-06-11 06:05:31 +07:00
parent 208a042afb
commit 98dea7bbaf
23 changed files with 248 additions and 263 deletions

View File

@@ -2,6 +2,7 @@ package ocelot.desktop
import buildinfo.BuildInfo
import li.flor.nativejfilechooser.NativeJFileChooser
import ocelot.desktop.inventory.Items
import ocelot.desktop.ui.UiHandler
import ocelot.desktop.ui.swing.SplashScreen
import ocelot.desktop.ui.widget._
@@ -64,6 +65,8 @@ object OcelotDesktop extends Logging {
Messages.load(Source.fromURL(getClass.getResource("/ocelot/desktop/messages.txt")))
ColorScheme.load(Source.fromURL(getClass.getResource("/ocelot/desktop/colorscheme.txt")))
Items.init()
splashScreen.setStatus("Initializing GUI...", 0.30f)
createWorkspace()

View File

@@ -64,6 +64,7 @@ trait Item {
*
* @example {{{
* override protected def fillTooltipBody(body: Widget): Unit = {
* super.fillTooltipBody(body)
* body.children :+= new Label {
* override def text: String = "Hello world"
* override def color: Color = Color.Grey

View File

@@ -51,4 +51,9 @@ trait ItemFactory {
* Constructs a new item instance.
*/
def build(): I
/**
* Returns a list of recoverers provided by the factory,
*/
def recoverers: Iterable[ItemRecoverer[_, _]]
}

View File

@@ -1,12 +1,18 @@
package ocelot.desktop.inventory
import scala.reflect.{ClassTag, classTag}
/**
* Allows recovering an [[Item]] from [[S]] (typically an [[totoro.ocelot.brain.entity.traits.Entity Entity]]).
*
* This is used for migration from old saves before the inventory system was introduced to Ocelot Desktop.
*/
trait ItemRecoverer[S] {
type I <: Item
final class ItemRecoverer[S: ClassTag, I <: Item](f: S => I) {
val sourceClass: Class[_] = classTag[S].runtimeClass
def recover(source: S): I
def recover(source: S): I = f(source)
}
object ItemRecoverer {
def apply[S: ClassTag, I <: Item](f: S => I): ItemRecoverer[S, I] = new ItemRecoverer(f)
}

View File

@@ -0,0 +1,140 @@
package ocelot.desktop.inventory
import ocelot.desktop.graphics.IconDef
import ocelot.desktop.inventory.item._
import ocelot.desktop.util.Logging
import ocelot.desktop.util.ReflectionUtils.linearizationOrder
import totoro.ocelot.brain.loot.Loot
import totoro.ocelot.brain.util.ExtendedTier.ExtendedTier
import totoro.ocelot.brain.util.Tier.Tier
import totoro.ocelot.brain.util.{ExtendedTier, Tier}
import scala.collection.mutable
import scala.collection.mutable.ArrayBuffer
object Items extends Logging {
private val _groups = ArrayBuffer.empty[ItemGroup]
private val _recoverers = mutable.Map.empty[Class[_], ItemRecoverer[_, _]]
// this is just to force load the class during initialization
def init(): Unit = {}
/** Registers a recoverer for [[ItemRecoverer.sourceClass]].
*/
def registerRecoverer(recoverer: ItemRecoverer[_, _]): Unit = {
if (!_recoverers.contains(recoverer.sourceClass)) {
_recoverers(recoverer.sourceClass) = recoverer
logger.info(s"Registered a recoverer for ${recoverer.sourceClass.getName}")
}
}
private def registerItemFactoryRecoverers(factory: ItemFactory): Unit = {
for (recoverer <- factory.recoverers) {
registerRecoverer(recoverer)
}
}
def registerSingleton(factory: ItemFactory): Unit = {
_groups += SingletonItemGroup(factory.name, factory)
registerItemFactoryRecoverers(factory)
}
def registerTiered(name: String, tiers: IterableOnce[Tier])(factory: Tier => ItemFactory): Unit = {
val group = TieredItemGroup(name, tiers.iterator.map(tier => (tier, factory(tier))).toSeq)
_groups += group
for ((_, factory) <- group.factories) {
registerItemFactoryRecoverers(factory)
}
}
def registerExtendedTiered(name: String, tiers: IterableOnce[ExtendedTier])(
factory: ExtendedTier => ItemFactory
): Unit = {
val group = ExtendedTieredItemGroup(name, tiers.iterator.map(tier => (tier, factory(tier))).toSeq)
_groups += group
for ((_, factory) <- group.factories) {
registerItemFactoryRecoverers(factory)
}
}
def registerArbitrary(name: String, icon: IconDef, factories: IterableOnce[(String, ItemFactory)]): Unit = {
val group = ArbitraryItemGroup(name, icon, factories.iterator.toSeq)
_groups += group
for ((_, factory) <- group.factories) {
registerItemFactoryRecoverers(factory)
}
}
def groups: Iterable[ItemGroup] = _groups
/** Attempts to recover an [[Item]] from `source`.
*
* Checks superclasses and traits while looking for a recoverer.
*/
def recover[A](source: A): Option[Item] = {
linearizationOrder(source.getClass.asInstanceOf[Class[_]])
.flatMap(_recoverers.get)
.map(_.asInstanceOf[ItemRecoverer[_ >: A, _ <: Item]].recover(source))
.nextOption()
}
sealed trait ItemGroup {
def name: String
}
case class SingletonItemGroup(name: String, factory: ItemFactory) extends ItemGroup
case class TieredItemGroup(name: String, factories: Seq[(Tier, ItemFactory)]) extends ItemGroup
case class ExtendedTieredItemGroup(name: String, factories: Seq[(ExtendedTier, ItemFactory)]) extends ItemGroup
case class ArbitraryItemGroup(name: String, icon: IconDef, factories: Seq[(String, ItemFactory)]) extends ItemGroup
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
registerTiered("CPU", Tier.One to Tier.Three)(new CpuItem.Factory(_))
registerTiered("APU", Tier.Two to Tier.Creative)(tier => new ApuItem.Factory(tier.saturatingSub(1)))
registerExtendedTiered("Memory", ExtendedTier.One to ExtendedTier.ThreeHalf)(new MemoryItem.Factory(_))
registerTiered("HDD", Tier.One to Tier.Three)(new HddItem.Factory(managed = true, _))
registerArbitrary(
"Floppy",
FloppyItem.Factory.Empty.icon,
Loot.Floppies.iterator
.map(new FloppyItem.Factory.Loot(_))
.map(factory => (factory.name, factory)) ++ Some(("Empty", FloppyItem.Factory.Empty))
)
registerArbitrary(
"EEPROM",
EepromItem.Factory.Empty.icon,
Loot.Eeproms.iterator
.map(new EepromItem.Factory.Loot(_))
.map(factory => (factory.name, factory)) ++ Some(("Empty", EepromItem.Factory.Empty))
)
registerTiered("Graphics Card", Tier.One to Tier.Three)(new GraphicsCardItem.Factory(_))
registerSingleton(NetworkCardItem.Factory)
registerTiered("Wireless Net. Card", Tier.One to Tier.Two) {
case Tier.One => WirelessNetworkCardItem.Tier1.Factory
case Tier.Two => WirelessNetworkCardItem.Tier2.Factory
}
registerSingleton(LinkedCardItem.Factory)
registerSingleton(InternetCardItem.Factory)
registerTiered("Redstone Card", Tier.One to Tier.Two) {
case Tier.One => RedstoneCardItem.Tier1.Factory
case Tier.Two => RedstoneCardItem.Tier2.Factory
}
registerTiered("Data Card", Tier.One to Tier.Three) {
case Tier.One => DataCardItem.Tier1.Factory
case Tier.Two => DataCardItem.Tier2.Factory
case Tier.Three => DataCardItem.Tier3.Factory
}
registerSingleton(SoundCardItem.Factory)
registerSingleton(SelfDestructingCardItem.Factory)
}

View File

@@ -1,57 +0,0 @@
package ocelot.desktop.inventory
import ocelot.desktop.inventory.item._
import ocelot.desktop.util.ReflectionUtils.linearizationOrder
import totoro.ocelot.brain.entity
import totoro.ocelot.brain.entity.sound_card.SoundCard
import totoro.ocelot.brain.entity.traits.Floppy
import scala.collection.mutable
import scala.reflect.{ClassTag, classTag}
/**
* A registry of all available [[ItemRecoverer item recoverers]].
*/
object Recoverers {
private val recoverers = mutable.HashMap.empty[Class[_], ItemRecoverer[_]]
/**
* Registers a recoverer for [[S]].
*/
def register[S: ClassTag](recoverer: ItemRecoverer[S]): Unit = {
recoverers(classTag[S].runtimeClass) = recoverer
}
/**
* Attempts to recover an [[Item]] from `source`.
*
* Checks superclasses and traits while looking for a recoverer.
*/
def recover[A](source: A): Option[Item] = {
linearizationOrder(source.getClass.asInstanceOf[Class[_]])
.flatMap(recoverers.get)
.map(_.asInstanceOf[ItemRecoverer[A]].recover(source))
.nextOption()
}
register[entity.APU](ApuItem.Factory)
register[entity.CPU](CpuItem.Factory)
register[entity.DataCard.Tier1](DataCardItem.Tier1.Factory)
register[entity.DataCard.Tier2](DataCardItem.Tier2.Factory)
register[entity.DataCard.Tier3](DataCardItem.Tier3.Factory)
register[entity.EEPROM](EepromItem.Factory)
register[Floppy](FloppyItem.Factory)
register[entity.GraphicsCard](GraphicsCardItem.Factory)
register[entity.HDDManaged](HddItem.Factory.ManagedRecoverer)
register[entity.HDDUnmanaged](HddItem.Factory.UnmangedRecoverer)
register[entity.InternetCard](InternetCardItem.Factory)
register[entity.LinkedCard](LinkedCardItem.Factory)
register[entity.Memory](MemoryItem.Factory)
register[entity.NetworkCard](NetworkCardItem.Factory)
register[entity.Redstone.Tier1](RedstoneCardItem.Tier1.Factory)
register[entity.Redstone.Tier2](RedstoneCardItem.Tier2.Factory)
register[entity.SelfDestructingCard](SelfDestructingCardItem.Factory)
register[SoundCard](SoundCardItem.Factory)
register[entity.WirelessNetworkCard.Tier1](WirelessNetworkCardItem.Tier1.Factory)
register[entity.WirelessNetworkCard.Tier2](WirelessNetworkCardItem.Tier2.Factory)
}

View File

@@ -23,7 +23,7 @@ import scala.annotation.tailrec
*
* When a new [[Item]] is added to the Desktop inventory, its [[ComponentItem.component]] is added to
* the [[brainInventory]]. When a new [[Entity]] is added to the [[brainInventory]], an [[Item]] is recovered from it
* by using an appropriate [[ItemRecoverer]] from [[Recoverers the registry]].
* by using an appropriate [[ItemRecoverer]] from [[Items the registry]].
*
* While synchronizing, relies on the convergence of changes, but guards against stack overflows
* by limiting the recursion depth.
@@ -170,7 +170,7 @@ trait SyncedInventory extends PersistedInventory with Logging {
case SyncDirection.BrainToDesktop =>
val item = brainInventory.inventory(slotIndex).get match {
case Some(entity) => Recoverers.recover(entity) match {
case Some(entity) => Items.recover(entity) match {
case Some(item) => Some(item.asInstanceOf[I])
case None =>

View File

@@ -26,11 +26,7 @@ object ApuItem {
override def icon: IconDef = Icons.Apu(_tier)
override def build(): ApuItem = new ApuItem(new APU(_tier))
}
object Factory extends ItemRecoverer[APU] {
override type I = ApuItem
override def recover(source: APU): ApuItem = new ApuItem(source)
override def recoverers: Iterable[ItemRecoverer[_, _]] = Some(ItemRecoverer(new ApuItem(_)))
}
}

View File

@@ -26,11 +26,7 @@ object CpuItem {
override def icon: IconDef = Icons.Cpu(_tier)
override def build(): CpuItem = new CpuItem(new CPU(_tier))
}
object Factory extends ItemRecoverer[CPU] {
override type I = CpuItem
override def recover(source: CPU): CpuItem = new CpuItem(source)
override def recoverers: Iterable[ItemRecoverer[_, _]] = Some(ItemRecoverer(new CpuItem(_)))
}
}

View File

@@ -24,7 +24,7 @@ object DataCardItem {
}
object Tier1 {
object Factory extends Factory with ItemRecoverer[DataCard.Tier1] {
object Factory extends Factory {
override type I = DataCardItem.Tier1
override val itemClass: Class[I] = classOf
@@ -33,7 +33,7 @@ object DataCardItem {
override def build(): DataCardItem.Tier1 = new DataCardItem.Tier1(new DataCard.Tier1)
override def recover(source: DataCard.Tier1): DataCardItem.Tier1 = new DataCardItem.Tier1(source)
override def recoverers: Iterable[ItemRecoverer[_, _]] = Some(ItemRecoverer(new DataCardItem.Tier1(_)))
}
}
@@ -44,7 +44,7 @@ object DataCardItem {
}
object Tier2 {
object Factory extends Factory with ItemRecoverer[DataCard.Tier2] {
object Factory extends Factory {
override type I = DataCardItem.Tier2
override val itemClass: Class[I] = classOf
@@ -53,7 +53,7 @@ object DataCardItem {
override def build(): DataCardItem.Tier2 = new DataCardItem.Tier2(new DataCard.Tier2)
override def recover(source: DataCard.Tier2): DataCardItem.Tier2 = new DataCardItem.Tier2(source)
override def recoverers: Iterable[ItemRecoverer[_, _]] = Some(ItemRecoverer(new DataCardItem.Tier2(_)))
}
}
@@ -64,7 +64,7 @@ object DataCardItem {
}
object Tier3 {
object Factory extends Factory with ItemRecoverer[DataCard.Tier3] {
object Factory extends Factory {
override type I = DataCardItem.Tier3
override val itemClass: Class[I] = classOf
@@ -73,7 +73,7 @@ object DataCardItem {
override def build(): DataCardItem.Tier3 = new DataCardItem.Tier3(new DataCard.Tier3)
override def recover(source: DataCard.Tier3): DataCardItem.Tier3 = new DataCardItem.Tier3(source)
override def recoverers: Iterable[ItemRecoverer[_, _]] = Some(ItemRecoverer(new DataCardItem.Tier3(_)))
}
}
}

View File

@@ -95,13 +95,11 @@ object EepromItem {
override def tier: Option[Tier] = None
override def icon: IconDef = Icons.Eeprom
override def recoverers: Iterable[ItemRecoverer[_, _]] = Some(ItemRecoverer(new EepromItem(_)))
}
object Factory extends ItemRecoverer[EEPROM] {
override type I = EepromItem
override def recover(source: EEPROM): EepromItem = new EepromItem(source)
object Factory {
class Loot(factory: EEPROMFactory) extends Factory {
override def name: String = factory.label

View File

@@ -73,13 +73,11 @@ object FloppyItem {
override def tier: Option[Tier] = None
override def icon: IconDef = Icons.FloppyDisk(color)
override def recoverers: Iterable[ItemRecoverer[_, _]] = Some(ItemRecoverer(new FloppyItem(_)))
}
object Factory extends ItemRecoverer[Floppy] {
override type I = FloppyItem
override def recover(source: Floppy): FloppyItem = new FloppyItem(source)
object Factory {
class Loot(factory: FloppyFactory) extends Factory(Some(factory.name), factory.color, managed = true) {
override def name: String = factory.name

View File

@@ -24,11 +24,7 @@ object GraphicsCardItem {
override val icon: IconDef = Icons.GraphicsCard(_tier)
override def build(): GraphicsCardItem = new GraphicsCardItem(new GraphicsCard(_tier))
}
object Factory extends ItemRecoverer[GraphicsCard] {
override type I = GraphicsCardItem
override def recover(source: GraphicsCard): GraphicsCardItem = new GraphicsCardItem(source)
override def recoverers: Iterable[ItemRecoverer[_, _]] = Some(ItemRecoverer(new GraphicsCardItem(_)))
}
}

View File

@@ -74,19 +74,10 @@ object HddItem {
if (managed) Hdd.Managed(new HDDManaged(_tier))
else Hdd.Unmanaged(new HDDUnmanaged(_tier)),
)
}
object Factory {
object ManagedRecoverer extends ItemRecoverer[HDDManaged] {
override type I = HddItem
override def recover(source: HDDManaged): HddItem = new HddItem(Hdd.Managed(source))
}
object UnmangedRecoverer extends ItemRecoverer[HDDUnmanaged] {
override type I = HddItem
override def recover(source: HDDUnmanaged): HddItem = new HddItem(Hdd.Unmanaged(source))
}
override def recoverers: Iterable[ItemRecoverer[_, _]] = Seq(
ItemRecoverer((hdd: HDDManaged) => new HddItem(Hdd.Managed(hdd))),
ItemRecoverer((hdd: HDDUnmanaged) => new HddItem(Hdd.Unmanaged(hdd))),
)
}
}

View File

@@ -15,7 +15,7 @@ class InternetCardItem(val card: InternetCard) extends Item with ComponentItem w
}
object InternetCardItem {
object Factory extends ItemFactory with ItemRecoverer[InternetCard] {
object Factory extends ItemFactory {
override type I = InternetCardItem
override def itemClass: Class[InternetCardItem] = classOf
@@ -28,6 +28,6 @@ object InternetCardItem {
override def build(): InternetCardItem = new InternetCardItem(new InternetCard)
override def recover(source: InternetCard): InternetCardItem = new InternetCardItem(source)
override def recoverers: Iterable[ItemRecoverer[_, _]] = Some(ItemRecoverer(new InternetCardItem(_)))
}
}

View File

@@ -40,7 +40,7 @@ class LinkedCardItem(val linkedCard: LinkedCard) extends Item with ComponentItem
}
object LinkedCardItem {
object Factory extends ItemFactory with ItemRecoverer[LinkedCard] {
object Factory extends ItemFactory {
override type I = LinkedCardItem
override val itemClass: Class[I] = classOf
@@ -53,6 +53,6 @@ object LinkedCardItem {
override def build(): LinkedCardItem = new LinkedCardItem(new LinkedCard)
override def recover(source: LinkedCard): LinkedCardItem = new LinkedCardItem(source)
override def recoverers: Iterable[ItemRecoverer[_, _]] = Some(ItemRecoverer(new LinkedCardItem(_)))
}
}

View File

@@ -27,11 +27,7 @@ object MemoryItem {
override def icon: IconDef = Icons.Memory(memoryTier)
override def build(): MemoryItem = new MemoryItem(new Memory(memoryTier))
}
object Factory extends ItemRecoverer[Memory] {
override type I = MemoryItem
override def recover(source: Memory): MemoryItem = new MemoryItem(source)
override def recoverers: Iterable[ItemRecoverer[_, _]] = Some(ItemRecoverer(new MemoryItem(_)))
}
}

View File

@@ -15,7 +15,7 @@ class NetworkCardItem(val networkCard: NetworkCard) extends Item with ComponentI
}
object NetworkCardItem {
object Factory extends ItemFactory with ItemRecoverer[NetworkCard] {
object Factory extends ItemFactory {
override type I = NetworkCardItem
override val itemClass: Class[I] = classOf
@@ -26,6 +26,6 @@ object NetworkCardItem {
override def build(): NetworkCardItem = new NetworkCardItem(new NetworkCard)
override def recover(source: NetworkCard): NetworkCardItem = new NetworkCardItem(source)
override def recoverers: Iterable[ItemRecoverer[_, _]] = Some(ItemRecoverer(new NetworkCardItem(_)))
}
}

View File

@@ -21,7 +21,7 @@ object RedstoneCardItem {
}
class Tier1(val redstoneCard: Redstone.Tier1) extends RedstoneCardItem {
protected val redstoneIoWindow: WindowProvider = new WindowProvider {
private val redstoneIoWindow: WindowProvider = new WindowProvider {
override val windowEntryLabel: String = "Redstone I/O"
override def makeWindow(): Window = new Redstone1Window(redstoneCard)
@@ -42,7 +42,7 @@ object RedstoneCardItem {
}
object Tier1 {
object Factory extends Factory with ItemRecoverer[Redstone.Tier1] {
object Factory extends Factory {
override type I = RedstoneCardItem.Tier1
override val itemClass: Class[I] = classOf
@@ -51,7 +51,7 @@ object RedstoneCardItem {
override def build(): RedstoneCardItem.Tier1 = new RedstoneCardItem.Tier1(new Redstone.Tier1)
override def recover(source: Redstone.Tier1): RedstoneCardItem.Tier1 = new RedstoneCardItem.Tier1(source)
override def recoverers: Iterable[ItemRecoverer[_, _]] = Some(ItemRecoverer(new RedstoneCardItem.Tier1(_)))
}
}
@@ -73,7 +73,7 @@ object RedstoneCardItem {
}
object Tier2 {
object Factory extends Factory with ItemRecoverer[Redstone.Tier2] {
object Factory extends Factory {
override type I = RedstoneCardItem.Tier2
override val itemClass: Class[I] = classOf
@@ -82,7 +82,7 @@ object RedstoneCardItem {
override def build(): RedstoneCardItem.Tier2 = new RedstoneCardItem.Tier2(new Redstone.Tier2)
override def recover(source: Redstone.Tier2): RedstoneCardItem.Tier2 = new RedstoneCardItem.Tier2(source)
override def recoverers: Iterable[ItemRecoverer[_, _]] = Some(ItemRecoverer(new RedstoneCardItem.Tier2(_)))
}
}
}

View File

@@ -20,7 +20,7 @@ class SelfDestructingCardItem(val card: SelfDestructingCard)
}
object SelfDestructingCardItem {
object Factory extends ItemFactory with ItemRecoverer[SelfDestructingCard] {
object Factory extends ItemFactory {
override type I = SelfDestructingCardItem
override val itemClass: Class[I] = classOf
@@ -33,6 +33,6 @@ object SelfDestructingCardItem {
override def build(): SelfDestructingCardItem = new SelfDestructingCardItem(new SelfDestructingCard)
override def recover(source: SelfDestructingCard): SelfDestructingCardItem = new SelfDestructingCardItem(source)
override def recoverers: Iterable[ItemRecoverer[_, _]] = Some(ItemRecoverer(new SelfDestructingCardItem(_)))
}
}

View File

@@ -29,7 +29,7 @@ class SoundCardItem(val soundCard: SoundCard) extends Item with ComponentItem wi
}
object SoundCardItem {
object Factory extends ItemFactory with ItemRecoverer[SoundCard] {
object Factory extends ItemFactory {
override type I = SoundCardItem
override val itemClass: Class[I] = classOf
@@ -42,6 +42,6 @@ object SoundCardItem {
override def build(): SoundCardItem = new SoundCardItem(new SoundCard)
override def recover(source: SoundCard): SoundCardItem = new SoundCardItem(source)
override def recoverers: Iterable[ItemRecoverer[_, _]] = Some(ItemRecoverer(new SoundCardItem(_)))
}
}

View File

@@ -29,7 +29,7 @@ object WirelessNetworkCardItem {
}
object Tier1 {
object Factory extends Factory with ItemRecoverer[WirelessNetworkCard.Tier1] {
object Factory extends Factory {
override type I = WirelessNetworkCardItem.Tier1
override val itemClass: Class[I] = classOf
@@ -39,8 +39,7 @@ object WirelessNetworkCardItem {
override def build(): WirelessNetworkCardItem.Tier1 =
new WirelessNetworkCardItem.Tier1(new WirelessNetworkCard.Tier1)
override def recover(source: WirelessNetworkCard.Tier1): WirelessNetworkCardItem.Tier1 =
new WirelessNetworkCardItem.Tier1(source)
override def recoverers: Iterable[ItemRecoverer[_, _]] = Some(ItemRecoverer(new WirelessNetworkCardItem.Tier1(_)))
}
}
@@ -49,7 +48,7 @@ object WirelessNetworkCardItem {
}
object Tier2 {
object Factory extends Factory with ItemRecoverer[WirelessNetworkCard.Tier2] {
object Factory extends Factory {
override type I = WirelessNetworkCardItem.Tier2
override val itemClass: Class[I] = classOf
@@ -59,8 +58,7 @@ object WirelessNetworkCardItem {
override def build(): WirelessNetworkCardItem.Tier2 =
new WirelessNetworkCardItem.Tier2(new WirelessNetworkCard.Tier2)
override def recover(source: WirelessNetworkCard.Tier2): WirelessNetworkCardItem.Tier2 =
new WirelessNetworkCardItem.Tier2(source)
override def recoverers: Iterable[ItemRecoverer[_, _]] = Some(ItemRecoverer(new WirelessNetworkCardItem.Tier2(_)))
}
}
}

View File

@@ -1,145 +1,63 @@
package ocelot.desktop.ui.widget.slot
import ocelot.desktop.inventory.item._
import ocelot.desktop.inventory.{Item, ItemFactory}
import ocelot.desktop.graphics.IconDef
import ocelot.desktop.inventory.Items.{ArbitraryItemGroup, ExtendedTieredItemGroup, SingletonItemGroup, TieredItemGroup}
import ocelot.desktop.inventory._
import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry, ContextMenuSubmenu}
import totoro.ocelot.brain.loot.Loot
import totoro.ocelot.brain.util.ExtendedTier.ExtendedTier
import totoro.ocelot.brain.util.{ExtendedTier, Tier}
import totoro.ocelot.brain.util.Tier.Tier
import scala.collection.mutable.ArrayBuffer
class ItemChooser[I <: Item](slot: SlotWidget[I]) extends ContextMenu {
for (entryFactory <- ItemChooser.Factories; entry <- entryFactory.make(slot)) {
addEntry(entry)
import ItemChooser._
private def makeMenuEntry(factory: ItemFactory, label: String): ContextMenuEntry = {
ContextMenuEntry(label, icon = Some(factory.icon)) {
slot.item = factory.build().asInstanceOf[I]
}
}
private def addSubmenu[T: HasLabel](
name: String,
factories: Seq[(T, ItemFactory)],
icon: Option[IconDef] = None
): Unit = {
val tierLabel = implicitly[HasLabel[T]].label _
val acceptedFactories = factories.iterator.filter(p => slot.isItemAccepted(p._2)).toSeq
if (acceptedFactories.nonEmpty) {
addEntry(
new ContextMenuSubmenu(name, icon = Some(icon.getOrElse(acceptedFactories.last._2.icon))) {
for ((tier, factory) <- acceptedFactories) {
addEntry(makeMenuEntry(factory, tierLabel(tier)))
}
}
)
}
}
for (group <- Items.groups) {
group match {
case SingletonItemGroup(name, factory) =>
if (slot.isItemAccepted(factory)) {
addEntry(makeMenuEntry(factory, name))
}
case TieredItemGroup(name, factories) => addSubmenu(name, factories)
case ExtendedTieredItemGroup(name, factories) => addSubmenu(name, factories)
case ArbitraryItemGroup(name, icon, factories) => addSubmenu(name, factories, Some(icon))
}
}
}
object ItemChooser {
private trait EntryFactory {
def make[I <: Item](slot: SlotWidget[I]): Option[ContextMenuEntry]
private trait HasLabel[A] {
def label(value: A): String
}
private def makeMenuEntry[I <: Item](slot: SlotWidget[I], factory: ItemFactory, label: String): ContextMenuEntry =
ContextMenuEntry(label, icon = Some(factory.icon)) {
slot.item = factory.build().asInstanceOf[I]
}
private case class SingletonEntryFactory(factory: ItemFactory) extends EntryFactory {
override def make[I <: Item](slot: SlotWidget[I]): Option[ContextMenuEntry] = {
Option.when(slot.isItemAccepted(factory)) {
makeMenuEntry(slot, factory, factory.name)
}
}
}
private case class TieredEntryFactory[T: TierLike](
name: String,
tiers: Iterable[T],
)(
factoryMaker: T => ItemFactory,
) extends EntryFactory {
override def make[I <: Item](slot: SlotWidget[I]): Option[ContextMenuEntry] = {
val tierLabel = implicitly[TierLike[T]].label _
val factories = tiers
.map(tier => (tier, factoryMaker(tier)))
.filter(p => slot.isItemAccepted(p._2))
.toArray
Option.unless(factories.isEmpty) {
new ContextMenuSubmenu(name, icon = Some(factories.last._2.icon)) {
for ((tier, factory) <- factories) {
addEntry(makeMenuEntry(slot, factory, tierLabel(tier)))
}
}
}
}
}
private trait TierLike[A] {
def label(tier: A): String
}
private implicit val TierIsTierLike: TierLike[Tier] = _.label
private implicit val ExtendedTierIsTierLike: TierLike[ExtendedTier] = _.label
private object FloppyEntryFactory extends EntryFactory {
override def make[I <: Item](slot: SlotWidget[I]): Option[ContextMenuEntry] = {
Option.when(slot.isItemAccepted(FloppyItem.Factory.Empty)) {
new ContextMenuSubmenu("Floppy", icon = Some(FloppyItem.Factory.Empty.icon)) {
def addEntry(factory: FloppyItem.Factory, label: String): Unit = {
addEntry(makeMenuEntry(slot, factory, label))
}
def addEntry(factory: FloppyItem.Factory): Unit = {
addEntry(factory, factory.name)
}
for (lootFactory <- Loot.Floppies) {
addEntry(new FloppyItem.Factory.Loot(lootFactory))
}
addEntry(FloppyItem.Factory.Empty, "Empty")
}
}
}
}
private object EepromEntryFactory extends EntryFactory {
override def make[I <: Item](slot: SlotWidget[I]): Option[ContextMenuEntry] = {
Option.when(slot.isItemAccepted(EepromItem.Factory.Empty)) {
new ContextMenuSubmenu("EEPROM", icon = Some(EepromItem.Factory.Empty.icon)) {
def addEntry(factory: EepromItem.Factory, label: String): Unit = {
addEntry(makeMenuEntry(slot, factory, label))
}
def addEntry(factory: EepromItem.Factory): Unit = {
addEntry(factory, factory.name)
}
for (lootFactory <- Loot.Eeproms) {
addEntry(new EepromItem.Factory.Loot(lootFactory))
}
addEntry(EepromItem.Factory.Empty, "Empty")
}
}
}
}
// Entry factories //
private val Factories = ArrayBuffer.empty[EntryFactory]
Factories += TieredEntryFactory("CPU", Tier.One to Tier.Three)(new CpuItem.Factory(_))
Factories += TieredEntryFactory("APU", Tier.One to Tier.Three)(new ApuItem.Factory(_))(_.saturatingAdd(1).label)
Factories += TieredEntryFactory("Memory", ExtendedTier.One to ExtendedTier.ThreeHalf)(new MemoryItem.Factory(_))
// TODO: unmanaged
Factories += TieredEntryFactory("HDD", Tier.One to Tier.Three)(new HddItem.Factory(managed = true, _))
Factories += FloppyEntryFactory
Factories += EepromEntryFactory
Factories += TieredEntryFactory("Graphics Card", Tier.One to Tier.Three)(new GraphicsCardItem.Factory(_))
Factories += SingletonEntryFactory(NetworkCardItem.Factory)
Factories += TieredEntryFactory("Wireless Net. Card", Tier.One to Tier.Two) {
case Tier.One => WirelessNetworkCardItem.Tier1.Factory
case Tier.Two => WirelessNetworkCardItem.Tier2.Factory
}
Factories += SingletonEntryFactory(LinkedCardItem.Factory)
Factories += SingletonEntryFactory(InternetCardItem.Factory)
Factories += TieredEntryFactory("Redstone Card", Tier.One to Tier.Two) {
case Tier.One => RedstoneCardItem.Tier1.Factory
case Tier.Two => RedstoneCardItem.Tier2.Factory
}
Factories += TieredEntryFactory("Data Card", Tier.One to Tier.Three) {
case Tier.One => DataCardItem.Tier1.Factory
case Tier.Two => DataCardItem.Tier2.Factory
case Tier.Three => DataCardItem.Tier3.Factory
}
Factories += SingletonEntryFactory(SoundCardItem.Factory)
Factories += SingletonEntryFactory(SelfDestructingCardItem.Factory)
private implicit val TierHasLabel: HasLabel[Tier] = _.label
private implicit val ExtendedTierHasLabel: HasLabel[ExtendedTier] = _.label
private implicit val StringHasLabel: HasLabel[String] = identity
}