From bade1b5dc9b36acd958451de2cb6da0d04bb0f75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Tue, 19 Nov 2013 01:34:25 +0100 Subject: [PATCH] applying weapon bonus damage to robot player for properly calculated damage on swing; applying tool durability reduction on attack, too; simulating a click for swing and use now via raytracing, falling back to simple use when that fails (basically like player click block vs player click air), which means stuff like using bone meal now also properly works; better feedback on what happened when a tool was used; added callback to query current tool's durability; building list of pre-break items earlier to allow detecting items generated in other callbacks than harvest (e.g. for redstone in motion blocks) --- li/cil/oc/Config.scala | 2 +- li/cil/oc/common/tileentity/Robot.scala | 20 ++++- li/cil/oc/server/component/Robot.scala | 97 ++++++++++++++++++++----- li/cil/oc/util/ActivationType.scala | 5 ++ li/cil/oc/util/RobotPlayer.scala | 72 +++++++++++------- 5 files changed, 149 insertions(+), 47 deletions(-) create mode 100644 li/cil/oc/util/ActivationType.scala diff --git a/li/cil/oc/Config.scala b/li/cil/oc/Config.scala index a1c0de7bb..f2bcfff07 100644 --- a/li/cil/oc/Config.scala +++ b/li/cil/oc/Config.scala @@ -83,7 +83,7 @@ object Config { var callOnItemUseFirst = false var allowActivateBlocks = true var canAttackPlayers = false - var itemDamageChance = 0.05 + var itemDamageRate = 0.05 // ----------------------------------------------------------------------- // diff --git a/li/cil/oc/common/tileentity/Robot.scala b/li/cil/oc/common/tileentity/Robot.scala index 66b24f2aa..f169f92e2 100644 --- a/li/cil/oc/common/tileentity/Robot.scala +++ b/li/cil/oc/common/tileentity/Robot.scala @@ -93,7 +93,13 @@ class Robot(isRemote: Boolean) extends Computer(isRemote) with Buffer with Power override def validate() { super.validate() - if (isClient) { + if (isServer) { + items(0) match { + case Some(item) => player_.getAttributeMap.applyAttributeModifiers(item.getAttributeModifiers) + case _ => + } + } + else { ClientPacketSender.sendRotatableStateRequest(this) ClientPacketSender.sendScreenBufferRequest(this) ClientPacketSender.sendRobotSelectedSlotRequest(this) @@ -198,8 +204,18 @@ class Robot(isRemote: Boolean) extends Computer(isRemote) with Buffer with Power case _ => false // Invalid slot. } + override protected def onItemRemoved(slot: Int, item: ItemStack) { + super.onItemRemoved(slot, item) + if (slot == 0) { + player_.getAttributeMap.removeAttributeModifiers(item.getAttributeModifiers) + } + } + override protected def onItemAdded(slot: Int, item: ItemStack) { - if (slot > 0 && slot < 3) { + if (slot == 0) { + player_.getAttributeMap.applyAttributeModifiers(item.getAttributeModifiers) + } + else if (slot == 1 || slot == 2) { super.onItemAdded(slot, item) } } diff --git a/li/cil/oc/server/component/Robot.scala b/li/cil/oc/server/component/Robot.scala index 2ecae4999..d8fa0e031 100644 --- a/li/cil/oc/server/component/Robot.scala +++ b/li/cil/oc/server/component/Robot.scala @@ -3,9 +3,11 @@ package li.cil.oc.server.component import li.cil.oc.api.network.{LuaCallback, Arguments, Context} import li.cil.oc.common.tileentity import li.cil.oc.server.{PacketSender => ServerPacketSender} +import li.cil.oc.util.ActivationType import net.minecraft.block.{BlockFluid, Block} import net.minecraft.entity.item.EntityItem import net.minecraft.item.{ItemStack, ItemBlock} +import net.minecraft.util.{EnumMovingObjectType, Vec3} import net.minecraftforge.common.ForgeDirection import net.minecraftforge.fluids.FluidRegistry import scala.Some @@ -40,7 +42,7 @@ class Robot(val robot: tileentity.Robot) extends Computer(robot) { ServerPacketSender.sendRobotSelectedSlotState(robot) } } - result(selectedSlot) + result(selectedSlot + 1) } @LuaCallback("count") @@ -200,32 +202,91 @@ class Robot(val robot: tileentity.Robot) extends Computer(robot) { @LuaCallback("swing") def swing(context: Context, args: Arguments): Array[AnyRef] = { // Swing the equipped tool (left click). - val lookSide = checkSideForAction(args, 0) - val side = if (args.isInteger(1)) checkSide(args, 1) else lookSide - if (side.getOpposite == lookSide) { + val facing = checkSideForAction(args, 0) + val side = if (args.isInteger(1)) checkSide(args, 1) else facing + if (side.getOpposite == facing) { throw new IllegalArgumentException("invalid side") } - val player = robot.player(lookSide) - val (bx, by, bz) = (x + lookSide.offsetX, y + lookSide.offsetY, z + lookSide.offsetZ) - result(player.clickBlock(bx, by, bz, side.getOpposite.ordinal)) - //robot.player().attackTargetEntityWithCurrentItem(entity) + val sneaky = args.isBoolean(2) && args.checkBoolean(2) + val player = robot.player(facing) + Option(simulateClick(facing, side, 0.49)) match { + case Some(hit) => + player.setSneaking(sneaky) + val what = hit.typeOfHit match { + case EnumMovingObjectType.ENTITY => + player.attackTargetEntityWithCurrentItem(hit.entityHit) + result(true, "entity") + case EnumMovingObjectType.TILE => + val broke = player.clickBlock(hit.blockX, hit.blockY, hit.blockZ, hit.sideHit) + result(broke, "block") + } + player.setSneaking(false) + what + case _ => + player.closestLivingEntity(facing) match { + case Some(entity) => + player.attackTargetEntityWithCurrentItem(entity) + result(true, "entity") + case _ => + result(false) + } + } } @LuaCallback("use") def use(context: Context, args: Arguments): Array[AnyRef] = { - val lookSide = checkSideForAction(args, 0) - val side = if (args.isInteger(1)) checkSide(args, 1) else lookSide - if (side.getOpposite == lookSide) { + val facing = checkSideForAction(args, 0) + val side = if (args.isInteger(1)) checkSide(args, 1) else facing + if (side.getOpposite == facing) { throw new IllegalArgumentException("invalid side") } val sneaky = args.isBoolean(2) && args.checkBoolean(2) - val player = robot.player(lookSide) - player.setSneaking(sneaky) - val (bx, by, bz) = (x + lookSide.offsetX, y + lookSide.offsetY, z + lookSide.offsetZ) - val (hx, hy, hz) = (0.5f + side.offsetX * 0.5f, 0.5f + side.offsetY * 0.5f, 0.5f + side.offsetZ * 0.5f) - val ok = player.activateBlockOrUseItem(bx, by, bz, side.getOpposite.ordinal, hx, hy, hz) - player.setSneaking(false) - result(ok) + val player = robot.player(facing) + Option(simulateClick(facing, side, 0.51)) match { + case Some(hit) => + player.setSneaking(sneaky) + val what = hit.typeOfHit match { + case EnumMovingObjectType.ENTITY => + // TODO Is there any practical use for this? Most of the stuff related to this is still 'obfuscated'... + result(false, "entity") + case EnumMovingObjectType.TILE => + val (hx, hy, hz) = ( + (hit.hitVec.xCoord - hit.blockX).toFloat, + (hit.hitVec.yCoord - hit.blockY).toFloat, + (hit.hitVec.zCoord - hit.blockZ).toFloat) + player.activateBlockOrUseItem(hit.blockX, hit.blockY, hit.blockZ, hit.sideHit, hx, hy, hz) match { + case ActivationType.BlockActivated => result(true, "block_activated") + case ActivationType.ItemPlaced => result(true, "item_placed") + case ActivationType.ItemUsed => result(true, "item_used") + case _ => result(false) + } + } + player.setSneaking(false) + what + case _ => + if (player.useEquippedItem()) result(true, "item_used") + else result(false) + } + } + + @LuaCallback("durability") + def durability(context: Context, args: Arguments): Array[AnyRef] = { + Option(robot.getStackInSlot(0)) match { + case Some(item) => + if (item.isItemStackDamageable) { + result(item.getMaxDamage - item.getItemDamage) + } + else result(Unit, "tool cannot be damaged") + case _ => result(Unit, "no tool equipped") + } + } + + def simulateClick(facing: ForgeDirection, side: ForgeDirection, range: Double) = { + val (bx, by, bz) = (x + facing.offsetX, y + facing.offsetY, z + facing.offsetZ) + val (hx, hy, hz) = (0.5 + side.offsetX * range, 0.5 + side.offsetY * range, 0.5 + side.offsetZ * range) + val origin = Vec3.createVectorHelper(x + 0.5, y + 0.5, z + 0.5) + val target = Vec3.createVectorHelper(bx + hx, by + hy, bz + hz) + world.clip(origin, target) } // ----------------------------------------------------------------------- // diff --git a/li/cil/oc/util/ActivationType.scala b/li/cil/oc/util/ActivationType.scala new file mode 100644 index 000000000..5bd9d81d1 --- /dev/null +++ b/li/cil/oc/util/ActivationType.scala @@ -0,0 +1,5 @@ +package li.cil.oc.util + +object ActivationType extends Enumeration { + val None, ItemUsed, ItemPlaced, BlockActivated = Value +} diff --git a/li/cil/oc/util/RobotPlayer.scala b/li/cil/oc/util/RobotPlayer.scala index 85929b7ef..7ed99196b 100644 --- a/li/cil/oc/util/RobotPlayer.scala +++ b/li/cil/oc/util/RobotPlayer.scala @@ -66,12 +66,7 @@ class RobotPlayer(val robot: Robot) extends FakePlayer(robot.world, "OpenCompute def entitiesOnSide[Type <: Entity : ClassTag](side: ForgeDirection) = { val (bx, by, bz) = (robot.x + side.offsetX, robot.y + side.offsetY, robot.z + side.offsetZ) - val id = world.getBlockId(bx, by, bz) - val block = Block.blocksList(id) - if (id == 0 || block == null || block.isAirBlock(world, bx, by, bz)) { - entitiesInBlock[Type](bx, by, bz) - } - else Iterable.empty + entitiesInBlock[Type](bx, by, bz) } def entitiesInBlock[Type <: Entity : ClassTag](x: Int, y: Int, z: Int) = { @@ -81,14 +76,20 @@ class RobotPlayer(val robot: Robot) extends FakePlayer(robot.world, "OpenCompute // ----------------------------------------------------------------------- // - // TODO override def canAttackWithItem = super.canAttackWithItem + override def attackTargetEntityWithCurrentItem(entity: Entity) { + val stack = getCurrentEquippedItem + val oldDamage = getCurrentEquippedItem.getItemDamage + super.attackTargetEntityWithCurrentItem(entity) + if (stack.stackSize > 0 && stack.isItemStackDamageable && getRNG.nextDouble() >= Config.itemDamageRate) { + val addedDamage = ((stack.getItemDamage - oldDamage) * Config.itemDamageRate).toInt + stack.setItemDamage(oldDamage + addedDamage) + } + } - // TODO override def attackTargetEntityWithCurrentItem(par1Entity: Entity) - - def activateBlockOrUseItem(x: Int, y: Int, z: Int, side: Int, hitX: Float, hitY: Float, hitZ: Float): Boolean = { + def activateBlockOrUseItem(x: Int, y: Int, z: Int, side: Int, hitX: Float, hitY: Float, hitZ: Float): ActivationType.Value = { val event = ForgeEventFactory.onPlayerInteract(this, Action.RIGHT_CLICK_BLOCK, x, y, z, side) - if (event.isCanceled) { - return false + if (event.isCanceled || event.useBlock == Event.Result.DENY) { + return ActivationType.None } val stack = inventory.getCurrentItem @@ -97,7 +98,7 @@ class RobotPlayer(val robot: Robot) extends FakePlayer(robot.world, "OpenCompute if (item != null && item.onItemUseFirst(stack, this, world, x, y, z, side, hitX, hitY, hitZ)) { if (stack.stackSize <= 0) ForgeEventFactory.onPlayerDestroyItem(this, stack) if (stack.stackSize <= 0) inventory.setInventorySlotContents(0, null) - return true + return ActivationType.ItemUsed } } @@ -105,17 +106,36 @@ class RobotPlayer(val robot: Robot) extends FakePlayer(robot.world, "OpenCompute val block = Block.blocksList(blockId) val canActivate = block != null && Config.allowActivateBlocks val shouldActivate = canActivate && (!isSneaking || (item == null || item.shouldPassSneakingClickToBlock(world, x, y, z))) - val activated = shouldActivate && (event.useBlock == Event.Result.DENY || - block.onBlockActivated(world, x, y, z, this, side, hitX, hitY, hitZ)) + if (shouldActivate && block.onBlockActivated(world, x, y, z, this, side, hitX, hitY, hitZ)) { + return ActivationType.BlockActivated + } - activated || (stack != null && ({ - val direction = ForgeDirection.getOrientation(side).getOpposite - val (onX, onY, onZ) = (x + direction.offsetX, y + direction.offsetY, z + direction.offsetZ) - val result = stack.tryPlaceItemIntoWorld(this, world, onX, onY, onZ, side, hitX, hitY, hitZ) + if (stack != null) { + val didPlace = stack.tryPlaceItemIntoWorld(this, world, x, y, z, side, hitX, hitY, hitZ) if (stack.stackSize <= 0) ForgeEventFactory.onPlayerDestroyItem(this, stack) if (stack.stackSize <= 0) inventory.setInventorySlotContents(0, null) - result - } || stack.getMaxItemUseDuration <= 0 && { + if (didPlace) { + return ActivationType.ItemPlaced + } + + if (tryUseItem(stack)) { + return ActivationType.ItemUsed + } + } + + ActivationType.None + } + + def useEquippedItem() = { + val event = ForgeEventFactory.onPlayerInteract(this, Action.RIGHT_CLICK_AIR, 0, 0, 0, -1) + if (!event.isCanceled && event.useItem != Event.Result.DENY) { + tryUseItem(getCurrentEquippedItem) + } + else false + } + + private def tryUseItem(stack: ItemStack) = + stack != null && stack.stackSize > 0 && stack.getMaxItemUseDuration <= 0 && { val oldSize = stack.stackSize val oldDamage = stack.getItemDamage val newStack = stack.useItemRightClick(world, this) @@ -126,8 +146,7 @@ class RobotPlayer(val robot: Robot) extends FakePlayer(robot.world, "OpenCompute else inventory.setInventorySlotContents(0, null) true } - })) - } + } def placeBlock(stack: ItemStack, x: Int, y: Int, z: Int, side: Int, hitX: Float, hitY: Float, hitZ: Float): Boolean = { val event = ForgeEventFactory.onPlayerInteract(this, Action.RIGHT_CLICK_BLOCK, x, y, z, side) @@ -184,16 +203,17 @@ class RobotPlayer(val robot: Robot) extends FakePlayer(robot.world, "OpenCompute if (stack.stackSize == 0) { destroyCurrentEquippedItem() } - else if (stack.isItemStackDamageable && getRNG.nextDouble() >= Config.itemDamageChance) { - stack.setItemDamage(oldDamage) + else if (stack.isItemStackDamageable && getRNG.nextDouble() >= Config.itemDamageRate) { + val addedDamage = ((stack.getItemDamage - oldDamage) * Config.itemDamageRate).toInt + stack.setItemDamage(oldDamage + addedDamage) } } + val itemsBefore = entitiesInBlock[EntityItem](x, y, z) block.onBlockHarvested(world, x, y, z, metadata, this) if (block.removeBlockByPlayer(world, this, x, y, z)) { block.onBlockDestroyedByPlayer(world, x, y, z, metadata) if (block.canHarvestBlock(this, metadata)) { - val itemsBefore = entitiesInBlock[EntityItem](x, y, z) block.harvestBlock(world, this, x, y, z, metadata) val itemsAfter = entitiesInBlock[EntityItem](x, y, z) val itemsDropped = itemsAfter -- itemsBefore