Started to modularize robots, making experience an upgrade and introducing a bunch of events.

This commit is contained in:
Florian Nücke 2014-05-06 21:07:55 +02:00
parent 9264d3c3d0
commit f2798786c0
30 changed files with 884 additions and 174 deletions

View File

@ -0,0 +1,21 @@
package li.cil.oc.api.event;
import li.cil.oc.api.machine.Robot;
import net.minecraft.entity.player.EntityPlayer;
/**
* Fired when an analyzer is used on a robot.
* <p/>
* Use this to echo additional information for custom components.
*/
public class RobotAnalyzeEvent extends RobotEvent {
/**
* The player that used the analyzer.
*/
public final EntityPlayer player;
public RobotAnalyzeEvent(Robot robot, EntityPlayer player) {
super(robot);
this.player = player;
}
}

View File

@ -0,0 +1,38 @@
package li.cil.oc.api.event;
import li.cil.oc.api.machine.Robot;
import net.minecraft.entity.Entity;
import net.minecraftforge.event.Cancelable;
public class RobotAttackEntityEvent extends RobotEvent {
/**
* The entity that the robot will attack.
*/
public final Entity target;
protected RobotAttackEntityEvent(Robot robot, Entity target) {
super(robot);
this.target = target;
}
/**
* Fired when a robot is about to attack an entity.
* <p/>
* Canceling this event will prevent the attack.
*/
@Cancelable
public static class Pre extends RobotAttackEntityEvent {
public Pre(Robot robot, Entity target) {
super(robot, target);
}
}
/**
* Fired after a robot has attacked an entity.
*/
public static class Post extends RobotAttackEntityEvent {
public Post(Robot robot, Entity target) {
super(robot, target);
}
}
}

View File

@ -0,0 +1,79 @@
package li.cil.oc.api.event;
import li.cil.oc.api.machine.Robot;
import net.minecraft.world.World;
import net.minecraftforge.event.Cancelable;
public abstract class RobotBreakBlockEvent extends RobotEvent {
protected RobotBreakBlockEvent(Robot robot) {
super(robot);
}
/**
* Fired when a robot is about to break a block.
* <p/>
* Canceling this event will prevent the block from getting broken.
*/
@Cancelable
public static class Pre extends RobotBreakBlockEvent {
/**
* The world in which the block will be broken.
*/
public final World world;
/**
* The coordinates at which the block will be broken.
*/
public final int x, y, z;
/**
* The time it takes to break the block.
*/
private double breakTime;
public Pre(Robot robot, World world, int x, int y, int z, double breakTime) {
super(robot);
this.world = world;
this.x = x;
this.y = y;
this.z = z;
this.breakTime = breakTime;
}
/**
* Sets the time it should take the robot to break the block.
* <p/>
* Note that the robot will still break the block instantly, but the
* robot's execution is paused for the specified amount of time.
*
* @param breakTime the time in seconds the break operation takes.
*/
public void setBreakTime(double breakTime) {
this.breakTime = Math.max(0.05, breakTime);
}
/**
* Gets the time that it will take to break the block.
*
* @see #setBreakTime(double)
*/
public double getBreakTime() {
return breakTime;
}
}
/**
* Fired after a robot broke a block.
*/
public static class Post extends RobotBreakBlockEvent {
/**
* The amount of experience the block that was broken generated (e.g. certain ores).
*/
public final double experience;
public Post(Robot robot, double experience) {
super(robot);
this.experience = experience;
}
}
}

View File

@ -0,0 +1,18 @@
package li.cil.oc.api.event;
import li.cil.oc.api.machine.Robot;
import net.minecraftforge.event.Event;
/**
* Base class for events generated by robots.
*/
public abstract class RobotEvent extends Event {
/**
* The robot for which this event was fired.
*/
public final Robot robot;
protected RobotEvent(Robot robot) {
this.robot = robot;
}
}

View File

@ -0,0 +1,19 @@
package li.cil.oc.api.event;
import li.cil.oc.api.machine.Robot;
/**
* Fired when a robot performed an action that would cause exhaustion for a
* player. Used for the experience upgrade, for example.
*/
public class RobotExhaustionEvent extends RobotEvent {
/**
* The amount of exhaustion that was generated.
*/
public final double exhaustion;
public RobotExhaustionEvent(Robot robot, double exhaustion) {
super(robot);
this.exhaustion = exhaustion;
}
}

View File

@ -0,0 +1,38 @@
package li.cil.oc.api.event;
import li.cil.oc.api.machine.Robot;
import net.minecraftforge.common.ForgeDirection;
import net.minecraftforge.event.Cancelable;
public abstract class RobotMoveEvent extends RobotEvent {
/**
* The direction in which the robot will be moving.
*/
public final ForgeDirection direction;
protected RobotMoveEvent(Robot robot, ForgeDirection direction) {
super(robot);
this.direction = direction;
}
/**
* Fired when a robot is about to move.
* <p/>
* Canceling the event will prevent the robot from moving.
*/
@Cancelable
public static class Pre extends RobotMoveEvent {
public Pre(Robot robot, ForgeDirection direction) {
super(robot, direction);
}
}
/**
* Fired after a robot moved.
*/
public static class Post extends RobotMoveEvent {
public Post(Robot robot, ForgeDirection direction) {
super(robot, direction);
}
}
}

View File

@ -0,0 +1,53 @@
package li.cil.oc.api.event;
import li.cil.oc.api.machine.Robot;
import net.minecraft.item.ItemStack;
import net.minecraft.world.World;
import net.minecraftforge.event.Cancelable;
public abstract class RobotPlaceBlockEvent extends RobotEvent {
/**
* The item that is used to place the block.
*/
public final ItemStack stack;
/**
* The world in which the block will be placed.
*/
public final World world;
/**
* The coordinates at which the block will be placed.
*/
public final int x, y, z;
protected RobotPlaceBlockEvent(Robot robot, ItemStack stack, World world, int x, int y, int z) {
super(robot);
this.stack = stack;
this.world = world;
this.x = x;
this.y = y;
this.z = z;
}
/**
* Fired when a robot is about to place a block.
* <p/>
* Canceling this event will prevent the block from being placed.
*/
@Cancelable
public static class Pre extends RobotPlaceBlockEvent {
public Pre(Robot robot, ItemStack stack, World world, int x, int y, int z) {
super(robot, stack, world, x, y, z);
}
}
/**
* Fired after a robot placed a block.
*/
public static class Post extends RobotPlaceBlockEvent {
public Post(Robot robot, ItemStack stack, World world, int x, int y, int z) {
super(robot, stack, world, x, y, z);
}
}
}

View File

