diff --git a/src/main/java/li/cil/oc/api/driver/item/UpgradeRenderer.java b/src/main/java/li/cil/oc/api/driver/item/UpgradeRenderer.java
index ca7bae41f..763549c52 100644
--- a/src/main/java/li/cil/oc/api/driver/item/UpgradeRenderer.java
+++ b/src/main/java/li/cil/oc/api/driver/item/UpgradeRenderer.java
@@ -6,6 +6,8 @@ import li.cil.oc.api.event.RobotRenderEvent;
import li.cil.oc.api.internal.Robot;
import net.minecraft.item.ItemStack;
+import java.util.Set;
+
/**
* This interface can be implemented by items to allow custom rendering of
* upgrades installed in robots.
@@ -19,38 +21,32 @@ import net.minecraft.item.ItemStack;
@SideOnly(Side.CLIENT)
public interface UpgradeRenderer {
/**
- * The priority with which to render this upgrade.
+ * Returns which mount point this renderer wants to render the specified
+ * upgrade in.
*
- * Upgrades with a higher priority are preferred for rendering over other
- * upgrades. Upgrades with higher priorities will be rendered in the top
- * slots, i.e. the assigned slot order is top then bottom.
+ * This method is used to determine which upgrade is rendered where, and is
+ * called for every installed, renderable upgrade. The available mount
+ * point names are defined in {@link MountPointName}, with the two special
+ * values None and Any.
*
- * You usually won't need the robot parameter, but in case you do
- * need some contextual information, this should provide you with anything
- * you could need.
+ * None means that the upgrade should not be rendered at all. This
+ * can be the case when there is no slot remaining that the upgrade may be
+ * rendered in. Returning null is equivalent to returning None.
+ *
+ * Any means that the upgrade doesn't really care where it's being
+ * rendered. Mount points not assigned by another upgrade preferring to be
+ * rendered in it will be assigned to such upgrades in the order they are
+ * installed in the robot.
+ *
+ * Returning a mount point not in the list of available mount points will
+ * be equivalent to returning None.
*
- * @param stack the item stack of the upgrade to render.
- * @param robot the robot the upgrade is rendered on.
- * @return the priority with which to render the upgrade.
+ * @param stack the item stack of the upgrade to render.
+ * @param robot the robot the upgrade is rendered on.
+ * @param availableMountPoints the mount points available for rendering in.
+ * @return the mount point to reserve for the upgrade.
*/
- int priority(ItemStack stack, Robot robot);
-
- /**
- * Whether the upgrade can be rendered in the specified mount point.
- *
- * This is used to determine whether an upgrade can be rendered in a
- * specific mount point, or not. Note that if the upgrade refuses to
- * be rendered in the offered mount point, it will not be rendered at all,
- * i.e. it will not be offered another mount point. To give the upgrade
- * a better chance to get a usable mount point, specify an appropriate
- * priority via {@link #priority}.
- *
- * @param stack the item stack of the upgrade to render.
- * @param mountPoint the mount-point to render the upgrade at.
- * @param robot the robot the upgrade is rendered on.
- * @return whether the upgrade can be rendered in the specified mount point.
- */
- boolean canRender(ItemStack stack, RobotRenderEvent.MountPoint mountPoint, Robot robot);
+ String computePreferredMountPoint(ItemStack stack, Robot robot, Set availableMountPoints);
/**
* Render the specified upgrade on a robot.
@@ -63,7 +59,8 @@ public interface UpgradeRenderer {
* relative to.
*
* If the stack cannot be rendered, the renderer should indicate so in
- * {@link #canRender}, otherwise it will still consume a mount point.
+ * {@link #computePreferredMountPoint}, otherwise it will still consume a mount
+ * point.
*
* You usually won't need the robot parameter, but in case you do
* need some contextual information, this should provide you with anything
@@ -75,4 +72,23 @@ public interface UpgradeRenderer {
* @param pt partial tick time, e.g. for animations.
*/
void render(ItemStack stack, RobotRenderEvent.MountPoint mountPoint, Robot robot, float pt);
+
+ /**
+ * Mount point names for {@link #computePreferredMountPoint}.
+ */
+ final class MountPointName {
+ public static final String None = "none";
+ public static final String Any = "any";
+
+ public static final String TopLeft = "top_left";
+ public static final String TopRight = "top_right";
+ public static final String TopBack = "top_back";
+ public static final String BottomLeft = "bottom_left";
+ public static final String BottomRight = "bottom_right";
+ public static final String BottomBack = "bottom_back";
+ public static final String BottomFront = "bottom_front";
+
+ private MountPointName() {
+ }
+ }
}
diff --git a/src/main/scala/li/cil/oc/client/renderer/item/UpgradeRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/item/UpgradeRenderer.scala
index 9dbc174c9..fa3f64206 100644
--- a/src/main/scala/li/cil/oc/client/renderer/item/UpgradeRenderer.scala
+++ b/src/main/scala/li/cil/oc/client/renderer/item/UpgradeRenderer.scala
@@ -2,6 +2,7 @@ package li.cil.oc.client.renderer.item
import li.cil.oc.Constants
import li.cil.oc.api
+import li.cil.oc.api.driver.item.UpgradeRenderer.MountPointName
import li.cil.oc.api.event.RobotRenderEvent.MountPoint
import li.cil.oc.client.Textures
import li.cil.oc.integration.opencomputers.Item
@@ -16,12 +17,15 @@ object UpgradeRenderer {
lazy val generatorUpgrade = api.Items.get(Constants.ItemName.GeneratorUpgrade)
lazy val inventoryUpgrade = api.Items.get(Constants.ItemName.InventoryUpgrade)
- def priority(stack: ItemStack): Int = {
+ def preferredMountPoint(stack: ItemStack, availableMountPoints: java.util.Set[String]): String = {
val descriptor = api.Items.get(stack)
- if (descriptor == craftingUpgrade) 5
- else if (descriptor == generatorUpgrade) 0
- else 10
+ if (descriptor == craftingUpgrade || descriptor == generatorUpgrade || descriptor == inventoryUpgrade) {
+ if (descriptor == generatorUpgrade && availableMountPoints.contains(MountPointName.BottomBack)) MountPointName.BottomBack
+ else if (descriptor == inventoryUpgrade && availableMountPoints.contains(MountPointName.TopBack)) MountPointName.TopBack
+ else MountPointName.Any
+ }
+ else MountPointName.None
}
def canRender(stack: ItemStack): Boolean = {
diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/RobotRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/RobotRenderer.scala
index a986a1057..12431e3c0 100644
--- a/src/main/scala/li/cil/oc/client/renderer/tileentity/RobotRenderer.scala
+++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/RobotRenderer.scala
@@ -4,6 +4,7 @@ import com.google.common.base.Strings
import li.cil.oc.OpenComputers
import li.cil.oc.Settings
import li.cil.oc.api.driver.item.UpgradeRenderer
+import li.cil.oc.api.driver.item.UpgradeRenderer.MountPointName
import li.cil.oc.api.event.RobotRenderEvent
import li.cil.oc.client.Textures
import li.cil.oc.common.EventHandler
@@ -19,6 +20,7 @@ import net.minecraft.client.renderer.entity.RendererLivingEntity
import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer
import net.minecraft.init.Items
import net.minecraft.item.ItemBlock
+import net.minecraft.item.ItemStack
import net.minecraft.tileentity.TileEntity
import net.minecraft.util.EnumChatFormatting
import net.minecraft.util.Vec3
@@ -30,11 +32,24 @@ import net.minecraftforge.common.util.ForgeDirection
import org.lwjgl.opengl.GL11
import org.lwjgl.opengl.GL12
+import scala.collection.convert.WrapAsJava._
+import scala.collection.mutable
+
object RobotRenderer extends TileEntitySpecialRenderer {
private val displayList = GLAllocation.generateDisplayLists(2)
private val mountPoints = Array.fill(7)(new RobotRenderEvent.MountPoint())
+ private val slotNameMapping = Map(
+ UpgradeRenderer.MountPointName.TopLeft -> 0,
+ UpgradeRenderer.MountPointName.TopRight -> 1,
+ UpgradeRenderer.MountPointName.TopBack -> 2,
+ UpgradeRenderer.MountPointName.BottomLeft -> 3,
+ UpgradeRenderer.MountPointName.BottomRight -> 4,
+ UpgradeRenderer.MountPointName.BottomBack -> 5,
+ UpgradeRenderer.MountPointName.BottomFront -> 6
+ )
+
private val gap = 1.0f / 28.0f
private val gt = 0.5f + gap
private val gb = 0.5f - gap
@@ -405,20 +420,31 @@ object RobotRenderer extends TileEntitySpecialRenderer {
}
if (MinecraftForgeClient.getRenderPass == 0) {
- var filterMount = 0
- //noinspection SortFilter We need to sort before we filter, because the filter is index sensitive.
- val stacks = (robot.componentSlots ++ robot.containerSlots).map(robot.getStackInSlot).
- collect { case stack if stack != null && stack.getItem.isInstanceOf[UpgradeRenderer] => (stack, stack.getItem.asInstanceOf[UpgradeRenderer]) }.
- sortBy { case (stack, renderer) => -renderer.priority(stack, robot) }.
- filter {
- case (stack, renderer) if filterMount < mountPoints.length && renderer.canRender(stack, mountPoints(filterMount), robot) =>
- filterMount += 1
- true
- case _ => false
+ lazy val availableSlots = slotNameMapping.keys.to[mutable.Set]
+ lazy val wildcardRenderers = mutable.Buffer.empty[(ItemStack, UpgradeRenderer)]
+ lazy val slotMapping = Array.fill(mountPoints.length)(null: (ItemStack, UpgradeRenderer))
+
+ val renderers = (robot.componentSlots ++ robot.containerSlots).map(robot.getStackInSlot).
+ collect { case stack if stack != null && stack.getItem.isInstanceOf[UpgradeRenderer] => (stack, stack.getItem.asInstanceOf[UpgradeRenderer]) }
+
+ for ((stack, renderer) <- renderers) {
+ val preferredSlot = renderer.computePreferredMountPoint(stack, robot, availableSlots)
+ if (availableSlots.remove(preferredSlot)) {
+ slotMapping(slotNameMapping(preferredSlot)) = (stack, renderer)
+ }
+ else if (preferredSlot == MountPointName.Any) {
+ wildcardRenderers += ((stack, renderer))
+ }
}
- val minLength = math.min(mountPoints.length, stacks.length)
- for (((stack, renderer), mountPoint) <- (stacks.take(minLength), mountPoints.take(minLength)).zipped) try {
+ var firstEmpty = slotMapping.indexOf(null)
+ for (entry <- wildcardRenderers if firstEmpty >= 0) {
+ slotMapping(firstEmpty) = entry
+ firstEmpty = slotMapping.indexOf(null)
+ }
+
+ for ((info, mountPoint) <- (slotMapping, mountPoints).zipped if info != null) try {
+ val (stack, renderer) = info
GL11.glPushMatrix()
GL11.glTranslatef(0.5f, 0.5f, 0.5f)
renderer.render(stack, mountPoint, robot, f)
diff --git a/src/main/scala/li/cil/oc/common/item/Delegator.scala b/src/main/scala/li/cil/oc/common/item/Delegator.scala
index 5f3e7ce23..87e9a371e 100644
--- a/src/main/scala/li/cil/oc/common/item/Delegator.scala
+++ b/src/main/scala/li/cil/oc/common/item/Delegator.scala
@@ -254,9 +254,7 @@ class Delegator extends Item with driver.item.UpgradeRenderer {
// ----------------------------------------------------------------------- //
- def priority(stack: ItemStack, robot: Robot): Int = UpgradeRenderer.priority(stack)
+ override def computePreferredMountPoint(stack: ItemStack, robot: Robot, availableMountPoints: util.Set[String]): String = UpgradeRenderer.preferredMountPoint(stack, availableMountPoints)
- def canRender(stack: ItemStack, mountPoint: MountPoint, robot: Robot): Boolean = UpgradeRenderer.canRender(stack)
-
- def render(stack: ItemStack, mountPoint: MountPoint, robot: Robot, pt: Float): Unit = UpgradeRenderer.render(stack, mountPoint)
+ override def render(stack: ItemStack, mountPoint: MountPoint, robot: Robot, pt: Float): Unit = UpgradeRenderer.render(stack, mountPoint)
}