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; package li.cil.oc.api.machine;
import li.cil.oc.api.Rotatable; import li.cil.oc.api.Rotatable;
import li.cil.oc.api.network.Environment;
import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
/** /**
* This interface allows interaction with robots. * 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 * 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 * case, the robot in question is the tile entity passed to item driver when
* asked to create the component's environment. * 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 { public interface Robot extends Rotatable {
/** /**
@ -17,18 +31,59 @@ public interface Robot extends Rotatable {
* <p/> * <p/>
* This will automatically be positioned and rotated to represent the * This will automatically be positioned and rotated to represent the
* robot's current position and rotation in the world. Use this to trigger * robot's current position and rotation in the world. Use this to trigger
* events involving the robot that require a player entity, and for * events involving the robot that require a player entity, and for more
* interacting with the robots' inventory. * in-depth interaction 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.
* *
* @return the fake player for the robot. * @return the fake player for the robot.
*/ */
EntityPlayer player(); 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. * 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"] ["oc:circuitChip1", workbench, "oc:circuitChip1"]
[ingotIron, "oc:materialCircuitBoardPrinted", ingotIron]] [ingotIron, "oc:materialCircuitBoardPrinted", ingotIron]]
} }
experienceUpgrade {
input: [[ingotIron, "", ingotIron]
["oc:circuitChip1", expBottle, "oc:circuitChip1"]
[ingotIron, "oc:materialCircuitBoardPrinted", ingotIron]]
}
generatorUpgrade { generatorUpgrade {
input: [[ingotIron, "", ingotIron] input: [[ingotIron, "", ingotIron]
["oc:circuitChip1", craftingPiston, "oc:circuitChip1"] ["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 // v1.3.0
Recipes.addItemDelegate(new item.LinkedCard(multi), "linkedCard", "oc:linkedCard") 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.RobotEquippedUpgradeChange => onRobotEquippedUpgradeChange(p)
case PacketType.RobotMove => onRobotMove(p) case PacketType.RobotMove => onRobotMove(p)
case PacketType.RobotSelectedSlotChange => onRobotSelectedSlotChange(p) case PacketType.RobotSelectedSlotChange => onRobotSelectedSlotChange(p)
case PacketType.RobotXp => onRobotXp(p)
case PacketType.RotatableState => onRotatableState(p) case PacketType.RotatableState => onRotatableState(p)
case PacketType.RouterActivity => onRouterActivity(p) case PacketType.RouterActivity => onRouterActivity(p)
case PacketType.TextBufferColorChange => onTextBufferColorChange(p) case PacketType.TextBufferColorChange => onTextBufferColorChange(p)
@ -230,14 +229,6 @@ class PacketHandler extends CommonPacketHandler {
case _ => // Invalid packet. 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) = def onRotatableState(p: PacketParser) =
p.readTileEntity[Rotatable]() match { p.readTileEntity[Rotatable]() match {
case Some(t) => case Some(t) =>

View File

@ -2,6 +2,7 @@ package li.cil.oc.client.renderer.tileentity
import com.google.common.base.Strings import com.google.common.base.Strings
import java.util.logging.Level import java.util.logging.Level
import li.cil.oc.api.event.RobotRenderEvent
import li.cil.oc.client.Textures import li.cil.oc.client.Textures
import li.cil.oc.common.tileentity import li.cil.oc.common.tileentity
import li.cil.oc.util.RenderState 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.IItemRenderer.ItemRenderType._ import net.minecraftforge.client.IItemRenderer.ItemRenderType._
import net.minecraftforge.client.MinecraftForgeClient import net.minecraftforge.client.MinecraftForgeClient
import net.minecraftforge.common.ForgeDirection import net.minecraftforge.common.{MinecraftForge, ForgeDirection}
import org.lwjgl.opengl.{GL12, GL11} import org.lwjgl.opengl.{GL12, GL11}
object RobotRenderer extends TileEntitySpecialRenderer { object RobotRenderer extends TileEntitySpecialRenderer {
private val displayList = GLAllocation.generateDisplayLists(2) private val displayList = GLAllocation.generateDisplayLists(2)
private val mountPoints = Array.fill(7)(new RobotRenderEvent.MountPoint())
private val gap = 1.0f / 28.0f private val gap = 1.0f / 28.0f
private val gt = 0.5f + gap private val gt = 0.5f + gap
private val gb = 0.5f - gap private val gb = 0.5f - gap
@ -104,7 +107,64 @@ object RobotRenderer extends TileEntitySpecialRenderer {
compileList() 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 size = 0.3f
val l = 0.5f - size val l = 0.5f - size
val h = 0.5f + size val h = 0.5f + size
@ -118,16 +178,11 @@ object RobotRenderer extends TileEntitySpecialRenderer {
(0.25f - vStep, 0.25f + vStep, 0.75f - vStep, 0.75f + vStep) (0.25f - vStep, 0.25f + vStep, 0.75f - vStep, 0.75f + vStep)
} }
resetMountPoints()
val event = new RobotRenderEvent(robot, mountPoints)
MinecraftForge.EVENT_BUS.post(event)
if (!event.isCanceled) {
bindTexture(Textures.blockRobot) 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) { if (!isRunning) {
GL11.glTranslatef(0, -2 * gap, 0) GL11.glTranslatef(0, -2 * gap, 0)
} }
@ -138,11 +193,11 @@ object RobotRenderer extends TileEntitySpecialRenderer {
GL11.glCallList(displayList + 1) GL11.glCallList(displayList + 1)
GL11.glColor3f(1, 1, 1) GL11.glColor3f(1, 1, 1)
if (isRunning) {
if (MinecraftForgeClient.getRenderPass == 0) { if (MinecraftForgeClient.getRenderPass == 0) {
RenderState.disableLighting() RenderState.disableLighting()
} }
if (isRunning) {
val t = Tessellator.instance val t = Tessellator.instance
t.startDrawingQuads() t.startDrawingQuads()
t.addVertexWithUV(l, gt, l, u0, v0) t.addVertexWithUV(l, gt, l, u0, v0)
@ -165,12 +220,13 @@ object RobotRenderer extends TileEntitySpecialRenderer {
t.addVertexWithUV(l, gb, l, u1, v1) t.addVertexWithUV(l, gb, l, u1, v1)
t.addVertexWithUV(l, gt, l, u1, v0) t.addVertexWithUV(l, gt, l, u1, v0)
t.draw() t.draw()
}
if (MinecraftForgeClient.getRenderPass == 0) { if (MinecraftForgeClient.getRenderPass == 0) {
RenderState.enableLighting() RenderState.enableLighting()
} }
} }
}
}
override def renderTileEntityAt(entity: TileEntity, x: Double, y: Double, z: Double, f: Float) { override def renderTileEntityAt(entity: TileEntity, x: Double, y: Double, z: Double, f: Float) {
val proxy = entity.asInstanceOf[tileentity.RobotProxy] val proxy = entity.asInstanceOf[tileentity.RobotProxy]
@ -224,7 +280,7 @@ object RobotRenderer extends TileEntitySpecialRenderer {
if (MinecraftForgeClient.getRenderPass == 0) { if (MinecraftForgeClient.getRenderPass == 0) {
val offset = timeJitter + worldTime / 20.0 val offset = timeJitter + worldTime / 20.0
renderChassis(robot.isRunning, robot.level, offset) renderChassis(robot, offset)
} }
robot.equippedItem match { robot.equippedItem match {

View File

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

View File

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

View File

@ -162,7 +162,7 @@ class RobotProxy(val parent: SpecialDelegator) extends RedstoneAware with Specia
case proxy: tileentity.RobotProxy => case proxy: tileentity.RobotProxy =>
val robot = proxy.robot val robot = proxy.robot
if (robot.player == player) return false 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()) parent.dropBlockAsItem(world, x, y, z, robot.createItemStack())
} }
if (Blocks.blockSpecial.subBlock(world, robot.moveFromX, robot.moveFromY, robot.moveFromZ).exists(_ == Blocks.robotAfterimage)) { 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._
import li.cil.oc.api.Driver import li.cil.oc.api.Driver
import li.cil.oc.api.driver.Slot 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.api.network._
import li.cil.oc.client.gui import li.cil.oc.client.gui
import li.cil.oc.common.block.Delegator import li.cil.oc.common.block.Delegator
@ -18,7 +19,7 @@ import net.minecraft.inventory.ISidedInventory
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.minecraft.nbt.NBTTagCompound import net.minecraft.nbt.NBTTagCompound
import net.minecraft.util.ChatMessageComponent import net.minecraft.util.ChatMessageComponent
import net.minecraftforge.common.ForgeDirection import net.minecraftforge.common.{MinecraftForge, ForgeDirection}
import net.minecraftforge.fluids.{BlockFluidBase, FluidRegistry} import net.minecraftforge.fluids.{BlockFluidBase, FluidRegistry}
import scala.io.Source 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 override def dynamicComponentCapacity = 3
//to cast their owner to it (to allow interacting with their owning robot).
override def componentCapacity = 3
override def inventorySize = getSizeInventory
override def getComponentInSlot(index: Int) = components(index).orNull
var selectedSlot = actualSlot(0) var selectedSlot = actualSlot(0)
@ -92,16 +98,6 @@ class Robot(val isRemote: Boolean) extends traits.Computer with traits.TextBuffe
var tag: NBTTagCompound = _ 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 globalBuffer, globalBufferSize = 0.0
var equippedItem: Option[ItemStack] = None 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) 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 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)) Settings.namespace + "gui.Analyzer.RobotOwner", owner))
player.sendChatToPlayer(ChatMessageComponent.createFromTranslationWithSubstitutions( player.sendChatToPlayer(ChatMessageComponent.createFromTranslationWithSubstitutions(
Settings.namespace + "gui.Analyzer.RobotName", player_.getCommandSenderName)) Settings.namespace + "gui.Analyzer.RobotName", player_.getCommandSenderName))
player.sendChatToPlayer(ChatMessageComponent.createFromTranslationWithSubstitutions( MinecraftForge.EVENT_BUS.post(new RobotAnalyzeEvent(this, player))
Settings.namespace + "gui.Analyzer.RobotXp", xp.formatted("%.2f"), level: Integer))
super.onAnalyze(player, side, hitX, hitY, hitZ) 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)) { if (!world.blockExists(nx, ny, nz)) {
return false // Don't fall off the earth. 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 blockId = world.getBlockId(nx, ny, nz)
val metadata = world.getBlockMetadata(nx, ny, nz) val metadata = world.getBlockMetadata(nx, ny, nz)
try { try {
@ -193,6 +176,7 @@ class Robot(val isRemote: Boolean) extends traits.Computer with traits.TextBuffe
if (isServer) { if (isServer) {
ServerPacketSender.sendRobotMove(this, ox, oy, oz, direction) ServerPacketSender.sendRobotMove(this, ox, oy, oz, direction)
checkRedstoneInputChanged() checkRedstoneInputChanged()
MinecraftForge.EVENT_BUS.post(new RobotMoveEvent.Post(this, direction))
} }
else { else {
// If we broke some replaceable block (like grass) play its break sound. // 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 stack = Blocks.robotProxy.createItemStack()
val tag = if (this.tag != null) this.tag.copy.asInstanceOf[NBTTagCompound] else new NBTTagCompound("tag") val tag = if (this.tag != null) this.tag.copy.asInstanceOf[NBTTagCompound] else new NBTTagCompound("tag")
stack.setTagCompound(tag) stack.setTagCompound(tag)
if (xp > 0) {
tag.setDouble(Settings.namespace + "xp", xp)
}
if (globalBuffer > 1) { if (globalBuffer > 1) {
tag.setInteger(Settings.namespace + "storedEnergy", globalBuffer.toInt) 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) { def parseItemStack(stack: ItemStack) {
if (stack.hasTagCompound) { if (stack.hasTagCompound) {
tag = stack.getTagCompound.copy.asInstanceOf[NBTTagCompound] tag = stack.getTagCompound.copy.asInstanceOf[NBTTagCompound]
xp = tag.getDouble(Settings.namespace + "xp")
updateXpInfo()
bot.node.changeBuffer(stack.getTagCompound.getInteger(Settings.namespace + "storedEnergy")) bot.node.changeBuffer(stack.getTagCompound.getInteger(Settings.namespace + "storedEnergy"))
// TODO migration: xp to xp upgrade
// xp = tag.getDouble(Settings.namespace + "xp")
} }
else { else {
tag = new NBTTagCompound("tag") tag = new NBTTagCompound("tag")
@ -323,10 +304,6 @@ class Robot(val isRemote: Boolean) extends traits.Computer with traits.TextBuffe
globalBuffer = bot.node.globalBuffer globalBuffer = bot.node.globalBuffer
globalBufferSize = bot.node.globalBufferSize globalBufferSize = bot.node.globalBufferSize
updatePowerInformation() updatePowerInformation()
if (xpChanged && world.getWorldInfo.getWorldTotalTime % 200 == 0) {
xpChanged = false
ServerPacketSender.sendRobotXp(this)
}
} }
else if (isRunning && isAnimatingMove) { else if (isRunning && isAnimatingMove) {
client.Sound.updatePosition(this) 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 Some(item) => player_.getAttributeMap.applyAttributeModifiers(item.getAttributeModifiers)
case _ => case _ =>
} }
updateXpInfo()
// Ensure we have a node address, because the proxy needs this to initialize // Ensure we have a node address, because the proxy needs this to initialize
// its own node to the same address ours has. // 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")) { if (nbt.hasKey(Settings.namespace + "tag")) {
tag = nbt.getCompoundTag(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) selectedSlot = nbt.getInteger(Settings.namespace + "selectedSlot") max actualSlot(0) min (getSizeInventory - 1)
animationTicksTotal = nbt.getInteger(Settings.namespace + "animationTicksTotal") animationTicksTotal = nbt.getInteger(Settings.namespace + "animationTicksTotal")
animationTicksLeft = nbt.getInteger(Settings.namespace + "animationTicksLeft") 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") swingingTool = nbt.getBoolean(Settings.namespace + "swingingTool")
turnAxis = nbt.getByte(Settings.namespace + "turnAxis") 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). // 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) { if (tag != null) {
nbt.setCompoundTag(Settings.namespace + "tag", tag) nbt.setCompoundTag(Settings.namespace + "tag", tag)
} }
nbt.setDouble(Settings.namespace + "xp", xp)
nbt.setInteger(Settings.namespace + "selectedSlot", selectedSlot) nbt.setInteger(Settings.namespace + "selectedSlot", selectedSlot)
if (isAnimatingMove || isAnimatingSwing || isAnimatingTurn) { if (isAnimatingMove || isAnimatingSwing || isAnimatingTurn) {
nbt.setInteger(Settings.namespace + "animationTicksTotal", animationTicksTotal) 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")) { if (nbt.hasKey("upgrade")) {
equippedUpgrade = Option(ItemStack.loadItemStackFromNBT(nbt.getCompoundTag("upgrade"))) equippedUpgrade = Option(ItemStack.loadItemStackFromNBT(nbt.getCompoundTag("upgrade")))
} }
xp = nbt.getDouble(Settings.namespace + "xp")
updateXpInfo()
animationTicksTotal = nbt.getInteger("animationTicksTotal") animationTicksTotal = nbt.getInteger("animationTicksTotal")
animationTicksLeft = nbt.getInteger("animationTicksLeft") animationTicksLeft = nbt.getInteger("animationTicksLeft")
moveFromX = nbt.getInteger("moveFromX") 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.setNewCompoundTag("upgrade", getStackInSlot(3).writeToNBT)
} }
nbt.setDouble(Settings.namespace + "xp", xp)
if (isAnimatingMove || isAnimatingSwing || isAnimatingTurn) { if (isAnimatingMove || isAnimatingSwing || isAnimatingTurn) {
nbt.setInteger("animationTicksTotal", animationTicksTotal) nbt.setInteger("animationTicksTotal", animationTicksTotal)
nbt.setInteger("animationTicksLeft", animationTicksLeft) 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 isRunning = robot.isRunning
override def setRunning(value: Boolean) = robot.setRunning(value) override def setRunning(value: Boolean) = robot.setRunning(value)
override def selectedSlot() = robot.selectedSlot
override def player() = robot.player() 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() override def saveUpgrade() = robot.saveUpgrade()
// ----------------------------------------------------------------------- // // ----------------------------------------------------------------------- //

View File

@ -201,15 +201,6 @@ object PacketSender {
pb.sendToNearbyPlayers(t, 16) 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) { def sendRotatableState(t: Rotatable) {
val pb = new PacketBuilder(PacketType.RotatableState) 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 cpw.mods.fml.common.ObfuscationReflectionHelper
import java.util.logging.Level import java.util.logging.Level
import li.cil.oc.api.event._
import li.cil.oc.common.tileentity 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 li.cil.oc.{OpenComputers, Settings}
import net.minecraft.block.{BlockPistonBase, BlockFluid, Block} import net.minecraft.block.{BlockPistonBase, BlockFluid, Block}
import net.minecraft.entity.item.EntityItem 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 { callUsingItemInSlot(0, stack => entity match {
case player: EntityPlayer if !canAttackPlayer(player) => // Avoid player damage. case player: EntityPlayer if !canAttackPlayer(player) => // Avoid player damage.
case _ => case _ =>
val event = new RobotAttackEntityEvent.Pre(robot, entity)
MinecraftForge.EVENT_BUS.post(event)
if (!event.isCanceled) {
super.attackTargetEntityWithCurrentItem(entity) super.attackTargetEntityWithCurrentItem(entity)
if (stack != null && entity.isDead) { MinecraftForge.EVENT_BUS.post(new RobotAttackEntityEvent.Post(robot, entity))
robot.addXp(Settings.get.robotActionXp)
} }
}) })
} }
@ -262,7 +265,11 @@ class Player(val robot: tileentity.Robot) extends EntityPlayer(robot.world, Sett
val breakTime = val breakTime =
if (cobwebOverride) Settings.get.swingDelay if (cobwebOverride) Settings.get.swingDelay
else hardness * 1.5 / strength 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 // Special handling for Tinkers Construct - tools like the hammers do
// their break logic in onBlockStartBreak but return true to cancel // 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. // check only serves to test whether the block can drop anything at all.
if (block.canHarvestBlock(this, metadata)) { if (block.canHarvestBlock(this, metadata)) {
block.harvestBlock(world, this, x, y, z, 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) { else if (stack != null) {
robot.addXp(Settings.get.robotActionXp) MinecraftForge.EVENT_BUS.post(new RobotBreakBlockEvent.Post(robot, 0))
} }
return adjustedBreakTime return adjustedBreakTime
} }
@ -351,39 +358,30 @@ class Player(val robot: tileentity.Robot) extends EntityPlayer(robot.world, Sett
} }
private def tryRepair(stack: ItemStack, oldStack: ItemStack) { private def tryRepair(stack: ItemStack, oldStack: ItemStack) {
def repair(high: Long, low: Long) = { val damageRate = new RobotUsedTool.ComputeDamageRate(robot, stack, oldStack, Settings.get.itemDamageRate)
val needsRepairing = low < high MinecraftForge.EVENT_BUS.post(damageRate)
val damageRate = Settings.get.itemDamageRate * math.max(1 - robot.level * Settings.get.toolEfficiencyPerLevel, 0) if (damageRate.getDamageRate < 1) {
val shouldRepair = needsRepairing && getRNG.nextDouble() >= damageRate MinecraftForge.EVENT_BUS.post(new RobotUsedTool.ApplyDamageRate(robot, stack, oldStack, damageRate.getDamageRate))
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))
} }
} }
private def tryPlaceBlockWhileHandlingFunnySpecialCases(stack: ItemStack, x: Int, y: Int, z: Int, side: Int, hitX: Float, hitY: Float, hitZ: Float) = { private def tryPlaceBlockWhileHandlingFunnySpecialCases(stack: ItemStack, x: Int, y: Int, z: Int, side: Int, hitX: Float, hitY: Float, hitZ: Float) = {
stack != null && stack.stackSize > 0 && { stack != null && stack.stackSize > 0 && {
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 val fakeEyeHeight = if (rotationPitch < 0 && isSomeKindOfPiston(stack)) 1.82 else 0
setPosition(posX, posY - fakeEyeHeight, posZ) setPosition(posX, posY - fakeEyeHeight, posZ)
val didPlace = stack.tryPlaceItemIntoWorld(this, world, x, y, z, side, hitX, hitY, hitZ) val didPlace = stack.tryPlaceItemIntoWorld(this, world, x, y, z, side, hitX, hitY, hitZ)
setPosition(posX, posY + fakeEyeHeight, posZ) setPosition(posX, posY + fakeEyeHeight, posZ)
if (didPlace) { if (didPlace) {
robot.addXp(Settings.get.robotActionXp) MinecraftForge.EVENT_BUS.post(new RobotPlaceBlockEvent.Post(robot, stack, world, x, y, z))
} }
didPlace didPlace
} }
} }
}
private def isSomeKindOfPiston(stack: ItemStack) = private def isSomeKindOfPiston(stack: ItemStack) =
stack.getItem match { stack.getItem match {
@ -409,7 +407,7 @@ class Player(val robot: tileentity.Robot) extends EntityPlayer(robot.world, Sett
if (Settings.get.robotExhaustionCost > 0) { if (Settings.get.robotExhaustionCost > 0) {
robot.bot.node.changeBuffer(-Settings.get.robotExhaustionCost * amount) 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) {} 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 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.api.network._
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}
@ -22,7 +22,7 @@ import li.cil.oc.common.component.ManagedComponent
class Robot(val robot: tileentity.Robot) extends ManagedComponent { class Robot(val robot: tileentity.Robot) extends ManagedComponent {
val node = api.Network.newNode(this, Visibility.Neighbors). val node = api.Network.newNode(this, Visibility.Neighbors).
withComponent("robot"). withComponent("robot").
withConnector(Settings.get.bufferRobot + 30 * Settings.get.bufferPerLevel). withConnector(Settings.get.bufferRobot).
create() create()
def actualSlot(n: Int) = robot.actualSlot(n) 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") 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 @Callback
def name(context: Context, args: Arguments): Array[AnyRef] = result(robot.name) 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)) { else if (robot.move(direction)) {
context.pause(Settings.get.moveDelay) context.pause(Settings.get.moveDelay)
robot.addXp(Settings.get.robotExhaustionXpRate * 0.01)
result(true) result(true)
} }
else { 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
}