@ -0,0 +1,51 @@
package li.cil.oc.api.event;
import li.cil.oc.api.machine.Robot;
import net.minecraft.util.Vec3;
import net.minecraftforge.event.Cancelable;
/**
* Fired directly before the robot's chassis is rendered.
* <p/>
* If this event is canceled, the chassis will <em>not</em> be rendered.
* Component items' item renderes will still be invoked, at the possibly
* modified mount points.
* <p/>
* <em>Important</em>: the robot instance may be null in this event, in
* case the render pass is for rendering the robot in an inventory.
*/
@Cancelable
public class RobotRenderEvent extends RobotEvent {
/**
* Points on the robot at which component models may be rendered.
* <p/>
* By convention, components should be rendered in order of their slots,
* meaning that some components may not be rendered at all, if there are
* not enough mount points.
* <p/>
* The equipped tool is rendered at a fixed position, this list does not
* contain a mount point for it.
*/
public final MountPoint[] mountPoints;
public RobotRenderEvent(Robot robot, MountPoint[] mountPoints) {
super(robot);
this.mountPoints = mountPoints;
}
/**
* Describes points on the robot model at which components are "mounted",
* i.e. where component models may be rendered.
*/
public static class MountPoint {
/**
* The position of the mount point, relative to the robot's center.
*/
public final Vec3 offset = Vec3.createVectorHelper(0, 0, 0);
/**
* The vector the mount point is facing.
*/
public final Vec3 normal = Vec3.createVectorHelper(0, 0, 0);
}
}

View File

@ -0,0 +1,70 @@
package li.cil.oc.api.event;
import li.cil.oc.api.machine.Robot;
import net.minecraft.item.ItemStack;
public class RobotUsedTool extends RobotEvent {
/**
* The tool that was used, before and after use.
*/
public final ItemStack toolBeforeUse, toolAfterUse;
protected double damageRate;
protected RobotUsedTool(Robot robot, ItemStack toolBeforeUse, ItemStack toolAfterUse, double damageRate) {
super(robot);
this.toolBeforeUse = toolBeforeUse;
this.toolAfterUse = toolAfterUse;
this.damageRate = damageRate;
}
/**
* The rate at which the used tool should lose durability, where one means
* it loses durability at full speed, zero means it doesn't lose durability
* at all.
* <p/>
* This value is in an interval of [0, 1].
*/
public double getDamageRate() {
return damageRate;
}
/**
* Fired when a robot used a tool and is about to apply the damage rate to
* partially undo the durability loss. This step is used to compute the
* rate at which the tool should lose durability, which is used by the
* experience upgrade, for example.
*/
public static class ComputeDamageRate extends RobotUsedTool {
public ComputeDamageRate(Robot robot, ItemStack toolBeforeUse, ItemStack toolAfterUse, double damageRate) {
super(robot, toolBeforeUse, toolAfterUse, damageRate);
}
/**
* Set the rate at which the tool actually gets damaged.
* <p/>
* This will be clamped to an iterval of [0, 1].
*
* @param damageRate the new damage rate.
*/
public void setDamageRate(double damageRate) {
this.damageRate = Math.max(0, Math.min(1, damageRate));
}
}
/**
* Fired when a robot used a tool and the previously fired damage rate
* computation returned a value smaller than one. The callbacks of this
* method are responsible for applying the inverse damage the tool took.
* The <tt>toolAfterUse</tt> item stack represents the actual tool, any
* changes must be applied to that variable. The <tt>toolBeforeUse</tt>
* item stack is passed for reference, to compute the actual amount of
* durability that was lost. This may be required for tools where the
* durability is stored in the item's NBT tag.
*/
public static class ApplyDamageRate extends RobotUsedTool {
public ApplyDamageRate(Robot robot, ItemStack toolBeforeUse, ItemStack toolAfterUse, double damageRate) {
super(robot, toolBeforeUse, toolAfterUse, damageRate);
}
}
}

View File

