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.
This commit is contained in:
Algent committed 2026-08-15 11:45:56 +02:00
1 parent 213ff0c8ea
commit cd5b801878
4 files changed
+462 -449

No files matched your search

+50
View File
@@ -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.
@@ -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<Object, ExtendedMOP> 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);
}
}
@@ -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<Object, ExtendedMOP> reduceMOP(MovingObjectPosition hit) {
ExtendedMOP ehit = (ExtendedMOP) hit;
Tuple2<Object, Object> 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<Object, ExtendedMOP> 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<ExtendedMOP> 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.<ExtendedMOP>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<Object, ExtendedMOP> 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<ItemStack> getDrops(World world, int x, int y, int z, int meta, int fortune) {
ArrayList<ItemStack> 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<AxisAlignedBB> 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<Object, ExtendedMOP> 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<Object, ExtendedMOP> 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<Object, ExtendedMOP> 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<Object, ExtendedMOP> 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<Object, ExtendedMOP> 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;
}
}
@@ -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
}