2025-10-27 16:58:21 +01:00

386 lines
11 KiB
Java

package com.hbm.blocks.machine;
import com.hbm.blocks.ModBlocks;
import java.util.Random;
import com.hbm.blocks.BlockEnums.LightType;
import com.hbm.blocks.ISpotlight;
import com.hbm.main.ResourceManager;
import com.hbm.world.gen.nbt.INBTBlockTransformable;
import cpw.mods.fml.client.registry.RenderingRegistry;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import net.minecraft.block.Block;
import net.minecraft.block.BlockSlab;
import net.minecraft.block.material.MapColor;
import net.minecraft.block.material.Material;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import net.minecraftforge.client.model.obj.WavefrontObject;
import net.minecraftforge.common.util.ForgeDirection;
public class Spotlight extends Block implements ISpotlight, INBTBlockTransformable {
public static final int META_YELLOW = 0;
public static final int META_GREEN = 1;
public static final int META_BLUE = 2;
public static boolean disableOnGeneration = true;
// I'd be extending the ReinforcedLamp class if it wasn't for the inverted behaviour of these specific lights
// I want these blocks to be eminently useful, so removing the need for redstone by default is desired,
// these act more like redstone torches, in that applying a signal turns them off
public boolean isOn;
public int beamLength;
public LightType type;
public Spotlight(Material mat, int beamLength, LightType type, boolean isOn) {
super(mat);
this.beamLength = beamLength;
this.type = type;
this.isOn = isOn;
this.setHardness(0.5F);
if(isOn) setLightLevel(1.0F);
}
public static int renderID = RenderingRegistry.getNextAvailableRenderId();
@Override
public int getRenderType() {
return renderID;
}
public WavefrontObject getModel() {
switch(type) {
case FLUORESCENT: return (WavefrontObject) ResourceManager.fluorescent_lamp;
case HALOGEN: return (WavefrontObject) ResourceManager.flood_lamp;
default: return (WavefrontObject) ResourceManager.cage_lamp;
}
}
public String getPartName(int connectionCount) {
switch(type) {
case HALOGEN: return "FloodLamp";
default: return "CageLamp";
}
}
@Override
public boolean isOpaqueCube() {
return false;
}
@Override
public boolean renderAsNormalBlock() {
return false;
}
@Override
// Ah yes, I love methods named the literal opposite of what they do
public boolean getBlocksMovement(IBlockAccess world, int x, int y, int z) {
return true;
}
@Override
public MapColor getMapColor(int meta) {
return MapColor.airColor;
}
@Override
public AxisAlignedBB getCollisionBoundingBoxFromPool(World p_149668_1_, int p_149668_2_, int p_149668_3_, int p_149668_4_) {
return null;
}
@Override
public void setBlockBoundsBasedOnState(IBlockAccess world, int x, int y, int z) {
ForgeDirection dir = getDirection(world, x, y, z);
float[] bounds = swizzleBounds(dir);
float[] offset = new float[] { 0.5F - dir.offsetX * (0.5F - bounds[0]), 0.5F - dir.offsetY * (0.5F - bounds[1]), 0.5F - dir.offsetZ * (0.5F - bounds[2]) };
setBlockBounds(offset[0] - bounds[0], offset[1] - bounds[1], offset[2] - bounds[2], offset[0] + bounds[0], offset[1] + bounds[1], offset[2] + bounds[2]);
}
private float[] swizzleBounds(ForgeDirection dir) {
float[] bounds = getBounds();
switch(dir) {
case EAST:
case WEST: return new float[] { bounds[2], bounds[1], bounds[0] };
case UP:
case DOWN: return new float[] { bounds[1], bounds[2], bounds[0] };
default: return bounds;
}
}
// Returns an xyz (half-)size for a given object type
private float[] getBounds() {
switch(type) {
case FLUORESCENT: return new float[] { 0.5F, 0.5F, 0.1F };
case HALOGEN: return new float[] { 0.35F, 0.25F, 0.2F };
default: return new float[] { 0.25F, 0.2F, 0.15F };
}
}
@Override
public int onBlockPlaced(World world, int x, int y, int z, int side, float hx, float hy, float hz, int initData) {
return side << 1;
}
@Override
public void onBlockAdded(World world, int x, int y, int z) {
if(world.isRemote) return;
if(updatePower(world, x, y, z)) return;
updateBeam(world, x, y, z);
}
private boolean updatePower(World world, int x, int y, int z) {
if(isBroken(world.getBlockMetadata(x, y, z))) return false;
boolean isPowered = world.isBlockIndirectlyGettingPowered(x, y, z);
if(isOn && isPowered) {
world.scheduleBlockUpdate(x, y, z, this, 4);
return true;
} else if(!isOn && !isPowered) {
world.setBlock(x, y, z, getOn(), world.getBlockMetadata(x, y, z), 2);
return true;
}
return false;
}
@Override
public void breakBlock(World world, int x, int y, int z, Block block, int metadata) {
ForgeDirection dir = getDirection(metadata);
super.breakBlock(world, x, y, z, block, metadata);
if(world.isRemote) return;
unpropagateBeam(world, x, y, z, dir);
}
@Override
public void updateTick(World world, int x, int y, int z, Random p_149674_5_) {
if (world.isRemote) return;
if (isOn && world.isBlockIndirectlyGettingPowered(x, y, z)) {
world.setBlock(x, y, z, getOff(), world.getBlockMetadata(x, y, z), 2);
}
}
// Repropagate the beam if we've become unblocked
@Override
public void onNeighborBlockChange(World world, int x, int y, int z, Block neighborBlock) {
if(world.isRemote) return;
if(neighborBlock instanceof SpotlightBeam) return;
if(neighborBlock == Blocks.air) return;
ForgeDirection dir = getDirection(world, x, y, z);
if(!canPlace(world, x, y, z, dir)) {
dropBlockAsItem(world, x, y, z, 0, 0);
world.setBlockToAir(x, y, z);
return;
}
if(updatePower(world, x, y, z)) return;
updateBeam(world, x, y, z);
}
@Override
public boolean canPlaceBlockOnSide(World world, int x, int y, int z, int side) {
if(!super.canPlaceBlockOnSide(world, x, y, z, side)) return false;
ForgeDirection dir = ForgeDirection.getOrientation(side);
return canPlace(world, x, y, z, dir);
}
// BlockSlab doesn't actually properly return isSideSolid,
// probably because MOJANK thought this would only ever be used for torches,
// which can't be placed on ceilings...
private boolean canPlace(World world, int x, int y, int z, ForgeDirection dir) {
x -= dir.offsetX;
y -= dir.offsetY;
z -= dir.offsetZ;
Block block = world.getBlock(x, y, z);
if(block instanceof BlockSlab) {
int meta = world.getBlockMetadata(x, y, z);
return dir == ((meta & 8) == 8 ? ForgeDirection.UP : ForgeDirection.DOWN) || block.func_149730_j();
}
return block.isSideSolid(world, x, y, z, dir);
}
private void updateBeam(World world, int x, int y, int z) {
if(!isOn) return;
ForgeDirection dir = getDirection(world, x, y, z);
propagateBeam(world, x, y, z, dir, beamLength, META_YELLOW);
}
public ForgeDirection getDirection(IBlockAccess world, int x, int y, int z) {
int metadata = world.getBlockMetadata(x, y, z);
return getDirection(metadata);
}
public ForgeDirection getDirection(int metadata) {
return ForgeDirection.getOrientation(metadata >> 1);
}
// Replace bulbs on broken lights with a click
@Override
public boolean onBlockActivated(World world, int x, int y, int z, EntityPlayer player, int side, float hitX, float hitY, float hitZ) {
int meta = world.getBlockMetadata(x, y, z);
if(!isBroken(meta)) return false;
repair(world, x, y, z);
return true;
}
private void repair(World world, int x, int y, int z) {
int meta = world.getBlockMetadata(x, y, z);
if(!isBroken(meta)) return;
world.setBlock(x, y, z, getOn(), meta - 1, 2);
for(ForgeDirection dir : ForgeDirection.VALID_DIRECTIONS) {
int ox = x + dir.offsetX;
int oy = y + dir.offsetY;
int oz = z + dir.offsetZ;
Block block = world.getBlock(ox, oy, oz);
if(block == this) repair(world, ox, oy, oz);
}
}
public boolean isBroken(int metadata) {
return (metadata & 1) == 1;
}
@Override
public Item getItemDropped(int i, Random r, int j) {
return Item.getItemFromBlock(getOn());
}
@Override
@SideOnly(Side.CLIENT)
public Item getItem(World world, int x, int y, int z) {
return Item.getItemFromBlock(getOn());
}
@Override
protected ItemStack createStackedBlock(int e) {
return new ItemStack(getOn());
}
// Recursively add beam blocks, updating any that already exist with new incoming light directions
public static void propagateBeam(World world, int x, int y, int z, ForgeDirection dir, int distance, int meta) {
distance--;
if(distance <= 0)
return;
x += dir.offsetX;
y += dir.offsetY;
z += dir.offsetZ;
Block block = world.getBlock(x, y, z);
if(!block.isAir(world, x, y, z))
return;
if(!(block instanceof SpotlightBeam)) {
world.setBlock(x, y, z, ModBlocks.spotlight_beam, meta, 3);
}
// If we encounter an existing beam, add a new INCOMING direction to the
// metadata, and cancel propagation if something goes wrong
if (SpotlightBeam.setDirection(world, x, y, z, dir, true) == 0)
return;
propagateBeam(world, x, y, z, dir, distance, meta);
}
// Recursively delete beam blocks, if they aren't still illuminated from a different direction
public static void unpropagateBeam(World world, int x, int y, int z, ForgeDirection dir) {
x += dir.offsetX;
y += dir.offsetY;
z += dir.offsetZ;
Block block = world.getBlock(x, y, z);
if(!(block instanceof SpotlightBeam))
return;
// Remove the metadata associated with this direction
// If all directions are set to zero, delete the beam
if(SpotlightBeam.setDirection(world, x, y, z, dir, false) == 0) {
world.setBlockToAir(x, y, z);
}
unpropagateBeam(world, x, y, z, dir);
}
// Travels back through a beam to the source, and if found, repropagates the beam
public static void backPropagate(World world, int x, int y, int z, ForgeDirection dir, int meta) {
x -= dir.offsetX;
y -= dir.offsetY;
z -= dir.offsetZ;
Block block = world.getBlock(x, y, z);
if(block instanceof ISpotlight) {
ISpotlight spot = (ISpotlight) block;
propagateBeam(world, x, y, z, dir, spot.getBeamLength(), meta);
} else if(!(block instanceof SpotlightBeam)) {
return;
}
backPropagate(world, x, y, z, dir, meta);
}
protected Block getOff() {
if(this == ModBlocks.spotlight_incandescent) return ModBlocks.spotlight_incandescent_off;
if(this == ModBlocks.spotlight_fluoro) return ModBlocks.spotlight_fluoro_off;
if(this == ModBlocks.spotlight_halogen) return ModBlocks.spotlight_halogen_off;
return this;
}
protected Block getOn() {
if(this == ModBlocks.spotlight_incandescent_off) return ModBlocks.spotlight_incandescent;
if(this == ModBlocks.spotlight_fluoro_off) return ModBlocks.spotlight_fluoro;
if(this == ModBlocks.spotlight_halogen_off) return ModBlocks.spotlight_halogen;
return this;
}
@Override
public int getBeamLength() {
return this.beamLength;
}
@Override
public int transformMeta(int meta, int coordBaseMode) {
// +1 to set as broken, won't turn on until broken and replaced
int disabled = disableOnGeneration ? 1 : 0;
return (INBTBlockTransformable.transformMetaDeco(meta >> 1, coordBaseMode) << 1) + disabled;
}
@Override
public Block transformBlock(Block block) {
if(!disableOnGeneration) return block;
if(block == ModBlocks.spotlight_incandescent) return ModBlocks.spotlight_incandescent_off;
if(block == ModBlocks.spotlight_fluoro) return ModBlocks.spotlight_fluoro_off;
if(block == ModBlocks.spotlight_halogen) return ModBlocks.spotlight_halogen_off;
return block;
}
}