From 5ac837125c76ecf0fb73eea7be235b4139b98dbc Mon Sep 17 00:00:00 2001 From: HbmMods Date: Wed, 22 Jul 2026 15:05:31 +0200 Subject: [PATCH] now technically operational --- .../blocks/machine/pile/BlockPileBrick.java | 2 +- .../blocks/machine/pile/BlockPileDevice.java | 30 +- .../com/hbm/items/machine/ItemPileRodMK2.java | 38 ++- .../machine/pile/TileEntityPileCore.java | 305 +++++++++++++++--- .../machine/pile/TileEntityPileVent.java | 2 +- 5 files changed, 320 insertions(+), 57 deletions(-) diff --git a/src/main/java/com/hbm/blocks/machine/pile/BlockPileBrick.java b/src/main/java/com/hbm/blocks/machine/pile/BlockPileBrick.java index f309e03ba..955b8ac53 100644 --- a/src/main/java/com/hbm/blocks/machine/pile/BlockPileBrick.java +++ b/src/main/java/com/hbm/blocks/machine/pile/BlockPileBrick.java @@ -127,7 +127,7 @@ public class BlockPileBrick extends Block implements IToolable { world.setBlock(iX, iY, iZ, ModBlocks.pile_block, BlockPile.META_CORE, 3); TileEntityPileCore core = (TileEntityPileCore) world.getTileEntity(iX, iY, iZ); core.orientation = PileOrientation.getOrientation(dir); - core.setupSize(posHeight + negHeight + 1, left + right + 1, depth + 1); + core.setupSize(posHeight + negHeight + 1, left, right, depth + 1); } else { int edgeCount = 0; if(h == -negHeight || h == posHeight) edgeCount++; diff --git a/src/main/java/com/hbm/blocks/machine/pile/BlockPileDevice.java b/src/main/java/com/hbm/blocks/machine/pile/BlockPileDevice.java index d362e0c7e..b59090bce 100644 --- a/src/main/java/com/hbm/blocks/machine/pile/BlockPileDevice.java +++ b/src/main/java/com/hbm/blocks/machine/pile/BlockPileDevice.java @@ -6,14 +6,17 @@ import java.util.List; import com.hbm.blocks.IBlockMulti; import com.hbm.blocks.ILookOverlay; import com.hbm.blocks.ITooltipProvider; +import com.hbm.blocks.ModBlocks; import com.hbm.main.NTMSounds; import com.hbm.tileentity.machine.pile.TileEntityPileControl; import com.hbm.tileentity.machine.pile.TileEntityPileLoader; import com.hbm.tileentity.machine.pile.TileEntityPileVent; import com.hbm.util.i18n.I18nUtil; +import api.hbm.block.IToolable; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; +import net.minecraft.block.Block; import net.minecraft.block.BlockContainer; import net.minecraft.block.material.Material; import net.minecraft.creativetab.CreativeTabs; @@ -28,7 +31,7 @@ import net.minecraft.world.World; import net.minecraftforge.client.event.RenderGameOverlayEvent.Pre; import net.minecraftforge.common.util.ForgeDirection; -public class BlockPileDevice extends BlockContainer implements IBlockMulti, ILookOverlay, ITooltipProvider { +public class BlockPileDevice extends BlockContainer implements IBlockMulti, ILookOverlay, ITooltipProvider, IToolable { public static final int ITEM_META_LOADER = 0; public static final int ITEM_META_VENT = 1; @@ -79,6 +82,8 @@ public class BlockPileDevice extends BlockContainer implements IBlockMulti, ILoo @Override public boolean onBlockActivated(World world, int x, int y, int z, EntityPlayer player, int side, float hitX, float hitY, float hitZ) { + if(player.isSneaking()) return false; + int meta = world.getBlockMetadata(x, y, z); meta -= meta % 4; @@ -184,4 +189,27 @@ public class BlockPileDevice extends BlockContainer implements IBlockMulti, ILoo if(meta == ITEM_META_CONTROL) return this.getUnlocalizedName() + ".control"; return this.getUnlocalizedName(); } + + @Override + public boolean onScrew(World world, EntityPlayer player, int x, int y, int z, int side, float fX, float fY, float fZ, ToolType tool) { + + int meta = world.getBlockMetadata(x, y, z); + + if(meta >= BLOCK_META_CONTROL) { + y -= 1; + side = 1; + } else { + ForgeDirection dir = ForgeDirection.getOrientation(meta % 4 + 2); + x -= dir.offsetX; + z -= dir.offsetZ; + side = dir.ordinal(); + } + + Block b = world.getBlock(x, y, z); + if(b == ModBlocks.pile_block) { + return ((BlockPile) b).onScrew(world, player, x, y, z, side, fX, fY, fZ, tool); + } + + return false; + } } diff --git a/src/main/java/com/hbm/items/machine/ItemPileRodMK2.java b/src/main/java/com/hbm/items/machine/ItemPileRodMK2.java index 2fd4fc344..7a0542ccc 100644 --- a/src/main/java/com/hbm/items/machine/ItemPileRodMK2.java +++ b/src/main/java/com/hbm/items/machine/ItemPileRodMK2.java @@ -29,18 +29,19 @@ public class ItemPileRodMK2 extends ItemEnumMulti { } public static enum EnumPileRod { - RA226BE(1D), - PO210BE(1D), - ZR( 0D, 0D, 0D), - NU( 1D, 1_000D, 1D), - RGP( 1D, 1_000D, 1D), - PU239( 1D, 1_000D, 1D), - WASTE( 1D, 1_000D, 1D); + /* 0 */ RA226BE(1D), + /* 1 */ PO210BE(1D), + /* 2 */ ZR( 0D, 0D, 0D, 2), + /* 3 */ NU( 1D, 1_000D, 1D, 4), + /* 4 */ PU239( 1D, 1_000D, 1D, 5), + /* 5 */ RGP( 1D, 1_000D, 1D, 6), + /* 6 */ WASTE( 1D, 0D, 1D, 6); public double reactionMult = 1.0D; public double life = 1_000D; - public double heatMult = 1.0D; + public double heatMult = 0.0D; public double neutronSource = 0D; + public int turnsInto; private EnumPileRod(double neutronSource) { this.neutronSource = neutronSource; @@ -49,10 +50,11 @@ public class ItemPileRodMK2 extends ItemEnumMulti { this.heatMult = 0; } - private EnumPileRod(double reaction, double life, double heat) { + private EnumPileRod(double reaction, double life, double heat, int turnsInto) { this.reactionMult = reaction; this.life = life; this.heatMult = heat; + this.turnsInto = turnsInto; } } @@ -87,4 +89,22 @@ public class ItemPileRodMK2 extends ItemEnumMulti { } return outFlux; } + + public static double getHeatPerNeutron(ItemStack stack) { + EnumPileRod rod = EnumUtil.grabEnumSafely(EnumPileRod.class, stack.getItemDamage()); + return rod.heatMult; + } + + public static ItemStack react(ItemStack stack, double inFlux) { + EnumPileRod rod = EnumUtil.grabEnumSafely(EnumPileRod.class, stack.getItemDamage()); + if(rod.life <= 0) return stack; + double dep = getDepletion(stack) + inFlux; + + if(dep < rod.life) { + setDepletion(stack, dep); + return stack; + } else { + return new ItemStack(stack.getItem(), 1, rod.turnsInto); + } + } } diff --git a/src/main/java/com/hbm/tileentity/machine/pile/TileEntityPileCore.java b/src/main/java/com/hbm/tileentity/machine/pile/TileEntityPileCore.java index a92e97f8d..b47823138 100644 --- a/src/main/java/com/hbm/tileentity/machine/pile/TileEntityPileCore.java +++ b/src/main/java/com/hbm/tileentity/machine/pile/TileEntityPileCore.java @@ -8,18 +8,25 @@ import com.hbm.blocks.ModBlocks; import com.hbm.blocks.machine.MachinePWRController; import com.hbm.blocks.machine.pile.BlockPile; import com.hbm.interfaces.NotableComments; +import com.hbm.items.machine.ItemPileRodMK2; +import com.hbm.items.machine.ItemPileRodMK2.EnumPileRod; import com.hbm.main.NTMSounds; import com.hbm.packet.PacketDispatcher; import com.hbm.packet.toclient.AuxParticlePacketNT; +import com.hbm.packet.toclient.PlayerInformPacket; import com.hbm.tileentity.TileEntityTickingBase; +import com.hbm.util.EnumUtil; +import com.hbm.util.fauxpointtwelve.BlockPos; import com.hbm.util.fauxpointtwelve.DirPos; import cpw.mods.fml.common.network.NetworkRegistry.TargetPoint; import net.minecraft.entity.item.EntityItem; import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; +import net.minecraft.util.MathHelper; import net.minecraftforge.common.util.ForgeDirection; @NotableComments @@ -56,12 +63,23 @@ public class TileEntityPileCore extends TileEntityTickingBase { public List ventilationChannels = new ArrayList(); public List controlChannels = new ArrayList(); + public int left; + public int right; + /** + * Segments are fuel and control channels grouped by vertical slices viewed from the front, this makes the simulation part easier. + */ + public PileSegment[] segments = new PileSegment[0]; + @Override public void readFromNBT(NBTTagCompound nbt) { super.readFromNBT(nbt); height = nbt.getInteger("height"); width = nbt.getInteger("width"); depth = nbt.getInteger("depth"); + left = nbt.getInteger("left"); + right = nbt.getInteger("right"); + + segments = new PileSegment[width]; int fuelCount = nbt.getByte("fc"); int ventCount = nbt.getByte("vc"); @@ -74,6 +92,30 @@ public class TileEntityPileCore extends TileEntityTickingBase { for(int i = 0; i < fuelCount; i++) fuelChannels.add(readChannelFromNBT(nbt, "f" + i)); for(int i = 0; i < ventCount; i++) ventilationChannels.add(readChannelFromNBT(nbt, "v" + i)); for(int i = 0; i < contCount; i++) controlChannels.add(readChannelFromNBT(nbt, "c" + i)); + + this.recalculateSegments(); + } + + @Override + public void writeToNBT(NBTTagCompound nbt) { + super.writeToNBT(nbt); + nbt.setInteger("height", height); + nbt.setInteger("width", width); + nbt.setInteger("depth", depth); + nbt.setInteger("left", left); + nbt.setInteger("right", right); + + int fuelCount = fuelChannels.size(); + int ventCount = ventilationChannels.size(); + int contCount = controlChannels.size(); + + nbt.setByte("fc", (byte) fuelCount); + nbt.setByte("vc", (byte) ventCount); + nbt.setByte("cc", (byte) contCount); + + for(int i = 0; i < fuelCount; i++) fuelChannels.get(i).writeChannelToNBT(nbt, "f" + i); + for(int i = 0; i < ventCount; i++) ventilationChannels.get(i).writeChannelToNBT(nbt, "v" + i); + for(int i = 0; i < contCount; i++) controlChannels.get(i).writeChannelToNBT(nbt, "c" + i); } public PileChannel getFuelChannel(int x, int y, int z) { return getChannel(x, y, z, fuelChannels); } @@ -93,30 +135,13 @@ public class TileEntityPileCore extends TileEntityTickingBase { return list.indexOf(chan); // more reliable when channels change and not that big of a deal because we have lists in single digit length } - @Override - public void writeToNBT(NBTTagCompound nbt) { - super.writeToNBT(nbt); - nbt.setInteger("height", height); - nbt.setInteger("width", width); - nbt.setInteger("depth", depth); - - int fuelCount = fuelChannels.size(); - int ventCount = ventilationChannels.size(); - int contCount = controlChannels.size(); - - nbt.setByte("fc", (byte) fuelCount); - nbt.setByte("vc", (byte) ventCount); - nbt.setByte("cc", (byte) contCount); - - for(int i = 0; i < fuelCount; i++) fuelChannels.get(i).writeChannelToNBT(nbt, "f" + i); - for(int i = 0; i < ventCount; i++) ventilationChannels.get(i).writeChannelToNBT(nbt, "v" + i); - for(int i = 0; i < contCount; i++) controlChannels.get(i).writeChannelToNBT(nbt, "c" + i); - } - - public TileEntityPileCore setupSize(int h, int w, int d) { + public TileEntityPileCore setupSize(int h, int l, int r, int d) { this.height = h; - this.width = w; + this.width = l + 1 + r; this.depth = d; + this.left = l; + this.right = r; + this.segments = new PileSegment[width]; return this; } @@ -148,6 +173,7 @@ public class TileEntityPileCore extends TileEntityTickingBase { worldObj.setBlockMetadataWithNotify(iX, iY, iZ, BlockPile.META_DUMMY, 3); } worldObj.playSoundEffect(x + 0.5, y + 0.5, z + 0.5, NTMSounds.VANILLA_PLINK, 1F, 0.75F); + recalculateSegments(); return true; } } @@ -190,6 +216,8 @@ public class TileEntityPileCore extends TileEntityTickingBase { worldObj.playSoundEffect(x + 0.5, y + 0.5, z + 0.5, NTMSounds.VANILLA_PLINK, 1F, 1.25F); this.markChanged(); + recalculateSegments(); + return true; } @@ -198,37 +226,168 @@ public class TileEntityPileCore extends TileEntityTickingBase { if(!worldObj.isRemote) { - for(PileChannel chan : this.ventilationChannels) { - if(chan.air <= 0) continue; - - int toUse = (int) Math.ceil(chan.air * 5D / 1_000); - chan.air -= toUse; - - if(worldObj.getTotalWorldTime() % 3 != 0) continue; - - double x = chan.entry.getX() + 0.5 + chan.entry.getDir().offsetX * (this.width - 0.375); - double y = chan.entry.getY() + 0.5; - double z = chan.entry.getZ() + 0.5 + chan.entry.getDir().offsetZ * (this.width - 0.375); - Random rand = worldObj.rand; - - NBTTagCompound data = new NBTTagCompound(); - data.setString("type", "tower"); - data.setFloat("lift", 1F); - data.setFloat("base", 0.125F + rand.nextFloat() * 0.125F); - data.setFloat("max", 1F); - data.setFloat("strafe", 0.0025F); - data.setBoolean("noWind", true); - data.setInteger("life", 20 + worldObj.rand.nextInt(30)); - data.setInteger("color", 0xa0a0a0); - PacketDispatcher.wrapper.sendToAllAround(new AuxParticlePacketNT(data, x, y, z), new TargetPoint(worldObj.provider.dimensionId, xCoord, yCoord, zCoord, 150)); + this.runSimulation(); + this.handleVentilation(); + } + } + + protected void runSimulation() { + + // reaction + for(PileChannel chan : this.fuelChannels) { + if(chan.length <= 0) continue; + double producedNeutrons = 0; + double latestDepletion = 0D; + + for(int i = 0; i < chan.rods.length; i++) { + ItemStack stack = chan.rods[i]; + if(stack != null && stack.getItem() instanceof ItemPileRodMK2) { + double neut = ItemPileRodMK2.getReactivity(stack, chan.incomingNeutrons / chan.length); + producedNeutrons += neut; + chan.heat += neut * ItemPileRodMK2.getHeatPerNeutron(stack); + chan.rods[i] = ItemPileRodMK2.react(stack, neut); + EnumPileRod rod = EnumUtil.grabEnumSafely(EnumPileRod.class, stack.getItemDamage()); + double life = rod.life < 1 ? 1 : rod.life; + latestDepletion = ItemPileRodMK2.getDepletion(chan.rods[i]) / life; + } + } + chan.outgoingNeutrons = producedNeutrons; + chan.incomingNeutrons = 0; + + /// DEBUG /// + int hash = BlockPos.getIdentity(xCoord, yCoord, zCoord); + + for(Object o : worldObj.playerEntities) { + if(o instanceof EntityPlayerMP) { + EntityPlayerMP player = (EntityPlayerMP) o; + int index = this.getFuelChannelNum(chan); + PacketDispatcher.wrapper.sendTo(new PlayerInformPacket("#" + index + " " + Math.round(chan.heat) + "TU " + Math.round(chan.outgoingNeutrons) + "n " + Math.round(latestDepletion * 100) + "%", hash + index), player); + } + } + /// DEBUG /// + } + + // intra-segment propagation + for(PileSegment seg : this.segments) { + if(seg == null || seg.segType != seg.segType.FUEL) continue; + double outgoing = 0D; + for(PileChannel chan : seg.channels) outgoing += chan.outgoingNeutrons; + for(PileChannel chan : seg.channels) chan.incomingNeutrons += outgoing; + } + + // inter-segment propagation + for(int i = 1; i < this.segments.length - 1; i++) { // bound chosen because edges can't have channels + PileSegment seg = this.segments[i]; + if(seg == null || seg.segType != seg.segType.FUEL) continue; + double outgoing = 0D; + for(PileChannel chan : seg.channels) outgoing += chan.outgoingNeutrons; + + double mult = 1D; + for(int j = i - 1; j >= 1; j--) { // center to left + PileSegment neighbor = this.segments[j]; + if(neighbor == null) continue; + mult *= neighbor.getNeutronMult(this); + if(neighbor.segType == neighbor.segType.FUEL) { + for(PileChannel chan : neighbor.channels) chan.incomingNeutrons += outgoing * mult; + } + } + + mult = 1D; + for(int j = i + 1; j < this.segments.length - 1; j++) { // center to right + PileSegment neighbor = this.segments[j]; + if(neighbor == null) continue; + mult *= neighbor.getNeutronMult(this); + if(neighbor.segType == neighbor.segType.FUEL) { + for(PileChannel chan : neighbor.channels) chan.incomingNeutrons += outgoing * mult; + } } } } + protected void handleVentilation() { + + for(PileChannel chan : this.ventilationChannels) { + if(chan.air <= 0) continue; + + double airCap = (double) chan.air / (double) chan.MAX_AIR; + + for(PileChannel fuel : this.fuelChannels) { + if(Math.abs(fuel.entry.getY() - chan.entry.getY()) <= 1) { + fuel.heat *= (1D - airCap * 0.5D); // channel can hold up to 1000mB, turning into a 0.5 heat mult for connected fuel channels + } + } + + int toUse = (int) Math.ceil(airCap * 5D); + chan.air -= toUse; + + if(worldObj.getTotalWorldTime() % 3 != 0) continue; + + double x = chan.entry.getX() + 0.5 + chan.entry.getDir().offsetX * (this.width - 0.375); + double y = chan.entry.getY() + 0.5; + double z = chan.entry.getZ() + 0.5 + chan.entry.getDir().offsetZ * (this.width - 0.375); + Random rand = worldObj.rand; + + NBTTagCompound data = new NBTTagCompound(); + data.setString("type", "tower"); + data.setFloat("lift", 1F); + data.setFloat("base", (0.125F + rand.nextFloat() * 0.125F) * (float) airCap); + data.setFloat("max", 1F * (float) airCap); + data.setFloat("strafe", 0.0025F); + data.setBoolean("noWind", true); + data.setInteger("life", 20 + worldObj.rand.nextInt(30)); + data.setInteger("color", 0xa0a0a0); + PacketDispatcher.wrapper.sendToAllAround(new AuxParticlePacketNT(data, x, y, z), new TargetPoint(worldObj.provider.dimensionId, xCoord, yCoord, zCoord, 150)); + } + } + + protected void recalculateSegments() { + this.segments = new PileSegment[width]; + + for(PileChannel chan : fuelChannels) { + int index = getChannelVerticalIndex(chan); + if(index < 0 || index >= this.segments.length) continue; + + if(this.segments[index] == null) { + this.segments[index] = new PileSegment(PileChannelType.FUEL).addChan(chan); + } else { + if(this.segments[index].segType == PileChannelType.FUEL) this.segments[index].addChan(chan); + } + } + + for(PileChannel chan : controlChannels) { + int index = getChannelVerticalIndex(chan); + if(index < 0 || index >= this.segments.length) continue; + + if(this.segments[index] == null) { + this.segments[index] = new PileSegment(PileChannelType.CONTROL).addChan(chan); + } else { + if(this.segments[index].segType == PileChannelType.CONTROL) this.segments[index].addChan(chan); + } + } + } + + /** + * Splits the entire pile into vertical slices, left to right, and returns the index of which slice a channel belongs to. + * Since this left-to-right approach assumes the front of the pile, this only is used for fuel channels and control rods. + */ + protected int getChannelVerticalIndex(PileChannel chan) { + DirPos pos = chan.entry; + ForgeDirection right = pos.getDir().getRotation(ForgeDirection.UP); + int deltaX = (pos.getX() - xCoord) * right.offsetX; + int deltaZ = (pos.getZ() - zCoord) * right.offsetZ; + int abs = deltaX == 0 ? deltaZ : deltaX; + int index = abs + this.left; + return index; + } + public void destroy() { worldObj.setBlock(xCoord, yCoord, zCoord, ModBlocks.pile_brick); } + /** + * The orientation of the pile, based on the direction the core is pointing in. + * Channels that follow the orientation are fuel, perpendicular ones are for ventilation. + */ public static enum PileOrientation { NORTH_SOUTH, EAST_WEST, @@ -241,6 +400,10 @@ public class TileEntityPileCore extends TileEntityTickingBase { } } + /** + * A channel, represents any type, therefore has the data of all three types. + * Could have been an abstract class + three child classes but then we'd just be overcomplicating it. + */ public class PileChannel { // originally called "PileHole", however i didn't want it sounding too sexual public final DirPos entry; // first channel block plus direction it faces inward @@ -249,6 +412,9 @@ public class TileEntityPileCore extends TileEntityTickingBase { public final ItemStack[] rods; public double heat = 0D; + public double outgoingNeutrons = 0D; + public double incomingNeutrons = 0D; + public static final int MAX_AIR = 1_000; public int air; public double control = 1D; // pulled out by default, unlike me who never pulls out @@ -286,6 +452,17 @@ public class TileEntityPileCore extends TileEntityTickingBase { } } nbt.setTag("items", list); + + nbt.setDouble("heat", heat); + nbt.setDouble("neutrons", incomingNeutrons); + } + + if(type == type.VENTILATION) { + nbt.setInteger("air", air); + } + + if(type == type.CONTROL) { + nbt.setDouble("control", control); } } @@ -335,6 +512,17 @@ public class TileEntityPileCore extends TileEntityTickingBase { chan.rods[b0] = ItemStack.loadItemStackFromNBT(nbt1); } } + + chan.heat = nbt.getDouble("heat"); + chan.incomingNeutrons = nbt.getDouble("neutrons"); + } + + if(chan.type == chan.type.VENTILATION) { + chan.air = nbt.getInteger("air"); + } + + if(chan.type == chan.type.CONTROL) { + chan.control = nbt.getDouble("control"); } return chan; @@ -354,4 +542,31 @@ public class TileEntityPileCore extends TileEntityTickingBase { } } } + + /** A vertical "slice" of the front of the reactor, can include multiple channels per slice, + * due to reactor geometry constraints, all channels are of the same type. */ + public static class PileSegment { + + public List channels = new ArrayList(); + public final PileChannelType segType; + + public PileSegment(PileChannelType segType) { + this.segType = segType; + } + + public PileSegment addChan(PileChannel chan) { + this.channels.add(chan); + return this; + } + + public double getNeutronMult(TileEntityPileCore core) { + if(this.segType != this.segType.CONTROL) return 1D; + int size = core.depth - 1; // minus one to make the calculation easier, a xixix pattern becomes xixi which means the rods cover 50% + if(size < 3) return 0D; // reactors this small can't exist, nor can they have control rods + double total = 0D; + + for(PileChannel chan : channels) total += chan.control; + return MathHelper.clamp_double(total / size, 0D, 0.5D); + } + } } diff --git a/src/main/java/com/hbm/tileentity/machine/pile/TileEntityPileVent.java b/src/main/java/com/hbm/tileentity/machine/pile/TileEntityPileVent.java index 149c4bec4..02b2b702b 100644 --- a/src/main/java/com/hbm/tileentity/machine/pile/TileEntityPileVent.java +++ b/src/main/java/com/hbm/tileentity/machine/pile/TileEntityPileVent.java @@ -62,7 +62,7 @@ public class TileEntityPileVent extends TileEntityPileDeviceBase implements IFlu if(ventChan != null) { this.chanNum = core.getVentilationChannelNum(ventChan); - int toFill = BobMathUtil.min(compair.getFill(), 1_000 - ventChan.air); + int toFill = BobMathUtil.min(compair.getFill(), ventChan.MAX_AIR - ventChan.air); ventChan.air += toFill; this.compair.setFill(this.compair.getFill() - toFill); this.isActive = toFill > 0;