Configurator upgrade, currently supports EIO conduits

Fixed forestry item converter, adding empty circuit list to all items
Added inventory_controller.installUpgrade function for robots, to use upgrade containers at runtime
This commit is contained in:
repo_alt 2022-02-01 11:56:10 +03:00
parent 872795c40f
commit 9245aa45d6
13 changed files with 386 additions and 10 deletions

View File

@ -21,16 +21,16 @@ dependencies {
compileOnly("com.github.GTNewHorizons:Railcraft:9.13.5:dev") {
transitive = false
}
compileOnly("com.github.GTNewHorizons:NotEnoughItems:2.2.3-GTNH:dev") {
compile("com.github.GTNewHorizons:NotEnoughItems:2.2.3-GTNH:dev") {
transitive = false
}
compileOnly("com.github.GTNewHorizons:ForgeMultipart:1.2.7:dev") {
transitive = false
}
compileOnly("com.github.GTNewHorizons:CodeChickenLib:1.1.5.1:dev") {
compile("com.github.GTNewHorizons:CodeChickenLib:1.1.5.1:dev") {
transitive = false
}
compileOnly("com.github.GTNewHorizons:CodeChickenCore:1.1.3:dev") {
compile("com.github.GTNewHorizons:CodeChickenCore:1.1.3:dev") {
transitive = false
}
compileOnly("com.github.GTNewHorizons:waila:1.5.18:dev") {
@ -54,7 +54,10 @@ dependencies {
compileOnly("com.github.GTNewHorizons:ExtraCells2:2.5.4:dev") {
transitive = false
}
compileOnly("com.github.GTNewHorizons:EnderIO:2.3.1.27:dev") {
compile("com.github.GTNewHorizons:EnderCore:0.2.6:dev") {
transitive = false
}
compile("com.github.GTNewHorizons:EnderIO:2.3.1.27:dev") {
transitive = false
}
compileOnly("com.github.GTNewHorizons:Avaritiaddons:1.5.2-GTNH:dev") {
@ -66,7 +69,6 @@ dependencies {
compileOnly("com.github.GTNewHorizons:WirelessRedstone-CBE:1.4.4:dev") {
transitive = false
}
compileOnly("appeng:RotaryCraft:V5c:api") {
transitive = false
}
@ -116,6 +118,7 @@ dependencies {
compileOnly files("dependencies/ic2classic-api.zip") //curseforge one does NOT work ...
compileOnly(deobf("https://github.com/purpleposeidon/fz_archive/raw/master/old/Factorization-1.7.10-0.8.108.jar"))
compileOnly("api:coloredlightscore:1")
compileOnly(deobf("http://immibis.com/mcmoddl/files/immibis-microblocks-59.1.2.jar"))
testCompile("org.mockito:mockito-all:1.10.19")
testCompile("org.scalactic:scalactic_2.11:2.2.6")

View File

@ -166,6 +166,7 @@ item.oc.Server3.name=Server (Tier 4)
item.oc.TabletCase3.name=Tablet Case (Tier 3)
item.oc.UpgradeBeekeeper.name=Beekeeper Upgrade
item.oc.UpgradeRITEG.name=RITEG Upgrade
item.oc.UpgradeConfigurator.name=Configurator Upgrade
# Entities
entity.oc.Drone.name=Drone

View File

@ -249,6 +249,13 @@ beekeeperUpgrade {
["oc:circuitChip3", {item="Forestry:beealyzer"}, "oc:circuitChip3"]
[ingotGold, "oc:materialCircuitBoardPrinted", ingotGold]]
}
configuratorUpgrade {
input: [[ingotIron, "", ingotIron]
["oc:circuitChip1", "oc:wrench", "oc:circuitChip1"]
[ingotIron, "oc:materialCircuitBoardPrinted", ingotIron]]
}
chunkloaderUpgrade {
input: [[ingotGold, blockGlass, ingotGold]
["oc:circuitChip3", eyeOfEnder, "oc:circuitChip3"]
@ -710,3 +717,4 @@ appengTunnel {
type: shapeless
input: [{item="appliedenergistics2:item.ItemMultiPart", subID=460}, "oc:adapter"]
}
ritegUpgrade = false

Binary file not shown.

After

Width:  |  Height:  |  Size: 585 B

View File

@ -76,6 +76,7 @@ object Constants {
final val ComponentBusTier2 = "componentBus2"
final val ComponentBusTier3 = "componentBus3"
final val ComponentBusCreative = "componentBusCreative"
final val ConfiguratorUpgrade = "configuratorUpgrade"
final val CPUTier1 = "cpu1"
final val CPUTier2 = "cpu2"
final val CPUTier3 = "cpu3"

View File

@ -14,8 +14,7 @@ import li.cil.oc.common.Loot
import li.cil.oc.common.Tier
import li.cil.oc.common.block.SimpleBlock
import li.cil.oc.common.item
import li.cil.oc.common.item.Delegator
import li.cil.oc.common.item.UpgradeLeash
import li.cil.oc.common.item.{Delegator, UpgradeConfigurator, UpgradeLeash}
import li.cil.oc.common.item.data.DroneData
import li.cil.oc.common.item.data.HoverBootsData
import li.cil.oc.common.item.data.MicrocontrollerData
@ -551,6 +550,10 @@ object Items extends ItemAPI {
Recipes.addSubItem(new item.WirelessNetworkCard(multi, Tier.One), Constants.ItemName.WirelessNetworkCardTier1, "oc:wlanCard1")
registerItem(new item.ComponentBus(multi, Tier.Four), Constants.ItemName.ComponentBusCreative)
// GTNH
Recipes.addSubItem(new UpgradeConfigurator(multi), Constants.ItemName.ConfiguratorUpgrade, "oc:configuratorUpgrade")
Recipes.addSubItem(new item.UpgradeRITEG(multi), Constants.ItemName.RITEGUpgrade)
// Register aliases.

View File

@ -0,0 +1,3 @@
package li.cil.oc.common.item
class UpgradeConfigurator(val parent: Delegator) extends traits.Delegate with traits.ItemTier

View File

@ -39,7 +39,7 @@ object Mods {
val CraftingCosts = new SimpleMod(IDs.CraftingCosts)
val DeepStorageUnit = new ClassBasedMod(IDs.DeepStorageUnit, "powercrystals.minefactoryreloaded.api.IDeepStorageUnit")
val ElectricalAge = new SimpleMod(IDs.ElectricalAge)
val EnderIO = new SimpleMod(IDs.EnderIO, version = "@[2.2,2.3)")
val EnderIO = new SimpleMod(IDs.EnderIO, version = "@[2.2,)")
val EnderStorage = new SimpleMod(IDs.EnderStorage)
val ExtraCells = new SimpleMod(IDs.ExtraCells, version = "@[2.2.73,)")
val Factorization = new SimpleMod(IDs.Factorization)

View File

@ -16,7 +16,8 @@ object ConverterItemStack extends Converter {
case stack: ItemStack if ChipsetManager.circuitRegistry.getCircuitboard(stack) != null => {
val cc = ChipsetManager.circuitRegistry.getCircuitboard(stack).getCircuits
val names = cc.collect{case c: ICircuit => c.getName}
output += "circuits" -> names
if (names.length > 0)
output += "circuits" -> names
}
case _ =>
}

View File

@ -0,0 +1,39 @@
package li.cil.oc.integration.opencomputers
import li.cil.oc.api.driver.EnvironmentProvider
import li.cil.oc.{Constants, api}
import li.cil.oc.api.driver.item.HostAware
import li.cil.oc.api.internal.Adapter
import li.cil.oc.api.network.{EnvironmentHost, ManagedEnvironment}
import li.cil.oc.common.entity.Drone
import li.cil.oc.common.tileentity.Robot
import li.cil.oc.common.tileentity.Microcontroller
import li.cil.oc.common.{Slot, Tier}
import li.cil.oc.server.component
import net.minecraft.item.ItemStack
object DriverUpgradeConfigurator extends Item with HostAware {
override def worksWith(stack: ItemStack): Boolean = isOneOf(stack,
api.Items.get(Constants.ItemName.ConfiguratorUpgrade))
override def createEnvironment(stack: ItemStack, host: EnvironmentHost) : ManagedEnvironment =
if (host.world != null && host.world.isRemote) null
else host match {
case host: EnvironmentHost with Adapter => new component.UpgradeConfigurator.Adapter(host)
case host: EnvironmentHost with Drone => new component.UpgradeConfigurator.Drone(host)
case host: EnvironmentHost with Robot => new component.UpgradeConfigurator.Robot(host)
case host: EnvironmentHost with Microcontroller => new component.UpgradeConfigurator.Microcontroller(host)
case _ => null
}
override def slot(stack: ItemStack): String = Slot.Upgrade
override def tier(stack: ItemStack): Int = Tier.Two
object Provider extends EnvironmentProvider {
override def getEnvironment(stack: ItemStack): Class[_] =
if (worksWith(stack))
classOf[component.UpgradeConfigurator.Robot]
else null
}
}

View File

@ -172,6 +172,7 @@ object ModOpenComputers extends ModProxy {
api.Driver.add(DriverUpgradeTractorBeam)
api.Driver.add(DriverUpgradeTrading)
api.Driver.add(DriverUpgradeMF)
api.Driver.add(DriverUpgradeConfigurator)
api.Driver.add(DriverAPU.Provider)
api.Driver.add(DriverDataCard.Provider)
@ -206,6 +207,7 @@ object ModOpenComputers extends ModProxy {
api.Driver.add(InventoryProviderDatabase)
api.Driver.add(InventoryProviderServer)
api.Driver.add(DriverUpgradeConfigurator.Provider)
blacklistHost(classOf[internal.Adapter],
Constants.BlockName.Geolyzer,
@ -313,7 +315,9 @@ object ModOpenComputers extends ModProxy {
Constants.ItemName.TankControllerUpgrade,
Constants.ItemName.LeashUpgrade,
Constants.ItemName.TradingUpgrade,
Constants.ItemName.BeekeeperUpgrade)
Constants.ItemName.BeekeeperUpgrade,
Constants.ItemName.ConfiguratorUpgrade
)
if (!WirelessRedstone.isAvailable) {
blacklistHost(classOf[internal.Drone], Constants.ItemName.RedstoneCardTier2)

View File

@ -0,0 +1,297 @@
package li.cil.oc.server.component
import java.util
import com.enderio.core.common.util.DyeColor
import crazypants.enderio.conduit.IConduitBundle
import crazypants.enderio.conduit.item.IItemConduit
import crazypants.enderio.conduit.item.filter.{IItemFilter, ItemFilter}
import crazypants.enderio.conduit.liquid.{AbstractEnderLiquidConduit, AbstractTankConduit, FluidFilter, ILiquidConduit}
import li.cil.oc.Constants
import li.cil.oc.api.driver.DeviceInfo
import li.cil.oc.api.driver.DeviceInfo.{DeviceAttribute, DeviceClass}
import li.cil.oc.api.machine.{Arguments, Callback, Context}
import li.cil.oc.api.network.{EnvironmentHost, Node, Visibility}
import li.cil.oc.api.{Network, internal, prefab}
import li.cil.oc.common.tileentity
import li.cil.oc.integration.Mods
import li.cil.oc.server.component.traits.{NetworkAware, SideRestricted, WorldAware}
import li.cil.oc.util.{BlockPosition, DatabaseAccess}
import li.cil.oc.util.ExtendedArguments.extendedArguments
import net.minecraft.inventory.IInventory
import net.minecraft.item.ItemStack
import net.minecraftforge.common.util.ForgeDirection
import net.minecraftforge.fluids.{FluidRegistry, FluidStack}
import scala.collection.convert.WrapAsJava._
object UpgradeConfigurator {
trait Common extends DeviceInfo {
private final lazy val deviceInfo = Map(
DeviceAttribute.Class -> DeviceClass.Generic,
DeviceAttribute.Description -> "External device configurator",
DeviceAttribute.Vendor -> Constants.DeviceInfo.DefaultVendor,
DeviceAttribute.Product -> "Sonic Screwdriver"
)
override def getDeviceInfo: util.Map[String, String] = deviceInfo
}
class Adapter(val host: EnvironmentHost) extends prefab.ManagedEnvironment with Configurator with Common {
override val node: Node = Network.newNode(this, Visibility.Network).
withComponent("configurator", Visibility.Network).
create()
override def position: BlockPosition = BlockPosition(host)
override protected def checkSideForAction(args: Arguments, n: Int): ForgeDirection = args.checkSideAny(n)
}
class Drone(val host: EnvironmentHost with internal.Agent) extends prefab.ManagedEnvironment with Configurator with Common {
override val node: Node = Network.newNode(this, Visibility.Network).
withComponent("configurator", Visibility.Neighbors).
create()
override def position: BlockPosition = BlockPosition(host)
override protected def checkSideForAction(args: Arguments, n: Int): ForgeDirection = args.checkSideAny(n)
}
class Robot(val host: EnvironmentHost with tileentity.Robot) extends prefab.ManagedEnvironment with Configurator with RobotConfigurator with Common {
override val node: Node = Network.newNode(this, Visibility.Network).
withComponent("configurator", Visibility.Neighbors).
create()
override def position: BlockPosition = BlockPosition(host)
override def inventory: IInventory = host.mainInventory
override def selectedSlot: Int = host.selectedSlot
override def selectedSlot_=(value: Int): Unit = host.setSelectedSlot(value)
override protected def checkSideForAction(args: Arguments, n: Int): ForgeDirection = host.toGlobal(args.checkSideForAction(n))
}
class Microcontroller(val host: EnvironmentHost with tileentity.Microcontroller) extends prefab.ManagedEnvironment with Configurator with Common {
override val node: Node = Network.newNode(this, Visibility.Network).
withComponent("configurator", Visibility.Neighbors).
create()
override def position: BlockPosition = BlockPosition(host)
override protected def checkSideForAction(args: Arguments, n: Int): ForgeDirection = host.toLocal(args.checkSideForAction(n))
}
trait ConfiguratorBase extends WorldAware
{
def conduitAt(position: BlockPosition): Option[IConduitBundle] =
position.world match {
case Some(world) => world.getTileEntity(position.x, position.y, position.z) match {
case conduit: IConduitBundle => Some(conduit)
}
case _ => None
}
def withItemConduit(side: ForgeDirection, f: IItemConduit => Array[AnyRef]): Array[AnyRef] =
conduitAt(position.offset(side)) match {
case Some(conduit) if conduit.hasType(classOf[IItemConduit])
=> f(conduit.getConduit(classOf[IItemConduit]))
case _ => result(Unit, "no item conduit here")
}
def withEnderFluidConduit(side: ForgeDirection, f: AbstractEnderLiquidConduit => Array[AnyRef]): Array[AnyRef] =
conduitAt(position.offset(side)) match {
case Some(conduit) if conduit.hasType(classOf[AbstractEnderLiquidConduit])
=> f(conduit.getConduit(classOf[AbstractEnderLiquidConduit]))
case _ => result(Unit, "no item conduit here")
}
}
trait Configurator extends ConfiguratorBase with SideRestricted with NetworkAware {
//noinspection ScalaUnusedSymbol
@Callback(doc = """function(side:number, direction: number):table -- Get conduit configuration at side facing direction""")
def getConduitConfiguration(context: Context, args: Arguments): Array[AnyRef] = if (Mods.EnderIO.isModAvailable) {
val facing = checkSideForAction(args, 0)
val dir = args.checkSideAny(1)
conduitAt(position.offset(facing)) match {
case Some(conduit) =>
var info = Map[AnyRef,AnyRef]("HasFacade" -> conduit.hasFacade.asInstanceOf[AnyRef])
if (conduit.hasType(classOf[IItemConduit]))
info += "ItemConduit" -> convert(dir, conduit.getConduit(classOf[IItemConduit]))
if (conduit.hasType(classOf[ILiquidConduit]))
info += "LiquidConduit" -> convert(dir, conduit.getConduit(classOf[ILiquidConduit]))
result(info)
case _ => result(null, "No conduit here")
}
}
else
result(null, "EnderIO not loaded")
//noinspection ScalaUnusedSymbol
@Callback(doc = """function(side:number, direction: number, color: string):boolean -- Set conduit input color at side facing direction""")
def setItemConduitInputColor(context: Context, args: Arguments): Array[AnyRef] = if (Mods.EnderIO.isModAvailable) {
val facing = checkSideForAction(args, 0)
val dir = args.checkSideAny(1)
withItemConduit(facing, c =>{
c.setInputColor(dir, DyeColor.valueOf(args.checkString(2)))
result(true)
})
}
else
result(false, "EnderIO not loaded")
//noinspection ScalaUnusedSymbol
@Callback(doc = """function(side:number, direction: number, color: string):boolean -- Set conduit output color at side facing direction""")
def setItemConduitOutputColor(context: Context, args: Arguments): Array[AnyRef] = if (Mods.EnderIO.isModAvailable) {
val facing = checkSideForAction(args, 0)
val dir = args.checkSideAny(1)
withItemConduit(facing, c => {
c.setOutputColor(dir, DyeColor.valueOf(args.checkString(2)))
result(true)
})
}
else
result(false, "EnderIO not loaded")
//noinspection ScalaUnusedSymbol
@Callback(doc = """function(side:number, direction: number, priority: number):boolean -- Set conduit output priority at side facing direction""")
def setItemConduitOutputPriority(context: Context, args: Arguments): Array[AnyRef] = if (Mods.EnderIO.isModAvailable) {
val facing = checkSideForAction(args, 0)
val dir = args.checkSideAny(1)
withItemConduit(facing, c => {
c.setOutputPriority(dir, args.checkInteger(2))
result(true)
})
}
else
result(false, "EnderIO not loaded")
//noinspection ScalaUnusedSymbol
@Callback(doc = """function(side:number, direction: number, database: string, dbSlot:number, filterIndex:number, isInput:boolean):boolean -- Set conduit input or output filter at side facing direction""")
def setItemConduitFilter(context: Context, args: Arguments): Array[AnyRef] = if (Mods.EnderIO.isModAvailable) {
val facing = checkSideForAction(args, 0)
val dir = args.checkSideAny(1)
val dbAddress = args.checkString(2)
withItemConduit(facing, c =>
DatabaseAccess.withDatabase(node, dbAddress, database => {
val dbSlot = args.checkSlot(database.data, 3)
val dbStack = database.getStackInSlot(dbSlot)
val filterSlot = args.checkInteger(4)
val isInput = args.checkBoolean(5)
val f = if (isInput) c.getInputFilter(dir) else c.getOutputFilter(dir)
if (f != null && f.getSlotCount > filterSlot && f.isInstanceOf[ItemFilter]) {
f.asInstanceOf[ItemFilter].setInventorySlotContents(filterSlot, dbStack)
result(true)
}
else result(false, "Wrong or no item filter")
})
)
}
else
result(false, "EnderIO not loaded")
//noinspection ScalaUnusedSymbol
@Callback(doc = """function(side:number, direction:number, fluid:string, blacklist:boolean, isInput:boolean[, filterIndex:number]):boolean -- Set ender liquid conduit filter at side facing direction""")
def setEnderLiquidConduitFilter(context: Context, args: Arguments): Array[AnyRef] = if (Mods.EnderIO.isModAvailable) {
val facing = checkSideForAction(args, 0)
val dir = args.checkSideAny(1)
val fluidName = args.checkString(2)
val filterIndex = args.optInteger(5, 0)
val ff = new FluidFilter
ff.setBlacklist(args.checkBoolean(3))
ff.setFluid(filterIndex, new FluidStack(FluidRegistry.getFluid(fluidName), 0))
withEnderFluidConduit(facing, c => {
c.setFilter(dir, ff, args.checkBoolean(4))
result(true)
})
}
else
result(false, "EnderIO not loaded")
//noinspection ScalaUnusedSymbol
@Callback(doc = """function(side:number, direction:number, isInput:boolean, color:string):boolean -- Set fluid conduit priority at side facing direction""")
def setEnderLiquidConduitColor(context: Context, args: Arguments): Array[AnyRef] = if (Mods.EnderIO.isModAvailable) {
val facing = checkSideForAction(args, 0)
val dir = args.checkSideAny(1)
withEnderFluidConduit(facing, c => {
val isInput = args.checkBoolean(2)
val color = DyeColor.valueOf(args.checkString(3))
if (isInput) c.setInputColor(dir, color) else c.setOutputColor(dir, color)
result(true)
})
}
else
result(false, "EnderIO not loaded")
private def convert(dir: ForgeDirection, ic: IItemConduit): Map[AnyRef, AnyRef] = Map(
"OutputColor" -> ic.getOutputColor(dir).getName,
"InputColor" -> ic.getInputColor(dir).getName,
"RoundRobinEnabled" -> ic.isRoundRobinEnabled(dir).asInstanceOf[AnyRef],
"SelfFeedEnabled" -> ic.isSelfFeedEnabled(dir).asInstanceOf[AnyRef],
"ExtractionRedstoneConditionMet" -> ic.isExtractionRedstoneConditionMet(dir).asInstanceOf[AnyRef],
"OutputPriority" -> ic.getOutputPriority(dir).asInstanceOf[AnyRef],
"InputFilter" -> convert(ic.getInputFilter(dir)),
"OutputFilter" -> convert(ic.getOutputFilter(dir))
)
private def convert(dir: ForgeDirection, ic: ILiquidConduit): Map[AnyRef, AnyRef] = ic match {
case c: AbstractEnderLiquidConduit => Map[AnyRef,AnyRef](
"InputFilter" -> convert(c.getFilter(dir, true)),
"OutputFilter" -> convert(c.getFilter(dir, false)),
"InputColor" -> c.getInputColor(dir).getName,
"OutputColor" -> c.getOutputColor(dir).getName
)
case c: AbstractTankConduit => Map[AnyRef,AnyRef](
"FluidType" -> c.getFluidType.getLocalizedName
)
case _ => null
}
private def convert(f: FluidFilter): Map[AnyRef, AnyRef] =
Map(
"blacklist"-> f.isBlacklist.asInstanceOf[AnyRef],
"1" -> Option(f.getFluidStackAt(0)).fold("")(_.getLocalizedName),
"2" -> Option(f.getFluidStackAt(1)).fold("")(_.getLocalizedName),
"3" -> Option(f.getFluidStackAt(2)).fold("")(_.getLocalizedName),
"4" -> Option(f.getFluidStackAt(3)).fold("")(_.getLocalizedName),
"5" -> Option(f.getFluidStackAt(4)).fold("")(_.getLocalizedName))
private def convert(f: IItemFilter): Map[AnyRef, AnyRef] = f match {
case f: ItemFilter =>
var filterInfo = Map[AnyRef,AnyRef](
"Advanced" -> f.isAdvanced.asInstanceOf[AnyRef],
"Blacklist" -> f.isBlacklist.asInstanceOf[AnyRef],
"MatchMeta" -> f.isMatchMeta.asInstanceOf[AnyRef],
"MatchNBT" -> f.isMatchNBT.asInstanceOf[AnyRef],
"UseOreDict" -> f.isUseOreDict.asInstanceOf[AnyRef],
"Sticky" -> f.isSticky.asInstanceOf[AnyRef],
"FuzzyMode" -> f.getFuzzyMode.toString
)
var items = List[ItemStack]()
for (i <- 0 to f.getSizeInventory) {
if (f.getStackInSlot(i) != null)
items = f.getStackInSlot(i) :: items
}
filterInfo += "FilterItems" -> items.toArray
filterInfo
case _ => Map[AnyRef,AnyRef]()
}
}
trait RobotConfigurator extends ConfiguratorBase with SideRestricted with NetworkAware with traits.InventoryAware {
//noinspection ScalaUnusedSymbol
@Callback(doc = """function(side:number, direction:number, input:boolean):boolean -- Replace conduit input or output filter at side facing direction with the filter in selected slot""")
def replaceConduitFilter(context: Context, args: Arguments): Array[AnyRef] = if (Mods.EnderIO.isModAvailable) {
val facing = checkSideForAction(args, 0)
val stack = inventory.getStackInSlot(selectedSlot)
val dir = args.checkSideAny(1)
val isInput = args.checkBoolean(2)
withItemConduit(facing, c => {
val old = if (isInput) c.getInputFilterUpgrade(dir) else c.getOutputFilterUpgrade(dir)
if (isInput) c.setInputFilterUpgrade(dir, stack) else c.setOutputFilterUpgrade(dir, stack)
inventory.setInventorySlotContents(selectedSlot, old)
result(true)
})
}
else
result(false, "EnderIO not loaded")
}
}

View File

@ -91,6 +91,22 @@ object UpgradeInventoryController {
}
else result(false)
}
@Callback(doc = """function([slot:number]):boolean -- Swaps the installed upgrade in the slot (1 by default) with the content of the currently selected inventory slot.""")
def installUpgrade(context: Context, args: Arguments): Array[AnyRef] = {
if (inventory.getSizeInventory > 0) {
val slot = args.optInteger(0, 1)
if (!host.isContainerSlot(slot))
return result(false, "not a container slot")
val selected = inventory.getStackInSlot(selectedSlot)
if (selected != null && !host.isItemValidForSlot(slot, selected))
return result(false, "Invalid upgrade")
val equipped = host.getStackInSlot(slot)
host.setInventorySlotContents(slot, selected)
inventory.setInventorySlotContents(selectedSlot, equipped)
result(true)
}
else result(false)
}
}
}