From cd5b8018789b06fcd9524abefbc5170ff5e76fc0 Mon Sep 17 00:00:00 2001 From: Algent Date: Sat, 15 Aug 2026 11:45:56 +0200 Subject: [PATCH] refactor: port BlockMultipart to Java Converts the Block face of the API and its companion object. BlockMultipart$ is descriptor-identical to the reference including reduceMOP's Tuple2 return, and the class differs by exactly one descriptor: hasTileEntity$default$1, the accessor Scala emits for a default argument, which Java has no equivalent for. No jar references any member of this class. addCollisionBoxesToList keeps its descriptor; only its generic signature changes from Scala's List to the raw List that vanilla's Block actually declares, which is the more faithful override. getClientTile returns TileMultipartClient, a Scala trait extending TileMultipart, so in bytecode it is a bare interface carrying none of the class's members; addHitEffects casts to reach the part list. Third occurrence of that pattern after JIconHitEffects and TRandomUpdateTick. Four anonfun classes removed, including two nested inner closures. Validated with 90 plain-JVM tests, a clean build, and the Java 8 Forge dedicated-server suite. Most of this class needs a player, a ray trace or a renderer, so breaking, selection, collision, pick block, activation and particles stay on the manual checklist. --- JAVA_MIGRATION_DIVERGENCES.md | 50 ++ .../multipart/BlockMultipart$.java | 35 ++ .../codechicken/multipart/BlockMultipart.java | 377 +++++++++++++++ .../multipart/BlockMultipart.scala | 449 ------------------ 4 files changed, 462 insertions(+), 449 deletions(-) create mode 100644 src/main/scala/codechicken/multipart/BlockMultipart$.java create mode 100644 src/main/scala/codechicken/multipart/BlockMultipart.java delete mode 100644 src/main/scala/codechicken/multipart/BlockMultipart.scala diff --git a/JAVA_MIGRATION_DIVERGENCES.md b/JAVA_MIGRATION_DIVERGENCES.md index 6feac1d..031ec15 100644 --- a/JAVA_MIGRATION_DIVERGENCES.md +++ b/JAVA_MIGRATION_DIVERGENCES.md @@ -796,3 +796,53 @@ Accepted divergence: the five `$$anonfun$` classes under `WorldTickScheduler.pos and the `preTick`/`postTick` pair all run for every server tick of the suite. - Scheduled and random tick firing over time, and the chunk NBT round trip, are not asserted by any test and stay on the manual checklist. + +## 2026-08-14 — BlockMultipart Java port + +The Block face of the API. No jar references any member of it; it appears only as the return type of +`MultipartProxy.block()`, which ProjRed and buildcraft-compat call, and by name in Waila and guidenh reflection. + +### Observable behavior + +No known divergence. Every override still resolves the tile through `getTile`, which continues to return null both +when the tile is not a multipart tile and when its part list is empty, so the null branches and the empty-tile branches +stay merged exactly as before. `isAir` still reports true for a missing tile, `getExplosionResistance` still returns 0 +rather than reaching the throwing max on an empty tile, and the redstone queries still flip the side with `side ^ 1`. + +`reduceMOP` still returns the struck part's index alongside a rebased `ExtendedMOP` carrying that part's own data and +the original distance. + +### Supported JVM API + +- `BlockMultipart$` is descriptor-identical to the reference, including `reduceMOP`'s `scala.Tuple2` return. +- `BlockMultipart` differs by exactly one descriptor, discussed below. `addCollisionBoxesToList` keeps its descriptor; + only its generic signature changes from Scala's `List` to the raw `List` that vanilla's `Block` actually + declares, which is the more faithful override. +- The four static forwarders `getTile`, `getClientTile`, `reduceMOP` and `drawHighlight` are unchanged. + +### Removed API + +`hasTileEntity$default$1()`, the accessor Scala emits for the default argument on `hasTileEntity(meta: Int = 0)`. +Java has no default arguments, so it disappears. It is a compiler artifact of the default parameter, not API, and no +jar references any member of this class. + +### TileMultipartClient is a bare interface here + +`getClientTile` returns `TileMultipartClient`, which is a Scala trait extending `TileMultipart`, so in bytecode it is +an interface carrying none of the class's members. `addHitEffects` casts to `TileMultipart` to reach the part list. +This is the third occurrence of the pattern, after `JIconHitEffects` and `TRandomUpdateTick`. + +### Compiler artifacts + +Accepted divergence: the four `$$anonfun$` classes under `getDrops` and `addCollisionBoxesToList`, including the two +nested inner closures, are removed. The Java loops produce no replacement classes. + +### Validation + +- Complete plain-JVM suite: 90 tests, 0 failures, 0 errors. +- Clean `spotlessApply checkstyleTest build`: passing. +- Java 8 Forge dedicated-server suite: 5 tests, 0 failures, 0 errors. The block is registered during startup and is + the block every generated multipart tile is placed against, so registration and `hasTileEntity` run for real. +- Almost everything else in this class needs a player, a ray trace or a renderer. Block breaking, selection boxes, + collision, pick block, activation, particles and light are all on the manual checklist and are the reason that + checklist exists. diff --git a/src/main/scala/codechicken/multipart/BlockMultipart$.java b/src/main/scala/codechicken/multipart/BlockMultipart$.java new file mode 100644 index 0000000..83979ac --- /dev/null +++ b/src/main/scala/codechicken/multipart/BlockMultipart$.java @@ -0,0 +1,35 @@ +package codechicken.multipart; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.world.IBlockAccess; +import net.minecraft.world.World; + +import codechicken.lib.raytracer.ExtendedMOP; +import scala.Tuple2; + +/** + * Scala companion singleton. Retained because compiled Scala consumers read MODULE$ and call these instance methods. + */ +public final class BlockMultipart$ { + + public static final BlockMultipart$ MODULE$ = new BlockMultipart$(); + + private BlockMultipart$() {} + + public TileMultipart getTile(IBlockAccess world, int x, int y, int z) { + return BlockMultipart.getTile(world, x, y, z); + } + + public TileMultipartClient getClientTile(IBlockAccess world, int x, int y, int z) { + return BlockMultipart.getClientTile(world, x, y, z); + } + + public Tuple2 reduceMOP(MovingObjectPosition hit) { + return BlockMultipart.reduceMOP(hit); + } + + public boolean drawHighlight(World world, EntityPlayer player, MovingObjectPosition hit, float frame) { + return BlockMultipart.drawHighlight(world, player, hit, frame); + } +} diff --git a/src/main/scala/codechicken/multipart/BlockMultipart.java b/src/main/scala/codechicken/multipart/BlockMultipart.java new file mode 100644 index 0000000..1da9f40 --- /dev/null +++ b/src/main/scala/codechicken/multipart/BlockMultipart.java @@ -0,0 +1,377 @@ +package codechicken.multipart; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import net.minecraft.block.Block; +import net.minecraft.block.material.MapColor; +import net.minecraft.block.material.Material; +import net.minecraft.client.Minecraft; +import net.minecraft.client.particle.EffectRenderer; +import net.minecraft.client.renderer.texture.IIconRegister; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.util.AxisAlignedBB; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.util.Vec3; +import net.minecraft.world.IBlockAccess; +import net.minecraft.world.World; +import net.minecraftforge.common.util.ForgeDirection; + +import codechicken.lib.raytracer.ExtendedMOP; +import codechicken.lib.raytracer.RayTracer; +import codechicken.lib.render.TextureUtils; +import codechicken.lib.vec.Cuboid6; +import scala.Tuple2; + +/** Block class for all multiparts, should be internal use only. */ +public class BlockMultipart extends Block { + + public BlockMultipart() { + super(new Material(MapColor.stoneColor)); + } + + public static TileMultipart getTile(IBlockAccess world, int x, int y, int z) { + net.minecraft.tileentity.TileEntity t = world.getTileEntity(x, y, z); + if (t instanceof TileMultipart && !((TileMultipart) t).jPartList().isEmpty()) { + return (TileMultipart) t; + } + return null; + } + + public static TileMultipartClient getClientTile(IBlockAccess world, int x, int y, int z) { + net.minecraft.tileentity.TileEntity t = world.getTileEntity(x, y, z); + if (t instanceof TileMultipartClient && !((TileMultipart) t).jPartList().isEmpty()) { + return (TileMultipartClient) t; + } + return null; + } + + /** Splits a hit into the index of the part it struck and a MOP rebased onto that part's own data. */ + public static Tuple2 reduceMOP(MovingObjectPosition hit) { + ExtendedMOP ehit = (ExtendedMOP) hit; + Tuple2 data = ExtendedMOP.getData(hit); + return new Tuple2<>(data._1(), new ExtendedMOP(ehit, data._2(), ehit.dist)); + } + + public static boolean drawHighlight(World world, EntityPlayer player, MovingObjectPosition hit, float frame) { + TileMultipart tile = getTile(world, hit.blockX, hit.blockY, hit.blockZ); + if (tile == null) { + return false; + } + + Tuple2 reduced = reduceMOP(hit); + int index = (Integer) reduced._1(); + if (tile.jPartList().get(index).drawHighlight(reduced._2(), player, frame)) { + return true; + } + + tile.jPartList().get(index).collisionRayTrace(RayTracer.getStartVec(player), RayTracer.getEndVec(player)); + return false; + } + + @Override + public boolean hasTileEntity(int meta) { + return true; + } + + @Override + public boolean isBlockSolid(IBlockAccess world, int x, int y, int z, int side) { + TileMultipart tile = getTile(world, x, y, z); + return tile != null && tile.isSolid(side); + } + + @Override + public boolean isSideSolid(IBlockAccess world, int x, int y, int z, ForgeDirection side) { + return isBlockSolid(world, x, y, z, side.ordinal()); + } + + @Override + public boolean canPlaceTorchOnTop(World world, int x, int y, int z) { + TileMultipart tile = getTile(world, x, y, z); + return tile != null && tile.canPlaceTorchOnTop(); + } + + @Override + public void onNeighborBlockChange(World world, int x, int y, int z, Block block) { + TileMultipart tile = getTile(world, x, y, z); + if (tile != null) { + tile.onNeighborBlockChange(); + } + } + + @Override + public ExtendedMOP collisionRayTrace(World world, int x, int y, int z, Vec3 start, Vec3 end) { + TileMultipart tile = getTile(world, x, y, z); + return tile == null ? null : tile.collisionRayTrace(start, end); + } + + public Iterable rayTraceAll(World world, int x, int y, int z, Vec3 start, Vec3 end) { + TileMultipart tile = getTile(world, x, y, z); + return tile == null ? java.util.Collections.emptyList() : tile.rayTraceAll(start, end); + } + + @Override + public boolean removedByPlayer(World world, EntityPlayer player, int x, int y, int z) { + MovingObjectPosition hit = RayTracer.retraceBlock(world, player, x, y, z); + TileMultipart tile = getTile(world, x, y, z); + + if (hit == null || tile == null) { + dropAndDestroy(world, x, y, z); + return true; + } + + Tuple2 reduced = reduceMOP(hit); + int index = (Integer) reduced._1(); + if (world.isRemote) { + tile.jPartList().get(index).addDestroyEffects(reduced._2(), Minecraft.getMinecraft().effectRenderer); + return true; + } + + tile.harvestPart(index, reduced._2(), player); + return world.getTileEntity(x, y, z) == null; + } + + public void dropAndDestroy(World world, int x, int y, int z) { + TileMultipart tile = getTile(world, x, y, z); + if (tile != null && !world.isRemote) { + tile.dropItems(getDrops(world, x, y, z, 0, 0)); + } + + world.setBlockToAir(x, y, z); + } + + @Override + public int quantityDropped(int meta, int fortune, Random random) { + return 0; + } + + @Override + public ArrayList getDrops(World world, int x, int y, int z, int meta, int fortune) { + ArrayList ai = new ArrayList<>(); + if (world.isRemote) { + return ai; + } + + TileMultipart tile = getTile(world, x, y, z); + if (tile != null) { + for (TMultiPart part : tile.jPartList()) { + for (ItemStack item : part.getDrops()) { + ai.add(item); + } + } + } + + return ai; + } + + @Override + @SuppressWarnings("unchecked") + public void addCollisionBoxesToList(World world, int x, int y, int z, AxisAlignedBB ebb, List list$, + Entity entity) { + List list = list$; + TileMultipart tile = getTile(world, x, y, z); + if (tile != null) { + for (TMultiPart part : tile.jPartList()) { + for (Cuboid6 c : part.getCollisionBoxes()) { + AxisAlignedBB aabb = c.toAABB().offset(x, y, z); + if (aabb.intersectsWith(ebb)) { + list.add(aabb); + } + } + } + } + } + + @Override + public boolean addHitEffects(World world, MovingObjectPosition hit, EffectRenderer effectRenderer) { + TileMultipartClient tile = getClientTile(world, hit.blockX, hit.blockY, hit.blockZ); + if (tile != null) { + // TileMultipartClient is a Scala trait, so it is a bare interface here and carries no TileMultipart + // members. + TileMultipart parts = (TileMultipart) tile; + Tuple2 reduced = reduceMOP(hit); + int index = (Integer) reduced._1(); + if (index < parts.jPartList().size()) { + parts.jPartList().get(index).addHitEffects(reduced._2(), effectRenderer); + } + } + + return true; + } + + @Override + public boolean addDestroyEffects(World world, int x, int y, int z, int s, EffectRenderer effectRenderer) { + return true; + } + + @Override + public boolean renderAsNormalBlock() { + return false; + } + + @Override + public boolean isOpaqueCube() { + return false; + } + + @Override + public int getRenderType() { + return TileMultipart.renderID(); + } + + @Override + public boolean isAir(IBlockAccess world, int x, int y, int z) { + TileMultipart tile = getTile(world, x, y, z); + return tile == null || tile.jPartList().isEmpty(); + } + + @Override + public boolean isReplaceable(IBlockAccess world, int x, int y, int z) { + return isAir(world, x, y, z); + } + + @Override + public int getRenderBlockPass() { + return 1; + } + + @Override + public boolean canRenderInPass(int pass) { + return true; + } + + @Override + public ItemStack getPickBlock(MovingObjectPosition hit, World world, int x, int y, int z) { + TileMultipart tile = getTile(world, x, y, z); + if (tile != null) { + if (!(hit instanceof ExtendedMOP)) { + return null; + } + Tuple2 reduced = reduceMOP(hit); + return tile.jPartList().get((Integer) reduced._1()).pickItem(reduced._2()); + } + return null; + } + + @Override + public float getPlayerRelativeBlockHardness(EntityPlayer player, World world, int x, int y, int z) { + MovingObjectPosition hit = RayTracer.retraceBlock(world, player, x, y, z); + TileMultipart tile = getTile(world, x, y, z); + if (hit != null && tile != null) { + Tuple2 reduced = reduceMOP(hit); + return tile.jPartList().get((Integer) reduced._1()).getStrength(reduced._2(), player) / 30f; + } + + return 1 / 100f; + } + + /** Kludge to set PROTECTED blockIcon to a blank icon. */ + @Override + public void registerBlockIcons(IIconRegister register) { + net.minecraft.util.IIcon icon = TextureUtils.getBlankIcon(16, register); + setBlockTextureName(icon.getIconName()); + super.registerBlockIcons(register); + } + + @Override + public int getLightValue(IBlockAccess world, int x, int y, int z) { + TileMultipart tile = getTile(world, x, y, z); + return tile == null ? 0 : tile.getLightValue(); + } + + @Override + public void randomDisplayTick(World world, int x, int y, int z, Random random) { + TileMultipartClient tile = getClientTile(world, x, y, z); + if (tile != null) { + tile.randomDisplayTick(random); + } + } + + @Override + public boolean onBlockActivated(World world, int x, int y, int z, EntityPlayer player, int side, float hitX, + float hitY, float hitZ) { + MovingObjectPosition hit = RayTracer.retraceBlock(world, player, x, y, z); + if (hit == null) { + return false; + } + + TileMultipart tile = getTile(world, x, y, z); + if (tile == null) { + return false; + } + + Tuple2 reduced = reduceMOP(hit); + return tile.jPartList().get((Integer) reduced._1()).activate(player, reduced._2(), player.getHeldItem()); + } + + @Override + public void onBlockClicked(World world, int x, int y, int z, EntityPlayer player) { + MovingObjectPosition hit = RayTracer.retraceBlock(world, player, x, y, z); + if (hit == null) { + return; + } + + TileMultipart tile = getTile(world, x, y, z); + if (tile == null) { + return; + } + + Tuple2 reduced = reduceMOP(hit); + tile.jPartList().get((Integer) reduced._1()).click(player, reduced._2(), player.getHeldItem()); + } + + @Override + public int isProvidingStrongPower(IBlockAccess world, int x, int y, int z, int side) { + TileMultipart tile = getTile(world, x, y, z); + return tile == null ? 0 : tile.strongPowerLevel(side ^ 1); + } + + @Override + public int isProvidingWeakPower(IBlockAccess world, int x, int y, int z, int side) { + TileMultipart tile = getTile(world, x, y, z); + return tile == null ? 0 : tile.weakPowerLevel(side ^ 1); + } + + @Override + public boolean canConnectRedstone(IBlockAccess world, int x, int y, int z, int side) { + TileMultipart tile = getTile(world, x, y, z); + return tile != null && tile.canConnectRedstone(side); + } + + @Override + public void onEntityCollidedWithBlock(World world, int x, int y, int z, Entity entity) { + TileMultipart tile = getTile(world, x, y, z); + if (tile != null) { + tile.onEntityCollision(entity); + } + } + + @Override + public void onNeighborChange(IBlockAccess world, int x, int y, int z, int tileX, int tileY, int tileZ) { + TileMultipart tile = getTile(world, x, y, z); + if (tile != null) { + tile.onNeighborTileChange(tileX, tileY, tileZ); + } + } + + @Override + public float getExplosionResistance(Entity entity, World world, int x, int y, int z, double explosionX, + double explosionY, double explosionZ) { + TileMultipart tile = getTile(world, x, y, z); + return tile == null ? 0 : tile.getExplosionResistance(entity); + } + + @Override + public boolean getWeakChanges(IBlockAccess world, int x, int y, int z) { + TileMultipart tile = getTile(world, x, y, z); + return tile != null && tile.getWeakChanges(); + } + + @Override + public boolean canProvidePower() { + return true; + } +} diff --git a/src/main/scala/codechicken/multipart/BlockMultipart.scala b/src/main/scala/codechicken/multipart/BlockMultipart.scala deleted file mode 100644 index e14808e..0000000 --- a/src/main/scala/codechicken/multipart/BlockMultipart.scala +++ /dev/null @@ -1,449 +0,0 @@ -package codechicken.multipart - -import java.util.List -import java.lang.Iterable -import net.minecraft.block.Block -import net.minecraft.block.material.{MapColor, Material} -import net.minecraft.entity.Entity -import net.minecraft.util.AxisAlignedBB -import net.minecraft.world.World -import net.minecraftforge.common.util.ForgeDirection -import net.minecraft.util.Vec3 -import net.minecraft.util.MovingObjectPosition -import codechicken.lib.raytracer.RayTracer -import net.minecraft.entity.player.EntityPlayer - -import java.util.Random -import java.util.ArrayList -import net.minecraft.item.ItemStack -import net.minecraft.client.particle.EffectRenderer -import net.minecraft.client.Minecraft -import net.minecraft.client.renderer.texture.IIconRegister -import codechicken.lib.render.TextureUtils -import net.minecraft.world.IBlockAccess -import codechicken.lib.raytracer.ExtendedMOP -import scala.collection.JavaConversions._ - -object BlockMultipart { - def getTile(world: IBlockAccess, x: Int, y: Int, z: Int) = - world.getTileEntity(x, y, z) match { - case t: TileMultipart if !t.partList.isEmpty => t - case _ => null - } - - def getClientTile(world: IBlockAccess, x: Int, y: Int, z: Int) = - world.getTileEntity(x, y, z) match { - case t: TileMultipartClient if !t.partList.isEmpty => t - case _ => null - } - - def reduceMOP(hit: MovingObjectPosition): (Int, ExtendedMOP) = { - val ehit = hit.asInstanceOf[ExtendedMOP] - val data: (Int, _) = ExtendedMOP.getData(hit) - return (data._1, new ExtendedMOP(ehit, data._2, ehit.dist)) - } - - def drawHighlight( - world: World, - player: EntityPlayer, - hit: MovingObjectPosition, - frame: Float - ): Boolean = { - val tile = getTile(world, hit.blockX, hit.blockY, hit.blockZ) - if (tile == null) - return false - - val (index, mop) = reduceMOP(hit) - if (tile.partList.apply(index).drawHighlight(mop, player, frame)) - return true - - tile.partList - .apply(index) - .collisionRayTrace( - RayTracer.getStartVec(player), - RayTracer.getEndVec(player) - ) - return false - } -} - -/** Block class for all multiparts, should be internal use only. - */ -class BlockMultipart extends Block(new Material(MapColor.stoneColor)) { - import BlockMultipart._ - - override def hasTileEntity(meta: Int = 0) = true - - override def isBlockSolid( - world: IBlockAccess, - x: Int, - y: Int, - z: Int, - side: Int - ): Boolean = - getTile(world, x, y, z) match { - case null => false - case tile => tile.isSolid(side) - } - - override def isSideSolid( - world: IBlockAccess, - x: Int, - y: Int, - z: Int, - side: ForgeDirection - ): Boolean = - isBlockSolid(world, x, y, z, side.ordinal()) - - override def canPlaceTorchOnTop(world: World, x: Int, y: Int, z: Int) = - getTile(world, x, y, z) match { - case null => false - case tile => tile.canPlaceTorchOnTop - } - - override def onNeighborBlockChange( - world: World, - x: Int, - y: Int, - z: Int, - block: Block - ) { - val tile = getTile(world, x, y, z) - if (tile != null) - tile.onNeighborBlockChange() - } - - override def collisionRayTrace( - world: World, - x: Int, - y: Int, - z: Int, - start: Vec3, - end: Vec3 - ): ExtendedMOP = - getTile(world, x, y, z) match { - case null => null - case tile => tile.collisionRayTrace(start, end) - } - - def rayTraceAll( - world: World, - x: Int, - y: Int, - z: Int, - start: Vec3, - end: Vec3 - ): Iterable[ExtendedMOP] = - getTile(world, x, y, z) match { - case null => Seq() - case tile => tile.rayTraceAll(start, end) - } - - override def removedByPlayer( - world: World, - player: EntityPlayer, - x: Int, - y: Int, - z: Int - ): Boolean = { - val hit = RayTracer.retraceBlock(world, player, x, y, z) - val tile = getTile(world, x, y, z) - - if (hit == null || tile == null) { - dropAndDestroy(world, x, y, z) - return true - } - - val (index, mop) = reduceMOP(hit) - if (world.isRemote) { - tile.partList - .apply(index) - .addDestroyEffects(mop, Minecraft.getMinecraft.effectRenderer) - return true - } - - tile.harvestPart(index, mop, player) - return world.getTileEntity(x, y, z) == null - } - - def dropAndDestroy(world: World, x: Int, y: Int, z: Int) { - val tile = getTile(world, x, y, z) - if (tile != null && !world.isRemote) - tile.dropItems(getDrops(world, x, y, z, 0, 0)) - - world.setBlockToAir(x, y, z) - } - - override def quantityDropped(meta: Int, fortune: Int, random: Random) = 0 - - override def getDrops( - world: World, - x: Int, - y: Int, - z: Int, - meta: Int, - fortune: Int - ): ArrayList[ItemStack] = { - val ai = new ArrayList[ItemStack]() - if (world.isRemote) - return ai - - val tile = getTile(world, x, y, z) - if (tile != null) - tile.partList.foreach(part => part.getDrops.foreach(item => ai.add(item))) - - return ai - } - - override def addCollisionBoxesToList( - world: World, - x: Int, - y: Int, - z: Int, - ebb: AxisAlignedBB, - list$ : List[_], - entity: Entity - ) { - val list = list$.asInstanceOf[List[AxisAlignedBB]] - val tile = getTile(world, x, y, z) - if (tile != null) - tile.partList.foreach(part => - part.getCollisionBoxes.foreach { c => - val aabb = c.toAABB.offset(x, y, z) - if (aabb.intersectsWith(ebb)) - list.add(aabb) - } - ) - } - - override def addHitEffects( - world: World, - hit: MovingObjectPosition, - effectRenderer: EffectRenderer - ): Boolean = { - val tile = getClientTile(world, hit.blockX, hit.blockY, hit.blockZ) - if (tile != null) { - val (index, mop) = reduceMOP(hit) - if (index < tile.partList.size) - tile.partList.apply(index).addHitEffects(mop, effectRenderer) - } - - return true - } - - override def addDestroyEffects( - world: World, - x: Int, - y: Int, - z: Int, - s: Int, - effectRenderer: EffectRenderer - ) = true - - override def renderAsNormalBlock() = false - - override def isOpaqueCube = false - - override def getRenderType = TileMultipart.renderID - - override def isAir(world: IBlockAccess, x: Int, y: Int, z: Int): Boolean = - getTile(world, x, y, z) match { - case null => true - case tile => tile.partList.isEmpty - } - - override def isReplaceable(world: IBlockAccess, x: Int, y: Int, z: Int) = - isAir(world, x, y, z) - - override def getRenderBlockPass = 1 - - override def canRenderInPass(pass: Int): Boolean = true - - override def getPickBlock( - hit: MovingObjectPosition, - world: World, - x: Int, - y: Int, - z: Int - ): ItemStack = { - val tile = getTile(world, x, y, z) - if (tile != null) { - if (!hit.isInstanceOf[ExtendedMOP]) { - return null - } - val (index, mop) = reduceMOP(hit) - return tile.partList.apply(index).pickItem(mop) - } - return null - } - - override def getPlayerRelativeBlockHardness( - player: EntityPlayer, - world: World, - x: Int, - y: Int, - z: Int - ): Float = { - val hit = RayTracer.retraceBlock(world, player, x, y, z) - val tile = getTile(world, x, y, z) - if (hit != null && tile != null) { - val (index, mop) = reduceMOP(hit) - return tile.partList.apply(index).getStrength(mop, player) / 30f - } - - return 1 / 100f - } - - /** Kludge to set PROTECTED blockIcon to a blank icon - */ - override def registerBlockIcons(register: IIconRegister) { - val icon = TextureUtils.getBlankIcon(16, register) - setBlockTextureName(icon.getIconName) - super.registerBlockIcons(register) - } - - override def getLightValue(world: IBlockAccess, x: Int, y: Int, z: Int): Int = - getTile(world, x, y, z) match { - case null => 0 - case tile => tile.getLightValue - } - - override def randomDisplayTick( - world: World, - x: Int, - y: Int, - z: Int, - random: Random - ) { - val tile = getClientTile(world, x, y, z) - if (tile != null) - tile.randomDisplayTick(random) - } - - override def onBlockActivated( - world: World, - x: Int, - y: Int, - z: Int, - player: EntityPlayer, - side: Int, - hitX: Float, - hitY: Float, - hitZ: Float - ): Boolean = { - val hit = RayTracer.retraceBlock(world, player, x, y, z) - if (hit == null) - return false - - val tile = getTile(world, x, y, z) - if (tile == null) - return false - - val (index, mop) = reduceMOP(hit) - return tile.partList.apply(index).activate(player, mop, player.getHeldItem) - } - - override def onBlockClicked( - world: World, - x: Int, - y: Int, - z: Int, - player: EntityPlayer - ) { - val hit = RayTracer.retraceBlock(world, player, x, y, z) - if (hit == null) - return - - val tile = getTile(world, x, y, z) - if (tile == null) - return - - val (index, mop) = reduceMOP(hit) - tile.partList.apply(index).click(player, mop, player.getHeldItem) - } - - override def isProvidingStrongPower( - world: IBlockAccess, - x: Int, - y: Int, - z: Int, - side: Int - ): Int = - getTile(world, x, y, z) match { - case null => 0 - case tile => tile.strongPowerLevel(side ^ 1) - } - - override def isProvidingWeakPower( - world: IBlockAccess, - x: Int, - y: Int, - z: Int, - side: Int - ): Int = - getTile(world, x, y, z) match { - case null => 0 - case tile => tile.weakPowerLevel(side ^ 1) - } - - override def canConnectRedstone( - world: IBlockAccess, - x: Int, - y: Int, - z: Int, - side: Int - ): Boolean = - getTile(world, x, y, z) match { - case null => false - case tile => tile.canConnectRedstone(side) - } - - override def onEntityCollidedWithBlock( - world: World, - x: Int, - y: Int, - z: Int, - entity: Entity - ) { - val tile = getTile(world, x, y, z) - if (tile != null) - tile.onEntityCollision(entity) - } - - override def onNeighborChange( - world: IBlockAccess, - x: Int, - y: Int, - z: Int, - tileX: Int, - tileY: Int, - tileZ: Int - ) { - val tile = getTile(world, x, y, z) - if (tile != null) - tile.onNeighborTileChange(tileX, tileY, tileZ) - } - - override def getExplosionResistance( - entity: Entity, - world: World, - x: Int, - y: Int, - z: Int, - explosionX: Double, - explosionY: Double, - explosionZ: Double - ): Float = - getTile(world, x, y, z) match { - case null => 0 - case tile => tile.getExplosionResistance(entity) - } - - override def getWeakChanges(world: IBlockAccess, x: Int, y: Int, z: Int) = - getTile(world, x, y, z) match { - case null => false - case tile => tile.getWeakChanges - } - - override def canProvidePower = true -}