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 allowActivateBlocks = true
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() {
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)
}
}

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.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)
val player = robot.player(facing)
Option(simulateClick(facing, side, 0.51)) match {
case Some(hit) =>
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)
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)
result(ok)
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,13 +66,8 @@ 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
}
def entitiesInBlock[Type <: Entity : ClassTag](x: Int, y: Int, z: Int) = {
val bounds = AxisAlignedBB.getAABBPool.getAABB(x, y, z, x + 1, y + 1, z + 1)
@ -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,7 +146,6 @@ 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 = {
@ -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