mirror of
https://github.com/HbmMods/Hbm-s-Nuclear-Tech-GIT.git
synced 2026-01-25 10:32:49 +00:00
427 lines
13 KiB
Java
427 lines
13 KiB
Java
package com.hbm.handler.neutron;
|
|
|
|
import com.hbm.blocks.machine.rbmk.RBMKBase;
|
|
import com.hbm.handler.neutron.NeutronNodeWorld.StreamWorld;
|
|
import com.hbm.handler.radiation.ChunkRadiationManager;
|
|
import com.hbm.tileentity.machine.rbmk.*;
|
|
import com.hbm.util.Compat;
|
|
import com.hbm.util.fauxpointtwelve.BlockPos;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.Iterator;
|
|
|
|
import net.minecraft.block.Block;
|
|
import net.minecraft.tileentity.TileEntity;
|
|
import net.minecraft.util.Vec3;
|
|
import net.minecraft.world.World;
|
|
import net.minecraftforge.common.util.ForgeDirection;
|
|
|
|
public class RBMKNeutronHandler {
|
|
|
|
static double moderatorEfficiency;
|
|
static double reflectorEfficiency;
|
|
static double absorberEfficiency;
|
|
static int columnHeight;
|
|
static int fluxRange;
|
|
|
|
public enum RBMKType {
|
|
ROD,
|
|
MODERATOR,
|
|
CONTROL_ROD,
|
|
REFLECTOR,
|
|
ABSORBER,
|
|
OUTGASSER,
|
|
OTHER // why do neutron calculations on them if they won't change anything?
|
|
}
|
|
|
|
private static TileEntity blockPosToTE(World worldObj, BlockPos pos) {
|
|
return Compat.getTileStandard(worldObj, pos.getX(), pos.getY(), pos.getZ());
|
|
}
|
|
|
|
public static RBMKNeutronNode makeNode(StreamWorld streamWorld, TileEntityRBMKBase tile) {
|
|
BlockPos pos = new BlockPos(tile);
|
|
RBMKNeutronNode node = (RBMKNeutronNode) streamWorld.getNode(pos);
|
|
return node != null ? node : new RBMKNeutronNode(tile, tile.getRBMKType(), tile.hasLid());
|
|
}
|
|
|
|
public static class RBMKNeutronNode extends NeutronNode {
|
|
|
|
public RBMKNeutronNode(TileEntityRBMKBase tile, RBMKType type, boolean hasLid) {
|
|
super(tile, NeutronStream.NeutronType.RBMK);
|
|
this.data.put("hasLid", hasLid);
|
|
this.data.put("type", type);
|
|
posInstance = new BlockPos(tile);
|
|
}
|
|
|
|
public void addLid() {
|
|
this.data.replace("hasLid", true);
|
|
}
|
|
|
|
public void removeLid() {
|
|
this.data.replace("hasLid", false);
|
|
}
|
|
|
|
protected BlockPos posInstance;
|
|
|
|
private int x;
|
|
private int z;
|
|
|
|
public Iterator<BlockPos> getReaSimNodes() {
|
|
|
|
x = -fluxRange;
|
|
z = -fluxRange;
|
|
|
|
return new Iterator<BlockPos>() {
|
|
@Override
|
|
public boolean hasNext() {
|
|
return (fluxRange + x) * (fluxRange * 2 + 1) + z + fluxRange + 1 < (fluxRange * 2 + 1) * (fluxRange * 2 + 1);
|
|
}
|
|
|
|
@Override
|
|
public BlockPos next() {
|
|
if(Math.pow(x, 2) + Math.pow(z, 2) <= fluxRange * fluxRange) {
|
|
z++;
|
|
if(z > fluxRange) {
|
|
z = -fluxRange;
|
|
x++;
|
|
}
|
|
return posInstance.mutate(tile.xCoord + x, tile.yCoord, tile.zCoord + z);
|
|
} else {
|
|
z++;
|
|
if(z > fluxRange) {
|
|
z = -fluxRange;
|
|
x++;
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
public List<BlockPos> checkNode(StreamWorld streamWorld) {
|
|
List<BlockPos> list = new ArrayList<>();
|
|
|
|
BlockPos pos = new BlockPos(this.tile);
|
|
|
|
RBMKNeutronStream[] streams = new RBMKNeutronStream[TileEntityRBMKRod.fluxDirs.length];
|
|
|
|
// Simulate streams coming out of the RBMK rod.
|
|
ForgeDirection[] fluxDirs = TileEntityRBMKRod.fluxDirs;
|
|
for(int i = 0; i < fluxDirs.length; i++) {
|
|
streams[i] = (new RBMKNeutronStream(this, Vec3.createVectorHelper(fluxDirs[i].offsetX, 0, fluxDirs[i].offsetZ)));
|
|
}
|
|
|
|
// Check if the rod should uncache nodes.
|
|
if(tile instanceof TileEntityRBMKRod && !(tile instanceof TileEntityRBMKRodReaSim)) {
|
|
TileEntityRBMKRod rod = (TileEntityRBMKRod) tile;
|
|
if(!rod.hasRod || rod.lastFluxQuantity == 0) {
|
|
|
|
for(RBMKNeutronStream stream : streams) {
|
|
for(NeutronNode node : stream.getNodes(streamWorld, false))
|
|
if(node != null)
|
|
list.add(new BlockPos(node.tile));
|
|
}
|
|
|
|
return list;
|
|
}
|
|
}
|
|
|
|
{
|
|
Iterator<BlockPos> reaSimNodes = getReaSimNodes();
|
|
|
|
// Check if the ReaSim rod should be culled from the cache due to no rod or no flux.
|
|
if(tile instanceof TileEntityRBMKRodReaSim) { // fuckkkkkkk
|
|
TileEntityRBMKRodReaSim rod = (TileEntityRBMKRodReaSim) tile;
|
|
if(!rod.hasRod || rod.lastFluxQuantity == 0) {
|
|
reaSimNodes.forEachRemaining((a) -> {
|
|
if(a != null)
|
|
list.add(a.clone()); // ae The RAM usage will be really high here but hopefully the GC can take care of it :pray:
|
|
});
|
|
return list;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check if non-rod nodes should be uncached... but now with ReaSim!
|
|
{ // Yeah, I don't want to contaminate the surrounding scope.
|
|
Iterator<BlockPos> reaSimNodes = getReaSimNodes();
|
|
|
|
boolean hasRod = false;
|
|
|
|
while(reaSimNodes.hasNext()) {
|
|
|
|
BlockPos nodePos = reaSimNodes.next();
|
|
|
|
if(nodePos == null)
|
|
continue;
|
|
|
|
NeutronNode node = streamWorld.getNode(nodePos);
|
|
|
|
if(node != null && node.tile instanceof TileEntityRBMKRod) {
|
|
|
|
TileEntityRBMKRod rod = (TileEntityRBMKRod) node.tile;
|
|
|
|
if(rod.hasRod && rod.lastFluxQuantity > 0) {
|
|
hasRod = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!hasRod) {
|
|
list.add(pos);
|
|
return list;
|
|
}
|
|
}
|
|
|
|
// Check if non-rod nodes should be uncached due to no rod in range.
|
|
for(RBMKNeutronStream stream : streams) {
|
|
|
|
NeutronNode[] nodes = stream.getNodes(streamWorld, false);
|
|
|
|
for(NeutronNode node : nodes) {
|
|
if(!(node == null) && node.tile instanceof TileEntityRBMKRod)
|
|
return list;
|
|
}
|
|
}
|
|
|
|
// If we get here, then no rods were found along this stream's path!
|
|
// This, most of the time, means we can just uncache all the nodes inside the stream's path.
|
|
// That other part of the time, streams will be crossing paths.
|
|
// This is fine though, we can just uncache them anyway and the streams later on (next tick) will recache them.
|
|
// /\ idk what this guy was on about but this is just plain wrong. /\
|
|
list.add(pos);
|
|
|
|
return list;
|
|
}
|
|
}
|
|
|
|
|
|
public static class RBMKNeutronStream extends NeutronStream {
|
|
|
|
public RBMKNeutronStream(NeutronNode origin, Vec3 vector) {
|
|
super(origin, vector);
|
|
}
|
|
|
|
public RBMKNeutronStream(NeutronNode origin, Vec3 vector, double flux, double ratio) {
|
|
super(origin, vector, flux, ratio, NeutronType.RBMK);
|
|
}
|
|
|
|
// Does NOT include the origin node
|
|
// USES THE CACHE!!!
|
|
public NeutronNode[] getNodes(StreamWorld streamWorld, boolean addNode) {
|
|
NeutronNode[] positions = new RBMKNeutronNode[fluxRange];
|
|
|
|
BlockPos pos = new BlockPos(origin.tile);
|
|
World world = origin.tile.getWorldObj();
|
|
|
|
for(int i = 1; i <= fluxRange; i++) {
|
|
int x = (int) Math.floor(0.5 + vector.xCoord * i);
|
|
int z = (int) Math.floor(0.5 + vector.zCoord * i);
|
|
|
|
pos.mutate(origin.tile.xCoord + x, origin.tile.yCoord, origin.tile.zCoord + z);
|
|
|
|
NeutronNode node = streamWorld.getNode(pos);
|
|
if(node instanceof RBMKNeutronNode) {
|
|
positions[i - 1] = node;
|
|
} else if(this.origin.tile.getBlockType() instanceof RBMKBase) {
|
|
TileEntity te = blockPosToTE(world, pos);
|
|
if(te instanceof TileEntityRBMKBase) {
|
|
TileEntityRBMKBase rbmkBase = (TileEntityRBMKBase) te;
|
|
node = makeNode(streamWorld, rbmkBase);
|
|
positions[i - 1] = node;
|
|
if(addNode) streamWorld.addNode(node);
|
|
}
|
|
}
|
|
}
|
|
return positions;
|
|
}
|
|
|
|
// The... small one? whatever it's still pretty big, runs the interaction for the stream.
|
|
public void runStreamInteraction(World worldObj, StreamWorld streamWorld) {
|
|
|
|
// do nothing if there's nothing to do lmao
|
|
if(fluxQuantity == 0D)
|
|
return;
|
|
|
|
BlockPos pos = new BlockPos(origin.tile);
|
|
|
|
TileEntityRBMKBase originTE;
|
|
|
|
NeutronNode node = streamWorld.getNode(pos);
|
|
if(node != null) {
|
|
originTE = (TileEntityRBMKBase) node.tile;
|
|
} else {
|
|
originTE = (TileEntityRBMKBase) blockPosToTE(worldObj, pos);
|
|
if(originTE == null) return; // Doesn't exist anymore!
|
|
|
|
streamWorld.addNode(new RBMKNeutronNode(originTE, originTE.getRBMKType(), originTE.hasLid()));
|
|
}
|
|
|
|
int moderatedCount = 0;
|
|
|
|
Iterator<BlockPos> iterator = getBlocks(fluxRange);
|
|
|
|
while(iterator.hasNext()) {
|
|
|
|
BlockPos targetPos = iterator.next();
|
|
|
|
if(fluxQuantity == 0D) // Whoops, used it all up!
|
|
return;
|
|
|
|
NeutronNode targetNode = streamWorld.getNode(targetPos);
|
|
if(targetNode == null) {
|
|
TileEntity te = blockPosToTE(worldObj, targetPos); // ok, maybe it didn't get added to the list somehow??
|
|
if(te instanceof TileEntityRBMKBase) {
|
|
targetNode = makeNode(streamWorld, (TileEntityRBMKBase) te);
|
|
streamWorld.addNode(targetNode); // whoops!
|
|
} else {
|
|
int hits = getHits(targetPos); // Get the amount of hits on blocks.
|
|
if(hits == columnHeight) // If stream is fully blocked.
|
|
return;
|
|
else if(hits > 0) { // If stream is partially blocked.
|
|
irradiateFromFlux(pos, hits);
|
|
fluxQuantity *= 1 - ((double) hits / columnHeight); // Inverse to get partial blocking by blocks.
|
|
continue;
|
|
} else { // Nothing hit!
|
|
irradiateFromFlux(pos, 0);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
RBMKType type = (RBMKType) targetNode.data.get("type");
|
|
|
|
if(type == RBMKType.OTHER || type == null) // pass right on by!
|
|
continue;
|
|
|
|
// we established earlier during `getNodes()` that they should all be RBMKBase TEs
|
|
// no issue with casting here!
|
|
TileEntityRBMKBase nodeTE = (TileEntityRBMKBase) targetNode.tile;
|
|
|
|
if(!(boolean) targetNode.data.get("hasLid"))
|
|
ChunkRadiationManager.proxy.incrementRad(worldObj, targetPos.getX(), targetPos.getY(), targetPos.getZ(), (float) (this.fluxQuantity * 0.05F));
|
|
|
|
if(type == RBMKType.MODERATOR || nodeTE.isModerated()) {
|
|
moderatedCount++;
|
|
moderateStream();
|
|
}
|
|
|
|
if(nodeTE instanceof IRBMKFluxReceiver) {
|
|
IRBMKFluxReceiver column = (IRBMKFluxReceiver) nodeTE;
|
|
|
|
if(type == RBMKType.ROD) {
|
|
TileEntityRBMKRod rod = (TileEntityRBMKRod) column;
|
|
|
|
if(rod.hasRod) {
|
|
rod.receiveFlux(this);
|
|
return;
|
|
}
|
|
|
|
} else if(type == RBMKType.OUTGASSER) {
|
|
TileEntityRBMKOutgasser outgasser = ((TileEntityRBMKOutgasser) column);
|
|
|
|
if(outgasser.canProcess()) {
|
|
column.receiveFlux(this);
|
|
return;
|
|
}
|
|
}
|
|
|
|
} else if(type == RBMKType.CONTROL_ROD) {
|
|
TileEntityRBMKControl rod = (TileEntityRBMKControl) nodeTE;
|
|
|
|
if(rod.level > 0.0D) {
|
|
|
|
this.fluxQuantity *= rod.getMult();
|
|
continue;
|
|
}
|
|
return;
|
|
} else if(type == RBMKType.REFLECTOR) {
|
|
|
|
if(((TileEntityRBMKBase) this.origin.tile).isModerated())
|
|
moderatedCount++;
|
|
|
|
if(this.fluxRatio > 0 && moderatedCount > 0)
|
|
for(int i = 0; i < moderatedCount; i++)
|
|
moderateStream();
|
|
|
|
if(reflectorEfficiency != 1.0D) {
|
|
this.fluxQuantity *= reflectorEfficiency;
|
|
continue;
|
|
}
|
|
|
|
((TileEntityRBMKRod) originTE).receiveFlux(this);
|
|
return;
|
|
} else if(type == RBMKType.ABSORBER) {
|
|
if(absorberEfficiency == 1)
|
|
return;
|
|
|
|
this.fluxQuantity *= absorberEfficiency;
|
|
}
|
|
}
|
|
|
|
NeutronNode[] nodes = getNodes(streamWorld, true);
|
|
|
|
NeutronNode lastNode = nodes[(nodes.length - 1)];
|
|
|
|
if(lastNode == null) { // This implies that there was *no* last node, meaning either way it was never caught.
|
|
// There is really no good way to figure out where exactly it should irradiate, so just irradiate at the origin tile.
|
|
irradiateFromFlux(new BlockPos(origin.tile.xCoord + this.vector.xCoord, origin.tile.yCoord, origin.tile.zCoord + this.vector.zCoord));
|
|
return;
|
|
}
|
|
|
|
RBMKType lastNodeType = (RBMKType) lastNode.data.get("type");
|
|
|
|
if(lastNodeType == RBMKType.CONTROL_ROD) {
|
|
TileEntityRBMKControl rod = (TileEntityRBMKControl) lastNode.tile;
|
|
if(rod.getMult() > 0.0D) {
|
|
this.fluxQuantity *= rod.getMult();
|
|
BlockPos posAfter = new BlockPos(lastNode.tile.xCoord + this.vector.xCoord, lastNode.tile.yCoord, lastNode.tile.zCoord + this.vector.zCoord);
|
|
|
|
// The below code checks if the block after the control rod is actually a block or if it's an RBMK rod.
|
|
// Resolves GitHub issue #1933.
|
|
if(NeutronNodeWorld.getNode(worldObj, pos) == null) {
|
|
TileEntity te = blockPosToTE(worldObj, posAfter);
|
|
if (te instanceof TileEntityRBMKBase) {
|
|
RBMKNeutronNode nodeAfter = makeNode(NeutronNodeWorld.getOrAddWorld(worldObj), (TileEntityRBMKBase) te);
|
|
NeutronNodeWorld.getOrAddWorld(worldObj).addNode(nodeAfter);
|
|
} else {
|
|
irradiateFromFlux(posAfter); // I'm so mad about this...
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public int getHits(BlockPos pos) {
|
|
int hits = 0;
|
|
|
|
for(int h = 0; h < columnHeight; h++) {
|
|
// holy fucking shit
|
|
// I have had this one line cause me like tens of problems
|
|
// I FUCKING HATE THIS
|
|
// total count of bugs fixed attributed to this function: 14
|
|
Block block = origin.tile.getWorldObj().getBlock(pos.getX(), pos.getY() + h, pos.getZ());
|
|
if(block.isOpaqueCube())
|
|
hits += 1;
|
|
}
|
|
|
|
return hits;
|
|
}
|
|
|
|
public void irradiateFromFlux(BlockPos pos) {
|
|
ChunkRadiationManager.proxy.incrementRad(origin.tile.getWorldObj(), pos.getX(), pos.getY(), pos.getZ(), (float) (fluxQuantity * 0.05F * (1 - (double) getHits(pos) / columnHeight)));
|
|
}
|
|
|
|
public void irradiateFromFlux(BlockPos pos, int hits) {
|
|
ChunkRadiationManager.proxy.incrementRad(origin.tile.getWorldObj(), pos.getX(), pos.getY(), pos.getZ(), (float) (fluxQuantity * 0.05F * (1 - (double) hits / columnHeight)));
|
|
}
|
|
|
|
public void moderateStream() {
|
|
fluxRatio *= (1 - moderatorEfficiency);
|
|
}
|
|
|
|
}
|
|
}
|