diff options
Diffstat (limited to 'src/main/java/Ic2ExpReactorPlanner')
14 files changed, 2967 insertions, 0 deletions
diff --git a/src/main/java/Ic2ExpReactorPlanner/AutomationSimulator.java b/src/main/java/Ic2ExpReactorPlanner/AutomationSimulator.java new file mode 100644 index 0000000000..729642e837 --- /dev/null +++ b/src/main/java/Ic2ExpReactorPlanner/AutomationSimulator.java @@ -0,0 +1,691 @@ +package Ic2ExpReactorPlanner; + +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.List; + +import Ic2ExpReactorPlanner.components.ReactorItem; +import gtPlusPlus.api.objects.Logger; +import gtPlusPlus.core.util.math.MathUtils; +import gtPlusPlus.xmod.gregtech.common.tileentities.misc.GT_TileEntity_ComputerCube; + +/** + * + * @author Brian McCloud + */ +public class AutomationSimulator { + + private final Reactor reactor; + + private final ArrayList<String> output; + + private final GT_TileEntity_ComputerCube mReactor; + + private final boolean[][] needsCooldown = new boolean[6][9]; + + private final int initialHeat; + + private double minEUoutput = Double.MAX_VALUE; + private double maxEUoutput = 0.0; + private double minHeatOutput = Double.MAX_VALUE; + private double maxHeatOutput = 0.0; + + private final int onPulseDuration; + private final int offPulseDuration; + private final int clockPeriod; + private final int suspendTemp; + private final int resumeTemp; + private final int maxSimulationTicks; + + private boolean reachedBelow50; + private boolean reachedBurn; + private boolean reachedEvaporate; + private boolean reachedHurt; + private boolean reachedLava; + private boolean reachedExplode; + + private boolean allFuelRodsDepleted = false; + private boolean componentsIntact = true; + private boolean anyRodsDepleted = false; + + private int activeTime = 0; + private int inactiveTime = 0; + private int currentActiveTime = 0; + private int minActiveTime = Integer.MAX_VALUE; + private int maxActiveTime = 0; + private int currentInactiveTime = 0; + private int minInactiveTime = Integer.MAX_VALUE; + private int maxInactiveTime = 0; + + private double totalHullHeating = 0; + private double totalComponentHeating = 0; + private double totalHullCooling = 0; + private double totalVentCooling = 0; + + private boolean showHeatingCoolingCalled = false; + + private boolean active = true; + + private int pauseTimer = 0; + + private int redstoneUsed = 0; + + private int lapisUsed = 0; + + + private boolean completed = false; + + private boolean mRunning = false; + private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#,##0.##"); + + private final SimulationData data = new SimulationData(); + public SimulationData getData() { + if (completed) { + return data; + } + return null; + } + + public AutomationSimulator(final Reactor reactor, final ArrayList<String> output2, final GT_TileEntity_ComputerCube aTile) { + this.reactor = reactor; + this.output = output2; + this.mReactor = aTile; + this.initialHeat = (int) reactor.getCurrentHeat(); + this.onPulseDuration = reactor.getOnPulse(); + this.offPulseDuration = reactor.getOffPulse(); + this.clockPeriod = onPulseDuration + offPulseDuration; + this.suspendTemp = reactor.getSuspendTemp(); + this.resumeTemp = reactor.getResumeTemp(); + this.maxSimulationTicks = reactor.getMaxSimulationTicks(); + } + + public void process() { + + mRunning = true; + completed = false; + long startTime = System.nanoTime(); + int reactorTicks = 0; + int cooldownTicks = 0; + int totalRodCount = 0; + + publish(""); // NOI18N + publish("Simulation.Started"); + reactor.setCurrentHeat(initialHeat); + reactor.clearVentedHeat(); + double minReactorHeat = initialHeat; + double maxReactorHeat = initialHeat; + reachedBelow50 = false; + reachedBurn = initialHeat >= 0.4 * reactor.getMaxHeat(); + reachedEvaporate = initialHeat >= 0.5 * reactor.getMaxHeat(); + reachedHurt = initialHeat >= 0.7 * reactor.getMaxHeat(); + reachedLava = initialHeat >= 0.85 * reactor.getMaxHeat(); + reachedExplode = false; + for (int row = 0; row < 6; row++) { + for (int col = 0; col < 9; col++) { + ReactorItem component = reactor.getComponentAt(row, col); + if (component != null) { + component.clearCurrentHeat(); + component.clearDamage(); + totalRodCount += component.getRodCount(); + } + publish(String.format("R%dC%d:0xC0C0C0", row, col)); // NOI18N + } + } + data.totalRodCount = totalRodCount; + double lastEUoutput = 0.0; + double totalEUoutput = 0.0; + double lastHeatOutput = 0.0; + double totalHeatOutput = 0.0; + double maxGeneratedHeat = 0.0; + double explosionPower = 10.0; + allFuelRodsDepleted = false; + componentsIntact = true; + anyRodsDepleted = false; + Logger.INFO("Reactor Current Heat: "+reactor.getCurrentHeat()); + Logger.INFO("Reactor Max Heat: "+reactor.getMaxHeat()); + Logger.INFO("Least EU Output: "+lastEUoutput); + Logger.INFO("Least Heat Output: "+lastHeatOutput); + Logger.INFO("Reactor Max Ticks: "+maxSimulationTicks); + Logger.INFO("All Fuel Depleted: "+allFuelRodsDepleted); + Logger.INFO("Running: "+isRunning()); + Logger.INFO("Stopped: "+hasStopped()); + while (reactor.getCurrentHeat() < reactor.getMaxHeat() && (!allFuelRodsDepleted || lastEUoutput > 0 || lastHeatOutput > 0) && reactorTicks < maxSimulationTicks && isRunning()) { + //Logger.INFO("Reactor Tick: "+reactorTicks); + reactorTicks++; + reactor.clearEUOutput(); + reactor.clearVentedHeat(); + for (int row = 0; row < 6; row++) { + for (int col = 0; col < 9; col++) { + ReactorItem component = reactor.getComponentAt(row, col); + if (component != null) { + component.preReactorTick(); + } + } + } + if (active) { + allFuelRodsDepleted = true; // assume rods depleted until one is + // found that isn't. + } + double generatedHeat = 0.0; + for (int row = 0; row < 6; row++) { + for (int col = 0; col < 9; col++) { + ReactorItem component = reactor.getComponentAt(row, col); + if (component != null && !component.isBroken()) { + if (allFuelRodsDepleted && component.getRodCount() > 0) { + allFuelRodsDepleted = false; + } + if (active) { + generatedHeat += component.generateHeat(); + } + component.dissipate(); + component.transfer(); + } + } + } + maxReactorHeat = Math.max(reactor.getCurrentHeat(), maxReactorHeat); + minReactorHeat = Math.min(reactor.getCurrentHeat(), minReactorHeat); + checkReactorTemperature(reactorTicks); + maxGeneratedHeat = Math.max(generatedHeat, maxGeneratedHeat); + if (active) { + for (int row = 0; row < 6; row++) { + for (int col = 0; col < 9; col++) { + ReactorItem component = reactor.getComponentAt(row, col); + if (component != null && !component.isBroken()) { + component.generateEnergy(); + } + } + } + } + lastEUoutput = reactor.getCurrentEUoutput(); + totalEUoutput += lastEUoutput; + lastHeatOutput = reactor.getVentedHeat(); + totalHeatOutput += lastHeatOutput; + if (reactor.getCurrentHeat() <= reactor.getMaxHeat()) { + if (reactor.isPulsed() || reactor.isAutomated()) { + if (active) { + activeTime++; + currentActiveTime++; + if (reactor.isPulsed() && (reactor.getCurrentHeat() >= suspendTemp || (reactorTicks % clockPeriod) >= onPulseDuration)) { + active = false; + minActiveTime = Math.min(currentActiveTime, minActiveTime); + maxActiveTime = Math.max(currentActiveTime, maxActiveTime); + currentActiveTime = 0; + } + } + else { + inactiveTime++; + currentInactiveTime++; + if (reactor.isAutomated() && pauseTimer > 0) { + pauseTimer--; + } + else if ((reactor.isPulsed() && reactor.getCurrentHeat() <= resumeTemp && (reactorTicks % clockPeriod) < onPulseDuration)) { + active = true; + minInactiveTime = Math.min(currentInactiveTime, minInactiveTime); + maxInactiveTime = Math.max(currentInactiveTime, maxInactiveTime); + currentInactiveTime = 0; + } + } + } + minEUoutput = Math.min(lastEUoutput, minEUoutput); + maxEUoutput = Math.max(lastEUoutput, maxEUoutput); + minHeatOutput = Math.min(lastHeatOutput, minHeatOutput); + maxHeatOutput = Math.max(lastHeatOutput, maxHeatOutput); + } + calculateHeatingCooling(reactorTicks); + handleAutomation(reactorTicks); + + } + + if (hasStopped()) { + publish("Simulation.CancelledAtTick", reactorTicks); + } + data.minTemp = (int) minReactorHeat; + data.maxTemp = (int) maxReactorHeat; + publish("Simulation.ReactorMinTemp", minReactorHeat); + publish("Simulation.ReactorMaxTemp", maxReactorHeat); + if (reactor.getCurrentHeat() < reactor.getMaxHeat()) { + publish("Simulation.TimeWithoutExploding", reactorTicks); + if (reactor.isPulsed()) { + String rangeString = ""; + if (maxActiveTime > minActiveTime) { + rangeString = rangeString("Simulation.ActiveTimeRange", minActiveTime, maxActiveTime); + } + else if (minActiveTime < activeTime) { + rangeString = "Simulation.ActiveTimeSingle "+minActiveTime; + } + publish("Simulation.ActiveTime", activeTime, rangeString); + rangeString = ""; + if (maxInactiveTime > minInactiveTime) { + rangeString = rangeString("Simulation.InactiveTimeRange", minInactiveTime, maxInactiveTime); + } + else if (minInactiveTime < inactiveTime) { + rangeString = "Simulation.InactiveTimeSingle " + minInactiveTime; + } + publish("Simulation.InactiveTime", inactiveTime, rangeString); + } + + if (reactorTicks > 0) { + data.totalReactorTicks = reactorTicks; + if (reactor.isFluid()) { + data.totalHUoutput = (int) (40 * totalHeatOutput); + data.avgHUoutput = (int) (2 * totalHeatOutput / reactorTicks); + data.minHUoutput = 2 * minHeatOutput; + data.maxHUoutput = (int) (2 * maxHeatOutput); + if (totalHeatOutput > 0) { + publish("Simulation.HeatOutputs", DECIMAL_FORMAT.format(40 * totalHeatOutput), DECIMAL_FORMAT.format(2 * totalHeatOutput / reactorTicks), DECIMAL_FORMAT.format(2 + * minHeatOutput), DECIMAL_FORMAT.format(2 * maxHeatOutput)); + if (totalRodCount > 0) { + publish("Simulation.Efficiency", totalHeatOutput / reactorTicks / 4 / totalRodCount, minHeatOutput / 4 / totalRodCount, maxHeatOutput / 4 / totalRodCount); + } + } + } + else { + data.totalEUoutput = (int) totalEUoutput; + data.avgEUoutput = MathUtils.roundToClosestInt(Math.ceil(totalEUoutput / (reactorTicks * 20))); + data.minEUoutput = minEUoutput / 20.0; + data.maxEUoutput = (int) (maxEUoutput / 20.0); + if (totalEUoutput > 0) { + publish("Simulation.EUOutputs", DECIMAL_FORMAT.format(totalEUoutput), DECIMAL_FORMAT.format(totalEUoutput / (reactorTicks * 20)), DECIMAL_FORMAT.format(minEUoutput + / 20.0), DECIMAL_FORMAT.format(maxEUoutput / 20.0)); + if (totalRodCount > 0) { + publish("Simulation.Efficiency", totalEUoutput / reactorTicks / 100 / totalRodCount, minEUoutput / 100 / totalRodCount, maxEUoutput / 100 / totalRodCount); + } + } + } + } + + if (reactor.getCurrentHeat() > 0.0) { + publish("Simulation.ReactorRemainingHeat", reactor.getCurrentHeat()); + } + double prevReactorHeat = reactor.getCurrentHeat(); + double prevTotalComponentHeat = 0.0; + for (int row = 0; row < 6; row++) { + for (int col = 0; col < 9; col++) { + ReactorItem component = reactor.getComponentAt(row, col); + if (component != null && !component.isBroken()) { + if (component.getCurrentHeat() > 0.0) { + prevTotalComponentHeat += component.getCurrentHeat(); + publish(String.format("R%dC%d:0xFFA500", row, col)); // NOI18N + component.info.append("ComponentInfo.RemainingHeat " + component.getCurrentHeat()); + } + } + } + } + if (prevReactorHeat == 0.0 && prevTotalComponentHeat == 0.0) { + publish("Simulation.NoCooldown"); + } + else if (reactor.getCurrentHeat() < reactor.getMaxHeat()) { + double currentTotalComponentHeat = prevTotalComponentHeat; + int reactorCooldownTime = 0; + do { + reactor.clearVentedHeat(); + prevReactorHeat = reactor.getCurrentHeat(); + if (prevReactorHeat == 0.0) { + reactorCooldownTime = cooldownTicks; + } + prevTotalComponentHeat = currentTotalComponentHeat; + for (int row = 0; row < 6; row++) { + for (int col = 0; col < 9; col++) { + ReactorItem component = reactor.getComponentAt(row, col); + if (component != null && !component.isBroken()) { + component.dissipate(); + component.transfer(); + } + } + } + lastHeatOutput = reactor.getVentedHeat(); + totalHeatOutput += lastHeatOutput; + minEUoutput = Math.min(lastEUoutput, minEUoutput); + maxEUoutput = Math.max(lastEUoutput, maxEUoutput); + minHeatOutput = Math.min(lastHeatOutput, minHeatOutput); + maxHeatOutput = Math.max(lastHeatOutput, maxHeatOutput); + cooldownTicks++; + currentTotalComponentHeat = 0.0; + for (int row = 0; row < 6; row++) { + for (int col = 0; col < 9; col++) { + ReactorItem component = reactor.getComponentAt(row, col); + if (component != null && !component.isBroken()) { + currentTotalComponentHeat += component.getCurrentHeat(); + if (component.getCurrentHeat() == 0.0 && needsCooldown[row][col]) { + component.info.append("ComponentInfo.CooldownTime " + cooldownTicks); + needsCooldown[row][col] = false; + } + } + } + } + } + while (lastHeatOutput > 0 && cooldownTicks < 50000); + if (reactor.getCurrentHeat() < reactor.getMaxHeat()) { + if (reactor.getCurrentHeat() == 0.0) { + publish("Simulation.ReactorCooldownTime", reactorCooldownTime); + } + else if (reactorCooldownTime > 0) { + publish("Simulation.ReactorResidualHeat", reactor.getCurrentHeat(), reactorCooldownTime); + } + publish("Simulation.TotalCooldownTime", cooldownTicks); + } + } + } + else { + publish("Simulation.ReactorOverheatedTime", reactorTicks); + explosionPower = 10.0; + double explosionPowerMult = 1.0; + for (int row = 0; row < 6; row++) { + for (int col = 0; col < 9; col++) { + ReactorItem component = reactor.getComponentAt(row, col); + if (component != null) { + explosionPower += component.getExplosionPowerOffset(); + explosionPowerMult *= component.getExplosionPowerMultiplier(); + } + } + } + explosionPower *= explosionPowerMult; + publish("Simulation.ExplosionPower", explosionPower); + } + double totalEffectiveVentCooling = 0.0; + double totalVentCoolingCapacity = 0.0; + double totalCellCooling = 0.0; + double totalCondensatorCooling = 0.0; + + for (int row = 0; row < 6; row++) { + for (int col = 0; col < 9; col++) { + ReactorItem component = reactor.getComponentAt(row, col); + if (component != null) { + if (component.getVentCoolingCapacity() > 0) { + component.info.append("ComponentInfo.UsedCooling " + component.getBestVentCooling() + " | " + component.getVentCoolingCapacity()); + totalEffectiveVentCooling += component.getBestVentCooling(); + totalVentCoolingCapacity += component.getVentCoolingCapacity(); + } + else if (component.getBestCellCooling() > 0) { + component.info.append("ComponentInfo.ReceivedHeat " + component.getBestCellCooling()); + totalCellCooling += component.getBestCellCooling(); + } + else if (component.getBestCondensatorCooling() > 0) { + component.info.append("ComponentInfo.ReceivedHeat " + component.getBestCondensatorCooling()); + totalCondensatorCooling += component.getBestCondensatorCooling(); + } + else if (component.getMaxHeatGenerated() > 0) { + if (!reactor.isFluid() && component.getMaxEUGenerated() > 0) { + component.info.append("ComponentInfo.GeneratedEU " + component.getMinEUGenerated() + " | " + component.getMaxEUGenerated()); + } + component.info.append("ComponentInfo.GeneratedHeat " + component.getMinHeatGenerated() + " | " + component.getMaxHeatGenerated()); + } + if (component.getMaxReachedHeat() > 0) { + component.info.append("ComponentInfo.ReachedHeat " + component.getMaxReachedHeat() + " | " + component.getMaxHeat()); + } + } + } + } + + // if (totalVentCoolingCapacity > 0) { + // publish("Simulation.TotalVentCooling", + // totalEffectiveVentCooling, totalVentCoolingCapacity); + // } + showHeatingCooling(reactorTicks); // Call to show this info in case it + // hasn't already been shown, such + // as for an automated reactor. + if (totalCellCooling > 0) { + publish("Simulation.TotalCellCooling", totalCellCooling); + } + if (totalCondensatorCooling > 0) { + publish("Simulation.TotalCondensatorCooling", totalCondensatorCooling); + } + if (maxGeneratedHeat > 0) { + publish("Simulation.MaxHeatGenerated", maxGeneratedHeat); + } + if (redstoneUsed > 0) { + publish("Simulation.RedstoneUsed", redstoneUsed); + } + if (lapisUsed > 0) { + publish("Simulation.LapisUsed", lapisUsed); + } + // double totalCooling = totalEffectiveVentCooling + totalCellCooling + + // totalCondensatorCooling; + // if (totalCooling >= maxGeneratedHeat) { + // publish("Simulation.ExcessCooling", totalCooling - + // maxGeneratedHeat); + // } else { + // publish("Simulation.ExcessHeating", maxGeneratedHeat - + // totalCooling); + // } + // return null; + + /* catch (Throwable e) { + if (cooldownTicks == 0) { + publish("Simulation.ErrorReactor", reactorTicks); + } else { + publish("Simulation.ErrorCooldown", cooldownTicks); + } + publish(e.toString(), " ", Arrays.toString(e.getStackTrace()); // NO18N + + }*/ + data.explosionPower = (int) explosionPower; + data.totalReactorTicks = reactorTicks; + long endTime = System.nanoTime(); + publish("Simulation.ElapsedTime", (endTime - startTime) / 1e9); + mRunning = false; + completed = true; + } + + + public boolean hasStopped() { + return !mRunning; + } + + public boolean isRunning() { + return mRunning; + } + + private void handleAutomation(final int reactorTicks) { + for (int row = 0; row < 6; row++) { + for (int col = 0; col < 9; col++) { + ReactorItem component = reactor.getComponentAt(row, col); + if (component != null && reactor.isAutomated()) { + if (component.getMaxHeat() > 1) { + if (component.getAutomationThreshold() > component.getInitialHeat() && component.getCurrentHeat() >= component.getAutomationThreshold()) { + component.clearCurrentHeat(); + component.info.append("ComponentInfo.ReplacedTime | " + reactorTicks); + if (component.getReactorPause() > 0) { + active = false; + pauseTimer = Math.max(pauseTimer, component.getReactorPause()); + minActiveTime = Math.min(currentActiveTime, minActiveTime); + maxActiveTime = Math.max(currentActiveTime, maxActiveTime); + currentActiveTime = 0; + } + } + else if (component.getAutomationThreshold() < component.getInitialHeat() && component.getCurrentHeat() <= component.getAutomationThreshold()) { + component.clearCurrentHeat(); + component.info.append("ComponentInfo.ReplacedTime | " +reactorTicks); + if (component.getReactorPause() > 0) { + active = false; + pauseTimer = Math.max(pauseTimer, component.getReactorPause()); + minActiveTime = Math.min(currentActiveTime, minActiveTime); + maxActiveTime = Math.max(currentActiveTime, maxActiveTime); + currentActiveTime = 0; + } + } + } + else if (component.isBroken() || (component.getMaxDamage() > 1 && component.getCurrentDamage() >= component.getAutomationThreshold())) { + component.clearDamage(); + component.info.append("ComponentInfo.ReplacedTime | " +reactorTicks); + if (component.getReactorPause() > 0) { + active = false; + pauseTimer = Math.max(pauseTimer, component.getReactorPause()); + minActiveTime = Math.min(currentActiveTime, minActiveTime); + maxActiveTime = Math.max(currentActiveTime, maxActiveTime); + currentActiveTime = 0; + } + } + } + if (reactor.isUsingReactorCoolantInjectors() && component != null && component.needsCoolantInjected()) { + component.injectCoolant(); + if ("rshCondensator".equals(component.baseName)) { + redstoneUsed++; + } + else if ("lzhCondensator".equals(component.baseName)) { + lapisUsed++; + } + } + } + } + } + + private void checkReactorTemperature(final int reactorTicks) { + if (reactor.getCurrentHeat() < 0.5 * reactor.getMaxHeat() && !reachedBelow50 && reachedEvaporate) { + publish("Simulation.TimeToBelow50", reactorTicks); + reachedBelow50 = true; + data.timeToBelow50 = reactorTicks; + } + if (reactor.getCurrentHeat() >= 0.4 * reactor.getMaxHeat() && !reachedBurn) { + publish("Simulation.TimeToBurn", reactorTicks); + reachedBurn = true; + data.timeToBurn = reactorTicks; + } + if (reactor.getCurrentHeat() >= 0.5 * reactor.getMaxHeat() && !reachedEvaporate) { + publish("Simulation.TimeToEvaporate", reactorTicks); + reachedEvaporate = true; + data.timeToEvaporate = reactorTicks; + } + if (reactor.getCurrentHeat() >= 0.7 * reactor.getMaxHeat() && !reachedHurt) { + publish("Simulation.TimeToHurt", reactorTicks); + reachedHurt = true; + data.timeToHurt = reactorTicks; + } + if (reactor.getCurrentHeat() >= 0.85 * reactor.getMaxHeat() && !reachedLava) { + publish("Simulation.TimeToLava", reactorTicks); + reachedLava = true; + data.timeToLava = reactorTicks; + } + if (reactor.getCurrentHeat() >= reactor.getMaxHeat() && !reachedExplode) { + publish("Simulation.TimeToXplode", reactorTicks); + reachedExplode = true; + data.timeToXplode = reactorTicks; + } + } + + private void calculateHeatingCooling(final int reactorTicks) { + if (reactorTicks > 20) { + for (int row = 0; row < 6; row++) { + for (int col = 0; col < 9; col++) { + ReactorItem component = reactor.getComponentAt(row, col); + if (component != null) { + totalHullHeating += component.getCurrentHullHeating(); + totalComponentHeating += component.getCurrentComponentHeating(); + totalHullCooling += component.getCurrentHullCooling(); + totalVentCooling += component.getCurrentVentCooling(); + } + } + } + } + } + + private void showHeatingCooling(final int reactorTicks) { + if (!showHeatingCoolingCalled) { + showHeatingCoolingCalled = true; + if (reactorTicks >= 40) { + double totalHullCoolingCapacity = 0; + double totalVentCoolingCapacity = 0; + for (int row = 0; row < 6; row++) { + for (int col = 0; col < 9; col++) { + ReactorItem component = reactor.getComponentAt(row, col); + if (component != null) { + totalHullCoolingCapacity += component.getHullCoolingCapacity(); + totalVentCoolingCapacity += component.getVentCoolingCapacity(); + } + } + } + data.hullHeating = totalHullHeating / (reactorTicks - 20); + data.componentHeating = totalComponentHeating / (reactorTicks - 20); + data.hullCooling = totalHullCooling / (reactorTicks - 20); + data.hullCoolingCapacity = totalHullCoolingCapacity; + data.ventCooling = totalVentCooling / (reactorTicks - 20); + data.ventCoolingCapacity = totalVentCoolingCapacity; + if (totalHullHeating > 0) { + publish("Simulation.HullHeating", totalHullHeating / (reactorTicks - 20)); + } + if (totalComponentHeating > 0) { + publish("Simulation.ComponentHeating", totalComponentHeating / (reactorTicks - 20)); + } + if (totalHullCoolingCapacity > 0) { + publish("Simulation.HullCooling | " +totalHullCooling / (reactorTicks - 20), totalHullCoolingCapacity); + } + if (totalVentCoolingCapacity > 0) { + publish("Simulation.VentCooling | " +totalVentCooling / (reactorTicks - 20), totalVentCoolingCapacity); + } + } + } + } + + private void publish(String string, double currentHeat, int reactorCooldownTime) { + publish(string + " | "+currentHeat+" | "+reactorCooldownTime); + } + + private void publish(String string, double d, double e, double f) { + publish(string + " | "+d+" | "+e+" | "+f); + } + + private void publish(String string, String format, String format2, String format3, String format4) { + publish(string + " | "+format+" | "+format2+" | "+format3+" | "+format4); + } + + private void publish(String string, int activeTime2, String rangeString) { + publish(string + " | "+activeTime2+" | "+rangeString); + } + + private void publish(String aString, double aData) { + publish(aString+":"+aData); + } + + private void publish(String aString, long aData) { + publish(aString+":"+aData); + } + + private void publish(String aString) { + output.add(aString); + } + + private String rangeString(String string, int aMin, int aMax) { + return string+" ("+aMin+"-"+aMax+")"; + } + + + protected void process(List<String> chunks) { + /* + for (String chunk : chunks) { + if (chunk.isEmpty()) { + output.add(""); // NO18N + } + else { + if (chunk.matches("R\\dC\\d:.*")) { // NO18N + String temp = chunk.substring(5); + int row = chunk.charAt(1) - '0'; + int col = chunk.charAt(3) - '0'; + if (temp.startsWith("0x")) { // NO18N + mReactorComponents[row][col].setBackground(Color.decode(temp)); + if ("0xC0C0C0".equals(temp)) { + mReactorComponents[row][col].setToolTipText(null); + } + else if ("0xFF0000".equals(temp)) { + mReactorComponents[row][col].setToolTipText(getI18n("ComponentTooltip.Broken")); + } + else if ("0xFFA500".equals(temp)) { + mReactorComponents[row][col].setToolTipText(getI18n("ComponentTooltip.ResidualHeat")); + } + } + } + else { + output.add(chunk); + } + } + } + */ + } + + public void cancel() { + Logger.INFO("Stopping Simulation."); + mRunning = false; + completed = true; + } + + +} diff --git a/src/main/java/Ic2ExpReactorPlanner/BigintStorage.java b/src/main/java/Ic2ExpReactorPlanner/BigintStorage.java new file mode 100644 index 0000000000..03960cd7b1 --- /dev/null +++ b/src/main/java/Ic2ExpReactorPlanner/BigintStorage.java @@ -0,0 +1,60 @@ +package Ic2ExpReactorPlanner; + +import java.math.BigInteger; +import java.util.Base64; + +/** + * Stores numbers of varying size inside a BigInteger, expecting each to have + * a defined limit (which need not be an exact power of 2). Numbers are to be + * extracted in reverse order they were stored, and special values can be used + * to make certain values optional for inclusion (the calling class is + * responsible for handling this logic, though). + * @author Brian McCloud + */ +public class BigintStorage { + private BigInteger storedValue = BigInteger.ZERO; + + /** + * Stores the specified value. Requires that 0 <= value <= max. + * @param value the value to store. + * @param max the expected maximum for the value. + */ + public void store(int value, int max) { + if (value < 0 || value > max) { + throw new IllegalArgumentException(); + } + storedValue = storedValue.multiply(BigInteger.valueOf(max + 1)).add(BigInteger.valueOf(value)); + } + + /** + * Extracts a value based on the specified maximum. + * @param max the expected maximum for the value. + * @return the extracted value. + */ + public int extract(int max) { + BigInteger[] values = storedValue.divideAndRemainder(BigInteger.valueOf(max + 1)); + storedValue = values[0]; + return values[1].intValue(); + } + + /** + * Takes input of a Base64 string, and converts it to a BigintStorage. + * @param code the Base64-encoded string (presumed to be from @outputBase64) + * @return the converted storage object. + */ + public static BigintStorage inputBase64(String code) { + BigintStorage result = new BigintStorage(); + byte[] temp = Base64.getDecoder().decode(code); + result.storedValue = new BigInteger(temp); + return result; + } + + /** + * Outputs the current value of this BigintStorage as a Base64-encoded string. + * @return the Base64-encoded string. + */ + public String outputBase64() { + byte[] temp = storedValue.toByteArray(); + return Base64.getEncoder().encodeToString(temp); + } +} diff --git a/src/main/java/Ic2ExpReactorPlanner/ComponentFactory.java b/src/main/java/Ic2ExpReactorPlanner/ComponentFactory.java new file mode 100644 index 0000000000..c613f27a32 --- /dev/null +++ b/src/main/java/Ic2ExpReactorPlanner/ComponentFactory.java @@ -0,0 +1,231 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package Ic2ExpReactorPlanner; + +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.stream.Collectors; + +import Ic2ExpReactorPlanner.components.Condensator; +import Ic2ExpReactorPlanner.components.CoolantCell; +import Ic2ExpReactorPlanner.components.Exchanger; +import Ic2ExpReactorPlanner.components.FuelRod; +import Ic2ExpReactorPlanner.components.Plating; +import Ic2ExpReactorPlanner.components.ReactorItem; +import Ic2ExpReactorPlanner.components.Reflector; +import Ic2ExpReactorPlanner.components.Vent; +import gregtech.api.enums.ItemList; +import gregtech.api.objects.GT_ItemStack; +import gregtech.api.util.GT_ModHandler; +import gtPlusPlus.api.objects.Logger; +import gtPlusPlus.core.lib.LoadedMods; +import gtPlusPlus.xmod.bartworks.BW_Utils; +import gtPlusPlus.xmod.bartworks.BW_Utils.NonMeta_MaterialItem; +import gtPlusPlus.xmod.goodgenerator.GG_Utils; +import gtPlusPlus.xmod.goodgenerator.GG_Utils.GG_Fuel_Rod; + +/** + * Factory class to handle creating components by id or name. + * @author Brian McCloud + */ +public class ComponentFactory { + + public static int MAX_COMPONENT_ID = 64; + + static ItemList[] aGtItems = new ItemList[]{ + ItemList.Neutron_Reflector, + ItemList.Moxcell_1, + ItemList.Moxcell_2, + ItemList.Moxcell_4 + }; + + private ComponentFactory() { + // do nothing, this class should not be instantiated. + } + + private static LinkedHashMap<Integer, ReactorItem> ITEM_LIST = new LinkedHashMap<Integer, ReactorItem>(); + + static { + int aID = 0; + ITEM_LIST.put(aID++, null); + ITEM_LIST.put(aID++, new FuelRod(1, "fuelRodUranium", new GT_ItemStack(GT_ModHandler.getIC2Item("reactorUraniumSimple", 1).copy()), 20e3, 1, null, 100, 2, 1, false)); + ITEM_LIST.put(aID++, new FuelRod(2, "dualFuelRodUranium", new GT_ItemStack(GT_ModHandler.getIC2Item("reactorUraniumDual", 1).copy()), 20e3, 1, null, 200, 4, 2, false)); + ITEM_LIST.put(aID++, new FuelRod(3, "quadFuelRodUranium", new GT_ItemStack(GT_ModHandler.getIC2Item("reactorUraniumQuad", 1).copy()), 20e3, 1, null, 400, 8, 4, false)); + ITEM_LIST.put(aID++, new FuelRod(4, "fuelRodMox", new GT_ItemStack(aGtItems[1].get(1).copy()), 10e3, 1, null, 100, 2, 1, true)); + ITEM_LIST.put(aID++, new FuelRod(5, "dualFuelRodMox", new GT_ItemStack(aGtItems[2].get(1).copy()), 10e3, 1, null, 200, 4, 2, true)); + ITEM_LIST.put(aID++, new FuelRod(6, "quadFuelRodMox", new GT_ItemStack(aGtItems[3].get(1).copy()), 10e3, 1, null, 400, 8, 4, true)); + ITEM_LIST.put(aID++, new Reflector(7, "neutronReflector", new GT_ItemStack(GT_ModHandler.getIC2Item("reactorReflector", 1).copy()), 30e3, 1, null)); + ITEM_LIST.put(aID++, new Reflector(8, "thickNeutronReflector", new GT_ItemStack(GT_ModHandler.getIC2Item("reactorReflectorThick", 1).copy()), 120e3, 1, null)); + ITEM_LIST.put(aID++, new Vent(9, "heatVent", new GT_ItemStack(GT_ModHandler.getIC2Item("reactorVent", 1).copy()), 1, 1000, null, 6, 0, 0)); + ITEM_LIST.put(aID++, new Vent(10, "advancedHeatVent", new GT_ItemStack(GT_ModHandler.getIC2Item("reactorVentDiamond", 1).copy()), 1, 1000, null, 12, 0, 0)); + ITEM_LIST.put(aID++, new Vent(11, "reactorHeatVent", new GT_ItemStack(GT_ModHandler.getIC2Item("reactorVentCore", 1).copy()), 1, 1000, null, 5, 5, 0)); + ITEM_LIST.put(aID++, new Vent(12, "componentHeatVent", new GT_ItemStack(GT_ModHandler.getIC2Item("reactorVentSpread", 1).copy()), 1, 1, null, 0, 0, 4)); + ITEM_LIST.put(aID++, new Vent(13, "overclockedHeatVent", new GT_ItemStack(GT_ModHandler.getIC2Item("reactorVentGold", 1).copy()), 1, 1000, null, 20, 36, 0)); + ITEM_LIST.put(aID++, new CoolantCell(14, "coolantCell10k", new GT_ItemStack(GT_ModHandler.getIC2Item("reactorCoolantSimple", 1).copy()), 1, 10e3, null)); + ITEM_LIST.put(aID++, new CoolantCell(15, "coolantCell30k", new GT_ItemStack(GT_ModHandler.getIC2Item("reactorCoolantTriple", 1).copy()), 1, 30e3, null)); + ITEM_LIST.put(aID++, new CoolantCell(16, "coolantCell60k", new GT_ItemStack(GT_ModHandler.getIC2Item("reactorCoolantSix", 1).copy()), 1, 60e3, null)); + ITEM_LIST.put(aID++, new Exchanger(17, "heatExchanger", new GT_ItemStack(GT_ModHandler.getIC2Item("reactorHeatSwitch", 1).copy()), 1, 2500, null, 12, 4)); + ITEM_LIST.put(aID++, new Exchanger(18, "advancedHeatExchanger", new GT_ItemStack(GT_ModHandler.getIC2Item("reactorHeatSwitchDiamond", 1).copy()), 1, 10e3, null, 24, 8)); + ITEM_LIST.put(aID++, new Exchanger(19, "coreHeatExchanger", new GT_ItemStack(GT_ModHandler.getIC2Item("reactorHeatSwitchCore", 1).copy()), 1, 5000, null, 0, 72)); + ITEM_LIST.put(aID++, new Exchanger(20, "componentHeatExchanger", new GT_ItemStack(GT_ModHandler.getIC2Item("reactorHeatSwitchSpread", 1).copy()), 1, 5000, null, 36, 0)); + ITEM_LIST.put(aID++, new Plating(21, "reactorPlating", new GT_ItemStack(GT_ModHandler.getIC2Item("reactorPlating", 1).copy()), 1, 1, null, 1000, 0.9025)); + ITEM_LIST.put(aID++, new Plating(22, "heatCapacityReactorPlating", new GT_ItemStack(GT_ModHandler.getIC2Item("reactorPlatingHeat", 1).copy()), 1, 1, null, 1700, 0.9801)); + ITEM_LIST.put(aID++, new Plating(23, "containmentReactorPlating", new GT_ItemStack(GT_ModHandler.getIC2Item("reactorPlatingExplosive", 1).copy()), 1, 1, null, 500, 0.81)); + ITEM_LIST.put(aID++, new Condensator(24, "rshCondensator", new GT_ItemStack(GT_ModHandler.getIC2Item("reactorCondensator", 1).copy()), 1, 20e3, null)); + ITEM_LIST.put(aID++, new Condensator(25, "lzhCondensator", new GT_ItemStack(GT_ModHandler.getIC2Item("reactorCondensatorLap", 1).copy()), 1, 100e3, null)); + ITEM_LIST.put(aID++, new FuelRod(26, "fuelRodThorium", new GT_ItemStack(ItemList.ThoriumCell_1.get(1).copy()), 50e3, 1, "GregTech", 20, 0.5, 1, false)); + ITEM_LIST.put(aID++, new FuelRod(27, "dualFuelRodThorium", new GT_ItemStack(ItemList.ThoriumCell_2.get(1).copy()), 50e3, 1, "GregTech", 40, 1, 2, false)); + ITEM_LIST.put(aID++, new FuelRod(28, "quadFuelRodThorium", new GT_ItemStack(ItemList.ThoriumCell_4.get(1).copy()), 50e3, 1, "GregTech", 80, 2, 4, false)); + ITEM_LIST.put(aID++, new CoolantCell(29, "coolantCellHelium60k", new GT_ItemStack(ItemList.Reactor_Coolant_He_1.get(1).copy()), 1, 60e3, "GregTech")); + ITEM_LIST.put(aID++, new CoolantCell(30, "coolantCellHelium180k", new GT_ItemStack(ItemList.Reactor_Coolant_He_3.get(1).copy()), 1, 180e3, "GregTech")); + ITEM_LIST.put(aID++, new CoolantCell(31, "coolantCellHelium360k", new GT_ItemStack(ItemList.Reactor_Coolant_He_6.get(1).copy()), 1, 360e3, "GregTech")); + ITEM_LIST.put(aID++, new CoolantCell(32, "coolantCellNak60k", new GT_ItemStack(ItemList.Reactor_Coolant_NaK_1.get(1).copy()), 1, 60e3, "GregTech")); + ITEM_LIST.put(aID++, new CoolantCell(33, "coolantCellNak180k", new GT_ItemStack(ItemList.Reactor_Coolant_NaK_3.get(1).copy()), 1, 180e3, "GregTech")); + ITEM_LIST.put(aID++, new CoolantCell(34, "coolantCellNak360k", new GT_ItemStack(ItemList.Reactor_Coolant_NaK_3.get(1).copy()), 1, 360e3, "GregTech")); + ITEM_LIST.put(aID++, new Reflector(35, "iridiumNeutronReflector", new GT_ItemStack(ItemList.Neutron_Reflector.get(1).copy()), 1, 1, null)); + ITEM_LIST.put(aID++, new FuelRod(36, "fuelRodNaquadah", new GT_ItemStack(ItemList.NaquadahCell_1.get(1).copy()), 100e3, 1, "GregTech", 100, 2, 1, true)); + ITEM_LIST.put(aID++, new FuelRod(37, "dualFuelRodNaquadah", new GT_ItemStack(ItemList.NaquadahCell_2.get(1).copy()), 100e3, 1, "GregTech", 200, 4, 2, true)); + ITEM_LIST.put(aID++, new FuelRod(38, "quadFuelRodNaquadah", new GT_ItemStack(ItemList.NaquadahCell_4.get(1).copy()), 100e3, 1, "GregTech", 400, 8, 4, true)); + + //aID = 39; + //ITEM_LIST.put(aID++, new FuelRod(39, "fuelRodCoaxium", null, 20e3, 1, "Coaxium", 100, 0, 1, false)); + //ITEM_LIST.put(aID++, new FuelRod(40, "dualFuelRodCoaxium", null, 20e3, 1, "Coaxium", 200, 0, 2, false)); + //ITEM_LIST.put(aID++, new FuelRod(41, "quadFuelRodCoaxium", null, 20e3, 1, "Coaxium", 400, 0, 4, false)); + //ITEM_LIST.put(aID++, new FuelRod(42, "fuelRodCesium", null, 10861, 1, "Coaxium", 200, 1, 1, false)); + //ITEM_LIST.put(aID++, new FuelRod(43, "dualFuelRodCesium", null, 10861, 1, "Coaxium", 400, 6, 2, false)); + //ITEM_LIST.put(aID++, new FuelRod(44, "quadFuelRodCesium", null, 10861, 1, "Coaxium", 800, 24, 4, false)); + + aID = 45; + ITEM_LIST.put(aID++, new FuelRod(45, "fuelRodNaquadahGTNH", new GT_ItemStack(ItemList.NaquadahCell_1.get(1).copy()), 100e3, 1, "GTNH", 100, 2, 1, false));//Naq rods are not MOX-like in GTNH, + ITEM_LIST.put(aID++, new FuelRod(46, "dualFuelRodNaquadahGTNH", new GT_ItemStack(ItemList.NaquadahCell_2.get(1).copy()), 100e3, 1, "GTNH", 200, 4, 2, false));//we have naquadria for that + ITEM_LIST.put(aID++, new FuelRod(47, "quadFuelRodNaquadahGTNH", new GT_ItemStack(ItemList.NaquadahCell_4.get(1).copy()), 100e3, 1, "GTNH", 400, 8, 4, false)); + ITEM_LIST.put(aID++, new FuelRod(48, "fuelRodNaquadria", new GT_ItemStack(ItemList.MNqCell_1.get(1).copy()), 100e3, 1, "GTNH", 100, 2, 1, true)); + ITEM_LIST.put(aID++, new FuelRod(49, "dualFuelRodNaquadria", new GT_ItemStack(ItemList.MNqCell_2.get(1).copy()), 100e3, 1, "GTNH", 200, 4, 2, true)); + ITEM_LIST.put(aID++, new FuelRod(50, "quadFuelRodNaquadria", new GT_ItemStack(ItemList.MNqCell_4.get(1).copy()), 100e3, 1, "GTNH", 400, 8, 4, true)); + + aID = 51; + if (LoadedMods.BartWorks) { + ITEM_LIST.put(aID++, new FuelRod(51, "fuelRodTiberium", new GT_ItemStack(BW_Utils.getBW_NonMeta_MaterialItems(NonMeta_MaterialItem.TiberiumCell_1, 1)), 50e3, 1, "Bartworks", 100, 1, 1, false)); + ITEM_LIST.put(aID++, new FuelRod(52, "dualFuelRodTiberium", new GT_ItemStack(BW_Utils.getBW_NonMeta_MaterialItems(NonMeta_MaterialItem.TiberiumCell_2, 1)), 50e3, 1, "Bartworks", 200, 2, 2, false)); + ITEM_LIST.put(aID++, new FuelRod(53, "quadFuelRodTiberium", new GT_ItemStack(BW_Utils.getBW_NonMeta_MaterialItems(NonMeta_MaterialItem.TiberiumCell_4, 1)), 50e3, 1, "Bartworks", 400, 4, 4, false)); + ITEM_LIST.put(aID++, new FuelRod(54, "fuelRodTheCore", new GT_ItemStack(BW_Utils.getBW_NonMeta_MaterialItems(NonMeta_MaterialItem.TheCoreCell, 1)), 100e3, 1, "Bartworks", 72534, 816, 32, false)); + } + + aID = 55; + ITEM_LIST.put(aID++, new CoolantCell(55, "coolantCellSpace180k", new GT_ItemStack(ItemList.Reactor_Coolant_Sp_1.get(1).copy()), 1, 180e3, "GTNH")); + ITEM_LIST.put(aID++, new CoolantCell(56, "coolantCellSpace360k", new GT_ItemStack(ItemList.Reactor_Coolant_Sp_2.get(1).copy()), 1, 360e3, "GTNH")); + ITEM_LIST.put(aID++, new CoolantCell(57, "coolantCellSpace540k", new GT_ItemStack(ItemList.Reactor_Coolant_Sp_3.get(1).copy()), 1, 540e3, "GTNH")); + ITEM_LIST.put(aID++, new CoolantCell(58, "coolantCellSpace1080k", new GT_ItemStack(ItemList.Reactor_Coolant_Sp_6.get(1).copy()), 1, 1080e3, "GTNH")); + + aID = 59; + if (LoadedMods.GoodGenerator) { + ITEM_LIST.put(aID++, new FuelRod(59, "fuelRodCompressedUranium", new GT_ItemStack(GG_Utils.getGG_Fuel_Rod(GG_Fuel_Rod.rodCompressedUranium, 1)), 50e3, 1, "GoodGenerator", 100, 1, 1, false)); + ITEM_LIST.put(aID++, new FuelRod(60, "fuelRodDoubleCompressedUranium", new GT_ItemStack(GG_Utils.getGG_Fuel_Rod(GG_Fuel_Rod.rodCompressedUranium_2, 1)), 50e3, 1, "GoodGenerator", 100, 1, 2, false)); + ITEM_LIST.put(aID++, new FuelRod(61, "fuelRodQuadCompressedUranium", new GT_ItemStack(GG_Utils.getGG_Fuel_Rod(GG_Fuel_Rod.rodCompressedUranium_4, 1)), 50e3, 1, "GoodGenerator", 100, 1, 4, false)); + ITEM_LIST.put(aID++, new FuelRod(62, "fuelRodCompressedPlutonium", new GT_ItemStack(GG_Utils.getGG_Fuel_Rod(GG_Fuel_Rod.rodCompressedPlutonium, 1)), 50e3, 1, "GoodGenerator", 50, 1, 1, true)); + ITEM_LIST.put(aID++, new FuelRod(63, "fuelRodDoubleCompressedPlutonium", new GT_ItemStack(GG_Utils.getGG_Fuel_Rod(GG_Fuel_Rod.rodCompressedPlutonium_2, 1)), 50e3, 1, "GoodGenerator", 50, 1, 2, true)); + ITEM_LIST.put(aID++, new FuelRod(64, "fuelRodQuadCompressedPlutonium", new GT_ItemStack(GG_Utils.getGG_Fuel_Rod(GG_Fuel_Rod.rodCompressedPlutonium_4, 1)), 50e3, 1, "GoodGenerator", 50, 1, 4, true)); + } + + } + + private static final Map<String, ReactorItem> ITEM_MAP = makeItemMap(); + + private static Map<String, ReactorItem> makeItemMap() { + Map<String, ReactorItem> result = new HashMap<>((int)(ITEM_LIST.size() * 1.5)); + for (ReactorItem reactorItem : ITEM_LIST.values()) { + if (reactorItem != null) { + result.put(reactorItem.baseName, reactorItem); + } + } + return Collections.unmodifiableMap(result); + } + + private static ReactorItem copy(ReactorItem source) { + if (source != null) { + Class<? extends ReactorItem> aClass = source.getClass(); + if (aClass == Condensator.class) { + return new Condensator((Condensator) source); + } else if (aClass == CoolantCell.class) { + return new CoolantCell((CoolantCell) source); + } else if (aClass == Exchanger.class) { + return new Exchanger((Exchanger) source); + } else if (aClass == FuelRod.class) { + return new FuelRod((FuelRod) source); + } else if (aClass == Plating.class) { + return new Plating((Plating) source); + } else if (aClass == Reflector.class) { + return new Reflector((Reflector) source); + } else if (aClass == Vent.class) { + return new Vent((Vent) source); + } + } + return null; + } + + /** + * Gets a default instances of the specified component (such as for drawing button images) + * @param id the id of the component. + * @return the component with the specified id, or null if the id is out of range. + */ + public static ReactorItem getDefaultComponent(int id) { + ReactorItem aItem = ITEM_LIST.get(id); + if (aItem != null) { + return aItem; + } + Logger.INFO("Tried to get default component with ID "+id+". This is invalid."); + return null; + } + + /** + * Gets a default instances of the specified component (such as for drawing button images) + * @param name the name of the component. + * @return the component with the specified name, or null if the name is not found. + */ + public static ReactorItem getDefaultComponent(String name) { + if (name != null) { + return ITEM_MAP.get(name); + } + return null; + } + + /** + * Creates a new instance of the specified component. + * @param id the id of the component to create. + * @return a new instance of the specified component, or null if the id is out of range. + */ + public static ReactorItem createComponent(int id) { + ReactorItem aItem = ITEM_LIST.get(id); + if (aItem != null) { + return copy(aItem); + } + Logger.INFO("Tried to create component with ID "+id+". This is invalid."); + return null; + } + + /** + * Creates a new instance of the specified component. + * @param name the name of the component to create. + * @return a new instance of the specified component, or null if the name is not found. + */ + public static ReactorItem createComponent(String name) { + if (name != null) { + return copy(ITEM_MAP.get(name)); + } + return null; + } + + /** + * Get the number of defined components. + * @return the number of defined components. + */ + public static int getComponentCount() { + return ITEM_LIST.size(); + } + +} diff --git a/src/main/java/Ic2ExpReactorPlanner/Reactor.java b/src/main/java/Ic2ExpReactorPlanner/Reactor.java new file mode 100644 index 0000000000..83b6c511ad --- /dev/null +++ b/src/main/java/Ic2ExpReactorPlanner/Reactor.java @@ -0,0 +1,752 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package Ic2ExpReactorPlanner; + +import java.awt.HeadlessException; +import java.util.ArrayList; + +import Ic2ExpReactorPlanner.components.ReactorItem; + + +/** + * Represents an IndustrialCraft2 Nuclear Reactor. + * @author Brian McCloud + */ +public class Reactor { + + private final ReactorItem[][] grid = new ReactorItem[6][9]; + + private double currentEUoutput = 0.0; + + private double currentHeat = 0.0; + + private double maxHeat = 10000.0; + + private double ventedHeat = 0.0; + + private boolean fluid = false; + + private boolean pulsed = false; + + private boolean automated = false; + + private boolean usingReactorCoolantInjectors = false; + + private static final int DEFAULT_ON_PULSE = (int)5e6; + + private int onPulse = DEFAULT_ON_PULSE; + + private static final int DEFAULT_OFF_PULSE = 0; + + private int offPulse = DEFAULT_OFF_PULSE; + + private static final int DEFAULT_SUSPEND_TEMP = (int)120e3; + + private int suspendTemp = DEFAULT_SUSPEND_TEMP; + + private static final int DEFAULT_RESUME_TEMP = (int)120e3; + + private int resumeTemp = DEFAULT_RESUME_TEMP; + + private int maxSimulationTicks = (int)5e6; + + // maximum paramatter types for a reactor component (current initial heat, automation threshold, reactor pause + private static final int MAX_PARAM_TYPES = 3; + + public static final int MAX_COMPONENT_HEAT = 1_080_000; + + public ReactorItem getComponentAt(final int row, final int column) { + if (row >= 0 && row < grid.length && column >= 0 && column < grid[row].length) { + return grid[row][column]; + } + return null; + } + + public void setComponentAt(final int row, final int column, final ReactorItem component) { + if (row >= 0 && row < grid.length && column >= 0 && column < grid[row].length) { + if (grid[row][column] != null) { + grid[row][column].removeFromReactor(); + } + grid[row][column] = component; + if (component != null) { + component.addToReactor(this, row, column); + } + } + } + + public void clearGrid() { + for (int row = 0; row < grid.length; row++) { + for (int col = 0; col < grid[row].length; col++) { + setComponentAt(row, col, null); + } + } + } + + /** + * @return the amount of EU output in the reactor tick just simulated. + */ + public double getCurrentEUoutput() { + return currentEUoutput; + } + + /** + * @return the current heat level of the reactor. + */ + public double getCurrentHeat() { + return currentHeat; + } + + /** + * @return the maximum heat of the reactor. + */ + public double getMaxHeat() { + return maxHeat; + } + + /** + * Adjust the maximum heat + * @param adjustment the adjustment amount (negative values decrease the max heat). + */ + public void adjustMaxHeat(final double adjustment) { + maxHeat += adjustment; + } + + /** + * Set the current heat of the reactor. Mainly to be used for simulating a pre-heated reactor, or for resetting to 0 for a new simulation. + * @param currentHeat the heat to set + */ + public void setCurrentHeat(final double currentHeat) { + this.currentHeat = currentHeat; + } + + /** + * Adjusts the reactor's current heat by a specified amount + * @param adjustment the adjustment amount. + */ + public void adjustCurrentHeat(final double adjustment) { + currentHeat += adjustment; + if (currentHeat < 0.0) { + currentHeat = 0.0; + } + } + + /** + * add some EU output. + * @param amount the amount of EU to output over 1 reactor tick (20 game ticks). + */ + public void addEUOutput(final double amount) { + currentEUoutput += amount; + } + + /** + * clears the EU output (presumably to start simulating a new reactor tick). + */ + public void clearEUOutput() { + currentEUoutput = 0.0; + } + + /** + * Gets a list of the materials needed to build the components. + * @return a list of the materials needed to build the components. + */ + public ArrayList getMaterials() { + return new ArrayList(); + } + + public ArrayList<ReactorItem> getComponentList() { + ArrayList<ReactorItem> result = new ArrayList<ReactorItem>(); + for (int col = 0; col < grid[0].length; col++) { + for (int row = 0; row < grid.length; row++) { + if (getComponentAt(row, col) != null) { + result.add(getComponentAt(row, col)); + } + } + } + return result; + } + + /** + * @return the amount of heat vented this reactor tick. + */ + public double getVentedHeat() { + return ventedHeat; + } + + /** + * Adds to the amount of heat vented this reactor tick, in case it is a new-style reactor with a pressure vessel and outputting heat to fluid instead of EU. + * @param amount the amount to add. + */ + public void ventHeat(final double amount) { + ventedHeat += amount; + } + + /** + * Clears the amount of vented heat, in case a new reactor tick is starting. + */ + public void clearVentedHeat() { + ventedHeat = 0; + } + + /** + * Get a code that represents the component set, which can be passed between forum users, etc. + * @return a code representing some ids for the components and arrangement. Passing the same code to setCode() should re-create an identical reactor setup, even if other changes have happened in the meantime. + */ + public String getCode() { + return "erp=" + buildCodeString(); + } + + /** + * Sets a code to configure the entire grid all at once. Expects the code to have originally been output by getCode(). + * @param code the code of the reactor setup to use. + */ + public void setCode(final String code) { + int pos = 0; + int[][] ids = new int[grid.length][grid[0].length]; + char[][][] paramTypes = new char[grid.length][grid[0].length][MAX_PARAM_TYPES]; + int[][][] params = new int[grid.length][grid[0].length][MAX_PARAM_TYPES]; + if (code.startsWith("erp=")) { + readCodeString(code.substring(4)); + } else if (code.length() >= 108 && code.matches("[0-9A-Za-z(),|]+")) { //NOI18N + try { + for (int row = 0; row < grid.length; row++) { + for (int col = 0; col < grid[row].length; col++) { + ids[row][col] = Integer.parseInt(code.substring(pos, pos + 2), 16); + pos += 2; + int paramNum = 0; + if (pos + 1 < code.length() && code.charAt(pos) == '(') { + paramTypes[row][col][paramNum] = code.charAt(pos + 1); + int tempPos = pos + 2; + StringBuilder param = new StringBuilder(10); + while (tempPos < code.length() && code.charAt(tempPos) != ')') { + if (code.charAt(tempPos) == ',') { + params[row][col][paramNum] = Integer.parseInt(param.toString(), 36); + paramNum++; + if (tempPos + 1 < code.length()) { + tempPos++; + paramTypes[row][col][paramNum] = code.charAt(tempPos); + } + param.setLength(0); + } else { + param.append(code.charAt(tempPos)); + } + tempPos++; + } + params[row][col][paramNum] = Integer.parseInt(param.toString(), 36); + pos = tempPos + 1; + } + } + } + for (int row = 0; row < grid.length; row++) { + for (int col = 0; col < grid[row].length; col++) { + final ReactorItem component = ComponentFactory.createComponent(ids[row][col]); + for (int paramNum = 0; paramNum < MAX_PARAM_TYPES; paramNum++) { + switch (paramTypes[row][col][paramNum]) { + case 'h': + component.setInitialHeat(params[row][col][paramNum]); + break; + case 'a': + component.setAutomationThreshold(params[row][col][paramNum]); + break; + case 'p': + component.setReactorPause(params[row][col][paramNum]); + break; + default: + break; + } + } + setComponentAt(row, col, component); + } + } + if (code.split("\\|").length > 1) { + String extraCode = code.split("\\|")[1]; + switch (extraCode.charAt(0)) { + case 'f': + fluid = true; + break; + case 'e': + fluid = false; + break; + default: + break; + } + switch (extraCode.charAt(1)) { + case 's': + pulsed = false; + automated = false; + break; + case 'p': + pulsed = true; + automated = false; + break; + case 'a': + pulsed = true; + automated = true; + break; + default: + break; + } + switch (extraCode.charAt(2)) { + case 'i': + usingReactorCoolantInjectors = true; + break; + case 'n': + usingReactorCoolantInjectors = false; + break; + default: + break; + } + if (extraCode.length() > 3) { + currentHeat = Integer.parseInt(extraCode.substring(3), 36); + } else { + currentHeat = 0; + } + } + if (code.split("\\|").length > 2) { + String[] moreCodes = code.split("\\|"); + for (int i = 2; i < moreCodes.length; i++) { + switch (moreCodes[i].charAt(0)) { + case 'n': + onPulse = Integer.parseInt(moreCodes[i].substring(1), 36); + break; + case 'f': + offPulse = Integer.parseInt(moreCodes[i].substring(1), 36); + break; + case 's': + suspendTemp = Integer.parseInt(moreCodes[i].substring(1), 36); + break; + case 'r': + resumeTemp = Integer.parseInt(moreCodes[i].substring(1), 36); + break; + default: + break; + } + } + } + } catch (NumberFormatException e) { + e.printStackTrace(); + } + } else { + String tempCode = code; + if (code.startsWith("http://www.talonfiremage.pwp.blueyonder.co.uk/v3/reactorplanner.html?")) { //NOI18N + tempCode = code.replace("http://www.talonfiremage.pwp.blueyonder.co.uk/v3/reactorplanner.html?", ""); //NOI18N + } + if (tempCode.matches("[0-9a-z]+")) { //NOI18N + // Possibly a code from Talonius's old planner + handleTaloniusCode(tempCode); + } else if (code.matches("[0-9A-Za-z+/=]+")) { //NOI18N + // Try to handle it as a newer code with the "erp=" prefix stripped + readCodeString(code); + } else if (!code.isEmpty()) { + //JOptionPane.showMessageDialog(null, String.format(getI18n("Warning.InvalidReactorCode"), code), getI18n("Warning.Title"), JOptionPane.WARNING_MESSAGE); + } + } + } + + private void handleTaloniusCode(String tempCode) throws HeadlessException { + StringBuilder warnings = new StringBuilder(500); + TaloniusDecoder decoder = new TaloniusDecoder(tempCode); + // initial heat, in multiples of 100 + currentHeat = 100 * decoder.readInt(10); + // reactor grid + for (int x = 8; x >= 0; x--) { + for (int y = 5; y >= 0; y--) { + int nextValue = decoder.readInt(7); + + // items are no longer stackable in IC2 reactors, but stack sizes from the planner code still need to be handled + if (nextValue > 64) { + nextValue = decoder.readInt(7); + } + + switch (nextValue) { + case 0: + setComponentAt(y, x, null); + break; + case 1: + setComponentAt(y, x, ComponentFactory.createComponent("fuelRodUranium")); + break; + case 2: + setComponentAt(y, x, ComponentFactory.createComponent("dualFuelRodUranium")); + break; + case 3: + setComponentAt(y, x, ComponentFactory.createComponent("quadFuelRodUranium")); + break; + case 5: + setComponentAt(y, x, ComponentFactory.createComponent("neutronReflector")); + break; + case 6: + setComponentAt(y, x, ComponentFactory.createComponent("thickNeutronReflector")); + break; + case 7: + setComponentAt(y, x, ComponentFactory.createComponent("heatVent")); + break; + case 8: + setComponentAt(y, x, ComponentFactory.createComponent("reactorHeatVent")); + break; + case 9: + setComponentAt(y, x, ComponentFactory.createComponent("overclockedHeatVent")); + break; + case 10: + setComponentAt(y, x, ComponentFactory.createComponent("advancedHeatVent")); + break; + case 11: + setComponentAt(y, x, ComponentFactory.createComponent("componentHeatVent")); + break; + case 12: + setComponentAt(y, x, ComponentFactory.createComponent("rshCondensator")); + break; + case 13: + setComponentAt(y, x, ComponentFactory.createComponent("lzhCondensator")); + break; + case 14: + setComponentAt(y, x, ComponentFactory.createComponent("heatExchanger")); + break; + case 15: + setComponentAt(y, x, ComponentFactory.createComponent("coreHeatExchanger")); + break; + case 16: + setComponentAt(y, x, ComponentFactory.createComponent("componentHeatExchanger")); + break; + case 17: + setComponentAt(y, x, ComponentFactory.createComponent("advancedHeatExchanger")); + break; + case 18: + setComponentAt(y, x, ComponentFactory.createComponent("reactorPlating")); + break; + case 19: + setComponentAt(y, x, ComponentFactory.createComponent("heatCapacityReactorPlating")); + break; + case 20: + setComponentAt(y, x, ComponentFactory.createComponent("containmentReactorPlating")); + break; + case 21: + setComponentAt(y, x, ComponentFactory.createComponent("coolantCell10k")); + break; + case 22: + setComponentAt(y, x, ComponentFactory.createComponent("coolantCell30k")); + break; + case 23: + setComponentAt(y, x, ComponentFactory.createComponent("coolantCell60k")); + break; + case 24: + warnings.append("Warning.Heating", y, x); + break; + case 32: + setComponentAt(y, x, ComponentFactory.createComponent("fuelRodThorium")); + break; + case 33: + setComponentAt(y, x, ComponentFactory.createComponent("dualFuelRodThorium")); + break; + case 34: + setComponentAt(y, x, ComponentFactory.createComponent("quadFuelRodThorium")); + break; + case 35: + warnings.append("Warning.Plutonium", y, x); + break; + case 36: + warnings.append("Warning.DualPlutonium", y, x); + break; + case 37: + warnings.append("Warning.QuadPlutonium", y, x); + break; + case 38: + setComponentAt(y, x, ComponentFactory.createComponent("iridiumNeutronReflector")); + break; + case 39: + setComponentAt(y, x, ComponentFactory.createComponent("coolantCellHelium60k")); + break; + case 40: + setComponentAt(y, x, ComponentFactory.createComponent("coolantCellHelium180k")); + break; + case 41: + setComponentAt(y, x, ComponentFactory.createComponent("coolantCellHelium360k")); + break; + case 42: + setComponentAt(y, x, ComponentFactory.createComponent("coolantCellNak60k")); + break; + case 43: + setComponentAt(y, x, ComponentFactory.createComponent("coolantCellNak180k")); + break; + case 44: + setComponentAt(y, x, ComponentFactory.createComponent("coolantCellNak360k")); + break; + default: + warnings.append("Warning.Unrecognized", y, x); + break; + } + } + } + if (warnings.length() > 0) { + warnings.setLength(warnings.length() - 1); // to remove last newline character + //JOptionPane.showMessageDialog(null, warnings, "Warning.Title", JOptionPane.WARNING_MESSAGE); + } + } + + // reads a Base64 code string for the reactor, after stripping the prefix. + private void readCodeString(final String code) { + BigintStorage storage = BigintStorage.inputBase64(code); + // read the code revision from the code itself instead of making it part of the prefix. + int codeRevision = storage.extract(255); + int maxComponentHeat; + if (codeRevision == 3) + maxComponentHeat = (int)1080e3; + else + maxComponentHeat = (int)360e3; + // Check if the code revision is supported yet. + if (codeRevision > 3) { + throw new IllegalArgumentException("Unsupported code revision in reactor code."); + } + // for code revision 1 or newer, read whether the reactor is pulsed and/or automated next. + if (codeRevision >= 1) { + pulsed = storage.extract(1) > 0; + automated = storage.extract(1) > 0; + } + // read the grid next + for (int row = 0; row < grid.length; row++) { + for (int col = 0; col < grid[row].length; col++) { + int componentId = 0; + // Changes may be coming to the number of components available, so make sure to check the code revision number. + if (codeRevision <= 1) { + componentId = storage.extract(38); + } else if (codeRevision == 2) { + componentId = storage.extract(44); + } else { + componentId = storage.extract(ComponentFactory.MAX_COMPONENT_ID); + } + if (componentId != 0) { + ReactorItem component = ComponentFactory.createComponent(componentId); + int hasSpecialAutomationConfig = storage.extract(1); + if (hasSpecialAutomationConfig > 0) { + component.setInitialHeat(storage.extract(maxComponentHeat)); + if (codeRevision == 0 || (codeRevision >= 1 && automated)) { + component.setAutomationThreshold(storage.extract(maxComponentHeat)); + component.setReactorPause(storage.extract((int)10e3)); + } + } + setComponentAt(row, col, component); + } else { + setComponentAt(row, col, null); + } + } + } + // next, read the inital temperature and other details. + currentHeat = storage.extract((int)120e3); + if (codeRevision == 0 || (codeRevision >= 1 && pulsed)) { + onPulse = storage.extract((int)5e6); + offPulse = storage.extract((int)5e6); + suspendTemp = storage.extract((int)120e3); + resumeTemp = storage.extract((int)120e3); + } + fluid = storage.extract(1) > 0; + usingReactorCoolantInjectors = storage.extract(1) > 0; + if (codeRevision == 0) { + pulsed = storage.extract(1) > 0; + automated = storage.extract(1) > 0; + } + maxSimulationTicks = storage.extract((int)5e6); + } + + // builds a Base64 code string, not including the prefix. + private String buildCodeString() { + BigintStorage storage = new BigintStorage(); + // first, store the extra details, in reverse order of expected reading. + storage.store(maxSimulationTicks, (int)5e6); + storage.store(usingReactorCoolantInjectors ? 1 : 0, 1); + storage.store(fluid ? 1 : 0, 1); + if (pulsed) { + storage.store(resumeTemp, (int)120e3); + storage.store(suspendTemp, (int)120e3); + storage.store(offPulse, (int)5e6); + storage.store(onPulse, (int)5e6); + } + storage.store((int)currentHeat, (int)120e3); + // grid is read (almost) first, so written (almost) last, and in reverse order + for (int row = grid.length - 1; row >= 0; row--) { + for (int col = grid[row].length - 1; col >= 0; col--) { + ReactorItem component = grid[row][col]; + if (component != null) { + int id = component.id; + // only store automation details for a component if non-default, and add a flag bit to indicate their presence. null components don't even need the flag bit. + if (component.getInitialHeat() > 0 || component.getAutomationThreshold() != ComponentFactory.getDefaultComponent(id).getAutomationThreshold() || component.getReactorPause() != ComponentFactory.getDefaultComponent(id).getReactorPause()) { + if (automated) { + storage.store(component.getReactorPause(), (int)10e3); + storage.store(component.getAutomationThreshold(), (int)1080e3); + } + storage.store((int)component.getInitialHeat(), (int)1080e3); + storage.store(1, 1); + } else { + storage.store(0, 1); + } + storage.store(id, ComponentFactory.MAX_COMPONENT_ID); + } else { + storage.store(0, ComponentFactory.MAX_COMPONENT_ID); + } + } + } + storage.store(automated ? 1 : 0, 1); + storage.store(pulsed ? 1 : 0, 1); + // store the code revision, allowing values up to 255 (8 bits) before adjusting how it is stored in the code. + storage.store(3, 255); + return storage.outputBase64(); + } + + // Get an old-style (pre-2.3.1) code for the reactor, for pasting into older versions of the planner. + public String getOldCode() { + StringBuilder result = new StringBuilder(108); + for (int row = 0; row < grid.length; row++) { + for (int col = 0; col < grid[row].length; col++) { + final ReactorItem component = getComponentAt(row, col); + final int id = (component != null) ? component.id : 0; + result.append(String.format("%02X", id)); //NOI18N + if (component != null && (component.getInitialHeat() > 0 + || (automated && component.getAutomationThreshold() != ComponentFactory.getDefaultComponent(id).getAutomationThreshold()) + || (automated && component.getReactorPause() != ComponentFactory.getDefaultComponent(id).getReactorPause()))) { + result.append("("); + if (component.getInitialHeat() > 0) { + result.append(String.format("h%s,", Integer.toString((int) component.getInitialHeat(), 36))); //NOI18N + } + if (automated && component.getAutomationThreshold() != ComponentFactory.getDefaultComponent(id).getAutomationThreshold()) { + result.append(String.format("a%s,", Integer.toString(component.getAutomationThreshold(), 36))); //NOI18N + } + if (automated && component.getReactorPause() != ComponentFactory.getDefaultComponent(id).getReactorPause()) { + result.append(String.format("p%s,", Integer.toString(component.getReactorPause(), 36))); //NOI18N + } + result.setLength(result.length() - 1); // remove the last comma, whichever parameter it came from. + result.append(")"); + } + } + } + result.append('|'); + if (fluid) { + result.append('f'); + } else { + result.append('e'); + } + if (automated) { + result.append('a'); + } else if (pulsed) { + result.append('p'); + } else { + result.append('s'); + } + if (usingReactorCoolantInjectors) { + result.append('i'); + } else { + result.append('n'); + } + if (currentHeat > 0) { + result.append(Integer.toString((int) currentHeat, 36)); + } + if (pulsed && onPulse != DEFAULT_ON_PULSE) { + result.append(String.format("|n%s", Integer.toString(onPulse, 36))); + } + if (pulsed && offPulse != DEFAULT_OFF_PULSE) { + result.append(String.format("|f%s", Integer.toString(offPulse, 36))); + } + if (pulsed && suspendTemp != DEFAULT_SUSPEND_TEMP) { + result.append(String.format("|s%s", Integer.toString(suspendTemp, 36))); + } + if (pulsed && resumeTemp != DEFAULT_SUSPEND_TEMP) { + result.append(String.format("|r%s", Integer.toString(resumeTemp, 36))); + } + return result.toString(); + } + + /** + * Checks whether the reactor is to simulate a fluid-style reactor, rather than a direct EU-output reactor. + * @return true if this was set to be a fluid-style reactor, false if this was set to be direct EU-output reactor. + */ + public boolean isFluid() { + return fluid; + } + + /** + * Sets whether the reactor is to simulate a fluid-style reactor, rather than a direct EU-output reactor. + * @param fluid true if this is to be a fluid-style reactor, false if this is to be direct EU-output reactor. + */ + public void setFluid(final boolean fluid) { + this.fluid = fluid; + } + + /** + * Checks whether the reactor is using Reactor Coolant Injectors (RCIs) + * @return true if this reactor was set to use RCIs, false otherwise. + */ + public boolean isUsingReactorCoolantInjectors() { + return usingReactorCoolantInjectors; + } + + /** + * Sets whether the reactor is to use Reactor Coolant Injectors (RCIs) + * @param usingReactorCoolantInjectors true if this reactor should use RCIs, false otherwise. + */ + public void setUsingReactorCoolantInjectors(final boolean usingReactorCoolantInjectors) { + this.usingReactorCoolantInjectors = usingReactorCoolantInjectors; + } + + public int getOnPulse() { + return onPulse; + } + + public void setOnPulse(final int onPulse) { + this.onPulse = onPulse; + } + + public int getOffPulse() { + return offPulse; + } + + public void setOffPulse(final int offPulse) { + this.offPulse = offPulse; + } + + public int getSuspendTemp() { + return suspendTemp; + } + + public void setSuspendTemp(final int suspendTemp) { + this.suspendTemp = suspendTemp; + } + + public int getResumeTemp() { + return resumeTemp; + } + + public void setResumeTemp(final int resumeTemp) { + this.resumeTemp = resumeTemp; + } + + public boolean isPulsed() { + return pulsed; + } + + public void setPulsed(boolean pulsed) { + this.pulsed = pulsed; + } + + public boolean isAutomated() { + return automated; + } + + public void setAutomated(boolean automated) { + this.automated = automated; + } + + public int getMaxSimulationTicks() { + return maxSimulationTicks; + } + + public void setMaxSimulationTicks(int maxSimulationTicks) { + this.maxSimulationTicks = maxSimulationTicks; + } + + public void resetPulseConfig() { + onPulse = DEFAULT_ON_PULSE; + offPulse = DEFAULT_OFF_PULSE; + suspendTemp = DEFAULT_SUSPEND_TEMP; + resumeTemp = DEFAULT_RESUME_TEMP; + } + +} diff --git a/src/main/java/Ic2ExpReactorPlanner/SimulationData.java b/src/main/java/Ic2ExpReactorPlanner/SimulationData.java new file mode 100644 index 0000000000..39ecdbc5f5 --- /dev/null +++ b/src/main/java/Ic2ExpReactorPlanner/SimulationData.java @@ -0,0 +1,79 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package Ic2ExpReactorPlanner; + +/** + * Simple container for data from a simulation of an IC2 nuclear reactor, to allow comparison wtih another simulation. + * @author Brian McCloud + */ +public class SimulationData { + // Values should only be written to by the simulator class and read by other classes, but this is not yet strictly enforced. + // Enforcement might require refactoring this to be an inner class of the simulator. + + // Times to temperature thresholds + public int timeToBelow50 = Integer.MAX_VALUE; + public int timeToBurn = Integer.MAX_VALUE; + public int timeToEvaporate = Integer.MAX_VALUE; + public int timeToHurt = Integer.MAX_VALUE; + public int timeToLava = Integer.MAX_VALUE; + public int timeToXplode = Integer.MAX_VALUE; + + // Special, for calculating efficiency + public int totalRodCount = 0; + + // First component broken details + public int firstComponentBrokenTime = Integer.MAX_VALUE; + public int firstComponentBrokenRow = -1; + public int firstComponentBrokenCol = -1; + public String firstComponentBrokenDescription = ""; + public double prebreakTotalEUoutput = 0; + public double prebreakAvgEUoutput = 0; + public double prebreakMinEUoutput = Double.MAX_VALUE; + public double prebreakMaxEUoutput = 0; + public double prebreakTotalHUoutput = 0; + public double prebreakAvgHUoutput = 0; + public double prebreakMinHUoutput = Double.MAX_VALUE; + public double prebreakMaxHUoutput = 0; + + // First rod depleted details + public int firstRodDepletedTime = Integer.MAX_VALUE; + public int firstRodDepletedRow = -1; + public int firstRodDepletedCol = -1; + public String firstRodDepletedDescription = ""; + public double predepleteTotalEUoutput = 0; + public double predepleteAvgEUoutput = 0; + public double predepleteMinEUoutput = Double.MAX_VALUE; + public double predepleteMaxEUoutput = 0; + public double predepleteTotalHUoutput = 0; + public double predepleteAvgHUoutput = 0; + public double predepleteMinHUoutput = Double.MAX_VALUE; + public double predepleteMaxHUoutput = 0; + public double predepleteMinTemp = Double.MAX_VALUE; + public double predepleteMaxTemp = 0; + + // Completed-simulation details + public int totalReactorTicks = 0; + public int totalEUoutput = 0; + public int avgEUoutput = 0; + public double minEUoutput = Double.MAX_VALUE; + public int maxEUoutput = 0; + public int totalHUoutput = 0; + public int avgHUoutput = 0; + public double minHUoutput = Double.MAX_VALUE; + public int maxHUoutput = 0; + public int minTemp = (int) Double.MAX_VALUE; + public int maxTemp = 0; + public int explosionPower = 0; + + // Heating and Cooling details + public double hullHeating = 0; + public double componentHeating = 0; + public double hullCooling = 0; + public double hullCoolingCapacity = 0; + public double ventCooling = 0; + public double ventCoolingCapacity = 0; + +} diff --git a/src/main/java/Ic2ExpReactorPlanner/TaloniusDecoder.java b/src/main/java/Ic2ExpReactorPlanner/TaloniusDecoder.java new file mode 100644 index 0000000000..89232eb007 --- /dev/null +++ b/src/main/java/Ic2ExpReactorPlanner/TaloniusDecoder.java @@ -0,0 +1,26 @@ + +package Ic2ExpReactorPlanner; + +import java.math.BigInteger; + +/** + * Pulls values out of codes from Talonius's old reactor planner. + * @author Brian McCloud + */ +public class TaloniusDecoder { + private BigInteger dataStack = null; + + public TaloniusDecoder(final String dataCode) { + dataStack = new BigInteger(dataCode, 36); + } + + public int readInt(final int bits) { + return readBigInteger(bits).intValue(); + } + + private BigInteger readBigInteger(final int bits) { + BigInteger data = dataStack.and(BigInteger.ONE.shiftLeft(bits).subtract(BigInteger.ONE)); + dataStack = dataStack.shiftRight(bits); + return data; + } +} diff --git a/src/main/java/Ic2ExpReactorPlanner/components/Condensator.java b/src/main/java/Ic2ExpReactorPlanner/components/Condensator.java new file mode 100644 index 0000000000..d38db4c704 --- /dev/null +++ b/src/main/java/Ic2ExpReactorPlanner/components/Condensator.java @@ -0,0 +1,48 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package Ic2ExpReactorPlanner.components; + +import gregtech.api.objects.GT_ItemStack; + +/** + * Represents a condensator in a reactor, either RSH or LZH. + * @author Brian McCloud + */ +public class Condensator extends ReactorItem { + + public Condensator(final int id, final String baseName, GT_ItemStack aItem, final double maxDamage, final double maxHeat, final String sourceMod) { + super(id, baseName, aItem, maxDamage, maxHeat, sourceMod); + } + + public Condensator(final Condensator other) { + super(other); + } + + @Override + public double adjustCurrentHeat(final double heat) { + if (heat < 0.0) { + return heat; + } + currentCondensatorCooling += heat; + bestCondensatorCooling = Math.max(currentCondensatorCooling, bestCondensatorCooling); + double acceptedHeat = Math.min(heat, getMaxHeat() - heat); + double result = heat - acceptedHeat; + currentHeat += acceptedHeat; + maxReachedHeat = Math.max(maxReachedHeat, currentHeat); + return result; + } + + @Override + public boolean needsCoolantInjected() { + return currentHeat > 0.85 * getMaxHeat(); + } + + @Override + public void injectCoolant() { + currentHeat = 0; + } + +} diff --git a/src/main/java/Ic2ExpReactorPlanner/components/CoolantCell.java b/src/main/java/Ic2ExpReactorPlanner/components/CoolantCell.java new file mode 100644 index 0000000000..f152b49985 --- /dev/null +++ b/src/main/java/Ic2ExpReactorPlanner/components/CoolantCell.java @@ -0,0 +1,31 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package Ic2ExpReactorPlanner.components; + +import gregtech.api.objects.GT_ItemStack; + +/** + * Represents a coolant cell in a reactor. + * @author Brian McCloud + */ +public class CoolantCell extends ReactorItem { + + public CoolantCell(final int id, final String baseName, GT_ItemStack aItem, final double maxDamage, final double maxHeat, final String sourceMod) { + super(id, baseName, aItem, maxDamage, maxHeat, sourceMod); + } + + public CoolantCell(final CoolantCell other) { + super(other); + } + + @Override + public double adjustCurrentHeat(final double heat) { + currentCellCooling += heat; + bestCellCooling = Math.max(currentCellCooling, bestCellCooling); + return super.adjustCurrentHeat(heat); + } + +} diff --git a/src/main/java/Ic2ExpReactorPlanner/components/Exchanger.java b/src/main/java/Ic2ExpReactorPlanner/components/Exchanger.java new file mode 100644 index 0000000000..efb9a826e2 --- /dev/null +++ b/src/main/java/Ic2ExpReactorPlanner/components/Exchanger.java @@ -0,0 +1,130 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package Ic2ExpReactorPlanner.components; + +import java.util.ArrayList; +import java.util.List; + +import gregtech.api.objects.GT_ItemStack; + +/** + * Represents a heat exchanger of some sort in a reactor. + * @author Brian McCloud + */ +public class Exchanger extends ReactorItem { + + private final int switchSide; + private final int switchReactor; + + public Exchanger(final int id, final String baseName, GT_ItemStack aItem, final double maxDamage, final double maxHeat, final String sourceMod, final int switchSide, final int switchReactor) { + super(id, baseName, aItem, maxDamage, maxHeat, sourceMod); + this.switchSide = switchSide; + this.switchReactor = switchReactor; + } + + public Exchanger(final Exchanger other) { + super(other); + this.switchSide = other.switchSide; + this.switchReactor = other.switchReactor; + } + + @Override + public void transfer() { + List<ReactorItem> heatableNeighbors = new ArrayList<>(4); + ReactorItem component = parent.getComponentAt(row, col - 1); + if (component != null && component.isHeatAcceptor()) { + heatableNeighbors.add(component); + } + component = parent.getComponentAt(row, col + 1); + if (component != null && component.isHeatAcceptor()) { + heatableNeighbors.add(component); + } + component = parent.getComponentAt(row - 1, col); + if (component != null && component.isHeatAcceptor()) { + heatableNeighbors.add(component); + } + component = parent.getComponentAt(row + 1, col); + if (component != null && component.isHeatAcceptor()) { + heatableNeighbors.add(component); + } + // Code adapted from decompiled IC2 code, class ItemReactorHeatSwitch, with permission from Thunderdark. + double myHeat = 0; + if (switchSide > 0) { + for (ReactorItem heatableNeighbor : heatableNeighbors) { + double mymed = getCurrentHeat() * 100.0 / getMaxHeat(); + double heatablemed = heatableNeighbor.getCurrentHeat() * 100.0 / heatableNeighbor.getMaxHeat(); + + double add = (int) (heatableNeighbor.getMaxHeat() / 100.0 * (heatablemed + mymed / 2.0)); + if (add > switchSide) { + add = switchSide; + } + if (heatablemed + mymed / 2.0 < 1.0) { + add = switchSide / 2; + } + if (heatablemed + mymed / 2.0 < 0.75) { + add = switchSide / 4; + } + if (heatablemed + mymed / 2.0 < 0.5) { + add = switchSide / 8; + } + if (heatablemed + mymed / 2.0 < 0.25) { + add = 1; + } + if (Math.round(heatablemed * 10.0) / 10.0 > Math.round(mymed * 10.0) / 10.0) { + add -= 2 * add; + } else if (Math.round(heatablemed * 10.0) / 10.0 == Math.round(mymed * 10.0) / 10.0) { + add = 0; + } + myHeat -= add; + if (add > 0) { + currentComponentHeating += add; + } + add = heatableNeighbor.adjustCurrentHeat(add); + myHeat += add; + } + } + if (switchReactor > 0) { + double mymed = getCurrentHeat() * 100.0 / getMaxHeat(); + double Reactormed = parent.getCurrentHeat() * 100.0 / parent.getMaxHeat(); + + int add = (int) Math.round(parent.getMaxHeat() / 100.0 * (Reactormed + mymed / 2.0)); + if (add > switchReactor) { + add = switchReactor; + } + if (Reactormed + mymed / 2.0 < 1.0) { + add = switchSide / 2; + } + if (Reactormed + mymed / 2.0 < 0.75) { + add = switchSide / 4; + } + if (Reactormed + mymed / 2.0 < 0.5) { + add = switchSide / 8; + } + if (Reactormed + mymed / 2.0 < 0.25) { + add = 1; + } + if (Math.round(Reactormed * 10.0) / 10.0 > Math.round(mymed * 10.0) / 10.0) { + add -= 2 * add; + } else if (Math.round(Reactormed * 10.0) / 10.0 == Math.round(mymed * 10.0) / 10.0) { + add = 0; + } + myHeat -= add; + parent.adjustCurrentHeat(add); + if (add > 0) { + currentHullHeating = add; + } else { + currentHullCooling = -add; + } + } + adjustCurrentHeat(myHeat); + } + + @Override + public double getHullCoolingCapacity() { + return switchReactor; + } + +} diff --git a/src/main/java/Ic2ExpReactorPlanner/components/FuelRod.java b/src/main/java/Ic2ExpReactorPlanner/components/FuelRod.java new file mode 100644 index 0000000000..588e221424 --- /dev/null +++ b/src/main/java/Ic2ExpReactorPlanner/components/FuelRod.java @@ -0,0 +1,166 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package Ic2ExpReactorPlanner.components; + +import java.util.ArrayList; +import java.util.List; + +import gregtech.api.objects.GT_ItemStack; + +/** + * Represents some form of fuel rod (may be single, dual, or quad). + * @author Brian McCloud + */ +public class FuelRod extends ReactorItem { + + private final int energyMult; + private final double heatMult; + private final int rodCount; + private final boolean moxStyle; + + private static boolean GT509behavior = false; + private static boolean GTNHbehavior = false; + + public static void setGT509Behavior(boolean value) { + GT509behavior = value; + } + + public static void setGTNHBehavior(boolean value) { + GTNHbehavior = value; + } + + public FuelRod(final int id, final String baseName, GT_ItemStack aItem, final double maxDamage, final double maxHeat, final String sourceMod, + final int energyMult, final double heatMult, final int rodCount, final boolean moxStyle) { + super(id, baseName, aItem, maxDamage, maxHeat, sourceMod); + this.energyMult = energyMult; + this.heatMult = heatMult; + this.rodCount = rodCount; + this.moxStyle = moxStyle; + } + + public FuelRod(final FuelRod other) { + super(other); + this.energyMult = other.energyMult; + this.heatMult = other.heatMult; + this.rodCount = other.rodCount; + this.moxStyle = other.moxStyle; + } + + @Override + public boolean isNeutronReflector() { + return !isBroken(); + } + + private int countNeutronNeighbors() { + int neutronNeighbors = 0; + ReactorItem component = parent.getComponentAt(row + 1, col); + if (component != null && component.isNeutronReflector()) { + neutronNeighbors++; + } + component = parent.getComponentAt(row - 1, col); + if (component != null && component.isNeutronReflector()) { + neutronNeighbors++; + } + component = parent.getComponentAt(row, col - 1); + if (component != null && component.isNeutronReflector()) { + neutronNeighbors++; + } + component = parent.getComponentAt(row, col + 1); + if (component != null && component.isNeutronReflector()) { + neutronNeighbors++; + } + return neutronNeighbors; + } + + protected void handleHeat(final int heat) { + List<ReactorItem> heatableNeighbors = new ArrayList<>(4); + ReactorItem component = parent.getComponentAt(row + 1, col); + if (component != null && component.isHeatAcceptor()) { + heatableNeighbors.add(component); + } + component = parent.getComponentAt(row - 1, col); + if (component != null && component.isHeatAcceptor()) { + heatableNeighbors.add(component); + } + component = parent.getComponentAt(row, col - 1); + if (component != null && component.isHeatAcceptor()) { + heatableNeighbors.add(component); + } + component = parent.getComponentAt(row, col + 1); + if (component != null && component.isHeatAcceptor()) { + heatableNeighbors.add(component); + } + if (heatableNeighbors.isEmpty()) { + parent.adjustCurrentHeat(heat); + currentHullHeating = heat; + } else { + currentComponentHeating = heat; + for (ReactorItem heatableNeighbor : heatableNeighbors) { + heatableNeighbor.adjustCurrentHeat(heat / heatableNeighbors.size()); + } + int remainderHeat = heat % heatableNeighbors.size(); + heatableNeighbors.get(0).adjustCurrentHeat(remainderHeat); + } + } + + @Override + public double generateHeat() { + int pulses = countNeutronNeighbors() + (rodCount == 1 ? 1 : (rodCount == 2) ? 2 : 3); + int heat = (int)(heatMult * pulses * (pulses + 1)); + if (moxStyle && parent.isFluid() && (parent.getCurrentHeat() / parent.getMaxHeat()) > 0.5) { + heat *= 2; + } + currentHeatGenerated = heat; + minHeatGenerated = Math.min(minHeatGenerated, heat); + maxHeatGenerated = Math.max(maxHeatGenerated, heat); + handleHeat(heat); + return currentHeatGenerated; + } + + @Override + public double generateEnergy() { + int pulses = countNeutronNeighbors() + (rodCount == 1 ? 1 : (rodCount == 2) ? 2 : 3); + double energy = energyMult * pulses; + if (GT509behavior || "GT5".equals(sourceMod)) { + energy *= 2;//EUx2 if from GT5.09 or in GT5.09 mode + if (moxStyle) { + energy *= (1 + 1.5 * parent.getCurrentHeat() / parent.getMaxHeat()); + } + } + else if (GTNHbehavior || "GTNH".equals(sourceMod)) { + energy *= 10;//EUx10 if from GTNH or in GTNH mode + if (moxStyle) { + energy *= (1 + 1.5 * parent.getCurrentHeat() / parent.getMaxHeat()); + } + } else if (moxStyle) { + energy *= (1 + 4.0 * parent.getCurrentHeat() / parent.getMaxHeat()); + } + minEUGenerated = Math.min(minEUGenerated, energy); + maxEUGenerated = Math.max(maxEUGenerated, energy); + currentEUGenerated = energy; + parent.addEUOutput(energy); + applyDamage(1.0); + return energy; + } + + @Override + public int getRodCount() { + return rodCount; + } + + @Override + public double getCurrentOutput() { + if (parent != null) { + if (parent.isFluid()) { + return currentHeatGenerated; + } else { + return currentEUGenerated; + } + } + return 0; + } + +} diff --git a/src/main/java/Ic2ExpReactorPlanner/components/Plating.java b/src/main/java/Ic2ExpReactorPlanner/components/Plating.java new file mode 100644 index 0000000000..d792e06035 --- /dev/null +++ b/src/main/java/Ic2ExpReactorPlanner/components/Plating.java @@ -0,0 +1,49 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package Ic2ExpReactorPlanner.components; + +import Ic2ExpReactorPlanner.Reactor; +import gregtech.api.objects.GT_ItemStack; + +/** + * Represents some form of plating, which changes how much heat the reactor can + * hold before causing external effects (up to and including explosion), as well + * as somewhat reducing explosion power. + * @author Brian McCloud + */ +public class Plating extends ReactorItem { + + private final int heatAdjustment; + + public Plating(final int id, final String baseName, GT_ItemStack aItem, final double maxDamage, final double maxHeat, final String sourceMod, final int heatAdjustment, final double explosionPowerMultiplier) { + super(id, baseName, aItem, maxDamage, maxHeat, sourceMod); + this.heatAdjustment = heatAdjustment; + this.explosionPowerMultiplier = explosionPowerMultiplier; + } + + public Plating(Plating other) { + super(other); + this.heatAdjustment = other.heatAdjustment; + this.explosionPowerMultiplier = other.explosionPowerMultiplier; + } + + @Override + public void addToReactor(final Reactor parent, final int row, final int col) { + super.addToReactor(parent, row, col); + if (parent != null) { + parent.adjustMaxHeat(heatAdjustment); + } + } + + @Override + public void removeFromReactor() { + if (parent != null) { + parent.adjustMaxHeat(-heatAdjustment); + } + super.removeFromReactor(); + } + +} diff --git a/src/main/java/Ic2ExpReactorPlanner/components/ReactorItem.java b/src/main/java/Ic2ExpReactorPlanner/components/ReactorItem.java new file mode 100644 index 0000000000..9a9b1ce341 --- /dev/null +++ b/src/main/java/Ic2ExpReactorPlanner/components/ReactorItem.java @@ -0,0 +1,531 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package Ic2ExpReactorPlanner.components; + +import java.util.HashMap; +import java.util.ResourceBundle; + +import Ic2ExpReactorPlanner.Reactor; +import gregtech.api.objects.GT_ItemStack; + +/** + * Represents an item (component) in an IndustrialCraft2 Experimental Nuclear + * Reactor. + * + * @author Brian McCloud + */ +public class ReactorItem { + + public static HashMap<String, ReactorItem> sComponentMap = new HashMap<String, ReactorItem>(); + // Fundamental values, set at object instantiation, should never need to be + // changed. + public final int id; + public final String baseName; // this is the non-localized version, for + // internal program use + public final String name; // this is expected to be localized, for display + // usage. + protected double maxDamage; + + public double getMaxDamage() { + return maxDamage; + } + + protected double maxHeat; + + public double getMaxHeat() { + return maxHeat; + } + + public final String sourceMod; // for potentially adjusting controls based + // on whether the mod is in use, will be + // null to indicate the item is part of base + // IC2. + public final GT_ItemStack mItem; + + // Simulation setting values + private double initialHeat = 0; + + public double getInitialHeat() { + return initialHeat; + } + + public void setInitialHeat(final double value) { + if (this.isHeatAcceptor() && value >= 0 && value < this.maxHeat) { + initialHeat = value; + } + } + private int automationThreshold = 9000; + + public int getAutomationThreshold() { + return automationThreshold; + } + + public void setAutomationThreshold(final int value) { + if (maxHeat > 1 || maxDamage > 1) { + automationThreshold = value; + } + } + private int reactorPause = 0; + + public int getReactorPause() { + return reactorPause; + } + + public void setReactorPause(final int value) { + if (maxHeat > 1 || maxDamage > 1) { + reactorPause = value; + } + } + + // fields below here are not to be copied by the copy constructor. + + // Parent reactor and position + protected Reactor parent = null; + protected int row = -10; + protected int col = -10; + + // Special variable for holding information about this item from last + // simulation. + // Usage of StringBuffer instead of StringBuilder is deliberate - this may + // be accessed by + // both the simulation worker thread and the event dispatch thread. + public final StringBuffer info = new StringBuffer(1000); + + // Calculated values - readable from outside, but only writable by + // subclasses. + protected double currentDamage = 0; + public double getCurrentDamage() { + return currentDamage; + } + protected double currentHeat = 0; + public double getCurrentHeat() { + return currentHeat; + } + protected double maxReachedHeat = 0; + public double getMaxReachedHeat() { + return maxReachedHeat; + } + + protected double currentEUGenerated = 0; + public double getCurrentEUGenerated() { + return currentEUGenerated; + } + protected double minEUGenerated = Double.MAX_VALUE; + public double getMinEUGenerated() { + return minEUGenerated; + } + protected double maxEUGenerated = 0; + public double getMaxEUGenerated() { + return maxEUGenerated; + } + + protected double currentHeatGenerated = 0; + public double getCurrentHeatGenerated() { + return currentHeatGenerated; + } + protected double minHeatGenerated = Double.MAX_VALUE; + public double getMinHeatGenerated() { + return minHeatGenerated; + } + protected double maxHeatGenerated = 0; + public double getMaxHeatGenerated() { + return maxHeatGenerated; + } + + protected double currentHullHeating = 0; + public double getCurrentHullHeating() { + return currentHullHeating; + } + protected double currentComponentHeating = 0; + public double getCurrentComponentHeating() { + return currentComponentHeating; + } + protected double currentHullCooling = 0; + public double getCurrentHullCooling() { + return currentHullCooling; + } + protected double currentVentCooling = 0; + public double getCurrentVentCooling() { + return currentVentCooling; + } + protected double bestVentCooling = 0; + public double getBestVentCooling() { + return bestVentCooling; + } + + protected double currentCellCooling = 0; + public double getCurrentCellCooling() { + return currentCellCooling; + } + protected double bestCellCooling = 0; + public double getBestCellCooling() { + return bestCellCooling; + } + + protected double currentCondensatorCooling = 0; + public double getCurrentCondensatorCooling() { + return currentCondensatorCooling; + } + protected double bestCondensatorCooling = 0; + public double getBestCondensatorCooling() { + return bestCondensatorCooling; + } + + protected double explosionPowerMultiplier = 1; + + protected ReactorItem(final int id, final String baseName, GT_ItemStack aItem, final double maxDamage, final double maxHeat, String sourceMod) { + this.id = id; + this.baseName = baseName; + this.name = aItem.mItem.getItemStackDisplayName(aItem.toStack()); + this.mItem = aItem; + this.maxDamage = maxDamage; + this.maxHeat = maxHeat; + if (maxHeat > 1) { + automationThreshold = (int) (maxHeat * 0.9); + } + else if (maxDamage > 1) { + automationThreshold = (int) (maxDamage * 1.1); + } + if (sourceMod == null) { + sourceMod = "IC2"; + } + this.sourceMod = sourceMod; + sComponentMap.put(sourceMod+"."+aItem.mItem.getUnlocalizedName()+"."+aItem.mMetaData, this); + } + + // Protected copy constructor for use by subclasses. Generalized copying + // should be done with a method in ComponentFactory (which can check which + // subclass copy constructor to use). + protected ReactorItem(final ReactorItem other) { + this.id = other.id; + this.baseName = other.baseName; + this.name = other.name; + this.mItem = other.mItem; + this.maxDamage = other.maxDamage; + this.maxHeat = other.maxHeat; + this.initialHeat = other.initialHeat; + this.automationThreshold = other.automationThreshold; + this.reactorPause = other.reactorPause; + this.sourceMod = other.sourceMod; + } + + /** + * Gets the name of the component, and the initial heat (if applicable). + * + * @return the name of this component, and potentially initial heat. + */ + @Override + public String toString() { + String result = name; + if (initialHeat > 0) { + result += String.format("\u0020(initial heat: %,d)", (int) initialHeat); + } + return result; + } + + /** + * Checks if this component can accept heat. (e.g. from adjacent fuel rods, + * or from an exchanger) + * + * @return true if this component can accept heat, false otherwise. + */ + public boolean isHeatAcceptor() { + // maxHeat of 1 means this component never accepts heat (though it might + // take damage instead) + return maxHeat > 1 && !isBroken(); + } + + /** + * Determines if this component can be cooled down, such as by a component + * heat vent. + * + * @return true if this component can be cooled down, false otherwise. + */ + public boolean isCoolable() { + return maxHeat > 1 && !(this instanceof Condensator); + } + + /** + * Checks if this component acts as a neutron reflector, and boosts + * performance of adjacent fuel rods, either by being a "neutron reflector" + * item or by being a fuel rod. + * + * @return true if this component reflects neutrons, false otherwise. + */ + public boolean isNeutronReflector() { + return false; + } + + /** + * Prepare for a new reactor tick. + */ + public void preReactorTick() { + currentHullHeating = 0.0; + currentComponentHeating = 0.0; + currentHullCooling = 0.0; + currentVentCooling = 0.0; + currentCellCooling = 0.0; + currentCondensatorCooling = 0.0; + currentEUGenerated = 0; + currentHeatGenerated = 0; + } + + /** + * Generate heat if appropriate for component type, and spread to reactor or + * adjacent cells. + * + * @return the amount of heat generated by this component. + */ + public double generateHeat() { + return 0.0; + } + + /** + * Generate energy if appropriate for component type. + * + * @return the number of EU generated by this component during the current + * reactor tick. + */ + public double generateEnergy() { + return 0.0; + } + + /** + * Dissipate (aka vent) heat if appropriate for component type. + * + * @return the amount of heat successfully vented during the current reactor + * tick. + */ + public double dissipate() { + return 0.0; + } + + /** + * Transfer heat between component, neighbors, and/or reactor, if + * appropriate for component type. + */ + public void transfer() { + // do nothing by default. + } + + /** + * Adds this component to a new reactor, and applies changes to the reactor + * when adding this component if appropriate, such as for reactor plating. + * + * @param parent + * the reactor to add this component to. + * @param row + * the row this component will be in. + * @param col + * the column this component will be in. + */ + public void addToReactor(final Reactor parent, final int row, final int col) { + // call removeFromReactor first, in case it had previously been added to + // a different reactor (unlikely) + removeFromReactor(); + this.parent = parent; + this.row = row; + this.col = col; + } + + /** + * Removes this component from its reactor (if any), and applies changes to + * the reactor when removing this component if appropriate, such as for + * reactor plating. + */ + public void removeFromReactor() { + parent = null; + this.row = -10; + this.col = -10; + } + + /** + * Resets heat to 0 (used when resetting simulation). + */ + public final void clearCurrentHeat() { + currentHeat = initialHeat; + bestVentCooling = 0.0; + bestCondensatorCooling = 0.0; + bestCellCooling = 0.0; + minEUGenerated = Double.MAX_VALUE; + maxEUGenerated = 0.0; + minHeatGenerated = Double.MAX_VALUE; + maxHeatGenerated = 0.0; + maxReachedHeat = initialHeat; + } + + /** + * Adjusts the component heat up or down + * + * @param heat + * the amount of heat to adjust by (positive to add heat, + * negative to remove heat). + * @return the amount of heat adjustment refused. (e.g. due to going below + * minimum heat, breaking due to excessive heat, or attempting to + * remove heat from a condensator) + */ + public double adjustCurrentHeat(final double heat) { + if (isHeatAcceptor()) { + double result = 0.0; + double tempHeat = getCurrentHeat(); + tempHeat += heat; + if (tempHeat > getMaxHeat()) { + result = getMaxHeat() - tempHeat + 1; + tempHeat = getMaxHeat(); + } + else if (tempHeat < 0.0) { + result = tempHeat; + tempHeat = 0.0; + } + currentHeat = tempHeat; + maxReachedHeat = Math.max(maxReachedHeat, currentHeat); + return result; + } + return heat; + } + + /** + * Clears the damage back to 0 (used when resetting simulation, or replacing + * the component in an automation simulation). + */ + public final void clearDamage() { + currentDamage = 0.0; + } + + /** + * Applies damage to the component, as opposed to heat. Mainly used for fuel + * rods and neutron reflectors that lose durability as the reactor runs, but + * can't recover it via cooling. + * + * @param damage + * the damage to apply (only used if positive). + */ + public final void applyDamage(final double damage) { + // maxDamage of 1 is treated as meaning the component doesn't accept + // damage (though it might accept heat instead) + // if someone actually writes a mod with such a flimsy component, I + // might have to rethink this. + if (maxDamage > 1 && damage > 0.0) { + currentDamage += damage; + } + } + + /** + * Determines if this component is broken in the current tick of the + * simulation + * + * @return true if the component has broken either from damage (e.g. neutron + * reflectors, fuel rods) or from heat (e.g. heat vents, coolant + * cells), false otherwise. + */ + public boolean isBroken() { + return currentHeat >= getMaxHeat() || currentDamage >= getMaxDamage(); + } + + /** + * The number of fuel rods in this component (0 for non-fuel-rod + * components). + * + * @return The number of fuel rods in this component, or 0 if this component + * has no fuel rods. + */ + public int getRodCount() { + return 0; + } + + /** + * Gets a value added in the formula for calculating explosion power. + * + * @return the additive value for explosion power caused by this component, + * or 0 if this component doesn't affect the addition part of the + * explosion calculation. + */ + public double getExplosionPowerOffset() { + if (!isBroken()) { + if (getRodCount() == 0 && isNeutronReflector()) { + return -1; + } + return 2 * getRodCount(); // all known fuel rods (including those + // from GT) use this formula, and + // non-rod components return 0 for + // getRodCount + } + return 0; + } + + /** + * Gets a value multiplied in the formula for calculating explosion power. + * + * @return the multiplier value for explosion power caused by this + * component, or 1 if this component doesn't affect the + * multiplication part of the explosion calculation. + */ + public double getExplosionPowerMultiplier() { + return explosionPowerMultiplier; + } + + /** + * Finds the theoretical maximum venting of this component, regardless of + * whether this venting is from itself, directly from the reactor, or from + * adjacent components. + * + * @return the capacity of this component to vent heat. + */ + public double getVentCoolingCapacity() { + return 0; + } + + /** + * Finds the theoretical maximum hull cooling of this component. + * + * @return the capacity of this component to remove heat from the reactor + * hull. + */ + public double getHullCoolingCapacity() { + return 0; + } + + /** + * Gets the current "output" of this component, presumably for writing to + * CSV data. What this "output" means may vary by component type or reactor + * type. + * + * @return the output of this component for the current reactor tick. + */ + public double getCurrentOutput() { + return 0; + } + + /** + * Determines whether this component expects to produces some sort of output + * each reactor tick, e.g. for purposes of tracking in a CSV file. + * + * @return true if this component produces output (such as EU or vented + * heat), false otherwise. + */ + public boolean producesOutput() { + return getVentCoolingCapacity() > 0 || getRodCount() > 0; + } + + /** + * Determines if this component needs input from a Reactor Coolant Injector. + * Simply returns false for non-condensator items. + * + * @return true if this is a condensator that has absorbed enough heat to + * require the appropriate item added to repair it, false otherwise. + */ + public boolean needsCoolantInjected() { + return false; + } + + /** + * Simulates having a coolant item added by a Reactor Coolant Injector. + */ + public void injectCoolant() { + // do nothing by default. + } + +} diff --git a/src/main/java/Ic2ExpReactorPlanner/components/Reflector.java b/src/main/java/Ic2ExpReactorPlanner/components/Reflector.java new file mode 100644 index 0000000000..7851a62804 --- /dev/null +++ b/src/main/java/Ic2ExpReactorPlanner/components/Reflector.java @@ -0,0 +1,63 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package Ic2ExpReactorPlanner.components; + +import gregtech.api.objects.GT_ItemStack; + +/** + * Represents a neutron reflector in a reactor. + * @author Brian McCloud + */ +public class Reflector extends ReactorItem { + + private static String mcVersion = "1.12.2"; + + public Reflector(final int id, final String baseName, final GT_ItemStack aStack, final double maxDamage, final double maxHeat, final String sourceMod) { + super(id, baseName, aStack, maxDamage, maxHeat, sourceMod); + } + + public Reflector(final Reflector other) { + super(other); + } + + @Override + public boolean isNeutronReflector() { + return !isBroken(); + } + + @Override + public double generateHeat() { + ReactorItem component = parent.getComponentAt(row - 1, col); + if (component != null) { + applyDamage(component.getRodCount()); + } + component = parent.getComponentAt(row, col + 1); + if (component != null) { + applyDamage(component.getRodCount()); + } + component = parent.getComponentAt(row + 1, col); + if (component != null) { + applyDamage(component.getRodCount()); + } + component = parent.getComponentAt(row, col - 1); + if (component != null) { + applyDamage(component.getRodCount()); + } + return 0; + } + + @Override + public double getMaxDamage() { + if (maxDamage > 1 && "1.7.10".equals(mcVersion)) { + return maxDamage / 3; + } + return maxDamage; + } + + public static void setMcVersion(String newVersion) { + mcVersion = newVersion; + } +} diff --git a/src/main/java/Ic2ExpReactorPlanner/components/Vent.java b/src/main/java/Ic2ExpReactorPlanner/components/Vent.java new file mode 100644 index 0000000000..a43383f694 --- /dev/null +++ b/src/main/java/Ic2ExpReactorPlanner/components/Vent.java @@ -0,0 +1,110 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package Ic2ExpReactorPlanner.components; + +import java.util.ArrayList; +import java.util.List; + +import gregtech.api.objects.GT_ItemStack; + +/** + * Represents some kind of vent in a reactor. + * @author Brian McCloud + */ +public class Vent extends ReactorItem { + + private final int selfVent; + private final int hullDraw; + private final int sideVent; + + public Vent(final int id, final String baseName, GT_ItemStack aItem, final double maxDamage, final double maxHeat, final String sourceMod, + final int selfVent, final int hullDraw, final int sideVent) { + super(id, baseName, aItem, maxDamage, maxHeat, sourceMod); + this.selfVent = selfVent; + this.hullDraw = hullDraw; + this.sideVent = sideVent; + } + + public Vent(final Vent other) { + super(other); + this.selfVent = other.selfVent; + this.hullDraw = other.hullDraw; + this.sideVent = other.sideVent; + } + + @Override + public double dissipate() { + double deltaHeat = Math.min(hullDraw, parent.getCurrentHeat()); + currentHullCooling = deltaHeat; + parent.adjustCurrentHeat(-deltaHeat); + this.adjustCurrentHeat(deltaHeat); + final double currentDissipation = Math.min(selfVent, getCurrentHeat()); + currentVentCooling = currentDissipation; + parent.ventHeat(currentDissipation); + adjustCurrentHeat(-currentDissipation); + if (sideVent > 0) { + List<ReactorItem> coolableNeighbors = new ArrayList<>(4); + ReactorItem component = parent.getComponentAt(row - 1, col); + if (component != null && component.isCoolable()) { + coolableNeighbors.add(component); + } + component = parent.getComponentAt(row, col + 1); + if (component != null && component.isCoolable()) { + coolableNeighbors.add(component); + } + component = parent.getComponentAt(row + 1, col); + if (component != null && component.isCoolable()) { + coolableNeighbors.add(component); + } + component = parent.getComponentAt(row, col - 1); + if (component != null && component.isCoolable()) { + coolableNeighbors.add(component); + } + for (ReactorItem coolableNeighbor : coolableNeighbors) { + double rejectedCooling = coolableNeighbor.adjustCurrentHeat(-sideVent); + double tempDissipatedHeat = sideVent + rejectedCooling; + parent.ventHeat(tempDissipatedHeat); + currentVentCooling += tempDissipatedHeat; + } + } + bestVentCooling = Math.max(bestVentCooling, currentVentCooling); + return currentDissipation; + } + + @Override + public double getVentCoolingCapacity() { + double result = selfVent; + if (sideVent > 0) { + ReactorItem component = parent.getComponentAt(row - 1, col); + if (component != null && component.isCoolable()) { + result += sideVent; + } + component = parent.getComponentAt(row, col + 1); + if (component != null && component.isCoolable()) { + result += sideVent; + } + component = parent.getComponentAt(row + 1, col); + if (component != null && component.isCoolable()) { + result += sideVent; + } + component = parent.getComponentAt(row, col - 1); + if (component != null && component.isCoolable()) { + result += sideVent; + } + } + return result; + } + + @Override + public double getHullCoolingCapacity() { + return hullDraw; + } + + @Override + public double getCurrentOutput() { + return currentVentCooling; + } + } |