diff --git a/assets/drone.tcn b/assets/drone.tcn new file mode 100644 index 000000000..0aeb4cfdf Binary files /dev/null and b/assets/drone.tcn differ diff --git a/assets/items.psd b/assets/items.psd index 981f4a3b1..0247ac978 100644 Binary files a/assets/items.psd and b/assets/items.psd differ diff --git a/src/main/resources/assets/opencomputers/lang/en_US.lang b/src/main/resources/assets/opencomputers/lang/en_US.lang index ef7e9972c..5471c3067 100644 --- a/src/main/resources/assets/opencomputers/lang/en_US.lang +++ b/src/main/resources/assets/opencomputers/lang/en_US.lang @@ -111,6 +111,7 @@ item.oc.UpgradeExperience.name=Experience Upgrade item.oc.UpgradeGenerator.name=Generator Upgrade item.oc.UpgradeInventory.name=Inventory Upgrade item.oc.UpgradeInventoryController.name=Inventory Controller Upgrade +item.oc.UpgradeLeash.name=Leash Upgrade item.oc.UpgradeNavigation.name=Navigation Upgrade item.oc.UpgradePiston.name=Piston Upgrade item.oc.UpgradeSign.name=Sign I/O Upgrade @@ -285,6 +286,7 @@ oc:tooltip.UpgradeExperience=This upgrade allows robots to accumulate experience oc:tooltip.UpgradeGenerator=Can be used to generate energy from fuel on the go. Burns items to generate energy over time, based on their fuel value.[nl] §fEfficiency§7: §a%s%%§7 oc:tooltip.UpgradeInventory=This upgrade provides inventory space to the robot. Without one of these, robots will not be able to store items internally. oc:tooltip.UpgradeInventoryController=This upgrade allows the robot more control in how it interacts with external inventories, and allows it to swap its equipped tool with an item in its inventory. +oc:tooltip.UpgradeLeash=Allows some devices, such as drones, to bind Isaa- excuse me... *chatter* My apologies. I'm just being told this is actually used to put animals on a leash. Multiple animals, even. Odd. oc:tooltip.UpgradeNavigation=Can be used to determine the position and orientation of the robot. The position is relative to the center of the map that was used to craft this upgrade. oc:tooltip.UpgradePiston=This upgrade is very pushy. It allows moving blocks, similar to when using a piston. It does §lnot§7 move entities, however. oc:tooltip.UpgradeSign=Allows reading text on and writing text to signs. @@ -323,6 +325,7 @@ item.oc.UpgradeExperience.usage=The §oExperience Upgrade§r is a very special u item.oc.UpgradeGenerator.usage=The §oGenerator Upgrade§r allows robots to refuel on the go. Currently it only supports solid fuels, such as coal. It has an internal intentory that can store one item stack of fuel. Surplus fuel can be removed from the generator using the according API method. When removing a generator upgrade from a robot its contents will be dropped into the world.[nl][nl]The efficiency of generators is lower than that of usual generators of other mods, meaning it is usually more fuel efficient to power robots using a §oCharger§r. item.oc.UpgradeInventory.usage=The §oInventory Upgrade§r provides inventory slots to robots. For each inventory upgrade a robot will gain an addition 16 inventory slots, up to a maximum of 64 slots in total. If no inventory upgrade is installed in a robot it will not be able to store or pick up items. item.oc.UpgradeInventoryController.usage=The §oInventory Controller Upgrade§r provides extended inventory interaction to robots. It allows the robots to excplicitly target slots in external inventories when dropping or sucking items. It also allows robots to read detailed information about item stacks. Lastly it provides robots with a means to change their equipped tool without external help.[nl][nl]This upgrade can also be placed in §oAdapters§r, where it provides similar inspection methods for inventories adjacent to the adapter as it does to the robot. It does not allow the adapter to move items into or out of inventories, however. This feature is only available in robots. +item.oc.UpgradeLeash.usage=The §oLeash Upgrade§r allows putting animals on a leash, bound to the entity that hosts the device the component is used by, for example drones. Using this upgrade, multiple animals can be leashed at the same time, which makes this quite useful for moving herds. item.oc.UpgradeNavigation.usage=The §oNavigation Upgrade§r provides location and orientation information to §oRobots§r and §oTablets§r it is installed in. The coordinates the upgrade provides are relative to the center of the map that was used to craft the upgrade, and the functional range is based on the size of that map.[nl][nl]Navigation upgrades can be re-crafted with a map to replace the map in the upgrade with another one. The old map will be returned. item.oc.UpgradeTank.usage=The §oTank Upgrade§r allows robots to store fluids. Each tank can only hold a single type of fluids, and provides a volume of 16 buckets (16000mB). Robots can drain liquids from the world and from other fluid tanks, and can fill the fluids back into fluid tanks, and, when supported by the fluid, place them back into the world. There is no limit to the number of tanks that can be installed in a robot. item.oc.UpgradeTankController.usage=The §oTank Controller Upgrade§r is to fluid tanks what the §oInventory Controller Upgrade§r is to normal inventories. It allows robots to query more detailed information about tanks in and next to the robot.[nl][nl]This upgrade can also be installed in §oAdapters§r, allowing computers connected to the adapter to query information about the tanks adjacent to the adapter. diff --git a/src/main/resources/assets/opencomputers/recipes/default.recipes b/src/main/resources/assets/opencomputers/recipes/default.recipes index f0de950ba..1a36749b0 100644 --- a/src/main/resources/assets/opencomputers/recipes/default.recipes +++ b/src/main/resources/assets/opencomputers/recipes/default.recipes @@ -9,7 +9,7 @@ analyzer { droneCase { input: [[{block="minecraft:end_stone"}, compass, {block="minecraft:end_stone"}] ["oc:circuitChip2", "oc:microcontrollerCase", "oc:circuitChip2"] - [{block="minecraft:end_stone"}, "oc:componentBus3", {block="minecraft:end_stone"}]] + [{block="minecraft:end_stone"}, "oc:componentBus2", {block="minecraft:end_stone"}]] } microcontrollerCase { input: [[nuggetIron, "oc:circuitChip1", nuggetIron] @@ -210,6 +210,11 @@ inventoryControllerUpgrade { [dispenser, "oc:circuitChip2", craftingPiston] [ingotGold, "oc:materialCircuitBoardPrinted", ingotGold]] } +leashUpgrade { + input: [[ingotIron, lead, ingotIron] + [lead, "oc:materialCU", lead] + [ingotIron, lead, ingotIron]] +} navigationUpgrade { input: [[ingotGold, compass, ingotGold] ["oc:circuitChip2", {item=map, subID=any}, "oc:circuitChip2"] diff --git a/src/main/resources/assets/opencomputers/textures/items/UpgradeLeash.png b/src/main/resources/assets/opencomputers/textures/items/UpgradeLeash.png new file mode 100644 index 000000000..54745103e Binary files /dev/null and b/src/main/resources/assets/opencomputers/textures/items/UpgradeLeash.png differ diff --git a/src/main/scala/li/cil/oc/common/asm/ClassTransformer.scala b/src/main/scala/li/cil/oc/common/asm/ClassTransformer.scala index 70239b8d9..3a784cb97 100644 --- a/src/main/scala/li/cil/oc/common/asm/ClassTransformer.scala +++ b/src/main/scala/li/cil/oc/common/asm/ClassTransformer.scala @@ -25,6 +25,7 @@ class ClassTransformer extends IClassTransformer { if (name == "li.cil.oc.common.tileentity.traits.Computer" || name == "li.cil.oc.common.tileentity.Rack") { transformedClass = ensureStargateTechCompatibility(transformedClass) } + if (transformedClass != null && !name.startsWith("net.minecraft.") && !name.startsWith("net.minecraftforge.") @@ -123,6 +124,75 @@ class ClassTransformer extends IClassTransformer { } } } + + // Inject some code into the EntityLiving classes recreateLeash method to allow + // proper loading of leashes tied to entities using the leash upgrade. This is + // necessary because entities only save the entity they are leashed to if that + // entity is an EntityLivingBase - which drones, for example, are not, for good + // reason. We work around this by re-leashing them in the load method of the + // leash upgrade. The leashed entity would then still unleash itself and, more + // problematically drop a leash item. To avoid this, we extend the + // if (this.isLeashed && this.field_110170_bx != null) + // check to read + // if (this.isLeashed && this.field_110170_bx != null && this.leashedToEntity == null) + // which should not interfere with any existing logic, but avoid leashing + // restored manually in the load phase to not be broken again. + if (transformedClass != null && name == "net.minecraft.entity.EntityLiving") { + insertInto(transformedClass, "recreateLeash", instructions => instructions.toArray.sliding(3, 1).exists { + case Array(varNode: VarInsnNode, fieldNode: FieldInsnNode, jumpNode: JumpInsnNode) + if varNode.getOpcode == Opcodes.ALOAD && varNode.`var` == 0 && + fieldNode.getOpcode == Opcodes.GETFIELD && fieldNode.name == "field_110170_bx" && + jumpNode.getOpcode == Opcodes.IFNULL => + val toInject = new InsnList() + toInject.add(new VarInsnNode(Opcodes.ALOAD, 0)) + toInject.add(new FieldInsnNode(Opcodes.GETFIELD, "net/minecraft/entity/EntityLiving", "leashedToEntity", "Lnet/minecraft/entity/Entity;")) + toInject.add(new JumpInsnNode(Opcodes.IFNONNULL, jumpNode.label)) + instructions.insert(jumpNode, toInject) + true + case _ => + false + }) match { + case Some(data) => transformedClass = data + case _ => + } + } + + // Little change to the renderer used to render leashes to center it on drones. + // This injects the code + // if (entity instanceof Drone) { + // d5 = 0.0; + // d6 = 0.0; + // d7 = -0.75; + // } + // before the `instanceof EntityHanging` check in func_110827_b. + if (transformedClass != null && name == "net.minecraft.client.renderer.entity.RenderLiving") { + insertInto(transformedClass, "func_110827_b", instructions => instructions.toArray.sliding(3, 1).exists { + case Array(varNode: VarInsnNode, typeNode: TypeInsnNode, jumpNode: JumpInsnNode) + if varNode.getOpcode == Opcodes.ALOAD && varNode.`var` == 10 && + typeNode.getOpcode == Opcodes.INSTANCEOF && typeNode.desc == "net/minecraft/entity/EntityHanging" && + jumpNode.getOpcode == Opcodes.IFEQ => + val toInject = new InsnList() + toInject.add(new VarInsnNode(Opcodes.ALOAD, 10)) + toInject.add(new TypeInsnNode(Opcodes.INSTANCEOF, "li/cil/oc/common/entity/Drone")) + val skip = new LabelNode() + toInject.add(new JumpInsnNode(Opcodes.IFEQ, skip)) + toInject.add(new LdcInsnNode(double2Double(0.0))) + toInject.add(new VarInsnNode(Opcodes.DSTORE, 16)) + toInject.add(new LdcInsnNode(double2Double(0.0))) + toInject.add(new VarInsnNode(Opcodes.DSTORE, 18)) + toInject.add(new LdcInsnNode(double2Double(-0.75))) + toInject.add(new VarInsnNode(Opcodes.DSTORE, 20)) + toInject.add(skip) + instructions.insertBefore(varNode, toInject) + true + case _ => + false + }) match { + case Some(data) => transformedClass = data + case _ => + } + } + transformedClass } catch { @@ -132,6 +202,24 @@ class ClassTransformer extends IClassTransformer { } } + private def insertInto(classData: Array[Byte], method: String, inserter: (InsnList) => Boolean): Option[Array[Byte]] = { + val classNode = newClassNode(classData) + classNode.methods.find(_.name == method) match { + case Some(methodNode) => + if (inserter(methodNode.instructions)) { + log.warn(s"Successfully patched ${classNode.name}.$method.") + Option(writeClass(classNode, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES)) + } + else { + log.warn(s"Failed patching ${classNode.name}.$method}, injection point not found.") + None + } + case _ => + log.warn(s"Failed patching ${classNode.name}.$method, method not found.") + None + } + } + private def classExists(name: String) = { loader.getClassBytes(name) != null || loader.getClassBytes(FMLDeobfuscatingRemapper.INSTANCE.unmap(name)) != null || diff --git a/src/main/scala/li/cil/oc/common/entity/Drone.scala b/src/main/scala/li/cil/oc/common/entity/Drone.scala index c606521b1..e150b2ff1 100644 --- a/src/main/scala/li/cil/oc/common/entity/Drone.scala +++ b/src/main/scala/li/cil/oc/common/entity/Drone.scala @@ -254,7 +254,7 @@ class Drone(val world: World) extends Entity(world) with MachineHost with intern val entity = new EntityItem(world, posX, posY, posZ, stack) entity.delayBeforeCanPickup = 15 world.spawnEntityInWorld(entity) - InventoryUtils.dropAllSlots(BlockPosition(this), inventory) + InventoryUtils.dropAllSlots(BlockPosition(this: Entity), inventory) } } @@ -357,7 +357,7 @@ class Drone(val world: World) extends Entity(world) with MachineHost with intern motionZ *= drag } else { - val groundDrag = worldObj.getBlock(BlockPosition(this).offset(ForgeDirection.DOWN)).slipperiness * drag + val groundDrag = worldObj.getBlock(BlockPosition(this: Entity).offset(ForgeDirection.DOWN)).slipperiness * drag motionX *= groundDrag motionY *= drag motionZ *= groundDrag diff --git a/src/main/scala/li/cil/oc/common/init/Items.scala b/src/main/scala/li/cil/oc/common/init/Items.scala index 93b39a122..f34e9f796 100644 --- a/src/main/scala/li/cil/oc/common/init/Items.scala +++ b/src/main/scala/li/cil/oc/common/init/Items.scala @@ -11,6 +11,7 @@ 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.SimpleItem +import li.cil.oc.common.item.UpgradeLeash import li.cil.oc.common.recipe.Recipes import li.cil.oc.integration.Mods import li.cil.oc.util.Color @@ -312,5 +313,6 @@ object Items extends ItemAPI { // 1.4.3 Recipes.addMultiItem(new item.DroneCase(multi), "droneCase", "oc:droneCase") registerItem(new item.Drone(multi), "drone") + Recipes.addMultiItem(new UpgradeLeash(multi), "leashUpgrade", "oc:leashUpgrade") } } diff --git a/src/main/scala/li/cil/oc/common/item/Delegator.scala b/src/main/scala/li/cil/oc/common/item/Delegator.scala index bd30f6e1b..e67c7ef08 100644 --- a/src/main/scala/li/cil/oc/common/item/Delegator.scala +++ b/src/main/scala/li/cil/oc/common/item/Delegator.scala @@ -106,13 +106,13 @@ class Delegator extends Item { override def onItemUseFirst(stack: ItemStack, player: EntityPlayer, world: World, x: Int, y: Int, z: Int, side: Int, hitX: Float, hitY: Float, hitZ: Float): Boolean = subItem(stack) match { - case Some(subItem) => subItem.onItemUseFirst(stack, player, BlockPosition(x, y, z, Option(world)), side, hitX, hitY, hitZ) + case Some(subItem) => subItem.onItemUseFirst(stack, player, BlockPosition(x, y, z, world), side, hitX, hitY, hitZ) case _ => super.onItemUseFirst(stack, player, world, x, y, z, side, hitX, hitY, hitZ) } override def onItemUse(stack: ItemStack, player: EntityPlayer, world: World, x: Int, y: Int, z: Int, side: Int, hitX: Float, hitY: Float, hitZ: Float): Boolean = subItem(stack) match { - case Some(subItem) => subItem.onItemUse(stack, player, BlockPosition(x, y, z, Option(world)), side, hitX, hitY, hitZ) + case Some(subItem) => subItem.onItemUse(stack, player, BlockPosition(x, y, z, world), side, hitX, hitY, hitZ) case _ => super.onItemUse(stack, player, world, x, y, z, side, hitX, hitY, hitZ) } diff --git a/src/main/scala/li/cil/oc/common/item/UpgradeLeash.scala b/src/main/scala/li/cil/oc/common/item/UpgradeLeash.scala new file mode 100644 index 000000000..c7c85f762 --- /dev/null +++ b/src/main/scala/li/cil/oc/common/item/UpgradeLeash.scala @@ -0,0 +1,3 @@ +package li.cil.oc.common.item + +class UpgradeLeash(val parent: Delegator) extends Delegate with ItemTier diff --git a/src/main/scala/li/cil/oc/common/tileentity/Robot.scala b/src/main/scala/li/cil/oc/common/tileentity/Robot.scala index 171ed4f75..7384576b2 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Robot.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Robot.scala @@ -728,10 +728,10 @@ class Robot extends traits.Computer with traits.PowerInformation with IFluidHand // ----------------------------------------------------------------------- // override def dropSlot(slot: Int, count: Int, direction: Option[ForgeDirection]) = - InventoryUtils.dropSlot(BlockPosition(x, y, z, Option(world)), dynamicInventory, slot, count, direction) + InventoryUtils.dropSlot(BlockPosition(x, y, z, world), dynamicInventory, slot, count, direction) override def dropAllSlots() = - InventoryUtils.dropAllSlots(BlockPosition(x, y, z, Option(world)), dynamicInventory) + InventoryUtils.dropAllSlots(BlockPosition(x, y, z, world), dynamicInventory) // ----------------------------------------------------------------------- // diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/Inventory.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/Inventory.scala index e23daff08..81495f114 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/Inventory.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/traits/Inventory.scala @@ -31,11 +31,11 @@ trait Inventory extends TileEntity with inventory.Inventory { // ----------------------------------------------------------------------- // def dropSlot(slot: Int, count: Int = getInventoryStackLimit, direction: Option[ForgeDirection] = None) = - InventoryUtils.dropSlot(BlockPosition(x, y, z, Option(world)), this, slot, count, direction) + InventoryUtils.dropSlot(BlockPosition(x, y, z, world), this, slot, count, direction) def dropAllSlots() = - InventoryUtils.dropAllSlots(BlockPosition(x, y, z, Option(world)), this) + InventoryUtils.dropAllSlots(BlockPosition(x, y, z, world), this) def spawnStackInWorld(stack: ItemStack, direction: Option[ForgeDirection] = None) = - InventoryUtils.spawnStackInWorld(BlockPosition(x, y, z, Option(world)), stack, direction) + InventoryUtils.spawnStackInWorld(BlockPosition(x, y, z, world), stack, direction) } diff --git a/src/main/scala/li/cil/oc/integration/appeng/DriverController.scala b/src/main/scala/li/cil/oc/integration/appeng/DriverController.scala index 361b6ed06..061136c76 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/DriverController.scala +++ b/src/main/scala/li/cil/oc/integration/appeng/DriverController.scala @@ -181,7 +181,7 @@ object DriverController extends DriverTileEntity with EnvironmentAware { } }) } - links ++= nbt.getTagList("links", NBT.TAG_LIST).map( + links ++= nbt.getTagList("links", NBT.TAG_COMPOUND).map( (nbt: NBTTagCompound) => Api.instance.storage.loadCraftingLink(nbt, this)) } diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverBlockEnvironments.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverBlockEnvironments.scala index 119a496f6..44add398a 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverBlockEnvironments.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverBlockEnvironments.scala @@ -4,6 +4,7 @@ import li.cil.oc.api import li.cil.oc.api.driver import li.cil.oc.api.driver.EnvironmentAware import li.cil.oc.api.network.Environment +import li.cil.oc.api.network.ManagedEnvironment import li.cil.oc.common import li.cil.oc.common.tileentity import li.cil.oc.integration.util.BundledRedstone @@ -29,11 +30,14 @@ object DriverBlockEnvironments extends driver.Block with EnvironmentAware { case block: ItemBlock if block.field_150939_a != null => if (isOneOf(block.field_150939_a, "accessPoint")) classOf[tileentity.AccessPoint] else if (isOneOf(block.field_150939_a, "assembler")) classOf[tileentity.Assembler] - else if (isOneOf(block.field_150939_a, "case1", "case2", "case3", "caseCreative")) classOf[Machine] + else if (isOneOf(block.field_150939_a, "case1", "case2", "case3", "caseCreative", "microcontroller")) classOf[Machine] else if (isOneOf(block.field_150939_a, "hologram1", "hologram2")) classOf[tileentity.Hologram] else if (isOneOf(block.field_150939_a, "motionSensor")) classOf[tileentity.MotionSensor] else if (isOneOf(block.field_150939_a, "redstone")) if (BundledRedstone.isAvailable) classOf[component.Redstone.Bundled] else classOf[component.Redstone.Simple] + else if (isOneOf(block.field_150939_a, "screen1")) classOf[common.component.TextBuffer].asInstanceOf[Class[_ <: Environment]] else if (isOneOf(block.field_150939_a, "screen2", "screen3")) classOf[common.component.Screen] + else if (isOneOf(block.field_150939_a, "robot")) classOf[component.robot.Robot].asInstanceOf[Class[_ <: Environment]] + else if (isOneOf(block.field_150939_a, "drone")) classOf[component.Drone].asInstanceOf[Class[_ <: Environment]] else null case _ => null } diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeLeash.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeLeash.scala new file mode 100644 index 000000000..15154b02a --- /dev/null +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradeLeash.scala @@ -0,0 +1,27 @@ +package li.cil.oc.integration.opencomputers + +import li.cil.oc.api +import li.cil.oc.api.driver.EnvironmentAware +import li.cil.oc.api.driver.EnvironmentHost +import li.cil.oc.api.driver.item.HostAware +import li.cil.oc.common.Slot +import li.cil.oc.common.Tier +import li.cil.oc.server.component +import net.minecraft.entity.Entity +import net.minecraft.item.ItemStack + +object DriverUpgradeLeash extends Item with HostAware with EnvironmentAware { + override def worksWith(stack: ItemStack) = + isOneOf(stack, api.Items.get("leashUpgrade")) + + override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = host match { + case entity: Entity => new component.UpgradeLeash(entity) + case _ => null + } + + override def slot(stack: ItemStack) = Slot.Upgrade + + override def tier(stack: ItemStack) = Tier.One + + override def providedEnvironment(stack: ItemStack) = classOf[component.UpgradeLeash] +} diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/ModOpenComputers.scala b/src/main/scala/li/cil/oc/integration/opencomputers/ModOpenComputers.scala index f0b0786d1..51869927d 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/ModOpenComputers.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/ModOpenComputers.scala @@ -95,6 +95,7 @@ object ModOpenComputers extends ModProxy { api.Driver.add(DriverUpgradeGenerator) api.Driver.add(DriverUpgradeInventory) api.Driver.add(DriverUpgradeInventoryController) + api.Driver.add(DriverUpgradeLeash) api.Driver.add(DriverUpgradeNavigation) api.Driver.add(DriverUpgradePiston) api.Driver.add(DriverUpgradeSign) @@ -103,10 +104,76 @@ object ModOpenComputers extends ModProxy { api.Driver.add(DriverUpgradeTankController) api.Driver.add(DriverUpgradeTractorBeam) - blacklistHost(classOf[internal.Adapter], "geolyzer", "keyboard", "screen1", "angelUpgrade", "batteryUpgrade1", "batteryUpgrade2", "batteryUpgrade3", "chunkloaderUpgrade", "craftingUpgrade", "experienceUpgrade", "generatorUpgrade", "inventoryUpgrade", "navigationUpgrade", "pistonUpgrade", "solarGeneratorUpgrade", "tankUpgrade", "tractorBeamUpgrade") - blacklistHost(classOf[internal.Microcontroller], "graphicsCard1", "graphicsCard2", "graphicsCard3", "keyboard", "screen1", "angelUpgrade", "chunkloaderUpgrade", "craftingUpgrade", "databaseUpgrade1", "databaseUpgrade2", "databaseUpgrade3", "experienceUpgrade", "generatorUpgrade", "inventoryUpgrade", "inventoryControllerUpgrade", "navigationUpgrade", "tankUpgrade", "tankControllerUpgrade", "tractorBeamUpgrade") - blacklistHost(classOf[internal.Drone], "graphicsCard1", "graphicsCard2", "graphicsCard3", "keyboard", "lanCard", "redstoneCard1", "screen1", "angelUpgrade", "craftingUpgrade", "experienceUpgrade") - blacklistHost(classOf[internal.Tablet], "lanCard", "redstoneCard1", "screen1", "angelUpgrade", "chunkloaderUpgrade", "craftingUpgrade", "databaseUpgrade1", "databaseUpgrade2", "databaseUpgrade3", "experienceUpgrade", "generatorUpgrade", "inventoryUpgrade", "inventoryControllerUpgrade", "tankUpgrade", "tankControllerUpgrade") + blacklistHost(classOf[internal.Adapter], + "geolyzer", + "keyboard", + "screen1", + "angelUpgrade", + "batteryUpgrade1", + "batteryUpgrade2", + "batteryUpgrade3", + "chunkloaderUpgrade", + "craftingUpgrade", + "experienceUpgrade", + "generatorUpgrade", + "inventoryUpgrade", + "navigationUpgrade", + "pistonUpgrade", + "solarGeneratorUpgrade", + "tankUpgrade", + "tractorBeamUpgrade", + "leashUpgrade") + blacklistHost(classOf[internal.Drone], + "graphicsCard1", + "graphicsCard2", + "graphicsCard3", + "keyboard", + "lanCard", + "redstoneCard1", + "screen1", + "angelUpgrade", + "craftingUpgrade", + "experienceUpgrade") + blacklistHost(classOf[internal.Microcontroller], + "graphicsCard1", + "graphicsCard2", + "graphicsCard3", + "keyboard", + "screen1", + "angelUpgrade", + "chunkloaderUpgrade", + "craftingUpgrade", + "databaseUpgrade1", + "databaseUpgrade2", + "databaseUpgrade3", + "experienceUpgrade", + "generatorUpgrade", + "inventoryUpgrade", + "inventoryControllerUpgrade", + "navigationUpgrade", + "tankUpgrade", + "tankControllerUpgrade", + "tractorBeamUpgrade", + "leashUpgrade") + blacklistHost(classOf[internal.Robot], + "leashUpgrade") + blacklistHost(classOf[internal.Tablet], + "lanCard", + "redstoneCard1", + "screen1", + "angelUpgrade", + "chunkloaderUpgrade", + "craftingUpgrade", + "databaseUpgrade1", + "databaseUpgrade2", + "databaseUpgrade3", + "experienceUpgrade", + "generatorUpgrade", + "inventoryUpgrade", + "inventoryControllerUpgrade", + "tankUpgrade", + "tankControllerUpgrade", + "leashUpgrade") if (!WirelessRedstone.isAvailable) { blacklistHost(classOf[internal.Drone], "redstoneCard2") diff --git a/src/main/scala/li/cil/oc/server/component/Drone.scala b/src/main/scala/li/cil/oc/server/component/Drone.scala index c77a8153e..b7d6b96be 100644 --- a/src/main/scala/li/cil/oc/server/component/Drone.scala +++ b/src/main/scala/li/cil/oc/server/component/Drone.scala @@ -11,6 +11,7 @@ import li.cil.oc.common.entity import li.cil.oc.util.BlockPosition import li.cil.oc.util.ExtendedArguments._ import li.cil.oc.util.InventoryUtils +import net.minecraft.entity.Entity import net.minecraft.entity.item.EntityItem import net.minecraftforge.common.util.ForgeDirection @@ -20,7 +21,7 @@ class Drone(val host: entity.Drone) extends prefab.ManagedEnvironment with trait withConnector(Settings.get.bufferDrone). create() - override protected def position = BlockPosition(host) + override protected def position = BlockPosition(host: Entity) override def inventory = host.inventory @@ -37,7 +38,7 @@ class Drone(val host: entity.Drone) extends prefab.ManagedEnvironment with trait override protected def checkSideForAction(args: Arguments, n: Int) = args.checkSide(n, ForgeDirection.VALID_DIRECTIONS: _*) - override protected def suckableItems(side: ForgeDirection) = entitiesInBlock(BlockPosition(host)) ++ super.suckableItems(side) + override protected def suckableItems(side: ForgeDirection) = entitiesInBlock(position) ++ super.suckableItems(side) override protected def onSuckCollect(entity: EntityItem) = { if (InventoryUtils.insertIntoInventory(entity.getEntityItem, inventory, slots = Option(insertionSlots))) { 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 8a112f95a..191534e52 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeInventoryController.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeInventoryController.scala @@ -11,6 +11,7 @@ import li.cil.oc.common.entity import li.cil.oc.common.tileentity import li.cil.oc.util.BlockPosition import li.cil.oc.util.ExtendedArguments._ +import net.minecraft.entity.Entity import net.minecraftforge.common.util.ForgeDirection object UpgradeInventoryController { @@ -34,7 +35,7 @@ object UpgradeInventoryController { // ----------------------------------------------------------------------- // - override protected def position = BlockPosition(host) + override protected def position = BlockPosition(host: Entity) override def inventory = host.inventory diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeLeash.scala b/src/main/scala/li/cil/oc/server/component/UpgradeLeash.scala new file mode 100644 index 000000000..c93e354cf --- /dev/null +++ b/src/main/scala/li/cil/oc/server/component/UpgradeLeash.scala @@ -0,0 +1,102 @@ +package li.cil.oc.server.component + +import java.util.UUID + +import li.cil.oc.OpenComputers +import li.cil.oc.api.Network +import li.cil.oc.api.machine.Arguments +import li.cil.oc.api.machine.Callback +import li.cil.oc.api.machine.Context +import li.cil.oc.api.network.Node +import li.cil.oc.api.network.Visibility +import li.cil.oc.api.prefab +import li.cil.oc.common.EventHandler +import li.cil.oc.util.BlockPosition +import li.cil.oc.util.ExtendedArguments._ +import li.cil.oc.util.ExtendedNBT._ +import net.minecraft.entity.Entity +import net.minecraft.entity.EntityLiving +import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.NBTTagString +import net.minecraft.network.play.server.S1BPacketEntityAttach +import net.minecraft.world.WorldServer +import net.minecraftforge.common.util.Constants.NBT +import net.minecraftforge.common.util.ForgeDirection + +import scala.collection.mutable + +class UpgradeLeash(val host: Entity) extends prefab.ManagedEnvironment with traits.WorldAware { + override val node = Network.newNode(this, Visibility.Network). + withComponent("leash"). + create() + + val leashedEntities = mutable.Set.empty[UUID] + + override protected def position = BlockPosition(host) + + @Callback(doc = """function(side:number):boolean -- Tries to put an entity on the specified side of the device onto a leash.""") + def leash(context: Context, args: Arguments): Array[AnyRef] = { + if (leashedEntities.size >= 8) return result(Unit, "too many leashed entities") + val side = args.checkSide(0, ForgeDirection.VALID_DIRECTIONS: _*) + val nearBounds = position.bounds + val farBounds = nearBounds.offset(side.offsetX * 2.0, side.offsetY * 2.0, side.offsetZ * 2.0) + val bounds = nearBounds.func_111270_a(farBounds) + entitiesInBounds[EntityLiving](bounds).find(_.allowLeashing()) match { + case Some(entity) => + entity.setLeashedToEntity(host, true) + leashedEntities += entity.getUniqueID + context.pause(0.1) + result(true) + case _ => result(Unit, "no unleashed entity") + } + } + + @Callback(doc = """function() -- Unleashes all currently leashed entities.""") + def unleash(context: Context, args: Arguments): Array[AnyRef] = { + unleashAll() + null + } + + override def onDisconnect(node: Node) { + super.onDisconnect(node) + if (node == this.node) { + unleashAll() + } + } + + private def unleashAll() { + entitiesInBounds[EntityLiving](position.bounds.expand(5, 5, 5)).foreach(entity => { + if (leashedEntities.contains(entity.getUniqueID) && entity.getLeashedToEntity == host) { + entity.clearLeashed(true, false) + } + }) + leashedEntities.clear() + } + + override def load(nbt: NBTTagCompound) { + super.load(nbt) + leashedEntities ++= nbt.getTagList("leashedEntities", NBT.TAG_STRING). + map((s: NBTTagString) => UUID.fromString(s.func_150285_a_())) + // Re-acquire leashed entities. Need to do this manually because leashed + // entities only remember their leashee if it's an EntityLivingBase... + EventHandler.schedule(() => { + val foundEntities = mutable.Set.empty[UUID] + entitiesInBounds[EntityLiving](position.bounds.expand(5, 5, 5)).foreach(entity => { + if (leashedEntities.contains(entity.getUniqueID)) { + entity.setLeashedToEntity(host, true) + foundEntities += entity.getUniqueID + } + }) + val missing = leashedEntities.diff(foundEntities) + if (missing.size > 0) { + OpenComputers.log.info(s"Could not find ${missing.size} leashed entities after loading!") + leashedEntities --= missing + } + }) + } + + override def save(nbt: NBTTagCompound) { + super.save(nbt) + nbt.setNewTagList("leashedEntities", leashedEntities.map(_.toString)) + } +} diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeTankController.scala b/src/main/scala/li/cil/oc/server/component/UpgradeTankController.scala index 5b0e97a07..7aa1a0ad2 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeTankController.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeTankController.scala @@ -9,6 +9,7 @@ import li.cil.oc.common.entity import li.cil.oc.common.tileentity import li.cil.oc.util.BlockPosition import li.cil.oc.util.ExtendedArguments._ +import net.minecraft.entity.Entity import net.minecraftforge.common.util.ForgeDirection object UpgradeTankController { @@ -30,7 +31,7 @@ object UpgradeTankController { withComponent("tank_controller", Visibility.Neighbors). create() - override protected def position = BlockPosition(host) + override protected def position = BlockPosition(host: Entity) override def inventory = host.inventory diff --git a/src/main/scala/li/cil/oc/server/component/traits/WorldAware.scala b/src/main/scala/li/cil/oc/server/component/traits/WorldAware.scala index 326ebf8e2..4ca0af843 100644 --- a/src/main/scala/li/cil/oc/server/component/traits/WorldAware.scala +++ b/src/main/scala/li/cil/oc/server/component/traits/WorldAware.scala @@ -7,6 +7,7 @@ import li.cil.oc.util.ExtendedWorld._ import net.minecraft.entity.Entity import net.minecraft.entity.EntityLivingBase import net.minecraft.entity.item.EntityMinecart +import net.minecraft.util.AxisAlignedBB import net.minecraft.world.WorldServer import net.minecraftforge.common.MinecraftForge import net.minecraftforge.common.util.FakePlayer @@ -32,8 +33,12 @@ trait WorldAware { player } + protected def entitiesInBounds[Type <: Entity : ClassTag](bounds: AxisAlignedBB) = { + world.getEntitiesWithinAABB(classTag[Type].runtimeClass, bounds).map(_.asInstanceOf[Type]) + } + protected def entitiesInBlock[Type <: Entity : ClassTag](blockPos: BlockPosition) = { - world.getEntitiesWithinAABB(classTag[Type].runtimeClass, blockPos.bounds).map(_.asInstanceOf[Type]) + entitiesInBounds[Type](blockPos.bounds) } protected def entitiesOnSide[Type <: Entity : ClassTag](side: ForgeDirection) = { diff --git a/src/main/scala/li/cil/oc/util/BlockPosition.scala b/src/main/scala/li/cil/oc/util/BlockPosition.scala index 363fb3edb..212264096 100644 --- a/src/main/scala/li/cil/oc/util/BlockPosition.scala +++ b/src/main/scala/li/cil/oc/util/BlockPosition.scala @@ -4,14 +4,14 @@ import appeng.api.util.DimensionalCoord import cpw.mods.fml.common.Optional import li.cil.oc.api.driver.EnvironmentHost import li.cil.oc.integration.Mods -import net.minecraft.tileentity.TileEntity +import net.minecraft.entity.Entity import net.minecraft.util.AxisAlignedBB import net.minecraft.util.ChunkCoordinates import net.minecraft.util.Vec3 import net.minecraft.world.World import net.minecraftforge.common.util.ForgeDirection -case class BlockPosition(x: Int, y: Int, z: Int, world: Option[World]) { +class BlockPosition(val x: Int, val y: Int, val z: Int, val world: Option[World]) { def this(x: Double, y: Double, z: Double, world: Option[World] = None) = this( math.floor(x).toInt, math.floor(y).toInt, @@ -19,13 +19,7 @@ case class BlockPosition(x: Int, y: Int, z: Int, world: Option[World]) { world ) - def this(host: EnvironmentHost) = this( - host.xPosition, - host.yPosition, - host.zPosition, - Option(host.world)) - - def offset(direction: ForgeDirection) = BlockPosition( + def offset(direction: ForgeDirection) = new BlockPosition( x + direction.offsetX, y + direction.offsetY, z + direction.offsetZ, @@ -55,8 +49,10 @@ object BlockPosition { def apply(x: Double, y: Double, z: Double) = new BlockPosition(x, y, z, None) - def apply(host: EnvironmentHost) = new BlockPosition(host) + def apply(host: EnvironmentHost): BlockPosition = BlockPosition(host.xPosition, host.yPosition, host.zPosition, host.world) + + def apply(entity: Entity): BlockPosition = BlockPosition(entity.posX, entity.posY, entity.posZ, entity.worldObj) @Optional.Method(modid = Mods.IDs.AppliedEnergistics2) - def apply(coord: DimensionalCoord) = new BlockPosition(coord.x, coord.y, coord.z, Option(coord.getWorld)) + def apply(coord: DimensionalCoord): BlockPosition = BlockPosition(coord.x, coord.y, coord.z, coord.getWorld) }