From 2d9cdfac7272e40ad97a630e5f35cf4cbafda4e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Wed, 1 Oct 2014 17:40:52 +0200 Subject: [PATCH] Added icons for tank upgrade and tank controller upgrade. Added localizations for the same. Added the tank controller upgrade. Added recipes for the two. Some fixery. --- assets/items.psd | Bin 514352 -> 519424 bytes src/main/resources/application.conf | 9 +- .../assets/opencomputers/lang/de_DE.lang | 4 + .../assets/opencomputers/lang/en_US.lang | 4 + .../opencomputers/recipes/default.recipes | 10 ++ .../textures/items/UpgradeTank.png | Bin 0 -> 693 bytes .../textures/items/UpgradeTankController.png | Bin 0 -> 631 bytes src/main/scala/li/cil/oc/Items.scala | 4 +- src/main/scala/li/cil/oc/Settings.scala | 7 + src/main/scala/li/cil/oc/common/Proxy.scala | 2 + .../common/item/UpgradeTankController.scala | 3 + .../UpgradeInventoryController.scala | 3 +- .../component/UpgradeTankController.scala | 125 ++++++++++++++++++ .../cil/oc/server/component/robot/Robot.scala | 71 +++------- .../server/driver/converter/FluidStack.scala | 25 ++++ .../driver/converter/FluidTankInfo.scala | 8 +- .../server/driver/converter/ItemStack.scala | 6 +- .../driver/item/UpgradeTankController.scala | 21 +++ 18 files changed, 236 insertions(+), 66 deletions(-) create mode 100644 src/main/resources/assets/opencomputers/textures/items/UpgradeTank.png create mode 100644 src/main/resources/assets/opencomputers/textures/items/UpgradeTankController.png create mode 100644 src/main/scala/li/cil/oc/common/item/UpgradeTankController.scala create mode 100644 src/main/scala/li/cil/oc/server/component/UpgradeTankController.scala create mode 100644 src/main/scala/li/cil/oc/server/driver/converter/FluidStack.scala create mode 100644 src/main/scala/li/cil/oc/server/driver/item/UpgradeTankController.scala diff --git a/assets/items.psd b/assets/items.psd index 7cbc4eeb7c3c04cc83c82460e73b262099aecf0e..18f11db5b6423da774a180cf4e4b572ff3cd1fbb 100644 GIT binary patch delta 29812 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 IDKqf@01*?iG5`Po delta 27554 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/opencomputers/textures/items/UpgradeTankController.png b/src/main/resources/assets/opencomputers/textures/items/UpgradeTankController.png new file mode 100644 index 0000000000000000000000000000000000000000..2ace967053162dfef825e94aeed7efab52324512 GIT binary patch literal 631 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbL!WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8&jCImu4!k|&ip?k7^=N*ck1)U#WVlUeDmfFpR=-byQ^T3 z7O$Pc>Xp$loo-X7PL-YDC7NcUupme%N^kr2?GmLn+$PeTT4F~I=J0!{@>t69+AB!a zJIw8|M5@V2 zB*{oP)*HJO$!4$Q59T#~W&%$>7u#mz(A* znr_OjEF_X>^#A{V0scqHK<@{Z1o;I6xl{!V8E;k`1RB2A)5S5Q;#SD{t3pi)0;~`A zzt|wa%KhfuyLS)%&UbSOxwHCiep>oD#%ZaHr;WccYJ6aCXo_R}#;ds0(8h_8eG-FH zNP242{L3*%DjPMFIA?5_aisaI`I6;|j&nE`X=t0N9Bho9vt*I~q{X+JCM#}vW#ql~ z)aUEbR~kPpxO9U5<(i3)gWgZ+P%HHLGu!=8@7bPXs&CSh-7mL(nVGQW*sr Array( + "misc.allowItemStackInspection" ) ) diff --git a/src/main/scala/li/cil/oc/common/Proxy.scala b/src/main/scala/li/cil/oc/common/Proxy.scala index 9c3b01c14..ce979281b 100644 --- a/src/main/scala/li/cil/oc/common/Proxy.scala +++ b/src/main/scala/li/cil/oc/common/Proxy.scala @@ -100,6 +100,7 @@ class Proxy { api.Driver.add(driver.item.UpgradeSign) api.Driver.add(driver.item.UpgradeSolarGenerator) api.Driver.add(driver.item.UpgradeTank) + api.Driver.add(driver.item.UpgradeTankController) api.Driver.add(driver.item.UpgradeTractorBeam) api.Driver.add(driver.item.WirelessNetworkCard) @@ -118,6 +119,7 @@ class Proxy { } OpenComputers.log.info("Initializing vanilla converters.") + api.Driver.add(driver.converter.FluidStack) api.Driver.add(driver.converter.FluidTankInfo) api.Driver.add(driver.converter.ItemStack) diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeTankController.scala b/src/main/scala/li/cil/oc/common/item/UpgradeTankController.scala new file mode 100644 index 000000000..34ccea9b5 --- /dev/null +++ b/src/main/scala/li/cil/oc/common/item/UpgradeTankController.scala @@ -0,0 +1,3 @@ +package li.cil.oc.common.item + +class UpgradeTankController(val parent: Delegator) extends Delegate with ItemTier \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeInventoryController.scala b/src/main/scala/li/cil/oc/server/component/UpgradeInventoryController.scala index dbfd81f16..d6a518243 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeInventoryController.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeInventoryController.scala @@ -29,7 +29,7 @@ class UpgradeInventoryController(val owner: Container with Robot) extends compon } @Callback(doc = """function():table -- Get a description of the stack in the the inventory on the specified side of the robot. Back refers to the robot's own inventory.""") - def getStackInSlot(context: Context, args: Arguments): Array[AnyRef] = { + def getStackInSlot(context: Context, args: Arguments): Array[AnyRef] = if (Settings.get.allowItemStackInspection) { val facing = checkSideForInventory(args, 0) val slot = args.checkInteger(1) - 1 if (facing == owner.facing.getOpposite) { @@ -43,6 +43,7 @@ class UpgradeInventoryController(val owner: Container with Robot) extends compon case _ => result(Unit, "no inventory") } } + else result(Unit, "not enabled in config") @Callback(doc = """function(facing:number, slot:number[, count:number]):boolean -- Drops the selected item stack into the specified slot of an inventory.""") def dropIntoSlot(context: Context, args: Arguments): Array[AnyRef] = { diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeTankController.scala b/src/main/scala/li/cil/oc/server/component/UpgradeTankController.scala new file mode 100644 index 000000000..ba13ad7c3 --- /dev/null +++ b/src/main/scala/li/cil/oc/server/component/UpgradeTankController.scala @@ -0,0 +1,125 @@ +package li.cil.oc.server.component + +import li.cil.oc.Settings +import li.cil.oc.api.Network +import li.cil.oc.api.driver.Container +import li.cil.oc.api.network._ +import li.cil.oc.common.component +import li.cil.oc.common.tileentity.Robot +import li.cil.oc.util.ExtendedArguments._ +import net.minecraft.item.ItemStack +import net.minecraftforge.common.ForgeDirection +import net.minecraftforge.fluids.{IFluidContainerItem, FluidContainerRegistry, IFluidHandler} + +class UpgradeTankController(val owner: Container with Robot) extends component.ManagedComponent { + val node = Network.newNode(this, Visibility.Network). + withComponent("tank_controller", Visibility.Neighbors). + withConnector(). + create() + + // ----------------------------------------------------------------------- // + + @Callback(doc = """function(side:number):number -- Get the capacity of the tank on the specified side of the robot. Back refers to the robot's own selected tank.""") + def getTankCapacity(context: Context, args: Arguments): Array[AnyRef] = { + val facing = checkSideForTank(args, 0) + if (facing == owner.facing.getOpposite) result(owner.getFluidTank(owner.selectedTank).fold(0)(_.getCapacity)) + else owner.world.getBlockTileEntity(math.floor(owner.xPosition).toInt + facing.offsetX, math.floor(owner.yPosition).toInt + facing.offsetY, math.floor(owner.zPosition).toInt + facing.offsetZ) match { + case handler: IFluidHandler => + result((owner.getFluidTank(owner.selectedTank) match { + case Some(tank) => handler.getTankInfo(facing.getOpposite).filter(info => info.fluid == null || info.fluid.isFluidEqual(tank.getFluid)) + case _ => handler.getTankInfo(facing.getOpposite) + }).map(_.capacity).foldLeft(0)((max, capacity) => math.max(max, capacity))) + case _ => result(Unit, "no tank") + } + } + + @Callback(doc = """function(side:number):table -- Get a description of the fluid in the the tank on the specified side of the robot. Back refers to the robot's own selected tank.""") + def getFluidInTank(context: Context, args: Arguments): Array[AnyRef] = if (Settings.get.allowItemStackInspection) { + val facing = checkSideForTank(args, 0) + if (facing == owner.facing.getOpposite) result(owner.getFluidTank(owner.selectedTank).map(_.getFluid).orNull) + else owner.world.getBlockTileEntity(math.floor(owner.xPosition).toInt + facing.offsetX, math.floor(owner.yPosition).toInt + facing.offsetY, math.floor(owner.zPosition).toInt + facing.offsetZ) match { + case handler: IFluidHandler => result(Option(handler.getTankInfo(facing.getOpposite)).map(_.map(_.fluid)).orNull) + case _ => result(Unit, "no tank") + } + } + else result(Unit, "not enabled in config") + + @Callback(doc = """function([amount:number]):boolean -- Transfers fluid from a tank in the selected inventory slot to the selected tank.""") + def drain(context: Context, args: Arguments): Array[AnyRef] = { + val amount = args.optionalFluidCount(0) + owner.getFluidTank(owner.selectedTank) match { + case Some(tank) => + owner.getStackInSlot(owner.selectedSlot) match { + case stack: ItemStack => + if (FluidContainerRegistry.isFilledContainer(stack)) { + val contents = FluidContainerRegistry.getFluidForFilledItem(stack) + val container = stack.getItem.getContainerItemStack(stack) + if (tank.getCapacity - tank.getFluidAmount < contents.amount) { + result(Unit, "tank is full") + } + else if (tank.fill(contents, false) < contents.amount) { + result(Unit, "incompatible fluid") + } + else { + tank.fill(contents, true) + owner.decrStackSize(owner.selectedSlot, 1) + owner.player().inventory.addItemStackToInventory(container) + result(true) + } + } + else stack.getItem match { + case container: IFluidContainerItem => + val drained = container.drain(stack, amount, false) + val transferred = tank.fill(drained, true) + if (transferred > 0) { + container.drain(stack, transferred, true) + result(true) + } + else result(Unit, "incompatible or no fluid") + case _ => result(Unit, "item is empty or not a fluid container") + } + case _ => result(Unit, "nothing selected") + } + case _ => result(Unit, "no tank") + } + } + + @Callback(doc = """function([amount:number]):boolean -- Transfers fluid from the selected tank to a tank in the selected inventory slot.""") + def fill(context: Context, args: Arguments): Array[AnyRef] = { + val amount = args.optionalFluidCount(0) + owner.getFluidTank(owner.selectedTank) match { + case Some(tank) => + owner.getStackInSlot(owner.selectedSlot) match { + case stack: ItemStack => + if (FluidContainerRegistry.isEmptyContainer(stack)) { + val drained = tank.drain(amount, false) + val filled = FluidContainerRegistry.fillFluidContainer(drained, stack) + if (filled == null) { + result(Unit, "tank is empty") + } + else { + tank.drain(FluidContainerRegistry.getFluidForFilledItem(filled).amount, true) + owner.decrStackSize(owner.selectedSlot, 1) + owner.player().inventory.addItemStackToInventory(filled) + result(true) + } + } + else stack.getItem match { + case container: IFluidContainerItem => + val drained = tank.drain(amount, false) + val transferred = container.fill(stack, drained, true) + if (transferred > 0) { + tank.drain(transferred, true) + result(true) + } + else result(Unit, "incompatible or no fluid") + case _ => result(Unit, "item is full or not a fluid container") + } + case _ => result(Unit, "nothing selected") + } + case _ => result(Unit, "no tank") + } + } + + private def checkSideForTank(args: Arguments, n: Int) = owner.toGlobal(args.checkSide(n, ForgeDirection.SOUTH, ForgeDirection.NORTH, ForgeDirection.UP, ForgeDirection.DOWN)) +} diff --git a/src/main/scala/li/cil/oc/server/component/robot/Robot.scala b/src/main/scala/li/cil/oc/server/component/robot/Robot.scala index 9653973c1..2fad0622b 100644 --- a/src/main/scala/li/cil/oc/server/component/robot/Robot.scala +++ b/src/main/scala/li/cil/oc/server/component/robot/Robot.scala @@ -535,33 +535,31 @@ class Robot(val robot: tileentity.Robot) extends ManagedComponent { @Callback def transferFluidTo(context: Context, args: Arguments): Array[AnyRef] = { - val index = checkSlot(args, 0) + val index = checkTank(args, 0) val count = args.optionalFluidCount(1) if (index == selectedTank || count == 0) { result(true) } - else result((getTank(selectedTank), getTank(index)) match { + else (getTank(selectedTank), getTank(index)) match { case (Some(from), Some(to)) => - if (haveSameFluidType(from.getFluid, to.getFluid)) { - val space = to.getCapacity - to.getFluidAmount - val amount = math.min(count, space) - if (amount > 0) { - to.fill(from.drain(amount, true), true) - robot.onInventoryChanged() - true - } - else false + val drained = from.drain(count, false) + val transferred = to.fill(drained, true) + if (transferred > 0) { + from.drain(transferred, true) + robot.onInventoryChanged() + result(true) } - else if (count >= from.getFluidAmount) { + else if (count >= from.getFluidAmount && to.getCapacity >= from.getFluidAmount && from.getCapacity >= to.getFluidAmount) { + // Swap. val tmp = to.drain(to.getFluidAmount, true) to.fill(from.drain(from.getFluidAmount, true), true) from.fill(tmp, true) robot.onInventoryChanged() - true + result(true) } - else false - case _ => false - }) + else result(Unit, "incompatible or no fluid") + case _ => result(Unit, "invalid index") + } } @Callback @@ -670,40 +668,6 @@ class Robot(val robot: tileentity.Robot) extends ManagedComponent { } } -// @Callback -// def getFluidInfo(context: Context, args: Arguments): Array[AnyRef] = { -// robot.getSelectedTank match { -// case Some(component) => -// component.getInfo match { -// case (info: FluidTankInfo) => info.fluid match { -// case (fluid: FluidStack) => result(fluid.getFluid.getName, fluid.getFluid.getLocalizedName, fluid.amount, info.capacity) -// case _ => result(Unit, "no fluid") -// } -// case _ => result(Unit, "no fluid") -// } -// case None => result(Unit, "no Container Selected") -// } -// } -// -// @Callback -// def storeFluid(context: Context, args: Arguments): Array[AnyRef] = { -// val stack = stackInSlot(selectedSlot) -// stack match { -// case Some(item) => return result(FluidContainerRegistry.isBucket(item), FluidContainerRegistry.isContainer(item)) -// case None => -// } -// result(Unit, "Not a valid inventory") -// } -// -// @Callback -// def ejectFluid(context: Context, args: Arguments): Array[AnyRef] = { -// val stack = stackInSlot(selectedSlot) -// stack match { -// case Some(item) => result(true) -// case None => result(Unit, "Not a valid inventory") -// } -// } - // ----------------------------------------------------------------------- // override def onConnect(node: Node) { @@ -837,9 +801,12 @@ class Robot(val robot: tileentity.Robot) extends ManagedComponent { private def stackInSlot(slot: Int) = Option(robot.getStackInSlot(slot)) - private def getTank(tank: Int) = robot.getFluidTank(tank) + private def getTank(index: Int) = robot.getFluidTank(index) - private def fluidInTank(tank: Int) = getTank(tank).map(_.getFluid) + private def fluidInTank(index: Int) = getTank(index) match { + case Some(tank) => Option(tank.getFluid) + case _ => None + } // ----------------------------------------------------------------------- // diff --git a/src/main/scala/li/cil/oc/server/driver/converter/FluidStack.scala b/src/main/scala/li/cil/oc/server/driver/converter/FluidStack.scala new file mode 100644 index 000000000..43169e050 --- /dev/null +++ b/src/main/scala/li/cil/oc/server/driver/converter/FluidStack.scala @@ -0,0 +1,25 @@ +package li.cil.oc.server.driver.converter + +import java.util + +import li.cil.oc.{Settings, api} + +import scala.collection.convert.WrapAsScala._ + +object FluidStack extends api.driver.Converter { + override def convert(value: scala.Any, output: util.Map[AnyRef, AnyRef]) = + value match { + case stack: net.minecraftforge.fluids.FluidStack => + if (Settings.get.insertIdsInConverters) { + output += "id" -> Int.box(stack.fluidID) + } + output += "amount" -> Int.box(stack.amount) + output += "hasTag" -> Boolean.box(stack.tag != null) + val fluid = stack.getFluid + if (fluid != null) { + output += "name" -> fluid.getName + output += "label" -> fluid.getLocalizedName + } + case _ => + } +} diff --git a/src/main/scala/li/cil/oc/server/driver/converter/FluidTankInfo.scala b/src/main/scala/li/cil/oc/server/driver/converter/FluidTankInfo.scala index f6477e548..6d008531c 100644 --- a/src/main/scala/li/cil/oc/server/driver/converter/FluidTankInfo.scala +++ b/src/main/scala/li/cil/oc/server/driver/converter/FluidTankInfo.scala @@ -13,13 +13,7 @@ object FluidTankInfo extends api.driver.Converter { case tankInfo: fluids.FluidTankInfo => output += "capacity" -> Int.box(tankInfo.capacity) if (tankInfo.fluid != null) { - output += "amount" -> Int.box(tankInfo.fluid.amount) - output += "id" -> Int.box(tankInfo.fluid.fluidID) - val fluid = tankInfo.fluid.getFluid - if (fluid != null) { - output += "name" -> fluid.getName - output += "label" -> fluid.getLocalizedName - } + FluidStack.convert(tankInfo.fluid, output) } else output += "amount" -> Int.box(0) case _ => diff --git a/src/main/scala/li/cil/oc/server/driver/converter/ItemStack.scala b/src/main/scala/li/cil/oc/server/driver/converter/ItemStack.scala index 266d57806..48f3ee4e9 100644 --- a/src/main/scala/li/cil/oc/server/driver/converter/ItemStack.scala +++ b/src/main/scala/li/cil/oc/server/driver/converter/ItemStack.scala @@ -2,7 +2,7 @@ package li.cil.oc.server.driver.converter import java.util -import li.cil.oc.api +import li.cil.oc.{Settings, api} import net.minecraft.item import scala.collection.convert.WrapAsScala._ @@ -11,7 +11,9 @@ object ItemStack extends api.driver.Converter { override def convert(value: AnyRef, output: util.Map[AnyRef, AnyRef]) = value match { case stack: item.ItemStack => - output += "id" -> Int.box(stack.itemID) + if (Settings.get.insertIdsInConverters) { + output += "id" -> Int.box(stack.itemID) + } output += "damage" -> Int.box(stack.getItemDamage) output += "maxDamage" -> Int.box(stack.getMaxDamage) output += "size" -> Int.box(stack.stackSize) diff --git a/src/main/scala/li/cil/oc/server/driver/item/UpgradeTankController.scala b/src/main/scala/li/cil/oc/server/driver/item/UpgradeTankController.scala new file mode 100644 index 000000000..3743c3953 --- /dev/null +++ b/src/main/scala/li/cil/oc/server/driver/item/UpgradeTankController.scala @@ -0,0 +1,21 @@ +package li.cil.oc.server.driver.item + +import li.cil.oc.api +import li.cil.oc.api.driver.{Container, Slot} +import li.cil.oc.common.Tier +import li.cil.oc.common.tileentity.Robot +import li.cil.oc.server.component +import net.minecraft.item.ItemStack + +object UpgradeTankController extends Item { + override def worksWith(stack: ItemStack) = isOneOf(stack, api.Items.get("tankControllerUpgrade")) + + override def createEnvironment(stack: ItemStack, container: Container) = container match { + case robot: Container with Robot => new component.UpgradeTankController(robot) + case _ => null + } + + override def slot(stack: ItemStack) = Slot.Upgrade + + override def tier(stack: ItemStack) = Tier.Two +}