diff --git a/changelog b/changelog index 4e97bda2f..0aa88b91f 100644 --- a/changelog +++ b/changelog @@ -12,6 +12,10 @@ * When cutting down hemp, willows, and other NTM double plants, only the top part will be cut off, no matter where the thresher touches it * Can also harvest items from sunflowers and tall grass without breaking the blocks * Can also handle tall plants like cacti and sugar cane +* New big-ass tank + * Successor to the BAT9k + * Larger footprint, way more storage space + * Needs to be placed on a proper foundation or else it'll sink into the ground ## Changed * RoR graphs can now have their min and max bounds set to a fixed number @@ -29,7 +33,7 @@ * Increased the yield of lye from wood ash * All fluid tanks larger than barrels now have dynamic IO speed instead of providing their entire contents instantly * Tank fill will slow down the fuller the tank becomes, tank drain also slow downs as it gets emptier - * This means that multiple tanks in IO mode in the same network will still ping pong, but instead of firing their entire contents over the network, they will even out + * This means that multiple tanks in IO mode in the same network will still ping pong, but only a small portion of fluid will be "lost" (i.e. invisible due to subtick transfer operations) * The stinger is now classified as a special weapon * The carbine can now take a scope * The battery socket now renders supports when a block is placed on top of it, allowing somewhat aesthetic stacking of sockets diff --git a/src/main/java/com/hbm/blocks/ModBlocks.java b/src/main/java/com/hbm/blocks/ModBlocks.java index 11b564ded..d74274682 100644 --- a/src/main/java/com/hbm/blocks/ModBlocks.java +++ b/src/main/java/com/hbm/blocks/ModBlocks.java @@ -778,6 +778,7 @@ public class ModBlocks { public static Block radio_torch_reader; public static Block radio_torch_controller; public static Block radio_telex; + public static Block radio_autocal; public static Block oc_cable_paintable; public static Block conveyor; @@ -1894,6 +1895,7 @@ public class ModBlocks { radio_torch_reader = new RadioTorchReader().setBlockName("radio_torch_reader").setHardness(0.1F).setResistance(10.0F).setCreativeTab(MainRegistry.machineTab).setBlockTextureName(RefStrings.MODID + ":rtty_reader"); radio_torch_controller = new RadioTorchController().setBlockName("radio_torch_controller").setHardness(0.1F).setResistance(10.0F).setCreativeTab(MainRegistry.machineTab).setBlockTextureName(RefStrings.MODID + ":rtty_controller"); radio_telex = new RadioTelex().setBlockName("radio_telex").setHardness(3F).setResistance(10.0F).setCreativeTab(MainRegistry.machineTab).setBlockTextureName(RefStrings.MODID + ":radio_telex"); + radio_autocal = new RadioAUTOCAL().setBlockName("radio_autocal").setHardness(3F).setResistance(10.0F).setCreativeTab(MainRegistry.machineTab).setBlockTextureName(RefStrings.MODID + ":block_steel"); conveyor = new BlockConveyor().setBlockName("conveyor").setHardness(2.0F).setResistance(2.0F).setCreativeTab(null).setBlockTextureName(RefStrings.MODID + ":conveyor"); conveyor_express = new BlockConveyorExpress().setBlockName("conveyor_express").setHardness(2.0F).setResistance(2.0F).setCreativeTab(null).setBlockTextureName(RefStrings.MODID + ":conveyor_express"); @@ -3175,6 +3177,7 @@ public class ModBlocks { register(radio_torch_reader); register(radio_torch_controller); register(radio_telex); + register(radio_autocal); register(crane_extractor); register(crane_inserter); diff --git a/src/main/java/com/hbm/blocks/machine/MachineBigAssTank9000.java b/src/main/java/com/hbm/blocks/machine/MachineBigAssTank9000.java index bb8759c0d..83884b4f3 100644 --- a/src/main/java/com/hbm/blocks/machine/MachineBigAssTank9000.java +++ b/src/main/java/com/hbm/blocks/machine/MachineBigAssTank9000.java @@ -27,6 +27,7 @@ import net.minecraftforge.common.util.ForgeDirection; import java.util.ArrayList; import java.util.List; +@Deprecated public class MachineBigAssTank9000 extends BlockDummyable implements IPersistentInfoProvider { public MachineBigAssTank9000(Material mat) { diff --git a/src/main/java/com/hbm/blocks/network/RadioAUTOCAL.java b/src/main/java/com/hbm/blocks/network/RadioAUTOCAL.java new file mode 100644 index 000000000..b1e20c3be --- /dev/null +++ b/src/main/java/com/hbm/blocks/network/RadioAUTOCAL.java @@ -0,0 +1,25 @@ +package com.hbm.blocks.network; + +import com.hbm.blocks.machine.BlockMachineBase; +import com.hbm.tileentity.network.TileEntityRadioAUTOCAL; + +import net.minecraft.block.material.Material; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.World; + +public class RadioAUTOCAL extends BlockMachineBase { + + public RadioAUTOCAL() { + super(Material.iron, 0); + this.rotatable = true; + } + + @Override + public TileEntity createNewTileEntity(World world, int meta) { + return new TileEntityRadioAUTOCAL(); + } + + @Override public int getRenderType(){ return -1; } + @Override public boolean isOpaqueCube() { return false; } + @Override public boolean renderAsNormalBlock() { return false; } +} diff --git a/src/main/java/com/hbm/config/GeneralConfig.java b/src/main/java/com/hbm/config/GeneralConfig.java index a4a37e7e9..cbabff837 100644 --- a/src/main/java/com/hbm/config/GeneralConfig.java +++ b/src/main/java/com/hbm/config/GeneralConfig.java @@ -129,7 +129,7 @@ public class GeneralConfig { preferredOutputMod = CommonConfig.createConfigStringList(config,CATEGORY_GENERAL,"1.42_preferredOutputMod", "The mod which is preferred as output when certain machines autogenerate recipes. Currently used for the shredder", new String[] {RefStrings.MODID}); enableLoadScreenReplacement = config.get(CATEGORY_GENERAL, "1.43_enableLoadScreenReplacement", true, "Tries to replace the vanilla load screen with the 'tip of the day' one, may clash with other mods trying to do the same.").getBoolean(true); - enableMachineGravity = config.get(CATEGORY_GENERAL, "1.44_enableMachineGravity", true, "Requires large large machines to have a proper foundation, or else they tilt and break. Independent from the 528 version of this config, which does the same, but only works with 528 enabled.").getBoolean(false); + enableMachineGravity = config.get(CATEGORY_GENERAL, "1.44_enableMachineGravity", false, "Requires large large machines to have a proper foundation, or else they tilt and break. Independent from the 528 version of this config, which does the same, but only works with 528 enabled.").getBoolean(false); enableExpensiveMode = config.get(CATEGORY_GENERAL, "1.99_enableExpensiveMode", false, "It does what the name implies.").getBoolean(false); final String CATEGORY_528 = CommonConfig.CATEGORY_528; diff --git a/src/main/java/com/hbm/inventory/gui/GUICalculator.java b/src/main/java/com/hbm/inventory/gui/GUICalculator.java index 9ac4bbda0..63933eec5 100644 --- a/src/main/java/com/hbm/inventory/gui/GUICalculator.java +++ b/src/main/java/com/hbm/inventory/gui/GUICalculator.java @@ -1,5 +1,6 @@ package com.hbm.inventory.gui; +import com.hbm.util.Calculator; import com.hbm.util.Tuple; import net.minecraft.client.gui.GuiScreen; @@ -12,8 +13,6 @@ import java.math.MathContext; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Deque; -import java.util.Locale; -import java.util.Stack; public class GUICalculator extends GuiScreen { private int xSize = 220; @@ -58,7 +57,7 @@ public class GUICalculator extends GuiScreen { selectedHist = -1; } else { try { - double result = evaluateExpression(input); + double result = Calculator.evaluateExpression(input); history.addFirst(new Tuple.Pair(input, result)); if (history.size() > maxHistory) history.removeLast(); String plainStringRepresentation = (new BigDecimal(result, MathContext.DECIMAL64)).toPlainString(); @@ -85,7 +84,7 @@ public class GUICalculator extends GuiScreen { } try { - latestResult = Double.toString(evaluateExpression(input)); + latestResult = Double.toString(Calculator.evaluateExpression(input)); } catch (Exception e) { latestResult = e.toString(); } } @@ -116,203 +115,4 @@ public class GUICalculator extends GuiScreen { i++; } } - - /** - * Mathematically evaluates user-inputted strings
- * It is recommended to catch all exceptions when using this - */ - public static double evaluateExpression(String input) { - if (input.contains("^")) input = preEvaluatePower(input); - - char[] tokens = input.toCharArray(); - Stack values = new Stack<>(); - Stack operators = new Stack<>(); - - for (int i = 0; i < tokens.length; i++) { - if (tokens[i] == ' ') continue; - - if (tokens[i] >= '0' && tokens[i] <= '9' || tokens[i] == '.' || (tokens[i] == '-' && (i == 0 || "+-*/^(".contains(String.valueOf(tokens[i - 1]))))) { - StringBuilder buffer = new StringBuilder(); - if (tokens[i] == '-') { - buffer.append('-'); // for negative numbers - i++; - } - while (i < tokens.length && (tokens[i] >= '0' && tokens[i] <= '9' || tokens[i] == '.')) buffer.append(tokens[i++]); - values.push(Double.parseDouble(buffer.toString())); - i--; - } else if (tokens[i] == '(') operators.push(Character.toString(tokens[i])); - else if (tokens[i] == ')') { - while (!operators.isEmpty() && operators.peek().charAt(0) != '(') - values.push(evaluateOperator(operators.pop().charAt(0), values.pop(), values.pop())); - operators.pop(); - if (!operators.isEmpty() && operators.peek().length() > 1) - values.push(evaluateFunction(operators.pop(), values.pop())); - } else if (tokens[i] == '+' || tokens[i] == '-' || tokens[i] == '*' || tokens[i] == '/' || tokens[i] == '^') { - while (!operators.isEmpty() && hasPrecedence(String.valueOf(tokens[i]), operators.peek())) - values.push(evaluateOperator(operators.pop().charAt(0), values.pop(), values.pop())); - operators.push(Character.toString(tokens[i])); - } else if (tokens[i] == '!') { - values.push((double) factorial((int) Math.round(values.pop()))); - }else if (tokens[i] >= 'A' && tokens[i] <= 'Z' || tokens[i] >= 'a' && tokens[i] <= 'z') { - StringBuilder charBuffer = new StringBuilder(); - while (i < tokens.length && (tokens[i] >= 'A' && tokens[i] <= 'Z' || tokens[i] >= 'a' && tokens[i] <= 'z')) - charBuffer.append(tokens[i++]); - String string = charBuffer.toString(); - if (string.equalsIgnoreCase("pi")) values.push(Math.PI); - else if (string.equalsIgnoreCase("e")) values.push(Math.E); - else operators.push(string.toLowerCase(Locale.ROOT)); - i--; - } - } - - // if the expression is correctly formatted, no function is remaining - while (!operators.empty()) values.push(evaluateOperator(operators.pop().charAt(0), values.pop(), values.pop())); - - return values.pop(); - } - - private static double evaluateOperator(char operator, double x, double y) { - switch (operator) { - case '+': return y + x; - case '-': return y - x; - case '*': return y * x; - case '/': return y / x; - - case '^': return Math.pow(y, x); // should not happen here, but oh well - } - return 0; - } - - private static double evaluateFunction(String function, double x) { - switch (function) { - case "sqrt": return Math.sqrt(x); - - case "sin": return Math.sin(x); - case "cos": return Math.cos(x); - case "tan": return Math.tan(x); - case "asin": return Math.asin(x); - case "acos": return Math.acos(x); - case "atan": return Math.atan(x); - - case "log": return Math.log10(x); - case "ln": return Math.log(x); - - case "ceil": return Math.ceil(x); - case "floor": return Math.floor(x); - case "round": return Math.round(x); - } - return 0; - } - - /** Returns whether {@code second} has precedence over {@code first} */ - private static boolean hasPrecedence(String first, String second) { - if (second.length() > 1) return false; - - char firstChar = first.charAt(0); - char secondChar = second.charAt(0); - - if (secondChar == '(' || secondChar == ')') return false; - else return (firstChar != '*' && firstChar != '/' && firstChar != '^') || (secondChar != '+' && secondChar != '-'); - } - - /** Returns the input with all powers evaluated */ - private static String preEvaluatePower(String input) { - do { - int powerOperatorIndex = input.lastIndexOf('^'); - - // find base - boolean previousTokenIsParentheses = input.charAt(powerOperatorIndex - 1) == ')'; - int parenthesesDepth = previousTokenIsParentheses ? 1 : 0; - int baseExpressionStart = previousTokenIsParentheses ? powerOperatorIndex - 2 : powerOperatorIndex - 1; - baseLoop: - for (; baseExpressionStart >= 0; baseExpressionStart--) { // search backwards - switch (input.charAt(baseExpressionStart)) { - case ')': - if (previousTokenIsParentheses) parenthesesDepth++; - else break baseLoop; - break; - case '(': - if (previousTokenIsParentheses && parenthesesDepth > 0) parenthesesDepth--; - else break baseLoop; - break; - case '+': case '-': case '*': case '/': case '^': - if (parenthesesDepth == 0) break baseLoop; - } - } - baseExpressionStart++; // go one token forward again - if (parenthesesDepth > 0) throw new IllegalArgumentException("Incomplete parentheses"); - - // find exponent - boolean nextTokenIsParentheses = input.charAt(powerOperatorIndex + 1) == '('; - parenthesesDepth = nextTokenIsParentheses ? 1 : 0; - int exponentExpressionEnd = nextTokenIsParentheses ? powerOperatorIndex + 2 : powerOperatorIndex + 1; - exponentLoop: - for (; exponentExpressionEnd < input.length(); exponentExpressionEnd++) { - switch (input.charAt(exponentExpressionEnd)) { - case '(': - if (nextTokenIsParentheses) parenthesesDepth++; - else break exponentLoop; - break; - case ')': - if (nextTokenIsParentheses && parenthesesDepth > 0) parenthesesDepth--; - else break exponentLoop; - break; - case '+': case '-': case '*': case '/': case '^': - if (parenthesesDepth == 0) break exponentLoop; - } - } - if (parenthesesDepth > 0) throw new IllegalArgumentException("Incomplete parentheses"); - - double base = evaluateExpression(input.substring(baseExpressionStart, powerOperatorIndex)); - double exponent = evaluateExpression(input.substring(powerOperatorIndex + 1, exponentExpressionEnd)); - double result = Math.pow(base, exponent); - // use big decimal to avoid scientific notation messing with the calculation - input = input.substring(0, baseExpressionStart) + (new BigDecimal(result, MathContext.DECIMAL64)).toPlainString() + input.substring(exponentExpressionEnd); - } while (input.contains("^")); - - return input; - } - - // TODO Maybe switch the whole calculator to using BigInteger/BigDecimal? - // SplitRecursive algorithm - private static int factorial(int in) { - if (in < 0) throw new IllegalArgumentException("Factorial needs n >= 0"); - if (in < 2) return 1; - int p = 1, r = 1; - factorialCurrentN = 1; - int h = 0, shift = 0, high = 1; - int log2n = log2(in); - while (h != in) { - shift += h; - h = in >> log2n--; - int len = high; - high = (h - 1) | 1; - len = (high - len) / 2; - - if (len > 0) { - p *= factorialProduct(len); - r *= p; - } - } - - return r << shift; - } - - private static int factorialCurrentN; - - private static int factorialProduct(int in) { - int m = in / 2; - if (m == 0) return factorialCurrentN += 2; - if (in == 2) return (factorialCurrentN += 2) * (factorialCurrentN += 2); - return factorialProduct(in - m) * factorialProduct(m); - } - - private static int log2(int in) { - int log = 0; - if((in & 0xffff0000) != 0) { in >>>= 16; log = 16; } - if(in >= 256) { in >>>= 8; log += 8; } - if(in >= 16) { in >>>= 4; log += 4; } - if(in >= 4) { in >>>= 2; log += 2; } - return log + (in >>> 1); - } } diff --git a/src/main/java/com/hbm/module/IParse.java b/src/main/java/com/hbm/module/IParse.java new file mode 100644 index 000000000..73bd8e0c4 --- /dev/null +++ b/src/main/java/com/hbm/module/IParse.java @@ -0,0 +1,34 @@ +package com.hbm.module; + +import java.util.HashMap; + +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.world.World; + +public interface IParse { + + public EnumStatementReturn eval(ParseContext ctx, String line); + + public static class ParseContext { + public World world; + public NBTTagCompound variables; + public HashMap jmp; + + public Object buffer; + public int clockSpeed; + + public ParseContext(World world, NBTTagCompound variables, HashMap jmp) { + this.world = world; + this.variables = variables; + this.jmp = jmp; + } + } + + public static enum EnumStatementReturn { + OK, + UNRECOGNIZED_COMMAND, + PARAMETER_ERROR, + END_TICK, + UNDEFINED + } +} diff --git a/src/main/java/com/hbm/module/ParseMSES1.java b/src/main/java/com/hbm/module/ParseMSES1.java new file mode 100644 index 000000000..cacc1ffe6 --- /dev/null +++ b/src/main/java/com/hbm/module/ParseMSES1.java @@ -0,0 +1,9 @@ +package com.hbm.module; + +public class ParseMSES1 implements IParse { + + @Override + public EnumStatementReturn eval(ParseContext ctx, String line) { + return EnumStatementReturn.OK; + } +} diff --git a/src/main/java/com/hbm/tileentity/TileEntityTickingBase.java b/src/main/java/com/hbm/tileentity/TileEntityTickingBase.java index 2924a3467..0381ef795 100644 --- a/src/main/java/com/hbm/tileentity/TileEntityTickingBase.java +++ b/src/main/java/com/hbm/tileentity/TileEntityTickingBase.java @@ -1,17 +1,11 @@ package com.hbm.tileentity; -import net.minecraftforge.fluids.FluidTank; - public abstract class TileEntityTickingBase extends TileEntityLoadedBase { public TileEntityTickingBase() { } public abstract String getInventoryName(); - public int getGaugeScaled(int i, FluidTank tank) { - return tank.getFluidAmount() * i / tank.getCapacity(); - } - //abstracting this method forces child classes to implement it //so i don't have to remember the fucking method name //was it update? onUpdate? updateTile? did it have any args? diff --git a/src/main/java/com/hbm/tileentity/machine/storage/TileEntityMachineBAT9000.java b/src/main/java/com/hbm/tileentity/machine/storage/TileEntityMachineBAT9000.java index 9ab3106a6..0d94c0389 100644 --- a/src/main/java/com/hbm/tileentity/machine/storage/TileEntityMachineBAT9000.java +++ b/src/main/java/com/hbm/tileentity/machine/storage/TileEntityMachineBAT9000.java @@ -7,6 +7,7 @@ import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; import net.minecraft.util.AxisAlignedBB; +@Deprecated public class TileEntityMachineBAT9000 extends TileEntityBarrel { public TileEntityMachineBAT9000() { diff --git a/src/main/java/com/hbm/tileentity/network/TileEntityRadioAUTOCAL.java b/src/main/java/com/hbm/tileentity/network/TileEntityRadioAUTOCAL.java new file mode 100644 index 000000000..beac744dd --- /dev/null +++ b/src/main/java/com/hbm/tileentity/network/TileEntityRadioAUTOCAL.java @@ -0,0 +1,41 @@ +package com.hbm.tileentity.network; + +import java.util.HashMap; + +import com.hbm.tileentity.TileEntityTickingBase; + +import io.netty.buffer.ByteBuf; +import net.minecraft.nbt.NBTTagCompound; + +public class TileEntityRadioAUTOCAL extends TileEntityTickingBase { + + public boolean isOn = false; + + public Object buffer; + public NBTTagCompound variables; + public String[] script; + public HashMap jmp = new HashMap(); + + @Override + public String getInventoryName() { + return "container.autocal"; + } + + @Override + public void updateEntity() { + + if(!worldObj.isRemote) { + + } + } + + @Override + public void serialize(ByteBuf buf) { + super.serialize(buf); + } + + @Override + public void deserialize(ByteBuf buf) { + super.deserialize(buf); + } +} diff --git a/src/main/java/com/hbm/util/Calculator.java b/src/main/java/com/hbm/util/Calculator.java new file mode 100644 index 000000000..88d91000f --- /dev/null +++ b/src/main/java/com/hbm/util/Calculator.java @@ -0,0 +1,208 @@ +package com.hbm.util; + +import java.math.BigDecimal; +import java.math.MathContext; +import java.util.Locale; +import java.util.Stack; + +public class Calculator { + + /** + * Mathematically evaluates user-inputted strings
+ * It is recommended to catch all exceptions when using this + */ + public static double evaluateExpression(String input) { + if (input.contains("^")) input = preEvaluatePower(input); + + char[] tokens = input.toCharArray(); + Stack values = new Stack<>(); + Stack operators = new Stack<>(); + + for (int i = 0; i < tokens.length; i++) { + if (tokens[i] == ' ') continue; + + if (tokens[i] >= '0' && tokens[i] <= '9' || tokens[i] == '.' || (tokens[i] == '-' && (i == 0 || "+-*/^(".contains(String.valueOf(tokens[i - 1]))))) { + StringBuilder buffer = new StringBuilder(); + if (tokens[i] == '-') { + buffer.append('-'); // for negative numbers + i++; + } + while (i < tokens.length && (tokens[i] >= '0' && tokens[i] <= '9' || tokens[i] == '.')) buffer.append(tokens[i++]); + values.push(Double.parseDouble(buffer.toString())); + i--; + } else if (tokens[i] == '(') operators.push(Character.toString(tokens[i])); + else if (tokens[i] == ')') { + while (!operators.isEmpty() && operators.peek().charAt(0) != '(') + values.push(evaluateOperator(operators.pop().charAt(0), values.pop(), values.pop())); + operators.pop(); + if (!operators.isEmpty() && operators.peek().length() > 1) + values.push(evaluateFunction(operators.pop(), values.pop())); + } else if (tokens[i] == '+' || tokens[i] == '-' || tokens[i] == '*' || tokens[i] == '/' || tokens[i] == '^') { + while (!operators.isEmpty() && hasPrecedence(String.valueOf(tokens[i]), operators.peek())) + values.push(evaluateOperator(operators.pop().charAt(0), values.pop(), values.pop())); + operators.push(Character.toString(tokens[i])); + } else if (tokens[i] == '!') { + values.push((double) factorial((int) Math.round(values.pop()))); + }else if (tokens[i] >= 'A' && tokens[i] <= 'Z' || tokens[i] >= 'a' && tokens[i] <= 'z') { + StringBuilder charBuffer = new StringBuilder(); + while (i < tokens.length && (tokens[i] >= 'A' && tokens[i] <= 'Z' || tokens[i] >= 'a' && tokens[i] <= 'z')) + charBuffer.append(tokens[i++]); + String string = charBuffer.toString(); + if (string.equalsIgnoreCase("pi")) values.push(Math.PI); + else if (string.equalsIgnoreCase("e")) values.push(Math.E); + else operators.push(string.toLowerCase(Locale.ROOT)); + i--; + } + } + + // if the expression is correctly formatted, no function is remaining + while (!operators.empty()) values.push(evaluateOperator(operators.pop().charAt(0), values.pop(), values.pop())); + + return values.pop(); + } + + private static double evaluateOperator(char operator, double x, double y) { + switch (operator) { + case '+': return y + x; + case '-': return y - x; + case '*': return y * x; + case '/': return y / x; + + case '^': return Math.pow(y, x); // should not happen here, but oh well + } + return 0; + } + + private static double evaluateFunction(String function, double x) { + switch (function) { + case "sqrt": return Math.sqrt(x); + + case "sin": return Math.sin(x); + case "cos": return Math.cos(x); + case "tan": return Math.tan(x); + case "asin": return Math.asin(x); + case "acos": return Math.acos(x); + case "atan": return Math.atan(x); + + case "log": return Math.log10(x); + case "ln": return Math.log(x); + + case "ceil": return Math.ceil(x); + case "floor": return Math.floor(x); + case "round": return Math.round(x); + } + return 0; + } + + /** Returns whether {@code second} has precedence over {@code first} */ + private static boolean hasPrecedence(String first, String second) { + if (second.length() > 1) return false; + + char firstChar = first.charAt(0); + char secondChar = second.charAt(0); + + if (secondChar == '(' || secondChar == ')') return false; + else return (firstChar != '*' && firstChar != '/' && firstChar != '^') || (secondChar != '+' && secondChar != '-'); + } + + /** Returns the input with all powers evaluated */ + private static String preEvaluatePower(String input) { + do { + int powerOperatorIndex = input.lastIndexOf('^'); + + // find base + boolean previousTokenIsParentheses = input.charAt(powerOperatorIndex - 1) == ')'; + int parenthesesDepth = previousTokenIsParentheses ? 1 : 0; + int baseExpressionStart = previousTokenIsParentheses ? powerOperatorIndex - 2 : powerOperatorIndex - 1; + baseLoop: + for (; baseExpressionStart >= 0; baseExpressionStart--) { // search backwards + switch (input.charAt(baseExpressionStart)) { + case ')': + if (previousTokenIsParentheses) parenthesesDepth++; + else break baseLoop; + break; + case '(': + if (previousTokenIsParentheses && parenthesesDepth > 0) parenthesesDepth--; + else break baseLoop; + break; + case '+': case '-': case '*': case '/': case '^': + if (parenthesesDepth == 0) break baseLoop; + } + } + baseExpressionStart++; // go one token forward again + if (parenthesesDepth > 0) throw new IllegalArgumentException("Incomplete parentheses"); + + // find exponent + boolean nextTokenIsParentheses = input.charAt(powerOperatorIndex + 1) == '('; + parenthesesDepth = nextTokenIsParentheses ? 1 : 0; + int exponentExpressionEnd = nextTokenIsParentheses ? powerOperatorIndex + 2 : powerOperatorIndex + 1; + exponentLoop: + for (; exponentExpressionEnd < input.length(); exponentExpressionEnd++) { + switch (input.charAt(exponentExpressionEnd)) { + case '(': + if (nextTokenIsParentheses) parenthesesDepth++; + else break exponentLoop; + break; + case ')': + if (nextTokenIsParentheses && parenthesesDepth > 0) parenthesesDepth--; + else break exponentLoop; + break; + case '+': case '-': case '*': case '/': case '^': + if (parenthesesDepth == 0) break exponentLoop; + } + } + if (parenthesesDepth > 0) throw new IllegalArgumentException("Incomplete parentheses"); + + double base = evaluateExpression(input.substring(baseExpressionStart, powerOperatorIndex)); + double exponent = evaluateExpression(input.substring(powerOperatorIndex + 1, exponentExpressionEnd)); + double result = Math.pow(base, exponent); + // use big decimal to avoid scientific notation messing with the calculation + input = input.substring(0, baseExpressionStart) + (new BigDecimal(result, MathContext.DECIMAL64)).toPlainString() + input.substring(exponentExpressionEnd); + } while (input.contains("^")); + + return input; + } + + // TODO Maybe switch the whole calculator to using BigInteger/BigDecimal? + // SplitRecursive algorithm + private static int factorial(int in) { + if (in < 0) throw new IllegalArgumentException("Factorial needs n >= 0"); + if (in < 2) return 1; + int p = 1, r = 1; + factorialCurrentN = 1; + int h = 0, shift = 0, high = 1; + int log2n = log2(in); + while (h != in) { + shift += h; + h = in >> log2n--; + int len = high; + high = (h - 1) | 1; + len = (high - len) / 2; + + if (len > 0) { + p *= factorialProduct(len); + r *= p; + } + } + + return r << shift; + } + + private static int factorialCurrentN; + + private static int factorialProduct(int in) { + int m = in / 2; + if (m == 0) return factorialCurrentN += 2; + if (in == 2) return (factorialCurrentN += 2) * (factorialCurrentN += 2); + return factorialProduct(in - m) * factorialProduct(m); + } + + private static int log2(int in) { + int log = 0; + if((in & 0xffff0000) != 0) { in >>>= 16; log = 16; } + if(in >= 256) { in >>>= 8; log += 8; } + if(in >= 16) { in >>>= 4; log += 4; } + if(in >= 4) { in >>>= 2; log += 2; } + return log + (in >>> 1); + } +} diff --git a/src/main/resources/assets/hbm/lang/de_DE.lang b/src/main/resources/assets/hbm/lang/de_DE.lang index f35e91ff8..72931a6b9 100644 --- a/src/main/resources/assets/hbm/lang/de_DE.lang +++ b/src/main/resources/assets/hbm/lang/de_DE.lang @@ -223,6 +223,7 @@ container.bat9000=Big-Ass Tank 9000 container.battery=Energiespeicher container.batteryREDD=FEnSU container.batterySocket=Batteriesockel +container.bigAssTank=Big-Ass Tank container.bombMulti=Mehrzweckbombe container.casingBag=Hülsentasche container.catalyticReformer=Katalytischer Reformer @@ -4414,6 +4415,7 @@ tile.machine_battery_potato.name=Kartoffelbatterieblock (LEGACY) tile.machine_battery_redd.name=FEnSU tile.machine_battery_socket.name=Batteriesockel tile.machine_battery_socket.desc=Erlaubt es, Batterien direkt mit$dem Stromnetz zu verbinden.$Verhält sich wie ein Kabel, alle$Verbindungen sind im selben Netzwek. +tile.machine_bigasstank.name=Big-Ass Tank tile.machine_boiler.name=Boiler tile.machine_boiler.desc=Großer Boiler zum Verdampfen von Wasser oder$Erhitzen von Öl. Benötigt externe Hitzequelle.$Wärmestransferrate: ΔT*0.1 TU/t tile.machine_boiler_electric_off.name=Elektrischer Ölwärmer diff --git a/src/main/resources/assets/hbm/lang/en_US.lang b/src/main/resources/assets/hbm/lang/en_US.lang index 274436882..6a86b68e1 100644 --- a/src/main/resources/assets/hbm/lang/en_US.lang +++ b/src/main/resources/assets/hbm/lang/en_US.lang @@ -624,6 +624,7 @@ container.bat9000=Big-Ass Tank 9000 container.battery=Energy Storage container.batteryREDD=FEnSU container.batterySocket=Battery Socket +container.bigAssTank=Big-Ass Tank container.bombMulti=Multi Purpose Bomb container.casingBag=Bullet Casing Bag container.catalyticReformer=Catalytic Reformer @@ -5662,6 +5663,7 @@ tile.machine_battery_potato.name=Potato Battery Block (LEGACY) tile.machine_battery_redd.name=FEnSU tile.machine_battery_socket.name=Battery Socket tile.machine_battery_socket.desc=Allows battery items to be connected$to the power grid directly.$Acts as a cable, all ports are connected$to the same network. +tile.machine_bigasstank.name=Big-Ass Tank tile.machine_boiler.name=Boiler tile.machine_boiler.desc=Large boiler that can boil water or heat up oil.$Requires external heat source.$Heat transfer rate: ΔT*0.1 TU/t tile.machine_boiler_electric_off.name=Electric Oil Heater