Scratch that and let's do it again. Might silence the voices in my head (aka @Vexatos).

Changed robot upgrade rendering to allow being picky. A bit more overhead, but hey, what's a few sets among friends?
This commit is contained in:
Florian Nücke 2015-04-22 23:26:41 +02:00
parent fda54f2f6f
commit 1528a6ee24
4 changed files with 93 additions and 49 deletions

View File

@ -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.
* <p/>
* 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 <tt>None</tt> and <tt>Any</tt>.
* <p/>
* You usually won't need the robot parameter, but in case you <em>do</em>
* need some contextual information, this should provide you with anything
* you could need.
* <tt>None</tt> 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 <tt>null</tt> is equivalent to returning <tt>None</tt>.
* <p/>
* <tt>Any</tt> 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.
* <p/>
* Returning a mount point not in the list of available mount points will
* be equivalent to returning <tt>None</tt>.
*
* @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.
* <p/>
* 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<String> availableMountPoints);
/**
* Render the specified upgrade on a robot.
@ -63,7 +59,8 @@ public interface UpgradeRenderer {
* relative to.
* <p/>
* 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.
* <p/>
* You usually won't need the robot parameter, but in case you <em>do</em>
* 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() {
}
}
}

View File

@ -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 = {

View File

@ -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)

View File

@ -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)
}