mirror of
https://github.com/GTNewHorizons/Draconic-Evolution.git
synced 2026-10-26 14:24:14 +00:00
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a8ea69085b | ||
|
|
b6826e7cba | ||
|
|
4535874ed6 |
No files matched your search
+44
-25
@@ -2,6 +2,7 @@ package com.brandon3055.draconicevolution.client.render.tile;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
@@ -39,18 +40,21 @@ public class PlacedItemDisplayListCache {
|
||||
MinecraftForge.EVENT_BUS.register(this);
|
||||
}
|
||||
|
||||
public CacheEntry getOrCompile(TilePlacedItem tile, ItemStack stack, int meta, Runnable renderBody) {
|
||||
public CacheEntry getOrCompile(TilePlacedItem tile, int meta, Runnable renderBody) {
|
||||
List<ItemStack> stacks = tile.getStacks();
|
||||
CacheEntry entry = displayListCache.get(tile);
|
||||
if (entry == null || !entry.isValid(stack, meta, tile.rotation, atlasVersion) || entry.needsRefresh()) {
|
||||
if (entry == null || !entry.isValid(stacks, meta, tile.rotation, atlasVersion) || entry.needsRefresh()) {
|
||||
dispose(tile);
|
||||
entry = compile(tile, stack, meta, renderBody);
|
||||
entry = compile(tile, stacks, meta, renderBody);
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
private CacheEntry compile(TilePlacedItem tile, ItemStack stack, int meta, Runnable renderBody) {
|
||||
private CacheEntry compile(TilePlacedItem tile, List<ItemStack> stacks, int meta, Runnable renderBody) {
|
||||
int listId = GL11.glGenLists(1);
|
||||
boolean mobSoul = stack.getItem() instanceof MobSoul;
|
||||
// A lone mob soul sits at the block centre, so its spin can be applied outside the list each frame. Souls in
|
||||
// a grid are baked with their current angle instead and the list is refreshed on a short TTL.
|
||||
boolean mobSoul = stacks.size() == 1 && stacks.get(0).getItem() instanceof MobSoul;
|
||||
boolean previousRotationFlag = RenderMobSoul.applyTimeRotation;
|
||||
GL11.glNewList(listId, GL11.GL_COMPILE);
|
||||
try {
|
||||
@@ -67,26 +71,34 @@ public class PlacedItemDisplayListCache {
|
||||
}
|
||||
CacheEntry entry = new CacheEntry(
|
||||
listId,
|
||||
stack,
|
||||
stack.stackSize,
|
||||
stacks,
|
||||
meta,
|
||||
tile.rotation,
|
||||
getTTL(stack, mobSoul),
|
||||
getTTL(stacks, mobSoul),
|
||||
mobSoul,
|
||||
atlasVersion);
|
||||
displayListCache.put(tile, entry);
|
||||
return entry;
|
||||
}
|
||||
|
||||
private long getTTL(ItemStack stack, boolean mobSoul) {
|
||||
if (mobSoul) {
|
||||
private long getTTL(List<ItemStack> stacks, boolean mobSoulOutsideList) {
|
||||
long ttl = Long.MAX_VALUE; // static (event-invalidated)
|
||||
for (ItemStack stack : stacks) {
|
||||
ttl = Math.min(ttl, getTTL(stack, mobSoulOutsideList));
|
||||
}
|
||||
return ttl;
|
||||
}
|
||||
|
||||
private long getTTL(ItemStack stack, boolean mobSoulOutsideList) {
|
||||
if (stack.getItem() instanceof MobSoul) {
|
||||
// "Any" soul swaps mob every second while named souls only need per-frame rotation outside the list
|
||||
return isAnyMobSoul(stack) ? MOBSOUL_TTL_MS : Long.MAX_VALUE;
|
||||
if (isAnyMobSoul(stack)) return MOBSOUL_TTL_MS;
|
||||
return mobSoulOutsideList ? Long.MAX_VALUE : ANIMATED_TTL_MS;
|
||||
}
|
||||
|
||||
if (stack.hasEffect()) return GLINT_TTL_MS; // Glint scroll (~30 fps)
|
||||
if (stack.getItem().getIconIndex(stack) instanceof ITickable) return ANIMATED_TTL_MS; // 20 Hz sprite ticks
|
||||
return Long.MAX_VALUE; // static (event-invalidated)
|
||||
return Long.MAX_VALUE;
|
||||
}
|
||||
|
||||
private boolean isAnyMobSoul(ItemStack stack) {
|
||||
@@ -108,7 +120,7 @@ public class PlacedItemDisplayListCache {
|
||||
while (it.hasNext()) {
|
||||
Map.Entry<TilePlacedItem, CacheEntry> entry = it.next();
|
||||
TilePlacedItem tile = entry.getKey();
|
||||
if (tile.isInvalid() || tile.getWorldObj() == null || tile.getStack() == null) {
|
||||
if (tile.isInvalid() || tile.getWorldObj() == null || tile.getDisplayCount() == 0) {
|
||||
GL11.glDeleteLists(entry.getValue().listId, 1);
|
||||
it.remove();
|
||||
}
|
||||
@@ -157,20 +169,24 @@ public class PlacedItemDisplayListCache {
|
||||
public static class CacheEntry {
|
||||
|
||||
public final int listId;
|
||||
public final ItemStack stack;
|
||||
public final int stackSize;
|
||||
private final ItemStack[] stacks;
|
||||
private final int[] stackSizes;
|
||||
public final int meta;
|
||||
public final float rotation;
|
||||
public final long ttlMs;
|
||||
/** True when the list holds a single mob soul whose spin is applied by the caller each frame. */
|
||||
public final boolean mobSoul;
|
||||
public final int atlasVersion;
|
||||
private long lastCompileMs;
|
||||
|
||||
private CacheEntry(int listId, ItemStack stack, int stackSize, int meta, float rotation, long ttlMs,
|
||||
boolean mobSoul, int atlasVersion) {
|
||||
private CacheEntry(int listId, List<ItemStack> stacks, int meta, float rotation, long ttlMs, boolean mobSoul,
|
||||
int atlasVersion) {
|
||||
this.listId = listId;
|
||||
this.stack = stack;
|
||||
this.stackSize = stackSize;
|
||||
this.stacks = stacks.toArray(new ItemStack[0]);
|
||||
this.stackSizes = new int[this.stacks.length];
|
||||
for (int i = 0; i < this.stacks.length; i++) {
|
||||
this.stackSizes[i] = this.stacks[i].stackSize;
|
||||
}
|
||||
this.meta = meta;
|
||||
this.rotation = rotation;
|
||||
this.ttlMs = ttlMs;
|
||||
@@ -179,12 +195,15 @@ public class PlacedItemDisplayListCache {
|
||||
this.lastCompileMs = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
private boolean isValid(ItemStack stack, int meta, float rotation, int atlasVersion) {
|
||||
// checking the pointer of the stack is enough
|
||||
return this.stack == stack && this.stackSize == stack.stackSize
|
||||
&& this.meta == meta
|
||||
&& this.rotation == rotation
|
||||
&& this.atlasVersion == atlasVersion;
|
||||
private boolean isValid(List<ItemStack> stacks, int meta, float rotation, int atlasVersion) {
|
||||
if (this.meta != meta || this.rotation != rotation || this.atlasVersion != atlasVersion) return false;
|
||||
if (this.stacks.length != stacks.size()) return false;
|
||||
for (int i = 0; i < this.stacks.length; i++) {
|
||||
// checking the pointer of the stack is enough
|
||||
ItemStack stack = stacks.get(i);
|
||||
if (this.stacks[i] != stack || this.stackSizes[i] != stack.stackSize) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean needsRefresh() {
|
||||
|
||||
+41
-6
@@ -34,14 +34,17 @@ public class RenderTilePlacedItem extends TileEntitySpecialRenderer {
|
||||
@Override
|
||||
public void renderTileEntityAt(TileEntity te, double x, double y, double z, float timeSinceLastTick) {
|
||||
if (!(te instanceof TilePlacedItem tile)) return;
|
||||
ItemStack stack = tile.getStack();
|
||||
if (stack == null) {
|
||||
int count = tile.getDisplayCount();
|
||||
if (count == 0) {
|
||||
displayListCache.dispose(tile);
|
||||
return;
|
||||
}
|
||||
if (tile.getWorldObj() == null) return;
|
||||
int meta = tile.getWorldObj().getBlockMetadata(tile.xCoord, tile.yCoord, tile.zCoord);
|
||||
CacheEntry entry = displayListCache.getOrCompile(tile, stack, meta, () -> renderItem(tile));
|
||||
if (meta < 0 || meta > 5) meta = 1;
|
||||
final int gridMeta = meta;
|
||||
CacheEntry entry = displayListCache.getOrCompile(tile, meta, () -> renderGrid(tile, gridMeta));
|
||||
|
||||
GL11.glPushMatrix();
|
||||
GL11.glPushAttrib(GL11.GL_ENABLE_BIT);
|
||||
GL11.glTranslated(x, y, z);
|
||||
@@ -55,9 +58,41 @@ public class RenderTilePlacedItem extends TileEntitySpecialRenderer {
|
||||
GL11.glPopMatrix();
|
||||
}
|
||||
|
||||
public void renderItem(TilePlacedItem tile) {
|
||||
ItemStack stack = tile.getStack();
|
||||
int meta = tile.getWorldObj().getBlockMetadata(tile.xCoord, tile.yCoord, tile.zCoord);
|
||||
/**
|
||||
* Renders every item on the display laid out in a grid on the mounting face. This is the body that gets compiled
|
||||
* into the cached display list.
|
||||
*/
|
||||
private void renderGrid(TilePlacedItem tile, int meta) {
|
||||
int count = tile.getDisplayCount();
|
||||
int gridSize = TilePlacedItem.getGridSize(count);
|
||||
float cell = 1F / gridSize;
|
||||
float[] pivot = TilePlacedItem.FACE_PIVOT[meta];
|
||||
float[] axisU = TilePlacedItem.FACE_AXIS_U[meta];
|
||||
float[] axisV = TilePlacedItem.FACE_AXIS_V[meta];
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
ItemStack stack = tile.getStack(i);
|
||||
if (stack == null) continue;
|
||||
|
||||
GL11.glPushMatrix();
|
||||
if (gridSize > 1) {
|
||||
float[] offset = TilePlacedItem.getCellOffset(i, count);
|
||||
float u = offset[0];
|
||||
float v = offset[1];
|
||||
|
||||
GL11.glTranslatef(
|
||||
pivot[0] + axisU[0] * u + axisV[0] * v,
|
||||
pivot[1] + axisU[1] * u + axisV[1] * v,
|
||||
pivot[2] + axisU[2] * u + axisV[2] * v);
|
||||
GL11.glScalef(cell, cell, cell);
|
||||
GL11.glTranslatef(-pivot[0], -pivot[1], -pivot[2]);
|
||||
}
|
||||
renderItem(tile, stack, meta);
|
||||
GL11.glPopMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
public void renderItem(TilePlacedItem tile, ItemStack stack, int meta) {
|
||||
final Item item = stack.getItem();
|
||||
boolean is3D = item.isFull3D();
|
||||
boolean isBlock = item instanceof ItemBlock;
|
||||
|
||||
@@ -147,47 +147,61 @@ public class PlacedItem extends BlockDE {
|
||||
@Override
|
||||
public void breakBlock(World world, int x, int y, int z, Block block, int meta) {
|
||||
TileEntity te = world.getTileEntity(x, y, z);
|
||||
if (te != null && te instanceof TilePlacedItem && ((TilePlacedItem) te).getStack() != null) {
|
||||
TilePlacedItem tile = (TilePlacedItem) te;
|
||||
|
||||
float spawnX = x + world.rand.nextFloat();
|
||||
float spawnY = y + world.rand.nextFloat();
|
||||
float spawnZ = z + world.rand.nextFloat();
|
||||
|
||||
EntityItem droppedItem = new EntityItem(world, spawnX, spawnY, spawnZ, tile.getStack());
|
||||
tile.setStack(null);
|
||||
|
||||
float multiplier = 0.05F;
|
||||
|
||||
droppedItem.motionX = (-0.5F + world.rand.nextFloat()) * multiplier;
|
||||
droppedItem.motionY = (4 + world.rand.nextFloat()) * multiplier;
|
||||
droppedItem.motionZ = (-0.5F + world.rand.nextFloat()) * multiplier;
|
||||
|
||||
world.spawnEntityInWorld(droppedItem);
|
||||
if (te instanceof TilePlacedItem tile) {
|
||||
ItemStack stack;
|
||||
while ((stack = tile.removeLastItem()) != null) {
|
||||
dropStack(world, x, y, z, stack);
|
||||
}
|
||||
}
|
||||
super.breakBlock(world, x, y, z, block, meta);
|
||||
}
|
||||
|
||||
private static void dropStack(World world, int x, int y, int z, ItemStack stack) {
|
||||
float spawnX = x + world.rand.nextFloat();
|
||||
float spawnY = y + world.rand.nextFloat();
|
||||
float spawnZ = z + world.rand.nextFloat();
|
||||
|
||||
EntityItem droppedItem = new EntityItem(world, spawnX, spawnY, spawnZ, stack);
|
||||
|
||||
float multiplier = 0.05F;
|
||||
|
||||
droppedItem.motionX = (-0.5F + world.rand.nextFloat()) * multiplier;
|
||||
droppedItem.motionY = (4 + world.rand.nextFloat()) * multiplier;
|
||||
droppedItem.motionZ = (-0.5F + world.rand.nextFloat()) * multiplier;
|
||||
|
||||
world.spawnEntityInWorld(droppedItem);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Item getItemDropped(int p_149650_1_, Random p_149650_2_, int p_149650_3_) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onBlockActivated(World world, int x, int y, int z, EntityPlayer player, int p_149727_6_,
|
||||
float p_149727_7_, float p_149727_8_, float p_149727_9_) {
|
||||
public boolean onBlockActivated(World world, int x, int y, int z, EntityPlayer player, int side, float hitX,
|
||||
float hitY, float hitZ) {
|
||||
TilePlacedItem tile = (world.getTileEntity(x, y, z) != null
|
||||
&& world.getTileEntity(x, y, z) instanceof TilePlacedItem)
|
||||
? (TilePlacedItem) world.getTileEntity(x, y, z)
|
||||
: null;
|
||||
if (tile == null) {
|
||||
world.setBlockToAir(x, y, z);
|
||||
return true;
|
||||
}
|
||||
if (player.isSneaking()) {
|
||||
TilePlacedItem tile = (world.getTileEntity(x, y, z) != null
|
||||
&& world.getTileEntity(x, y, z) instanceof TilePlacedItem)
|
||||
? (TilePlacedItem) world.getTileEntity(x, y, z)
|
||||
: null;
|
||||
if (tile == null) {
|
||||
world.setBlockToAir(x, y, z);
|
||||
}
|
||||
tile.rotation += 5.625F;
|
||||
} else {
|
||||
if (!world.isRemote) breakBlock(world, x, y, z, this, world.getBlockMetadata(x, y, z));
|
||||
world.setBlockToAir(x, y, z);
|
||||
// Remove the item the player clicked on; the block is removed once the last one is taken.
|
||||
if (!world.isRemote) {
|
||||
int index = tile.getStackIndexAt(world.getBlockMetadata(x, y, z), hitX, hitY, hitZ);
|
||||
ItemStack stack = tile.removeItem(index);
|
||||
if (stack != null) {
|
||||
dropStack(world, x, y, z, stack);
|
||||
}
|
||||
if (tile.getDisplayCount() == 0) {
|
||||
world.setBlockToAir(x, y, z);
|
||||
}
|
||||
}
|
||||
}
|
||||
world.markBlockForUpdate(x, y, z);
|
||||
return true;
|
||||
@@ -195,45 +209,42 @@ public class PlacedItem extends BlockDE {
|
||||
|
||||
@Override
|
||||
public void onBlockClicked(World world, int x, int y, int z, EntityPlayer player) {
|
||||
if (player.isSneaking()) {
|
||||
TilePlacedItem tile = (world.getTileEntity(x, y, z) != null
|
||||
&& world.getTileEntity(x, y, z) instanceof TilePlacedItem)
|
||||
? (TilePlacedItem) world.getTileEntity(x, y, z)
|
||||
: null;
|
||||
if (tile == null) {
|
||||
world.setBlockToAir(x, y, z);
|
||||
}
|
||||
tile.rotation += 22.5F;
|
||||
} else {
|
||||
TilePlacedItem tile = (world.getTileEntity(x, y, z) != null
|
||||
&& world.getTileEntity(x, y, z) instanceof TilePlacedItem)
|
||||
? (TilePlacedItem) world.getTileEntity(x, y, z)
|
||||
: null;
|
||||
if (tile == null) {
|
||||
world.setBlockToAir(x, y, z);
|
||||
}
|
||||
tile.rotation -= 22.5F;
|
||||
TilePlacedItem tile = (world.getTileEntity(x, y, z) != null
|
||||
&& world.getTileEntity(x, y, z) instanceof TilePlacedItem)
|
||||
? (TilePlacedItem) world.getTileEntity(x, y, z)
|
||||
: null;
|
||||
if (tile == null) {
|
||||
world.setBlockToAir(x, y, z);
|
||||
return;
|
||||
}
|
||||
tile.rotation += player.isSneaking() ? 22.5F : -22.5F;
|
||||
world.markBlockForUpdate(x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLightValue(IBlockAccess world, int x, int y, int z) {
|
||||
TileEntity te = world.getTileEntity(x, y, z);
|
||||
if (te != null && te instanceof TilePlacedItem && ((TilePlacedItem) te).getStack() != null) {
|
||||
TilePlacedItem tile = (TilePlacedItem) te;
|
||||
if (tile.getStack() != null && tile.getStack().getItem() instanceof ItemBlock)
|
||||
return Block.getBlockFromItem(tile.getStack().getItem()).getLightValue();
|
||||
int light = 0;
|
||||
if (te instanceof TilePlacedItem tile) {
|
||||
for (ItemStack stack : tile.getStacks()) {
|
||||
if (stack != null && stack.getItem() instanceof ItemBlock) {
|
||||
light = Math.max(light, Block.getBlockFromItem(stack.getItem()).getLightValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
return light;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getPickBlock(MovingObjectPosition target, World world, int x, int y, int z) {
|
||||
TileEntity te = world.getTileEntity(x, y, z);
|
||||
if (te != null && te instanceof TilePlacedItem && ((TilePlacedItem) te).getStack() != null) {
|
||||
TilePlacedItem tile = (TilePlacedItem) te;
|
||||
return tile.getStack();
|
||||
if (te instanceof TilePlacedItem tile && target != null && target.hitVec != null) {
|
||||
int index = tile.getStackIndexAt(
|
||||
world.getBlockMetadata(x, y, z),
|
||||
(float) (target.hitVec.xCoord - x),
|
||||
(float) (target.hitVec.yCoord - y),
|
||||
(float) (target.hitVec.zCoord - z));
|
||||
return tile.getStack(index);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ public class ConfigHandler {
|
||||
// GENERAL
|
||||
public static boolean disableEarthBlock;
|
||||
public static int teleporterUsesPerPearl;
|
||||
public static int placedItemStackLimit;
|
||||
public static int soulDropChance;
|
||||
public static int passiveSoulDropChance;
|
||||
public static int cometRarity;
|
||||
@@ -113,6 +114,14 @@ public class ConfigHandler {
|
||||
"Teleporter Uses PerPearl",
|
||||
1,
|
||||
"Charm of Dislocation uses per Ender pearl").getInt(1);
|
||||
placedItemStackLimit = snapToAllowedStackLimit(
|
||||
config.get(
|
||||
Configuration.CATEGORY_GENERAL,
|
||||
"Placed Item Stack Limit",
|
||||
4,
|
||||
"Maximum number of items that can be placed in the same block with the Place Item key. Valid values: 1, 2, 4, 8, 16",
|
||||
1,
|
||||
16).getInt(4));
|
||||
bowBlockDamage = config.get(
|
||||
Configuration.CATEGORY_GENERAL,
|
||||
"Bow Block Damage",
|
||||
@@ -478,6 +487,16 @@ public class ConfigHandler {
|
||||
if (config.hasChanged()) config.save();
|
||||
}
|
||||
}
|
||||
|
||||
private static int snapToAllowedStackLimit(int value) {
|
||||
int snapped = 1;
|
||||
for (int allowed : new int[] { 1, 2, 4, 8, 16 }) {
|
||||
if (value >= allowed) {
|
||||
snapped = allowed;
|
||||
}
|
||||
}
|
||||
return snapped;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* { //config.load(); disableSunDial = config.getInt("Disable Sun Dial", Configuration.CATEGORY_GENERAL, disableSunDial,
|
||||
|
||||
+33
-2
@@ -62,9 +62,19 @@ public class PlacedItemPacket implements IMessage {
|
||||
World world = ctx.getServerHandler().playerEntity.worldObj;
|
||||
EntityPlayer player = ctx.getServerHandler().playerEntity;
|
||||
|
||||
if (!world.isAirBlock(x, y, z) || player.getHeldItem() == null
|
||||
|| !ModBlocks.isEnabled(ModBlocks.placedItem))
|
||||
if (player.getHeldItem() == null || !ModBlocks.isEnabled(ModBlocks.placedItem)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Aiming directly at an existing placed item display adds the held item to it.
|
||||
if (addToExistingDisplay(world, message.blockX, message.blockY, message.blockZ, player)
|
||||
|| addToExistingDisplay(world, x, y, z, player)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!world.isAirBlock(x, y, z)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
BlockEvent.PlaceEvent event = new BlockEvent.PlaceEvent(
|
||||
new BlockSnapshot(world, x, y, z, ModBlocks.placedItem, 0),
|
||||
@@ -93,5 +103,26 @@ public class PlacedItemPacket implements IMessage {
|
||||
player.destroyCurrentEquippedItem();
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean addToExistingDisplay(World world, int x, int y, int z, EntityPlayer player) {
|
||||
if (!(world.getTileEntity(x, y, z) instanceof TilePlacedItem tile)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BlockEvent.PlaceEvent event = new BlockEvent.PlaceEvent(
|
||||
new BlockSnapshot(world, x, y, z, ModBlocks.placedItem, world.getBlockMetadata(x, y, z)),
|
||||
world.getBlock(x, y, z),
|
||||
player);
|
||||
MinecraftForge.EVENT_BUS.post(event);
|
||||
if (event.isCanceled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!tile.addItem(player.getHeldItem().copy())) {
|
||||
return false;
|
||||
}
|
||||
player.destroyCurrentEquippedItem();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
+182
-21
@@ -1,6 +1,8 @@
|
||||
package com.brandon3055.draconicevolution.common.tileentities;
|
||||
|
||||
import net.minecraft.init.Blocks;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.network.NetworkManager;
|
||||
@@ -11,6 +13,7 @@ import net.minecraft.util.AxisAlignedBB;
|
||||
import net.minecraft.util.Vec3;
|
||||
|
||||
import com.brandon3055.draconicevolution.common.ModBlocks;
|
||||
import com.brandon3055.draconicevolution.common.handler.ConfigHandler;
|
||||
import com.brandon3055.draconicevolution.common.utils.LogHelper;
|
||||
|
||||
/**
|
||||
@@ -18,7 +21,21 @@ import com.brandon3055.draconicevolution.common.utils.LogHelper;
|
||||
*/
|
||||
public class TilePlacedItem extends TileEntity {
|
||||
|
||||
public ItemStack stack;
|
||||
public static final int ABSOLUTE_MAX_STACKS = 16;
|
||||
|
||||
/**
|
||||
* Pivot point on the mounting face and the two in-plane axes used to lay out the item grid, indexed by block
|
||||
* metadata (the side the display is attached to). Shared by the renderer and the hit test so that the stack a
|
||||
* player clicks on is the one they see.
|
||||
*/
|
||||
public static final float[][] FACE_PIVOT = { { 0.5F, 1F, 0.5F }, { 0.5F, 0F, 0.5F }, { 0.5F, 0.5F, 1F },
|
||||
{ 0.5F, 0.5F, 0F }, { 1F, 0.5F, 0.5F }, { 0F, 0.5F, 0.5F } };
|
||||
public static final float[][] FACE_AXIS_U = { { 1F, 0F, 0F }, { 1F, 0F, 0F }, { 1F, 0F, 0F }, { 1F, 0F, 0F },
|
||||
{ 0F, 0F, 1F }, { 0F, 0F, 1F } };
|
||||
public static final float[][] FACE_AXIS_V = { { 0F, 0F, 1F }, { 0F, 0F, 1F }, { 0F, 1F, 0F }, { 0F, 1F, 0F },
|
||||
{ 0F, 1F, 0F }, { 0F, 1F, 0F } };
|
||||
|
||||
private final List<ItemStack> stacks = new ArrayList<>(ABSOLUTE_MAX_STACKS);
|
||||
public float rotation = 0F;
|
||||
private boolean hasUpdated = false;
|
||||
private AxisAlignedBB renderBoundingBox;
|
||||
@@ -34,7 +51,7 @@ public class TilePlacedItem extends TileEntity {
|
||||
|
||||
@Override
|
||||
public void updateEntity() {
|
||||
if (!hasUpdated && stack != null) {
|
||||
if (!hasUpdated && !stacks.isEmpty()) {
|
||||
worldObj.markBlockForUpdate(xCoord, yCoord, zCoord);
|
||||
hasUpdated = true;
|
||||
}
|
||||
@@ -50,44 +67,188 @@ public class TilePlacedItem extends TileEntity {
|
||||
@Override
|
||||
public void onDataPacket(NetworkManager net, S35PacketUpdateTileEntity pkt) {
|
||||
readFromNBT(pkt.func_148857_g());
|
||||
worldObj.markBlockForUpdate(xCoord, yCoord, zCoord);
|
||||
}
|
||||
|
||||
public void setStack(ItemStack stack) {
|
||||
this.stack = stack;
|
||||
stacks.clear();
|
||||
if (stack != null) {
|
||||
stacks.add(stack);
|
||||
}
|
||||
worldObj.scheduleBlockUpdate(xCoord, yCoord, zCoord, ModBlocks.placedItem, 20);
|
||||
}
|
||||
|
||||
public ItemStack getStack() {
|
||||
/**
|
||||
* Adds an item to this display if there is room for it. The limit is the lower of the configured stack limit and
|
||||
* {@link #ABSOLUTE_MAX_STACKS}.
|
||||
*
|
||||
* @return true if the item was added.
|
||||
*/
|
||||
public boolean addItem(ItemStack stack) {
|
||||
if (stack == null || stacks.size() >= getStackLimit()) {
|
||||
return false;
|
||||
}
|
||||
stacks.add(stack);
|
||||
markDirtyAndSync();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes and returns the most recently added item, or null if empty.
|
||||
*/
|
||||
public ItemStack removeLastItem() {
|
||||
if (stacks.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
ItemStack stack = stacks.remove(stacks.size() - 1);
|
||||
markDirtyAndSync();
|
||||
return stack;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes and returns the item at {@code index}, or null if the index is out of range.
|
||||
*/
|
||||
public ItemStack removeItem(int index) {
|
||||
if (index < 0 || index >= stacks.size()) {
|
||||
return null;
|
||||
}
|
||||
ItemStack stack = stacks.remove(index);
|
||||
markDirtyAndSync();
|
||||
return stack;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the number of columns (and maximum rows) in the grid used to display {@code count} items.
|
||||
*/
|
||||
public static int getGridSize(int count) {
|
||||
return (int) Math.ceil(Math.sqrt(count));
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes where the centre of grid cell {@code index} sits relative to the face pivot, in block units along
|
||||
* {@link #FACE_AXIS_U} and {@link #FACE_AXIS_V}. Rows are centred so a partially filled last row does not hang to
|
||||
* one side.
|
||||
*
|
||||
* @return {u, v}
|
||||
*/
|
||||
public static float[] getCellOffset(int index, int count) {
|
||||
int gridSize = getGridSize(count);
|
||||
if (gridSize <= 1) {
|
||||
return new float[] { 0F, 0F };
|
||||
}
|
||||
int rows = (count + gridSize - 1) / gridSize;
|
||||
float cell = 1F / gridSize;
|
||||
int row = index / gridSize;
|
||||
int col = index % gridSize;
|
||||
int rowLength = Math.min(gridSize, count - row * gridSize);
|
||||
return new float[] { (col - (rowLength - 1) / 2F) * cell, ((rows - 1) / 2F - row) * cell };
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the stack whose grid cell is closest to a point on this block.
|
||||
*
|
||||
* @param meta the block metadata (mounting side)
|
||||
* @param hitX x of the hit, relative to the block (0..1)
|
||||
* @param hitY y of the hit, relative to the block (0..1)
|
||||
* @param hitZ z of the hit, relative to the block (0..1)
|
||||
* @return the index of the closest stack, or -1 if the display is empty
|
||||
*/
|
||||
public int getStackIndexAt(int meta, float hitX, float hitY, float hitZ) {
|
||||
int count = stacks.size();
|
||||
if (count == 0) {
|
||||
return -1;
|
||||
}
|
||||
if (meta < 0 || meta > 5) {
|
||||
meta = 1;
|
||||
}
|
||||
float[] pivot = FACE_PIVOT[meta];
|
||||
float[] axisU = FACE_AXIS_U[meta];
|
||||
float[] axisV = FACE_AXIS_V[meta];
|
||||
float dx = hitX - pivot[0];
|
||||
float dy = hitY - pivot[1];
|
||||
float dz = hitZ - pivot[2];
|
||||
float u = dx * axisU[0] + dy * axisU[1] + dz * axisU[2];
|
||||
float v = dx * axisV[0] + dy * axisV[1] + dz * axisV[2];
|
||||
|
||||
int closest = 0;
|
||||
float closestDistSq = Float.MAX_VALUE;
|
||||
for (int i = 0; i < count; i++) {
|
||||
float[] offset = getCellOffset(i, count);
|
||||
float du = u - offset[0];
|
||||
float dv = v - offset[1];
|
||||
float distSq = du * du + dv * dv;
|
||||
if (distSq < closestDistSq) {
|
||||
closestDistSq = distSq;
|
||||
closest = i;
|
||||
}
|
||||
}
|
||||
return closest;
|
||||
}
|
||||
|
||||
public static int getStackLimit() {
|
||||
return Math.min(ConfigHandler.placedItemStackLimit, ABSOLUTE_MAX_STACKS);
|
||||
}
|
||||
|
||||
private void markDirtyAndSync() {
|
||||
markDirty();
|
||||
if (worldObj != null) {
|
||||
worldObj.markBlockForUpdate(xCoord, yCoord, zCoord);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the first stack in this display, or null if empty. Retained for the single-item code paths (pick block,
|
||||
* collision box).
|
||||
*/
|
||||
public ItemStack getStack() {
|
||||
return stacks.isEmpty() ? null : stacks.get(0);
|
||||
}
|
||||
|
||||
public ItemStack getStack(int index) {
|
||||
return index >= 0 && index < stacks.size() ? stacks.get(index) : null;
|
||||
}
|
||||
|
||||
public List<ItemStack> getStacks() {
|
||||
return stacks;
|
||||
}
|
||||
|
||||
public int getDisplayCount() {
|
||||
return stacks.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToNBT(NBTTagCompound compound) {
|
||||
super.writeToNBT(compound);
|
||||
NBTTagCompound[] tag = new NBTTagCompound[1];
|
||||
tag[0] = new NBTTagCompound();
|
||||
if (stack != null) {
|
||||
stack.writeToNBT(tag[0]);
|
||||
compound.setByte("Count", (byte) stacks.size());
|
||||
for (int i = 0; i < stacks.size(); i++) {
|
||||
NBTTagCompound tag = new NBTTagCompound();
|
||||
stacks.get(i).writeToNBT(tag);
|
||||
compound.setTag("Item" + i, tag);
|
||||
}
|
||||
compound.setTag("Item" + 0, tag[0]);
|
||||
compound.setFloat("Rotation", rotation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFromNBT(NBTTagCompound compound) {
|
||||
super.readFromNBT(compound);
|
||||
NBTTagCompound[] tag = new NBTTagCompound[1];
|
||||
tag[0] = compound.getCompoundTag("Item" + 0);
|
||||
stack = ItemStack.loadItemStackFromNBT(tag[0]);
|
||||
if (stack == null) {
|
||||
// the stack can be null if the placed item was
|
||||
// an item that got removed in between mod updates
|
||||
stack = new ItemStack(Blocks.stone);
|
||||
stacks.clear();
|
||||
// Old worlds have no Count tag and always stored a single stack in Item0.
|
||||
int count = compound.hasKey("Count") ? compound.getByte("Count") : 1;
|
||||
for (int i = 0; i < count && i < ABSOLUTE_MAX_STACKS; i++) {
|
||||
ItemStack stack = ItemStack.loadItemStackFromNBT(compound.getCompoundTag("Item" + i));
|
||||
if (stack != null) {
|
||||
stacks.add(stack);
|
||||
} else {
|
||||
// the stack can be null if the placed item was
|
||||
// an item that got removed in between mod updates
|
||||
LogHelper.error(
|
||||
"Cannot load a Placed Item stack at location "
|
||||
+ Vec3.createVectorHelper(this.xCoord, this.yCoord, this.zCoord)
|
||||
+ " because the associated item is null, it will be removed.");
|
||||
}
|
||||
}
|
||||
if (stacks.isEmpty()) {
|
||||
this.invalidate();
|
||||
LogHelper.error(
|
||||
"Cannot load the Placed Item at location "
|
||||
+ Vec3.createVectorHelper(this.xCoord, this.yCoord, this.zCoord)
|
||||
+ " because the associated item is null, it will be removed.");
|
||||
}
|
||||
rotation = compound.getFloat("Rotation");
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user