@ -1,7 +1,9 @@
package li.cil.oc.api.machine;
import li.cil.oc.api.Rotatable;
import li.cil.oc.api.network.Environment;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
/**
* This interface allows interaction with robots.
@ -9,6 +11,18 @@ import net.minecraft.entity.player.EntityPlayer;
* It is intended to be used by components when installed in a robot. In that
* case, the robot in question is the tile entity passed to item driver when
* asked to create the component's environment.
* <p/>
* A robot's inventory contains component items and items in the actual
* inventory. The physical layout in the underlying 'real' inventory is as
* follows:
* <ul>
* <li>Slot 0: Tool</li>
* <li>Slot [1, dynamicComponentCapacity + 1): hot-swappable components.</li>
* <li>Slot [dynamicComponentCapacity + 1, componentCapacity + 1): hard-wired components.</li>
* <li>Slot [componentCapacity + 1, inventorySize): actual inventory.</li>
* </ul>
* Note that either of these intervals may be empty, depending on the parts
* the robot is built from.
*/
public interface Robot extends Rotatable {
/**
@ -17,18 +31,59 @@ public interface Robot extends Rotatable {
* <p/>
* This will automatically be positioned and rotated to represent the
* robot's current position and rotation in the world. Use this to trigger
* events involving the robot that require a player entity, and for
* interacting with the robots' inventory.
* <p/>
* Note that the inventory of each robot is structured such that the first
* four slots are the "equipment" slots, from left to right, i.e. slot one
* is the tool slot, slot two is the card slot, three the disk slot and
* slot four is for upgrades. The inventory proper starts after that.
* events involving the robot that require a player entity, and for more
* in-depth interaction with the robots' inventory.
*
* @return the fake player for the robot.
*/
EntityPlayer player();
/**
* The number of hot-swappable component slots in this robot.
*/
int dynamicComponentCapacity();
/**
* The <em>total</em> number of component slots in this robot, including
* hot-swappable component slots.
*/
int componentCapacity();
/**
* The <em>total</em> inventory space in this robot, including tool and
* component slots.
*/
int inventorySize();
/**
* Get the item stack in the specified inventory slot.
* <p/>
* This operates on the underlying, real inventory, as described in the
* comment on top of this class. The starting index of the part of the
* inventory that is accessible to the robot for manipulation is at
* <tt>componentCapacity + 1</tt>.
* <p/>
* This will return <tt>null</tt> for empty slots.
*
* @param index the index of the slot from which to get the stack.
* @return the content of that slot, or <tt>null</tt>.
*/
ItemStack getStackInSlot(int index);
/**
* Get the environment for the component in the specified slot.
* <p/>
* This operates on the underlying, real inventory, as described in the
* comment on top of this class.
* <p/>
* This will return <tt>null</tt> for slots that do not contain components,
* or components that do not have an environment (on the calling side).
*
* @param index the index of the slot from which to get the environment.
* @return the environment for that slot, or <tt>null</tt>.
*/
Environment getComponentInSlot(int index);
/**
* Gets the index of the currently selected slot in the robot's inventory.
*

View File

@ -123,6 +123,11 @@ craftingUpgrade {
["oc:circuitChip1", workbench, "oc:circuitChip1"]
[ingotIron, "oc:materialCircuitBoardPrinted", ingotIron]]
}
experienceUpgrade {
input: [[ingotIron, "", ingotIron]
["oc:circuitChip1", expBottle, "oc:circuitChip1"]
[ingotIron, "oc:materialCircuitBoardPrinted", ingotIron]]
}
generatorUpgrade {
input: [[ingotIron, "", ingotIron]
["oc:circuitChip1", craftingPiston, "oc:circuitChip1"]

Binary file not shown.

After

Width:  |  Height:  |  Size: 325 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 483 B

View File

@ -172,5 +172,6 @@ object Items extends ItemAPI {
// v1.3.0
Recipes.addItemDelegate(new item.LinkedCard(multi), "linkedCard", "oc:linkedCard")
Recipes.addItemDelegate(new item.UpgradeExperience(multi), "experienceUpgrade", "oc:experienceUpgrade")
}
}

View File

@ -42,7 +42,6 @@ class PacketHandler extends CommonPacketHandler {
case PacketType.RobotEquippedUpgradeChange => onRobotEquippedUpgradeChange(p)
case PacketType.RobotMove => onRobotMove(p)
case PacketType.RobotSelectedSlotChange => onRobotSelectedSlotChange(p)
case PacketType.RobotXp => onRobotXp(p)
case PacketType.RotatableState => onRotatableState(p)
case PacketType.RouterActivity => onRouterActivity(p)
case PacketType.TextBufferColorChange => onTextBufferColorChange(p)
@ -230,14 +229,6 @@ class PacketHandler extends CommonPacketHandler {
case _ => // Invalid packet.
}
def onRobotXp(p: PacketParser) =
p.readTileEntity[RobotProxy]() match {
case Some(t) =>
t.robot.xp = p.readDouble()
t.robot.updateXpInfo()
case _ => // Invalid packet.
}
def onRotatableState(p: PacketParser) =
p.readTileEntity[Rotatable]() match {
case Some(t) =>

View File

@ -2,6 +2,7 @@ package li.cil.oc.client.renderer.tileentity
import com.google.common.base.Strings
import java.util.logging.Level
import li.cil.oc.api.event.RobotRenderEvent
import li.cil.oc.client.Textures
import li.cil.oc.common.tileentity
import li.cil.oc.util.RenderState
@ -17,12 +18,14 @@ import net.minecraftforge.client.IItemRenderer.ItemRendererHelper._
import net.minecraftforge.client.IItemRenderer.ItemRenderType
import net.minecraftforge.client.IItemRenderer.ItemRenderType._
import net.minecraftforge.client.MinecraftForgeClient
import net.minecraftforge.common.ForgeDirection
import net.minecraftforge.common.{MinecraftForge, ForgeDirection}
import org.lwjgl.opengl.{GL12, GL11}
object RobotRenderer extends TileEntitySpecialRenderer {
private val displayList = GLAllocation.generateDisplayLists(2)
private val mountPoints = Array.fill(7)(new RobotRenderEvent.MountPoint())
private val gap = 1.0f / 28.0f
private val gt = 0.5f + gap
private val gb = 0.5f - gap
@ -104,7 +107,64 @@ object RobotRenderer extends TileEntitySpecialRenderer {
compileList()
def renderChassis(isRunning: Boolean = false, level: Int = 0, offset: Double = 0) {
def resetMountPoints() {
// Back.
mountPoints(0).offset.xCoord = 0
mountPoints(0).offset.yCoord = 0.33
mountPoints(0).offset.zCoord = -0.33
mountPoints(0).normal.xCoord = 0
mountPoints(0).normal.yCoord = 0
mountPoints(0).normal.zCoord = -1
mountPoints(0).offset.xCoord = 0
mountPoints(0).offset.yCoord = -0.33
mountPoints(0).offset.zCoord = -0.33
mountPoints(0).normal.xCoord = 0
mountPoints(0).normal.yCoord = 0
mountPoints(0).normal.zCoord = -1
// Front.
mountPoints(0).offset.xCoord = 0
mountPoints(0).offset.yCoord = -0.33
mountPoints(0).offset.zCoord = 0.33
mountPoints(0).normal.xCoord = 0
mountPoints(0).normal.yCoord = 0
mountPoints(0).normal.zCoord = 1
// Left.
mountPoints(0).offset.xCoord = -0.33
mountPoints(0).offset.yCoord = 0.33
mountPoints(0).offset.zCoord = 0
mountPoints(0).normal.xCoord = -1
mountPoints(0).normal.yCoord = 0
mountPoints(0).normal.zCoord = 0
mountPoints(0).offset.xCoord = -0.33
mountPoints(0).offset.yCoord = -0.33
mountPoints(0).offset.zCoord = 0
mountPoints(0).normal.xCoord = -1
mountPoints(0).normal.yCoord = 0
mountPoints(0).normal.zCoord = 0
// Right.
mountPoints(0).offset.xCoord = 0.33
mountPoints(0).offset.yCoord = 0.33
mountPoints(0).offset.zCoord = 0
mountPoints(0).normal.xCoord = 1
mountPoints(0).normal.yCoord = 0
mountPoints(0).normal.zCoord = 0
mountPoints(0).offset.xCoord = 0.33
mountPoints(0).offset.yCoord = -0.33
mountPoints(0).offset.zCoord = 0
mountPoints(0).normal.xCoord = 1
mountPoints(0).normal.yCoord = 0
mountPoints(0).normal.zCoord = 0
}
def renderChassis(robot: tileentity.Robot = null, offset: Double = 0) {
val isRunning = if (robot == null) false else robot.isRunning
val size = 0.3f
val l = 0.5f - size
val h = 0.5f + size
@ -118,57 +178,53 @@ object RobotRenderer extends TileEntitySpecialRenderer {
(0.25f - vStep, 0.25f + vStep, 0.75f - vStep, 0.75f + vStep)
}
bindTexture(Textures.blockRobot)
if (level > 19) {
GL11.glColor3f(0.4f, 1, 1)
}
else if (level > 9) {
GL11.glColor3f(1, 1, 0.4f)
}
else {
GL11.glColor3f(0.5f, 0.5f, 0.5f)
}
if (!isRunning) {
GL11.glTranslatef(0, -2 * gap, 0)
}
GL11.glCallList(displayList)
if (!isRunning) {
GL11.glTranslatef(0, 2 * gap, 0)
}
GL11.glCallList(displayList + 1)
GL11.glColor3f(1, 1, 1)
resetMountPoints()
val event = new RobotRenderEvent(robot, mountPoints)
MinecraftForge.EVENT_BUS.post(event)
if (!event.isCanceled) {
bindTexture(Textures.blockRobot)
if (!isRunning) {
GL11.glTranslatef(0, -2 * gap, 0)
}
GL11.glCallList(displayList)
if (!isRunning) {
GL11.glTranslatef(0, 2 * gap, 0)
}
GL11.glCallList(displayList + 1)
GL11.glColor3f(1, 1, 1)
if (MinecraftForgeClient.getRenderPass == 0) {
RenderState.disableLighting()
}
if (isRunning) {
if (MinecraftForgeClient.getRenderPass == 0) {
RenderState.disableLighting()
}
if (isRunning) {
val t = Tessellator.instance
t.startDrawingQuads()
t.addVertexWithUV(l, gt, l, u0, v0)
t.addVertexWithUV(l, gb, l, u0, v1)
t.addVertexWithUV(l, gb, h, u1, v1)
t.addVertexWithUV(l, gt, h, u1, v0)
val t = Tessellator.instance
t.startDrawingQuads()
t.addVertexWithUV(l, gt, l, u0, v0)
t.addVertexWithUV(l, gb, l, u0, v1)
t.addVertexWithUV(l, gb, h, u1, v1)
t.addVertexWithUV(l, gt, h, u1, v0)
t.addVertexWithUV(l, gt, h, u0, v0)
t.addVertexWithUV(l, gb, h, u0, v1)
t.addVertexWithUV(h, gb, h, u1, v1)
t.addVertexWithUV(h, gt, h, u1, v0)
t.addVertexWithUV(l, gt, h, u0, v0)
t.addVertexWithUV(l, gb, h, u0, v1)
t.addVertexWithUV(h, gb, h, u1, v1)
t.addVertexWithUV(h, gt, h, u1, v0)
t.addVertexWithUV(h, gt, h, u0, v0)
t.addVertexWithUV(h, gb, h, u0, v1)
t.addVertexWithUV(h, gb, l, u1, v1)
t.addVertexWithUV(h, gt, l, u1, v0)
t.addVertexWithUV(h, gt, h, u0, v0)
t.addVertexWithUV(h, gb, h, u0, v1)
t.addVertexWithUV(h, gb, l, u1, v1)
t.addVertexWithUV(h, gt, l, u1, v0)
t.addVertexWithUV(h, gt, l, u0, v0)
t.addVertexWithUV(h, gb, l, u0, v1)
t.addVertexWithUV(l, gb, l, u1, v1)
t.addVertexWithUV(l, gt, l, u1, v0)
t.draw()
}
t.addVertexWithUV(h, gt, l, u0, v0)
t.addVertexWithUV(h, gb, l, u0, v1)
t.addVertexWithUV(l, gb, l, u1, v1)
t.addVertexWithUV(l, gt, l, u1, v0)
t.draw()
if (MinecraftForgeClient.getRenderPass == 0) {
RenderState.enableLighting()
if (MinecraftForgeClient.getRenderPass == 0) {
RenderState.enableLighting()
}
}
}
}
@ -224,7 +280,7 @@ object RobotRenderer extends TileEntitySpecialRenderer {
if (MinecraftForgeClient.getRenderPass == 0) {
val offset = timeJitter + worldTime / 20.0
renderChassis(robot.isRunning, robot.level, offset)
renderChassis(robot, offset)
}
robot.equippedItem match {

View File

@ -22,7 +22,6 @@ object PacketType extends Enumeration {
RobotEquippedUpgradeChange,
RobotMove,
RobotSelectedSlotChange,
RobotXp,
RotatableState,
RouterActivity,
TextBufferColorChange,

View File

@ -21,6 +21,7 @@ import net.minecraft.item.{Item, ItemStack}
import net.minecraft.block.Block
import net.minecraftforge.oredict.OreDictionary
import scala.collection.convert.WrapAsScala._
import li.cil.oc.common.event.{ExperienceUpgradeHandler, UniversalElectricityToolHandler, RobotCommonHandler}
class Proxy {
def preInit(e: FMLPreInitializationEvent) {
@ -77,6 +78,7 @@ class Proxy {
api.Driver.add(driver.item.RedstoneCard)
api.Driver.add(driver.item.Screen)
api.Driver.add(driver.item.UpgradeCrafting)
api.Driver.add(driver.item.UpgradeExperience)
api.Driver.add(driver.item.UpgradeGenerator)
api.Driver.add(driver.item.UpgradeNavigation)
api.Driver.add(driver.item.UpgradeSign)
@ -94,6 +96,12 @@ class Proxy {
api.Driver.add(driver.converter.FluidTankInfo)
api.Driver.add(driver.converter.ItemStack)
MinecraftForge.EVENT_BUS.register(RobotCommonHandler)
MinecraftForge.EVENT_BUS.register(ExperienceUpgradeHandler)
if (Mods.UniversalElectricity.isAvailable) {
MinecraftForge.EVENT_BUS.register(UniversalElectricityToolHandler)
}
Recipes.init()
GameRegistry.registerCraftingHandler(CraftingHandler)

View File

@ -162,7 +162,7 @@ class RobotProxy(val parent: SpecialDelegator) extends RedstoneAware with Specia
case proxy: tileentity.RobotProxy =>
val robot = proxy.robot
if (robot.player == player) return false
if (!world.isRemote && (!player.capabilities.isCreativeMode || proxy.globalBuffer > 1 || proxy.robot.xp > 0)) {
if (!world.isRemote) {
parent.dropBlockAsItem(world, x, y, z, robot.createItemStack())
}
if (Blocks.blockSpecial.subBlock(world, robot.moveFromX, robot.moveFromY, robot.moveFromZ).exists(_ == Blocks.robotAfterimage)) {

View File

@ -0,0 +1,109 @@
package li.cil.oc.common.event
import li.cil.oc.Settings
import li.cil.oc.api.event._
import li.cil.oc.api.machine.Robot
import li.cil.oc.server.component
import net.minecraft.util.ChatMessageComponent
import net.minecraftforge.event.ForgeSubscribe
import org.lwjgl.opengl.GL11
object ExperienceUpgradeHandler {
@ForgeSubscribe
def onRobotAnalyze(e: RobotAnalyzeEvent) {
val (level, experience) = getLevelAndExperience(e.robot)
// This is basically a 'does it have an experience upgrade' check.
if (experience != 0.0) {
e.player.sendChatToPlayer(ChatMessageComponent.createFromTranslationWithSubstitutions(
Settings.namespace + "gui.Analyzer.RobotXp", "%.2f".format(experience), level: Integer))
}
}
@ForgeSubscribe
def onRobotComputeDamageRate(e: RobotUsedTool.ComputeDamageRate) {
e.setDamageRate(e.getDamageRate * math.max(0, 1 - getLevel(e.robot) * Settings.get.toolEfficiencyPerLevel))
}
@ForgeSubscribe
def onRobotBreakBlockPre(e: RobotBreakBlockEvent.Pre) {
val boost = math.max(0, 1 - getLevel(e.robot) * Settings.get.harvestSpeedBoostPerLevel)
e.setBreakTime(e.getBreakTime * boost)
}
@ForgeSubscribe
def onRobotAttackEntityPost(e: RobotAttackEntityEvent.Post) {
if (e.robot.getComponentInSlot(e.robot.selectedSlot()) != null && e.target.isDead) {
addExperience(e.robot, Settings.get.robotActionXp)
}
}
@ForgeSubscribe
def onRobotBreakBlockPost(e: RobotBreakBlockEvent.Post) {
addExperience(e.robot, e.experience * Settings.get.robotOreXpRate + Settings.get.robotActionXp)
}
@ForgeSubscribe
def onRobotPlaceBlockPost(e: RobotPlaceBlockEvent.Post) {
addExperience(e.robot, Settings.get.robotActionXp)
}
@ForgeSubscribe
def onRobotMovePost(e: RobotMoveEvent.Post) {
addExperience(e.robot, Settings.get.robotExhaustionXpRate * 0.01)
}
@ForgeSubscribe
def onRobotExhaustion(e: RobotExhaustionEvent) {
addExperience(e.robot, Settings.get.robotExhaustionXpRate * e.exhaustion)
}
@ForgeSubscribe
def onRobotRender(e: RobotRenderEvent) {
val level = if (e.robot != null) getLevel(e.robot) else 0
if (level > 19) {
GL11.glColor3f(0.4f, 1, 1)
}
else if (level > 9) {
GL11.glColor3f(1, 1, 0.4f)
}
else {
GL11.glColor3f(0.5f, 0.5f, 0.5f)
}
}
private def getLevel(robot: Robot) = {
var level = 0
for (index <- 1 to robot.componentCapacity) {
robot.getComponentInSlot(index) match {
case upgrade: component.UpgradeExperience =>
level += upgrade.level
case _ =>
}
}
level
}
private def getLevelAndExperience(robot: Robot) = {
var level = 0
var experience = 0.0
for (index <- 1 to robot.componentCapacity) {
robot.getComponentInSlot(index) match {
case upgrade: component.UpgradeExperience =>
level += upgrade.level
experience += upgrade.experience
case _ =>
}
}
(level, experience)
}
private def addExperience(robot: Robot, amount: Double) {
for (index <- 1 to robot.componentCapacity) {
robot.getComponentInSlot(index) match {
case upgrade: component.UpgradeExperience =>
upgrade.addExperience(amount)
case _ =>
}
}
}
}

View File

@ -0,0 +1,18 @@
package li.cil.oc.common.event
import net.minecraftforge.event.ForgeSubscribe
import li.cil.oc.api.event.RobotUsedTool
object RobotCommonHandler {
@ForgeSubscribe
def onRobotApplyDamageRate(e: RobotUsedTool.ApplyDamageRate) {
if (e.toolAfterUse.isItemStackDamageable) {
val damage = e.toolAfterUse.getItemDamage - e.toolBeforeUse.getItemDamage
if (damage > 0) {
val actualDamage = damage * e.getDamageRate
val repairedDamage = if (e.robot.player.getRNG.nextDouble() > 0.5) damage - math.floor(actualDamage).toInt else damage - math.ceil(actualDamage).toInt
e.toolAfterUse.setItemDamage(e.toolAfterUse.getItemDamage - repairedDamage)
}
}
}
}

View File

@ -0,0 +1,19 @@
package li.cil.oc.common.event
import net.minecraftforge.event.ForgeSubscribe
import li.cil.oc.api.event.RobotUsedTool
import li.cil.oc.util.mods.UniversalElectricity
object UniversalElectricityToolHandler {
@ForgeSubscribe
def onRobotApplyDamageRate(e: RobotUsedTool.ApplyDamageRate) {
if (UniversalElectricity.isEnergyItem(e.toolAfterUse)) {
val damage = UniversalElectricity.getEnergyInItem(e.toolBeforeUse) - UniversalElectricity.getEnergyInItem(e.toolAfterUse)
if (damage > 0) {
val actualDamage = damage * e.getDamageRate
val repairedDamage = if (e.robot.player.getRNG.nextDouble() > 0.5) damage - math.floor(actualDamage).toLong else damage - math.ceil(actualDamage).toLong
UniversalElectricity.chargeItem(e.toolAfterUse, repairedDamage)
}
}
}
}

View File

@ -0,0 +1,25 @@
package li.cil.oc.common.item
import java.util
import li.cil.oc.Settings
import li.cil.oc.util.Tooltip
import net.minecraft.client.renderer.texture.IconRegister
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.item.{ItemStack, EnumRarity}
class UpgradeExperience(val parent: Delegator) extends Delegate {
val unlocalizedName = "UpgradeExperience"
override def rarity = EnumRarity.epic
override def tooltipLines(stack: ItemStack, player: EntityPlayer, tooltip: util.List[String], advanced: Boolean) {
tooltip.addAll(Tooltip.get(unlocalizedName))
super.tooltipLines(stack, player, tooltip, advanced)
}
override def registerIcons(iconRegister: IconRegister) = {
super.registerIcons(iconRegister)
icon = iconRegister.registerIcon(Settings.resourceDomain + ":upgrade_experience")
}
}

View File

@ -5,6 +5,7 @@ import java.util.logging.Level
import li.cil.oc._
import li.cil.oc.api.Driver
import li.cil.oc.api.driver.Slot
import li.cil.oc.api.event.{RobotAnalyzeEvent, RobotMoveEvent}
import li.cil.oc.api.network._
import li.cil.oc.client.gui
import li.cil.oc.common.block.Delegator
@ -18,7 +19,7 @@ import net.minecraft.inventory.ISidedInventory
import net.minecraft.item.ItemStack
import net.minecraft.nbt.NBTTagCompound
import net.minecraft.util.ChatMessageComponent
import net.minecraftforge.common.ForgeDirection
import net.minecraftforge.common.{MinecraftForge, ForgeDirection}
import net.minecraftforge.fluids.{BlockFluidBase, FluidRegistry}
import scala.io.Source
@ -39,8 +40,13 @@ class Robot(val isRemote: Boolean) extends traits.Computer with traits.TextBuffe
// ----------------------------------------------------------------------- //
// Note: we implement IRobotContext in the TE to allow external components
//to cast their owner to it (to allow interacting with their owning robot).
override def dynamicComponentCapacity = 3
override def componentCapacity = 3
override def inventorySize = getSizeInventory
override def getComponentInSlot(index: Int) = components(index).orNull
var selectedSlot = actualSlot(0)
@ -92,16 +98,6 @@ class Robot(val isRemote: Boolean) extends traits.Computer with traits.TextBuffe
var tag: NBTTagCompound = _
var xp = 0.0
def xpForNextLevel = xpForLevel(level + 1)
def xpForLevel(level: Int) = Settings.get.baseXpToLevel + Math.pow(level * Settings.get.constantXpGrowth, Settings.get.exponentialXpGrowth)
var level = 0
var xpChanged = false
var globalBuffer, globalBufferSize = 0.0
var equippedItem: Option[ItemStack] = None
@ -120,25 +116,6 @@ class Robot(val isRemote: Boolean) extends traits.Computer with traits.TextBuffe
private lazy val player_ = new robot.Player(this)
def addXp(value: Double) {
if (level < 30 && isServer) {
xp = xp + value
xpChanged = true
if (xp >= xpForNextLevel) {
updateXpInfo()
}
}
}
def updateXpInfo() {
// xp(level) = base + (level * const) ^ exp
// pow(xp(level) - base, 1/exp) / const = level
level = math.min((Math.pow(xp - Settings.get.baseXpToLevel, 1 / Settings.get.exponentialXpGrowth) / Settings.get.constantXpGrowth).toInt, 30)
if (isServer) {
bot.node.setLocalBufferSize(Settings.get.bufferRobot + Settings.get.bufferPerLevel * level)
}
}
override def maxComponents = 8
// ----------------------------------------------------------------------- //
@ -148,8 +125,7 @@ class Robot(val isRemote: Boolean) extends traits.Computer with traits.TextBuffe
Settings.namespace + "gui.Analyzer.RobotOwner", owner))
player.sendChatToPlayer(ChatMessageComponent.createFromTranslationWithSubstitutions(
Settings.namespace + "gui.Analyzer.RobotName", player_.getCommandSenderName))
player.sendChatToPlayer(ChatMessageComponent.createFromTranslationWithSubstitutions(
Settings.namespace + "gui.Analyzer.RobotXp", xp.formatted("%.2f"), level: Integer))
MinecraftForge.EVENT_BUS.post(new RobotAnalyzeEvent(this, player))
super.onAnalyze(player, side, hitX, hitY, hitZ)
}
@ -166,6 +142,13 @@ class Robot(val isRemote: Boolean) extends traits.Computer with traits.TextBuffe
if (!world.blockExists(nx, ny, nz)) {
return false // Don't fall off the earth.
}
if (isServer) {
val event = new RobotMoveEvent.Pre(this, direction)
MinecraftForge.EVENT_BUS.post(event)
if (event.isCanceled) return false
}
val blockId = world.getBlockId(nx, ny, nz)
val metadata = world.getBlockMetadata(nx, ny, nz)
try {
@ -193,6 +176,7 @@ class Robot(val isRemote: Boolean) extends traits.Computer with traits.TextBuffe
if (isServer) {
ServerPacketSender.sendRobotMove(this, ox, oy, oz, direction)
checkRedstoneInputChanged()
MinecraftForge.EVENT_BUS.post(new RobotMoveEvent.Post(this, direction))
}
else {
// If we broke some replaceable block (like grass) play its break sound.
@ -226,9 +210,6 @@ class Robot(val isRemote: Boolean) extends traits.Computer with traits.TextBuffe
val stack = Blocks.robotProxy.createItemStack()
val tag = if (this.tag != null) this.tag.copy.asInstanceOf[NBTTagCompound] else new NBTTagCompound("tag")
stack.setTagCompound(tag)
if (xp > 0) {
tag.setDouble(Settings.namespace + "xp", xp)
}
if (globalBuffer > 1) {
tag.setInteger(Settings.namespace + "storedEnergy", globalBuffer.toInt)
}
@ -238,9 +219,9 @@ class Robot(val isRemote: Boolean) extends traits.Computer with traits.TextBuffe
def parseItemStack(stack: ItemStack) {
if (stack.hasTagCompound) {
tag = stack.getTagCompound.copy.asInstanceOf[NBTTagCompound]
xp = tag.getDouble(Settings.namespace + "xp")
updateXpInfo()
bot.node.changeBuffer(stack.getTagCompound.getInteger(Settings.namespace + "storedEnergy"))
// TODO migration: xp to xp upgrade
// xp = tag.getDouble(Settings.namespace + "xp")
}
else {
tag = new NBTTagCompound("tag")
@ -323,10 +304,6 @@ class Robot(val isRemote: Boolean) extends traits.Computer with traits.TextBuffe
globalBuffer = bot.node.globalBuffer
globalBufferSize = bot.node.globalBufferSize
updatePowerInformation()
if (xpChanged && world.getWorldInfo.getWorldTotalTime % 200 == 0) {
xpChanged = false
ServerPacketSender.sendRobotXp(this)
}
}
else if (isRunning && isAnimatingMove) {
client.Sound.updatePosition(this)
@ -339,7 +316,6 @@ class Robot(val isRemote: Boolean) extends traits.Computer with traits.TextBuffe
case Some(item) => player_.getAttributeMap.applyAttributeModifiers(item.getAttributeModifiers)
case _ =>
}
updateXpInfo()
// Ensure we have a node address, because the proxy needs this to initialize
// its own node to the same address ours has.
@ -372,8 +348,6 @@ class Robot(val isRemote: Boolean) extends traits.Computer with traits.TextBuffe
if (nbt.hasKey(Settings.namespace + "tag")) {
tag = nbt.getCompoundTag(Settings.namespace + "tag")
}
xp = nbt.getDouble(Settings.namespace + "xp") max 0
updateXpInfo()
selectedSlot = nbt.getInteger(Settings.namespace + "selectedSlot") max actualSlot(0) min (getSizeInventory - 1)
animationTicksTotal = nbt.getInteger(Settings.namespace + "animationTicksTotal")
animationTicksLeft = nbt.getInteger(Settings.namespace + "animationTicksLeft")
@ -384,6 +358,9 @@ class Robot(val isRemote: Boolean) extends traits.Computer with traits.TextBuffe
swingingTool = nbt.getBoolean(Settings.namespace + "swingingTool")
turnAxis = nbt.getByte(Settings.namespace + "turnAxis")
}
// TODO migration: xp to xp upgrade
// xp = nbt.getDouble(Settings.namespace + "xp") max 0
}
// Side check for Waila (and other mods that may call this client side).
@ -398,7 +375,6 @@ class Robot(val isRemote: Boolean) extends traits.Computer with traits.TextBuffe
if (tag != null) {
nbt.setCompoundTag(Settings.namespace + "tag", tag)
}
nbt.setDouble(Settings.namespace + "xp", xp)
nbt.setInteger(Settings.namespace + "selectedSlot", selectedSlot)
if (isAnimatingMove || isAnimatingSwing || isAnimatingTurn) {
nbt.setInteger(Settings.namespace + "animationTicksTotal", animationTicksTotal)
@ -424,8 +400,6 @@ class Robot(val isRemote: Boolean) extends traits.Computer with traits.TextBuffe
if (nbt.hasKey("upgrade")) {
equippedUpgrade = Option(ItemStack.loadItemStackFromNBT(nbt.getCompoundTag("upgrade")))
}
xp = nbt.getDouble(Settings.namespace + "xp")
updateXpInfo()
animationTicksTotal = nbt.getInteger("animationTicksTotal")
animationTicksLeft = nbt.getInteger("animationTicksLeft")
moveFromX = nbt.getInteger("moveFromX")
@ -459,7 +433,6 @@ class Robot(val isRemote: Boolean) extends traits.Computer with traits.TextBuffe
}
nbt.setNewCompoundTag("upgrade", getStackInSlot(3).writeToNBT)
}
nbt.setDouble(Settings.namespace + "xp", xp)
if (isAnimatingMove || isAnimatingSwing || isAnimatingTurn) {
nbt.setInteger("animationTicksTotal", animationTicksTotal)
nbt.setInteger("animationTicksLeft", animationTicksLeft)

View File

@ -29,17 +29,22 @@ class RobotProxy(val robot: Robot) extends traits.Computer with traits.TextBuffe
// ----------------------------------------------------------------------- //
// Note: we implement IRobotContext in the TE to allow external components
//to cast their owner to it (to allow interacting with their owning robot).
override def isRunning = robot.isRunning
override def setRunning(value: Boolean) = robot.setRunning(value)
override def selectedSlot() = robot.selectedSlot
override def player() = robot.player()
override def dynamicComponentCapacity = robot.dynamicComponentCapacity
override def componentCapacity = robot.componentCapacity
override def inventorySize = robot.inventorySize
override def getComponentInSlot(index: Int) = robot.getComponentInSlot(index)
override def selectedSlot() = robot.selectedSlot
override def saveUpgrade() = robot.saveUpgrade()
// ----------------------------------------------------------------------- //

View File

@ -201,15 +201,6 @@ object PacketSender {
pb.sendToNearbyPlayers(t, 16)
}
def sendRobotXp(t: tileentity.Robot) {
val pb = new PacketBuilder(PacketType.RobotXp)
pb.writeTileEntity(t)
pb.writeDouble(t.xp)
pb.sendToNearbyPlayers(t)
}
def sendRotatableState(t: Rotatable) {
val pb = new PacketBuilder(PacketType.RotatableState)

View File

@ -0,0 +1,58 @@
package li.cil.oc.server.component
import li.cil.oc.{Settings, api}
import li.cil.oc.api.network.{Arguments, Context, Callback, Visibility}
import li.cil.oc.common.component.ManagedComponent
import net.minecraft.nbt.NBTTagCompound
class UpgradeExperience extends ManagedComponent {
def node = api.Network.newNode(this, Visibility.Network).
withComponent("experience").
withConnector(30 * Settings.get.bufferPerLevel).
create()
var experience = 0.0
var level = 0
def xpForLevel(level: Int) = Settings.get.baseXpToLevel + Math.pow(level * Settings.get.constantXpGrowth, Settings.get.exponentialXpGrowth)
def xpForNextLevel = xpForLevel(level + 1)
def addExperience(value: Double) {
if (level < 30) {
experience = experience + value
if (experience >= xpForNextLevel) {
updateXpInfo()
}
// ServerPacketSender.sendRobotXp(this)
}
}
def updateXpInfo() {
// xp(level) = base + (level * const) ^ exp
// pow(xp(level) - base, 1/exp) / const = level
level = math.min((Math.pow(experience - Settings.get.baseXpToLevel, 1 / Settings.get.exponentialXpGrowth) / Settings.get.constantXpGrowth).toInt, 30)
if (node != null) {
node.setLocalBufferSize(Settings.get.bufferPerLevel * level)
}
}
@Callback(direct = true)
def level(context: Context, args: Arguments): Array[AnyRef] = {
val xpNeeded = xpForNextLevel - xpForLevel(level)
val xpProgress = math.max(0, experience - xpForLevel(level))
result(level + xpProgress / xpNeeded)
}
override def save(nbt: NBTTagCompound) {
super.save(nbt)
nbt.setDouble(Settings.namespace + "xp", experience)
}
override def load(nbt: NBTTagCompound) {
super.load(nbt)
experience = nbt.getDouble(Settings.namespace + "xp") max 0
updateXpInfo()
}
}

View File

@ -2,8 +2,9 @@ package li.cil.oc.server.component.robot
import cpw.mods.fml.common.ObfuscationReflectionHelper
import java.util.logging.Level
import li.cil.oc.api.event._
import li.cil.oc.common.tileentity
import li.cil.oc.util.mods.{Mods, UniversalElectricity, TinkersConstruct, PortalGun}
import li.cil.oc.util.mods.{Mods, TinkersConstruct, PortalGun}
import li.cil.oc.{OpenComputers, Settings}
import net.minecraft.block.{BlockPistonBase, BlockFluid, Block}
import net.minecraft.entity.item.EntityItem
@ -99,9 +100,11 @@ class Player(val robot: tileentity.Robot) extends EntityPlayer(robot.world, Sett
callUsingItemInSlot(0, stack => entity match {
case player: EntityPlayer if !canAttackPlayer(player) => // Avoid player damage.
case _ =>
super.attackTargetEntityWithCurrentItem(entity)
if (stack != null && entity.isDead) {
robot.addXp(Settings.get.robotActionXp)
val event = new RobotAttackEntityEvent.Pre(robot, entity)
MinecraftForge.EVENT_BUS.post(event)
if (!event.isCanceled) {
super.attackTargetEntityWithCurrentItem(entity)
MinecraftForge.EVENT_BUS.post(new RobotAttackEntityEvent.Post(robot, entity))
}
})
}
@ -262,7 +265,11 @@ class Player(val robot: tileentity.Robot) extends EntityPlayer(robot.world, Sett
val breakTime =
if (cobwebOverride) Settings.get.swingDelay
else hardness * 1.5 / strength
val adjustedBreakTime = math.max(0.05, breakTime * Settings.get.harvestRatio * math.max(1 - robot.level * Settings.get.harvestSpeedBoostPerLevel, 0))
val preEvent = new RobotBreakBlockEvent.Pre(robot, world, x, y, z, breakTime * Settings.get.harvestRatio)
MinecraftForge.EVENT_BUS.post(preEvent)
if (preEvent.isCanceled) return 0
val adjustedBreakTime = math.max(0.05, preEvent.getBreakTime)
// Special handling for Tinkers Construct - tools like the hammers do
// their break logic in onBlockStartBreak but return true to cancel
@ -295,10 +302,10 @@ class Player(val robot: tileentity.Robot) extends EntityPlayer(robot.world, Sett
// check only serves to test whether the block can drop anything at all.
if (block.canHarvestBlock(this, metadata)) {
block.harvestBlock(world, this, x, y, z, metadata)
robot.addXp(breakEvent.getExpToDrop * Settings.get.robotOreXpRate)
MinecraftForge.EVENT_BUS.post(new RobotBreakBlockEvent.Post(robot, breakEvent.getExpToDrop))
}
if (stack != null) {
robot.addXp(Settings.get.robotActionXp)
else if (stack != null) {
MinecraftForge.EVENT_BUS.post(new RobotBreakBlockEvent.Post(robot, 0))
}
return adjustedBreakTime
}
@ -351,37 +358,28 @@ class Player(val robot: tileentity.Robot) extends EntityPlayer(robot.world, Sett
}
private def tryRepair(stack: ItemStack, oldStack: ItemStack) {
def repair(high: Long, low: Long) = {
val needsRepairing = low < high
val damageRate = Settings.get.itemDamageRate * math.max(1 - robot.level * Settings.get.toolEfficiencyPerLevel, 0)
val shouldRepair = needsRepairing && getRNG.nextDouble() >= damageRate
if (shouldRepair) {
// If an item takes a lot of damage at once we don't necessarily want to
// make *all* of that damage go away. Instead we scale it according to
// our damage probability. This makes sure we don't discard massive
// damage spikes (e.g. on axes when using the TreeCapitator mod or such).
math.ceil((high - low) * (1 - damageRate)).toInt
}
else 0
}
if (Mods.UniversalElectricity.isAvailable && UniversalElectricity.isEnergyItem(stack)) {
UniversalElectricity.chargeItem(stack, repair(UniversalElectricity.getEnergyInItem(oldStack), UniversalElectricity.getEnergyInItem(stack)))
}
else if (stack.isItemStackDamageable) {
stack.setItemDamage(stack.getItemDamage - repair(stack.getItemDamage, oldStack.getItemDamage))
val damageRate = new RobotUsedTool.ComputeDamageRate(robot, stack, oldStack, Settings.get.itemDamageRate)
MinecraftForge.EVENT_BUS.post(damageRate)
if (damageRate.getDamageRate < 1) {
MinecraftForge.EVENT_BUS.post(new RobotUsedTool.ApplyDamageRate(robot, stack, oldStack, damageRate.getDamageRate))
}
}
private def tryPlaceBlockWhileHandlingFunnySpecialCases(stack: ItemStack, x: Int, y: Int, z: Int, side: Int, hitX: Float, hitY: Float, hitZ: Float) = {
stack != null && stack.stackSize > 0 && {
val fakeEyeHeight = if (rotationPitch < 0 && isSomeKindOfPiston(stack)) 1.82 else 0
setPosition(posX, posY - fakeEyeHeight, posZ)
val didPlace = stack.tryPlaceItemIntoWorld(this, world, x, y, z, side, hitX, hitY, hitZ)
setPosition(posX, posY + fakeEyeHeight, posZ)
if (didPlace) {
robot.addXp(Settings.get.robotActionXp)
val event = new RobotPlaceBlockEvent.Pre(robot, stack, world, x, y, z)
MinecraftForge.EVENT_BUS.post(event)
if (event.isCanceled) false
else {
val fakeEyeHeight = if (rotationPitch < 0 && isSomeKindOfPiston(stack)) 1.82 else 0
setPosition(posX, posY - fakeEyeHeight, posZ)
val didPlace = stack.tryPlaceItemIntoWorld(this, world, x, y, z, side, hitX, hitY, hitZ)
setPosition(posX, posY + fakeEyeHeight, posZ)
if (didPlace) {
MinecraftForge.EVENT_BUS.post(new RobotPlaceBlockEvent.Post(robot, stack, world, x, y, z))
}
didPlace
}
didPlace
}
}
@ -409,7 +407,7 @@ class Player(val robot: tileentity.Robot) extends EntityPlayer(robot.world, Sett
if (Settings.get.robotExhaustionCost > 0) {
robot.bot.node.changeBuffer(-Settings.get.robotExhaustionCost * amount)
}
robot.addXp(Settings.get.robotExhaustionXpRate * amount)
MinecraftForge.EVENT_BUS.post(new RobotExhaustionEvent(robot, amount))
}
override def openGui(mod: AnyRef, modGuiId: Int, world: World, x: Int, y: Int, z: Int) {}

View File

@ -1,6 +1,6 @@
package li.cil.oc.server.component.robot
import li.cil.oc.{Items, api, OpenComputers, Settings}
import li.cil.oc.{api, OpenComputers, Settings}
import li.cil.oc.api.network._
import li.cil.oc.common.tileentity
import li.cil.oc.server.{PacketSender => ServerPacketSender}
@ -22,7 +22,7 @@ import li.cil.oc.common.component.ManagedComponent
class Robot(val robot: tileentity.Robot) extends ManagedComponent {
val node = api.Network.newNode(this, Visibility.Neighbors).
withComponent("robot").
withConnector(Settings.get.bufferRobot + 30 * Settings.get.bufferPerLevel).
withConnector(Settings.get.bufferRobot).
create()
def actualSlot(n: Int) = robot.actualSlot(n)
@ -48,13 +48,6 @@ class Robot(val robot: tileentity.Robot) extends ManagedComponent {
def hasAngelUpgrade = api.Items.get(robot.getStackInSlot(3)) == api.Items.get("angelUpgrade")
@Callback(direct = true)
def level(context: Context, args: Arguments): Array[AnyRef] = {
val xpNeeded = robot.xpForNextLevel - robot.xpForLevel(robot.level)
val xpProgress = math.max(0, robot.xp - robot.xpForLevel(robot.level))
result(robot.level + xpProgress / xpNeeded)
}
@Callback
def name(context: Context, args: Arguments): Array[AnyRef] = result(robot.name)
@ -521,7 +514,6 @@ class Robot(val robot: tileentity.Robot) extends ManagedComponent {
}
else if (robot.move(direction)) {
context.pause(Settings.get.moveDelay)
robot.addXp(Settings.get.robotExhaustionXpRate * 0.01)
result(true)
}
else {

View File

@ -0,0 +1,20 @@
package li.cil.oc.server.driver.item
import li.cil.oc.api
import li.cil.oc.api.driver.Slot
import li.cil.oc.api.machine.Robot
import li.cil.oc.server.component
import net.minecraft.item.ItemStack
import net.minecraftforge.common.MinecraftForge
object UpgradeExperience extends Item {
override def worksWith(stack: ItemStack) = isOneOf(stack, api.Items.get("experienceUpgrade"))
override def createEnvironment(stack: ItemStack, container: component.Container) =
container.tileEntity match {
case Some(robot: Robot) => new component.UpgradeExperience()
case _ => null
}
override def slot(stack: ItemStack) = Slot.Upgrade
}