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)

This commit is contained in:
Florian Nücke 2013-11-19 01:34:25 +01:00
parent 942027d957
commit bade1b5dc9
5 changed files with 149 additions and 47 deletions

View File

@ -83,7 +83,7 @@ object Config {
var callOnItemUseFirst = false var callOnItemUseFirst = false
var allowActivateBlocks = true var allowActivateBlocks = true
var canAttackPlayers = false var canAttackPlayers = false
var itemDamageChance = 0.05 var itemDamageRate = 0.05
// ----------------------------------------------------------------------- // // ----------------------------------------------------------------------- //

View File

@ -93,7 +93,13 @@ class Robot(isRemote: Boolean) extends Computer(isRemote) with Buffer with Power
override def validate() { override def validate() {
super.validate() super.validate()
if (isClient) { if (isServer) {
items(0) match {
case Some(item) => player_.getAttributeMap.applyAttributeModifiers(item.getAttributeModifiers)
case _ =>
}
}
else {
ClientPacketSender.sendRotatableStateRequest(this) ClientPacketSender.sendRotatableStateRequest(this)
ClientPacketSender.sendScreenBufferRequest(this) ClientPacketSender.sendScreenBufferRequest(this)
ClientPacketSender.sendRobotSelectedSlotRequest(this) ClientPacketSender.sendRobotSelectedSlotRequest(this)
@ -198,8 +204,18 @@ class Robot(isRemote: Boolean) extends Computer(isRemote) with Buffer with Power
case _ => false // Invalid slot. 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) { 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) super.onItemAdded(slot, item)
} }
} }

View File

@ -3,9 +3,11 @@ package li.cil.oc.server.component
import li.cil.oc.api.network.{LuaCallback, Arguments, Context} import li.cil.oc.api.network.{LuaCallback, Arguments, Context}
import li.cil.oc.common.tileentity import li.cil.oc.common.tileentity
import li.cil.oc.server.{PacketSender => ServerPacketSender} import li.cil.oc.server.{PacketSender => ServerPacketSender}
import li.cil.oc.util.ActivationType
import net.minecraft.block.{BlockFluid, Block} import net.minecraft.block.{BlockFluid, Block}
import net.minecraft.entity.item.EntityItem import net.minecraft.entity.item.EntityItem
import net.minecraft.item.{ItemStack, ItemBlock} import net.minecraft.item.{ItemStack, ItemBlock}
import net.minecraft.util.{EnumMovingObjectType, Vec3}
import net.minecraftforge.common.ForgeDirection import net.minecraftforge.common.ForgeDirection
import net.minecraftforge.fluids.FluidRegistry import net.minecraftforge.fluids.FluidRegistry
import scala.Some import scala.Some
@ -40,7 +42,7 @@ class Robot(val robot: tileentity.Robot) extends Computer(robot) {
ServerPacketSender.sendRobotSelectedSlotState(robot) ServerPacketSender.sendRobotSelectedSlotState(robot)
} }
} }
result(selectedSlot) result(selectedSlot + 1)
} }
@LuaCallback("count") @LuaCallback("count")
@ -200,32 +202,91 @@ class Robot(val robot: tileentity.Robot) extends Computer(robot) {
@LuaCallback("swing") @LuaCallback("swing")
def swing(context: Context, args: Arguments): Array[AnyRef] = { def swing(context: Context, args: Arguments): Array[AnyRef] = {
// Swing the equipped tool (left click). // Swing the equipped tool (left click).
val lookSide = checkSideForAction(args, 0) val facing = checkSideForAction(args, 0)
val side = if (args.isInteger(1)) checkSide(args, 1) else lookSide val side = if (args.isInteger(1)) checkSide(args, 1) else facing
if (side.getOpposite == lookSide) { if (side.getOpposite == facing) {
throw new IllegalArgumentException("invalid side") throw new IllegalArgumentException("invalid side")
} }
val player = robot.player(lookSide) val sneaky = args.isBoolean(2) && args.checkBoolean(2)
val (bx, by, bz) = (x + lookSide.offsetX, y + lookSide.offsetY, z + lookSide.offsetZ) val player = robot.player(facing)
result(player.clickBlock(bx, by, bz, side.getOpposite.ordinal)) Option(simulateClick(facing, side, 0.49)) match {
//robot.player().attackTargetEntityWithCurrentItem(entity) 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") @LuaCallback("use")
def use(context: Context, args: Arguments): Array[AnyRef] = { def use(context: Context, args: Arguments): Array[AnyRef] = {
val lookSide = checkSideForAction(args, 0) val facing = checkSideForAction(args, 0)
val side = if (args.isInteger(1)) checkSide(args, 1) else lookSide val side = if (args.isInteger(1)) checkSide(args, 1) else facing
if (side.getOpposite == lookSide) { if (side.getOpposite == facing) {
throw new IllegalArgumentException("invalid side") throw new IllegalArgumentException("invalid side")
} }
val sneaky = args.isBoolean(2) && args.checkBoolean(2) val sneaky = args.isBoolean(2) && args.checkBoolean(2)
val player = robot.player(lookSide) val player = robot.player(facing)
player.setSneaking(sneaky) Option(simulateClick(facing, side, 0.51)) match {
val (bx, by, bz) = (x + lookSide.offsetX, y + lookSide.offsetY, z + lookSide.offsetZ) case Some(hit) =>
val (hx, hy, hz) = (0.5f + side.offsetX * 0.5f, 0.5f + side.offsetY * 0.5f, 0.5f + side.offsetZ * 0.5f) player.setSneaking(sneaky)
val ok = player.activateBlockOrUseItem(bx, by, bz, side.getOpposite.ordinal, hx, hy, hz) val what = hit.typeOfHit match {
player.setSneaking(false) case EnumMovingObjectType.ENTITY =>
result(ok) // 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)
} }
// ----------------------------------------------------------------------- // // ----------------------------------------------------------------------- //

View File

@ -0,0 +1,5 @@
package li.cil.oc.util
object ActivationType extends Enumeration {
val None, ItemUsed, ItemPlaced, BlockActivated = Value
}

View File

@ -66,12 +66,7 @@ class RobotPlayer(val robot: Robot) extends FakePlayer(robot.world, "OpenCompute
def entitiesOnSide[Type <: Entity : ClassTag](side: ForgeDirection) = { def entitiesOnSide[Type <: Entity : ClassTag](side: ForgeDirection) = {
val (bx, by, bz) = (robot.x + side.offsetX, robot.y + side.offsetY, robot.z + side.offsetZ) val (bx, by, bz) = (robot.x + side.offsetX, robot.y + side.offsetY, robot.z + side.offsetZ)
val id = world.getBlockId(bx, by, bz) entitiesInBlock[Type](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
} }
def entitiesInBlock[Type <: Entity : ClassTag](x: Int, y: Int, z: Int) = { 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): ActivationType.Value = {
def activateBlockOrUseItem(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) val event = ForgeEventFactory.onPlayerInteract(this, Action.RIGHT_CLICK_BLOCK, x, y, z, side)
if (event.isCanceled) { if (event.isCanceled || event.useBlock == Event.Result.DENY) {
return false return ActivationType.None
} }
val stack = inventory.getCurrentItem 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 (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) ForgeEventFactory.onPlayerDestroyItem(this, stack)
if (stack.stackSize <= 0) inventory.setInventorySlotContents(0, null) 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 block = Block.blocksList(blockId)
val canActivate = block != null && Config.allowActivateBlocks val canActivate = block != null && Config.allowActivateBlocks
val shouldActivate = canActivate && (!isSneaking || (item == null || item.shouldPassSneakingClickToBlock(world, x, y, z))) val shouldActivate = canActivate && (!isSneaking || (item == null || item.shouldPassSneakingClickToBlock(world, x, y, z)))
val activated = shouldActivate && (event.useBlock == Event.Result.DENY || if (shouldActivate && block.onBlockActivated(world, x, y, z, this, side, hitX, hitY, hitZ)) {
block.onBlockActivated(world, x, y, z, this, side, hitX, hitY, hitZ)) return ActivationType.BlockActivated
}
activated || (stack != null && ({ if (stack != null) {
val direction = ForgeDirection.getOrientation(side).getOpposite val didPlace = stack.tryPlaceItemIntoWorld(this, world, x, y, z, side, hitX, hitY, hitZ)
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.stackSize <= 0) ForgeEventFactory.onPlayerDestroyItem(this, stack) if (stack.stackSize <= 0) ForgeEventFactory.onPlayerDestroyItem(this, stack)
if (stack.stackSize <= 0) inventory.setInventorySlotContents(0, null) if (stack.stackSize <= 0) inventory.setInventorySlotContents(0, null)
result if (didPlace) {
} || stack.getMaxItemUseDuration <= 0 && { 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 oldSize = stack.stackSize
val oldDamage = stack.getItemDamage val oldDamage = stack.getItemDamage
val newStack = stack.useItemRightClick(world, this) 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) else inventory.setInventorySlotContents(0, null)
true true
} }
})) }
}
def placeBlock(stack: ItemStack, x: Int, y: Int, z: Int, side: Int, hitX: Float, hitY: Float, hitZ: Float): Boolean = { 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) 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) { if (stack.stackSize == 0) {
destroyCurrentEquippedItem() destroyCurrentEquippedItem()
} }
else if (stack.isItemStackDamageable && getRNG.nextDouble() >= Config.itemDamageChance) { else if (stack.isItemStackDamageable && getRNG.nextDouble() >= Config.itemDamageRate) {
stack.setItemDamage(oldDamage) 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) block.onBlockHarvested(world, x, y, z, metadata, this)
if (block.removeBlockByPlayer(world, this, x, y, z)) { if (block.removeBlockByPlayer(world, this, x, y, z)) {
block.onBlockDestroyedByPlayer(world, x, y, z, metadata) block.onBlockDestroyedByPlayer(world, x, y, z, metadata)
if (block.canHarvestBlock(this, metadata)) { if (block.canHarvestBlock(this, metadata)) {
val itemsBefore = entitiesInBlock[EntityItem](x, y, z)
block.harvestBlock(world, this, x, y, z, metadata) block.harvestBlock(world, this, x, y, z, metadata)
val itemsAfter = entitiesInBlock[EntityItem](x, y, z) val itemsAfter = entitiesInBlock[EntityItem](x, y, z)
val itemsDropped = itemsAfter -- itemsBefore val itemsDropped = itemsAfter -- itemsBefore