Compare commits

...

4 Commits

Author SHA1 Message Date
HbmMods
c99d1da3bf AUTOCAL 2026-05-22 23:29:58 +02:00
Boblet
7db05f01ec i need chicken nuggets and a nap 2026-05-22 14:16:45 +02:00
HbmMods
2556741a6f electric scrap 2026-05-21 23:17:01 +02:00
Boblet
0074c20f91 i spent all afternoon in a hole 2026-05-21 16:56:03 +02:00
24 changed files with 1123 additions and 215 deletions

View File

@ -12,6 +12,13 @@
* 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
* AUTOCAL Automatic Calculator
* Highly advanced programmable RoR device
* Can send and receive signals and do calculations with the same system as NTM's calculator
## Changed
* RoR graphs can now have their min and max bounds set to a fixed number
@ -29,7 +36,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

View File

@ -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);

View File

@ -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) {

View File

@ -0,0 +1,38 @@
package com.hbm.blocks.network;
import com.hbm.blocks.BlockDummyable;
import com.hbm.main.MainRegistry;
import com.hbm.tileentity.network.TileEntityRadioAUTOCAL;
import cpw.mods.fml.common.network.internal.FMLNetworkHandler;
import net.minecraft.block.material.Material;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.world.World;
public class RadioAUTOCAL extends BlockDummyable {
public RadioAUTOCAL() {
super(Material.iron);
}
@Override
public TileEntity createNewTileEntity(World world, int meta) {
if(meta >= 12) return new TileEntityRadioAUTOCAL();
return null;
}
@Override public int[] getDimensions() { return new int[] {1, 0, 0, 0, 0 ,0}; }
@Override public int getOffset() { return 0; }
@Override
public boolean onBlockActivated(World world, int x, int y, int z, EntityPlayer player, int side, float hitX, float hitY, float hitZ) {
if(player.isSneaking()) return false;
if(world.isRemote) {
int[] pos = this.findCore(world, x, y, z);
if(pos == null) return false;
FMLNetworkHandler.openGui(player, MainRegistry.instance, 0, world, pos[0], pos[1], pos[2]);
}
return true;
}
}

View File

@ -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;

View File

@ -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<String,Double>(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<br>
* 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<Double> values = new Stack<>();
Stack<String> 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);
}
}

View File

@ -0,0 +1,159 @@
package com.hbm.inventory.gui;
import java.awt.Desktop;
import java.io.File;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import org.lwjgl.opengl.GL11;
import com.hbm.lib.RefStrings;
import com.hbm.main.MainRegistry;
import com.hbm.packet.PacketDispatcher;
import com.hbm.packet.toserver.NBTControlPacket;
import com.hbm.tileentity.network.TileEntityRadioAUTOCAL;
import net.minecraft.client.Minecraft;
import net.minecraft.client.audio.PositionedSoundRecord;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.util.ResourceLocation;
public class GUIScreenRadioAUTOCAL extends GuiScreen {
protected static final ResourceLocation texture = new ResourceLocation(RefStrings.MODID + ":textures/gui/machine/gui_rtty_autocal.png");
protected TileEntityRadioAUTOCAL autocal;
protected int xSize = 150;
protected int ySize = 90;
protected int guiLeft;
protected int guiTop;
public GUIScreenRadioAUTOCAL(TileEntityRadioAUTOCAL autocal) {
this.autocal = autocal;
}
@Override
public void initGui() {
super.initGui();
this.guiLeft = (this.width - this.xSize) / 2;
this.guiTop = (this.height - this.ySize) / 2;
}
@Override
public void drawScreen(int mouseX, int mouseY, float f) {
this.drawDefaultBackground();
this.drawGuiContainerBackgroundLayer(f, mouseX, mouseY);
GL11.glDisable(GL11.GL_LIGHTING);
this.drawGuiContainerForegroundLayer(mouseX, mouseY);
GL11.glEnable(GL11.GL_LIGHTING);
}
@Override
protected void mouseClicked(int x, int y, int i) {
super.mouseClicked(x, y, i);
NBTTagCompound data = null;
if(checkClick(x, y, 8, 36, 18, 18)) { data = new NBTTagCompound(); data.setBoolean("on", true); }
if(checkClick(x, y, 28, 36, 18, 18)) { data = new NBTTagCompound(); data.setBoolean("ignore", true); }
if(checkClick(x, y, 48, 36, 18, 18)) { data = new NBTTagCompound(); data.setBoolean("auto", true); }
// open folder and generate new script file
if(checkClick(x, y, 104, 36, 18, 18)) {
try {
File uploadFolder = new File(MainRegistry.configDir.getParentFile(), "hbmComputerUpload");
File script = new File(uploadFolder, "script.txt");
if(!uploadFolder.exists()) uploadFolder.mkdir();
if(!script.exists()) script.createNewFile();
script.setExecutable(false);
Class oclass = Class.forName("java.awt.Desktop");
Object object = oclass.getMethod("getDesktop", new Class[0]).invoke((Object) null, new Object[0]);
oclass.getMethod("browse", new Class[] { URI.class }).invoke(object, new Object[] { script.toURI() });
//oclass.getMethod("edit", File.class).invoke(object, new Object[] { script });
} catch(Throwable ex) { MainRegistry.logger.error("Couldn't open link", ex); }
}
if(checkClick(x, y, 84, 36, 18, 18)) {
try {
File uploadFolder = new File(MainRegistry.configDir.getParentFile(), "hbmComputerUpload");
File script = new File(uploadFolder, "script.txt");
if(!uploadFolder.exists()) uploadFolder.mkdir();
if(!script.exists()) {
script.createNewFile();
script.setExecutable(false);
return;
}
/*FileReader reader = new FileReader(script);
BufferedReader buffer = new BufferedReader(reader);
String[] lines = buffer.lines().toArray(String[]::new);
buffer.close();
// this is going to blow the fuck up once we hit the max packet size, but let's ignore that for now
data = new NBTTagCompound();
StringBuilder builder = new StringBuilder();
for(int l = 0; l < lines.length; l++) {
builder.append(lines[i]);
if(l < lines.length - 1) builder.append("\n"); // yeah why the fuck not
}
data.setString("payload", builder.toString());*/
byte[] bytes = Files.readAllBytes(Paths.get(script.toURI()));
data = new NBTTagCompound();
data.setString("payload", new String(bytes, StandardCharsets.UTF_8));
} catch(Throwable ex) { }
}
// this thing can both upload and download files so let's be careful about this
// the upload is simple, it's just text that is handled by the AUTOCAL, so doing anything malicious isn't more likely than with any other package
// download is iffy, because we take text from the server, fully user-definable, and save it to disk. it's stored as a txt so accudentally running it
// or getting it to run itself, should it be a malicious script, is unlikely. still, we want to minimized the chances as much as we can
// option 1: set file attribute to disallow running (i.e. disable executable perm)
// option 2: add fluff that would break scripts, however they might work. we can't change the actual lines because we want the script to be edited,
// but we can add some extra crap that would either halt common scripting langs entirely or at least disrupt them into not functioning
// option 3: enforce validation so only MS-ES1 script can be received by the client. this means that info such as comments or incorrectly written commands
// are lost, however this is the safest way because it becomes impossible to send malicious code, but it also interferes with regular user operation more
if(data != null) {
mc.getSoundHandler().playSound(PositionedSoundRecord.func_147674_a(new ResourceLocation("gui.button.press"), 1.0F));
PacketDispatcher.wrapper.sendToServer(new NBTControlPacket(data, autocal.xCoord, autocal.yCoord, autocal.zCoord));
}
}
protected boolean checkClick(int x, int y, int left, int top, int sizeX, int sizeY) {
return guiLeft + left <= x && guiLeft + left + sizeX > x && guiTop + top < y && guiTop + top + sizeY >= y;
}
private void drawGuiContainerForegroundLayer(int x, int y) {
if(checkClick(x, y, 8, 36, 18, 18)) this.func_146283_a(Arrays.asList(new String[] {EnumChatFormatting.RED + "ON/OFF"}), x, y);
if(checkClick(x, y, 28, 36, 18, 18)) this.func_146283_a(Arrays.asList(new String[] {EnumChatFormatting.RED + "Ignore Errors", "Skips instructions that error,", "leaving the computer turned on.", "May cause unintended behavior", "and inconsistencies."}), x, y);
if(checkClick(x, y, 48, 36, 18, 18)) this.func_146283_a(Arrays.asList(new String[] {EnumChatFormatting.RED + "Automatic Reboot", "Restarts the computer automatically when", "the program stops due to an error", "or after finishing."}), x, y);
if(checkClick(x, y, 84, 36, 18, 18)) this.func_146283_a(Arrays.asList(new String[] {EnumChatFormatting.BLUE + "Upload Program"}), x, y);
if(checkClick(x, y, 104, 36, 18, 18)) this.func_146283_a(Arrays.asList(new String[] {EnumChatFormatting.BLUE + "Open Program File"}), x, y);
if(checkClick(x, y, 124, 36, 18, 18)) this.func_146283_a(Arrays.asList(new String[] {EnumChatFormatting.BLUE + "Download Program", EnumChatFormatting.RED + "Currently unsupported!"}), x, y);
}
private void drawGuiContainerBackgroundLayer(float f, int mouseX, int mouseY) {
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
Minecraft.getMinecraft().getTextureManager().bindTexture(texture);
drawTexturedModalRect(guiLeft, guiTop, 0, 0, xSize, ySize);
if(autocal.isOn) drawTexturedModalRect(guiLeft + 8, guiTop + 36, 150, 0, 18, 18);
if(!autocal.ignoreError) drawTexturedModalRect(guiLeft + 28, guiTop + 36, 150, 18, 18, 18);
if(!autocal.autoReboot) drawTexturedModalRect(guiLeft + 48, guiTop + 36, 150, 36, 18, 18);
}
@Override
protected void keyTyped(char c, int b) {
if(b == 1 || b == Minecraft.getMinecraft().gameSettings.keyBindInventory.getKeyCode()) {
Minecraft.getMinecraft().thePlayer.closeScreen();
}
}
@Override public boolean doesGuiPauseGame() { return false; }
}

View File

@ -22,7 +22,7 @@ import net.minecraft.util.EnumChatFormatting;
import net.minecraft.util.MathHelper;
import net.minecraft.util.ResourceLocation;
public class GuiScreenRadioTelex extends GuiScreen {
public class GUIScreenRadioTelex extends GuiScreen {
protected static final ResourceLocation texture = new ResourceLocation(RefStrings.MODID + ":textures/gui/machine/gui_telex.png");
protected TileEntityRadioTelex telex;
@ -37,7 +37,7 @@ public class GuiScreenRadioTelex extends GuiScreen {
protected String[] txBuffer;
protected int cursorPos = 0;
public GuiScreenRadioTelex(TileEntityRadioTelex tile) {
public GUIScreenRadioTelex(TileEntityRadioTelex tile) {
this.telex = tile;
this.txBuffer = new String[tile.txBuffer.length];

View File

@ -358,6 +358,7 @@ public class ClientProxy extends ServerProxy {
ClientRegistry.bindTileEntitySpecialRenderer(TileEntityPistonInserter.class, new RenderPistonInserter());
ClientRegistry.bindTileEntitySpecialRenderer(TileEntityConveyorPress.class, new RenderConveyorPress());
ClientRegistry.bindTileEntitySpecialRenderer(TileEntityRadioTelex.class, new RenderTelex());
ClientRegistry.bindTileEntitySpecialRenderer(TileEntityRadioAUTOCAL.class, new RenderAUTOCAL());
ClientRegistry.bindTileEntitySpecialRenderer(TileEntityMachinePumpSteam.class, new RenderPump());
ClientRegistry.bindTileEntitySpecialRenderer(TileEntityMachinePumpElectric.class, new RenderPump());
ClientRegistry.bindTileEntitySpecialRenderer(TileEntityMachineArcWelder.class, new RenderArcWelder());

View File

@ -421,6 +421,7 @@ public class ResourceManager {
//TELEX
public static final IModelCustom telex = AdvancedModelLoader.loadModel(new ResourceLocation(RefStrings.MODID, "models/machines/telex.obj"));
public static final IModelCustom autocal = AdvancedModelLoader.loadModel(new ResourceLocation(RefStrings.MODID, "models/machines/autocal.obj"));
////Textures TEs
@ -877,6 +878,7 @@ public class ResourceManager {
//TELEX
public static final ResourceLocation telex_tex = new ResourceLocation(RefStrings.MODID, "textures/models/machines/telex.png");
public static final ResourceLocation autocal_tex = new ResourceLocation(RefStrings.MODID, "textures/models/machines/autocal.png");
////Obj Items

View File

@ -0,0 +1,50 @@
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 void generateJumpPoints(ParseContext ctx, String line, int index);
public static class ParseContext {
public World world;
public NBTTagCompound variables = new NBTTagCompound();
public HashMap<String, Integer> jmp = new HashMap();
public String buffer = "";
public int clockSpeed = 1;
public int current = 0;
public ParseContext(World world) {
this.world = world;
}
public void turnOff() {
this.clockSpeed = 1;
this.current = 0;
this.buffer = "";
if(!this.variables.hasNoTags()) this.variables = new NBTTagCompound();
}
}
public static enum EnumStatementReturn {
/** The command executed correctly (more or less) */
OK,
/** The command hasn't been recognized */
UNRECOGNIZED_COMMAND,
/** The expected parameters aren't present, or the parameters couldn't be parsed (i.e. using an undefined jump point) */
PARAMETER_ERROR,
/** Requests the AUTOCAL unit to end the tick, regardless of how many clock cycles are left */
END_TICK,
/** Requests an AUTOCAL shutdown */
SHUTDOWN,
/** Skips the instruction, doesn't use up a clock cycle */
SKIP,
/** General undefined behavior */
UNDEFINED
}
}

View File

@ -0,0 +1,271 @@
package com.hbm.module;
import java.util.Locale;
import com.hbm.tileentity.network.RTTYSystem;
import com.hbm.tileentity.network.RTTYSystem.RTTYChannel;
import com.hbm.tileentity.network.TileEntityRadioAUTOCAL;
import com.hbm.util.Calculator;
public class ParseMSES1 implements IParse {
@Override
public EnumStatementReturn eval(ParseContext ctx, String line) {
String lower = line.toLowerCase(Locale.US);
// jump point destination, skip
if(lower.startsWith("dest ") || lower.startsWith("# ")) {
return EnumStatementReturn.SKIP;
}
// no operation, still eats up a clock cycle
if(lower.equals("nop")) {
return EnumStatementReturn.OK;
}
// sets the desired clock speed, then skips the operation
if(lower.startsWith("clockspeed ")) {
if(line.length() <= 11) return EnumStatementReturn.PARAMETER_ERROR;
try {
int speed = Integer.parseInt(line.substring(11));
if(speed < 1 || speed > TileEntityRadioAUTOCAL.MAX_CLOCK_SPEED) return EnumStatementReturn.PARAMETER_ERROR;
ctx.clockSpeed = speed;
} catch(Throwable ex) { return EnumStatementReturn.PARAMETER_ERROR; }
return EnumStatementReturn.SKIP;
}
// sets the script index to the jump point
if(lower.startsWith("jmp ")) {
if(line.length() <= 4) return EnumStatementReturn.PARAMETER_ERROR;
String jmpKey = line.substring(4);
if(ctx.jmp.containsKey(jmpKey)) {
ctx.current = ctx.jmp.get(jmpKey);
return EnumStatementReturn.OK;
}
return EnumStatementReturn.PARAMETER_ERROR;
}
// sets the script index to the jump point, if the buffer is the string 'true'
if(lower.startsWith("jmpif ")) {
if(line.length() <= 6) return EnumStatementReturn.PARAMETER_ERROR;
if(!ctx.buffer.equals("true")) return EnumStatementReturn.OK;
String jmpKey = line.substring(6);
if(ctx.jmp.containsKey(jmpKey)) {
ctx.current = ctx.jmp.get(jmpKey);
return EnumStatementReturn.OK;
}
return EnumStatementReturn.PARAMETER_ERROR;
}
// sets the script index to the jump point, if the buffer is the NOT 'true'
if(lower.startsWith("jmpnot ")) {
if(line.length() <= 7) return EnumStatementReturn.PARAMETER_ERROR;
if(ctx.buffer.equals("true")) return EnumStatementReturn.OK;
String jmpKey = line.substring(7);
if(ctx.jmp.containsKey(jmpKey)) {
ctx.current = ctx.jmp.get(jmpKey);
return EnumStatementReturn.OK;
}
return EnumStatementReturn.PARAMETER_ERROR;
}
// ends the tick regardless of remaining clock cycles
if(lower.equals("endtick")) {
return EnumStatementReturn.END_TICK;
}
// requests unit to shut down
if(lower.equals("shutdown")) {
return EnumStatementReturn.SHUTDOWN;
}
// loads the requested variable into the buffer
if(lower.startsWith("load ")) {
if(line.length() <= 5) return EnumStatementReturn.PARAMETER_ERROR;
ctx.buffer = ctx.variables.getString(line.substring(5));
return EnumStatementReturn.OK;
}
// saves the buffer with the specified name
if(lower.startsWith("save ")) {
if(line.length() <= 5 || ctx.buffer.isEmpty()) return EnumStatementReturn.PARAMETER_ERROR;
ctx.variables.setString(line.substring(5), ctx.buffer);
return EnumStatementReturn.OK;
}
// stores the specified value in the buffer
if(lower.startsWith("buffer ")) {
if(line.length() <= 7) return EnumStatementReturn.PARAMETER_ERROR;
ctx.buffer = line.substring(7);
return EnumStatementReturn.OK;
}
// runs the calculation, allows string substitution, saves result to buffer
if(lower.startsWith("eval ")) {
if(line.length() <= 5) return EnumStatementReturn.PARAMETER_ERROR;
String statement = substitute(ctx, line.substring(5));
try {
double result = Calculator.evaluateExpression(statement);
ctx.buffer = "" + result;
} catch(Throwable ex) { return EnumStatementReturn.PARAMETER_ERROR; }
return EnumStatementReturn.OK;
}
// runs the calculation, allows string substitution, rounds, saves result to buffer,
if(lower.startsWith("evalr ")) {
if(line.length() <= 6) return EnumStatementReturn.PARAMETER_ERROR;
String statement = substitute(ctx, line.substring(6));
try {
double result = Calculator.evaluateExpression(statement);
ctx.buffer = "" + (int) Math.round(result);
} catch(Throwable ex) { return EnumStatementReturn.PARAMETER_ERROR; }
return EnumStatementReturn.OK;
}
// runs the calculation from the buffer, allows string substitution, saves result to buffer
if(lower.equals("evalr")) {
if(ctx.buffer.isEmpty()) return EnumStatementReturn.PARAMETER_ERROR;
String statement = substitute(ctx, ctx.buffer);
try {
double result = Calculator.evaluateExpression(statement);
ctx.buffer = "" + (int) Math.round(result);
} catch(Throwable ex) { return EnumStatementReturn.PARAMETER_ERROR; }
return EnumStatementReturn.OK;
}
// rounds the buffer down to the nearest integer
if(lower.equals("rounddown") || lower.equals("floor")) {
if(ctx.buffer.isEmpty()) return EnumStatementReturn.PARAMETER_ERROR;
try {
double d = Double.parseDouble(ctx.buffer);
ctx.buffer = "" + (int) Math.floor(d);
} catch(Exception ex) { return EnumStatementReturn.PARAMETER_ERROR; }
return EnumStatementReturn.OK;
}
// rounds the buffer up to the nearest integer
if(lower.equals("roundup") || lower.equals("ceil")) {
if(ctx.buffer.isEmpty()) return EnumStatementReturn.PARAMETER_ERROR;
try {
double d = Double.parseDouble(ctx.buffer);
ctx.buffer = "" + (int) Math.ceil(d);
} catch(Exception ex) { return EnumStatementReturn.PARAMETER_ERROR; }
return EnumStatementReturn.OK;
}
// rounds the buffer to the nearest integer (.5 cutoff rule)
if(lower.equals("round") || lower.equals("nearest")) {
if(ctx.buffer.isEmpty()) return EnumStatementReturn.PARAMETER_ERROR;
try {
double d = Double.parseDouble(ctx.buffer);
ctx.buffer = "" + (int) Math.round(d);
} catch(Exception ex) { return EnumStatementReturn.PARAMETER_ERROR; }
return EnumStatementReturn.OK;
}
// concatenate, same as buffer but evaluates $var$ substitutions
if(lower.startsWith("concat ")) {
if(line.length() <= 7 || ctx.buffer.isEmpty()) return EnumStatementReturn.PARAMETER_ERROR;
String concat = substitute(ctx, line.substring(5));
ctx.buffer = concat;
return EnumStatementReturn.OK;
}
// compares the buffer with a value, allows substitutions
if(lower.startsWith("eq ")) {
if(line.length() <= 3 || ctx.buffer.isEmpty()) return EnumStatementReturn.PARAMETER_ERROR;
ctx.buffer = ctx.buffer.equals(substitute(ctx, line.substring(3))) ? "true" : "false";
return EnumStatementReturn.OK;
}
// greater than buffer
if(lower.startsWith("gtb ")) {
if(line.length() <= 3 || ctx.buffer.isEmpty()) return EnumStatementReturn.PARAMETER_ERROR;
try {
double buffer = Double.parseDouble(ctx.buffer);
double val = Double.parseDouble(line.substring(3));
ctx.buffer = val > buffer ? "true" : "false";
} catch(Exception ex) { return EnumStatementReturn.PARAMETER_ERROR; }
return EnumStatementReturn.OK;
}
// lower than buffer
if(lower.startsWith("ltb ")) {
if(line.length() <= 3 || ctx.buffer.isEmpty()) return EnumStatementReturn.PARAMETER_ERROR;
try {
double buffer = Double.parseDouble(ctx.buffer);
double val = Double.parseDouble(line.substring(3));
ctx.buffer = val < buffer ? "true" : "false";
} catch(Exception ex) { return EnumStatementReturn.PARAMETER_ERROR; }
return EnumStatementReturn.OK;
}
// greater than or equal buffer
if(lower.startsWith("geb ")) {
if(line.length() <= 3 || ctx.buffer.isEmpty()) return EnumStatementReturn.PARAMETER_ERROR;
try {
double buffer = Double.parseDouble(ctx.buffer);
double val = Double.parseDouble(line.substring(3));
ctx.buffer = val >= buffer ? "true" : "false";
} catch(Exception ex) { return EnumStatementReturn.PARAMETER_ERROR; }
return EnumStatementReturn.OK;
}
// lower than or equal buffer
if(lower.startsWith("leb ")) {
if(line.length() <= 3 || ctx.buffer.isEmpty()) return EnumStatementReturn.PARAMETER_ERROR;
try {
double buffer = Double.parseDouble(ctx.buffer);
double val = Double.parseDouble(line.substring(3));
ctx.buffer = val <= buffer ? "true" : "false";
} catch(Exception ex) { return EnumStatementReturn.PARAMETER_ERROR; }
return EnumStatementReturn.OK;
}
// sends an RoR signal using the buffer's contents as the message over the supplied channel
if(lower.startsWith("send ")) {
if(line.length() <= 5 || ctx.buffer.isEmpty()) return EnumStatementReturn.PARAMETER_ERROR;
RTTYSystem.broadcast(ctx.world, line.substring(5), ctx.buffer);
return EnumStatementReturn.OK;
}
// listnes to an RoR signal using the buffer as the channel, then saves the signal to the buffer
if(lower.equals("listen")) {
// this picks up expired signals too, so polling has to be implemented by the user
RTTYChannel chan = RTTYSystem.listen(ctx.world, ctx.buffer);
if(chan != null) ctx.buffer = chan.signal + "";
return EnumStatementReturn.OK;
}
return EnumStatementReturn.UNRECOGNIZED_COMMAND;
}
public String substitute(ParseContext ctx, String statement) {
if(!statement.contains("$")) return statement;
String[] frags = statement.split("\\$");
if(frags.length % 2 == 0 || frags.length < 3) return statement;
// since var names are enclosed with $ signs, we assume that every evenly numbered fragment is a var name
// example: 5 + $val1$ * $val2$ / (-$val3$)
// equals "5 + ", "val1", "* ", "val2", "/ (-", "val3", ")"
// 1 2 3 4 5 6 7
for(int i = 1; i < frags.length; i += 2) {
// special case, if we try to substitute $buffer$ then read the literal buffer
if(frags[i].equals("buffer")) {
frags[i] = ctx.buffer;
} else {
frags[i] = ctx.variables.getString(frags[i]);
}
}
return String.join("", frags);
}
@Override
public void generateJumpPoints(ParseContext ctx, String line, int index) {
if(line.startsWith("dest ") && line.length() > 5) {
ctx.jmp.put(line.substring(5), index);
}
}
}

View File

@ -0,0 +1,55 @@
package com.hbm.render.tileentity;
import org.lwjgl.opengl.GL11;
import com.hbm.blocks.BlockDummyable;
import com.hbm.blocks.ModBlocks;
import com.hbm.main.ResourceManager;
import com.hbm.render.item.ItemRenderBase;
import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer;
import net.minecraft.item.Item;
import net.minecraft.tileentity.TileEntity;
import net.minecraftforge.client.IItemRenderer;
public class RenderAUTOCAL extends TileEntitySpecialRenderer implements IItemRendererProvider {
@Override
public void renderTileEntityAt(TileEntity tile, double x, double y, double z, float interp) {
GL11.glPushMatrix();
GL11.glTranslated(x + 0.5, y, z + 0.5);
GL11.glEnable(GL11.GL_LIGHTING);
GL11.glDisable(GL11.GL_CULL_FACE);
switch(tile.getBlockMetadata() - BlockDummyable.offset) {
case 2: GL11.glRotatef(90, 0F, 1F, 0F); break;
case 4: GL11.glRotatef(180, 0F, 1F, 0F); break;
case 3: GL11.glRotatef(270, 0F, 1F, 0F); break;
case 5: GL11.glRotatef(0, 0F, 1F, 0F); break;
}
bindTexture(ResourceManager.autocal_tex);
ResourceManager.autocal.renderAll();
GL11.glEnable(GL11.GL_CULL_FACE);
GL11.glPopMatrix();
}
@Override
public Item getItemForRenderer() {
return Item.getItemFromBlock(ModBlocks.radio_autocal);
}
@Override
public IItemRenderer getRenderer() {
return new ItemRenderBase( ) {
public void renderInventory() {
GL11.glTranslated(0, -4, 0);
GL11.glScaled(6.25, 6.25, 6.25);
}
public void renderCommon() {
bindTexture(ResourceManager.autocal_tex);
ResourceManager.autocal.renderAll();
}};
}
}

View File

@ -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?

View File

@ -480,6 +480,7 @@ public class TileMappings {
put(TileEntityRadioTorchReader.class, "tileentity_rtty_reader");
put(TileEntityRadioTorchController.class, "tileentity_rtty_controller");
put(TileEntityRadioTelex.class, "tileentity_rtty_telex");
put(TileEntityRadioAUTOCAL.class, "tileentity_rtty_autocal");
put(TileEntityDroneWaypoint.class, "tileentity_drone_waypoint");
put(TileEntityDroneCrate.class, "tileentity_drone_crate");

View File

@ -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() {

View File

@ -0,0 +1,177 @@
package com.hbm.tileentity.network;
import com.hbm.interfaces.IControlReceiver;
import com.hbm.inventory.gui.GUIScreenRadioAUTOCAL;
import com.hbm.module.IParse;
import com.hbm.module.IParse.EnumStatementReturn;
import com.hbm.module.IParse.ParseContext;
import com.hbm.module.ParseMSES1;
import com.hbm.tileentity.IGUIProvider;
import com.hbm.tileentity.TileEntityTickingBase;
import io.netty.buffer.ByteBuf;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.Container;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.nbt.NBTTagString;
import net.minecraft.world.World;
public class TileEntityRadioAUTOCAL extends TileEntityTickingBase implements IControlReceiver, IGUIProvider {
public boolean isOn = false;
public boolean ignoreError = false;
public boolean autoReboot = false;
public static final int MAX_CLOCK_SPEED = 5;
public String[] script = new String[0];
public IParse msesv1 = new ParseMSES1();
public ParseContext ctx;
@Override
public String getInventoryName() {
return "container.autocal";
}
@Override
public void updateEntity() {
if(!worldObj.isRemote) {
if(this.worldObj.getTotalWorldTime() % 60 == 0) this.markChanged(); // ensure we're always saved to disk
if(this.ctx == null) {
this.ctx = new ParseContext(worldObj);
}
if(this.ctx.world != this.worldObj) this.ctx.world = this.worldObj;
if(!this.isOn && this.autoReboot) {
this.isOn = true;
}
if(this.isOn) {
int emergencyBrake = 100;
for(int i = 0; i < this.ctx.clockSpeed && emergencyBrake > 0; i++) {
emergencyBrake--;
if(this.ctx.current == this.script.length) { this.stop("Program has terminated"); break; }
if(this.ctx.current < 0 || this.ctx.current >= this.script.length) { this.stop("Program index is out of bounds"); break; }
try {
int index = this.ctx.current;
this.ctx.current ++;
EnumStatementReturn ret = msesv1.eval(ctx, this.script[index]);
if(ret == EnumStatementReturn.END_TICK) break;
if(ret == EnumStatementReturn.SHUTDOWN) this.stop("Program requested shutdown");
if(!this.ignoreError) {
if(ret == EnumStatementReturn.UNRECOGNIZED_COMMAND) this.stop("Unrecognized command");
if(ret == EnumStatementReturn.PARAMETER_ERROR) this.stop("Parameter error");
if(ret == EnumStatementReturn.UNDEFINED) this.stop("Undefined behavior");
}
if(ret == EnumStatementReturn.SKIP) i--;
} catch(Exception ex) {
this.stop("Evaluation unsuccessful");
}
}
}
this.networkPackNT(15);
}
}
public void stop(String reason) {
this.isOn = false;
this.ctx.turnOff();
}
@Override
public void serialize(ByteBuf buf) {
super.serialize(buf);
buf.writeBoolean(isOn);
buf.writeBoolean(ignoreError);
buf.writeBoolean(autoReboot);
}
@Override
public void deserialize(ByteBuf buf) {
super.deserialize(buf);
this.isOn = buf.readBoolean();
this.ignoreError = buf.readBoolean();
this.autoReboot = buf.readBoolean();
}
@Override
public void readFromNBT(NBTTagCompound nbt) {
super.readFromNBT(nbt);
this.isOn = nbt.getBoolean("isOn");
this.ignoreError = nbt.getBoolean("ignoreError");
this.autoReboot = nbt.getBoolean("autoReboot");
this.ctx = new ParseContext(null);
this.ctx.current = nbt.getInteger("current");
this.ctx.clockSpeed = nbt.getInteger("clockSpeed");
this.ctx.buffer = nbt.getString("buffer");
NBTTagList lineList = nbt.getTagList("script", 8);
this.script = new String[lineList.tagList.size()];
for(int i = 0; i < script.length; i++) {
this.script[i] = lineList.getStringTagAt(i);
this.msesv1.generateJumpPoints(ctx, script[i], i);
}
this.ctx.variables = nbt.getCompoundTag("variables");
}
@Override
public void writeToNBT(NBTTagCompound nbt) {
super.writeToNBT(nbt);
nbt.setBoolean("isOn", isOn);
nbt.setBoolean("ignoreError", ignoreError);
nbt.setBoolean("autoReboot", autoReboot);
nbt.setInteger("current", ctx.current);
nbt.setInteger("clockSpeed", ctx.clockSpeed);
nbt.setString("buffer", ctx.buffer);
NBTTagList lineList = new NBTTagList();
for(String line : this.script) {
lineList.appendTag(new NBTTagString(line));
}
nbt.setTag("script", lineList);
nbt.setTag("variables", this.ctx.variables);
}
@Override public Container provideContainer(int ID, EntityPlayer player, World world, int x, int y, int z) { return null; }
@Override public Object provideGUI(int ID, EntityPlayer player, World world, int x, int y, int z) { return new GUIScreenRadioAUTOCAL(this); }
@Override
public boolean hasPermission(EntityPlayer player) {
return player.getDistanceSq(xCoord + 0.5, yCoord + 1, zCoord + 0.5) <= 15 * 15;
}
@Override
public void receiveControl(NBTTagCompound data) {
if(data.hasKey("on")) {
if(this.isOn) stop("User requested shutdown");
else this.isOn = true;
}
if(data.hasKey("ignore")) this.ignoreError = !this.ignoreError;
if(data.hasKey("auto")) this.autoReboot = !this.autoReboot;
if(data.hasKey("payload")) {
this.ctx.jmp.clear();
this.script = data.getString("payload").split("\n");
for(int i = 0; i < script.length; i++) {
script[i] = script[i].trim();
this.msesv1.generateJumpPoints(ctx, script[i], i);
}
if(this.isOn) stop("Script has changed");
}
this.markChanged();
}
}

View File

@ -5,7 +5,7 @@ import java.util.List;
import com.hbm.handler.CompatHandler;
import com.hbm.interfaces.IControlReceiver;
import com.hbm.inventory.gui.GuiScreenRadioTelex;
import com.hbm.inventory.gui.GUIScreenRadioTelex;
import com.hbm.tileentity.IGUIProvider;
import com.hbm.tileentity.TileEntityLoadedBase;
import com.hbm.tileentity.network.RTTYSystem.RTTYChannel;
@ -234,7 +234,7 @@ public class TileEntityRadioTelex extends TileEntityLoadedBase implements IContr
@Override
@SideOnly(Side.CLIENT)
public Object provideGUI(int ID, EntityPlayer player, World world, int x, int y, int z) {
return new GuiScreenRadioTelex(this);
return new GUIScreenRadioTelex(this);
}
AxisAlignedBB bb = null;

View File

@ -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<br>
* 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<Double> values = new Stack<>();
Stack<String> 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);
}
}

View File

@ -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
@ -4760,6 +4762,7 @@ tile.pwr_port.desc=Erlaubt IO für Items und Flüssigkeiten$Platzierung: Hülle
tile.pwr_reflector.name=PWR Neutronenreflektor
tile.pwr_reflector.desc=Reflektier Neutronen auf Brennstäbe zurück$Platzierung: Hülle, für höhere Reaktivität$Gültiger Block für Hülle
tile.radar_screen.name=Radarbildschirm
tile.radio_autocal.name=AUTOCAL-Rechenautomat
tile.radio_telex.name=Telex-Maschine
tile.radio_torch_controller.name=Redstone-over-Radio Controller
tile.radio_torch_controller.desc=Empfängt Steuersignale und gibt$sie an die verbundene Maschine weiter

View File

@ -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
@ -6026,6 +6028,7 @@ tile.pwr_reflector.name=PWR Neutron Reflector
tile.pwr_reflector.desc=Reflects neutrons back to fuel rods$Placement: Grid, for increased reactivity$Valid casing material
tile.rad_lava_block.name=Volcanic Lava
tile.radar_screen.name=Radar Screen
tile.radio_autocal.name=AUTOCAL Automatic Calculator
tile.radio_telex.name=Telex Machine
tile.radio_torch_controller.name=Redstone-over-Radio Controller
tile.radio_torch_controller.desc=Can receive command signals and relays$them to the connected machine

View File

@ -0,0 +1,134 @@
# Blender v2.79 (sub 0) OBJ File: 'autocal.blend'
# www.blender.org
o Plane
v 0.500000 0.125000 0.375000
v 0.500000 0.125000 -0.375000
v 0.500000 1.500000 0.375000
v 0.500000 1.500000 -0.375000
v -0.500000 0.125000 0.375000
v 0.500000 0.000000 0.500000
v 0.500000 0.000000 -0.500000
v -0.500000 0.125000 -0.375000
v -0.500000 1.500000 0.375000
v 0.500000 2.000000 0.500000
v 0.500000 2.000000 -0.500000
v -0.500000 1.500000 -0.375000
v -0.500000 0.000000 -0.500000
v -0.500000 0.000000 0.500000
v -0.500000 2.000000 -0.500000
v -0.500000 2.000000 0.500000
v 0.437500 0.125000 0.375000
v 0.437500 0.125000 -0.375000
v 0.437500 1.500000 0.375000
v 0.437500 1.500000 -0.375000
v -0.437500 0.125000 -0.375000
v -0.437500 0.125000 0.375000
v -0.437500 1.500000 -0.375000
v -0.437500 1.500000 0.375000
v 0.501000 1.671875 0.375000
v 0.501000 1.578125 0.375000
v 0.501000 1.671875 -0.375000
v 0.501000 1.578125 -0.375000
vt 0.428571 0.000000
vt 0.285714 0.250000
vt 0.285714 0.000000
vt 0.285714 0.750000
vt 0.428571 1.000000
vt 0.285714 1.000000
vt 0.580357 0.640625
vt 0.687500 0.625000
vt 0.687500 0.640625
vt 0.821429 0.625000
vt 0.812500 0.281250
vt 0.821429 0.281250
vt 0.687500 0.265625
vt 0.580357 0.281250
vt 0.580357 0.265625
vt 0.303571 0.281250
vt 0.428571 0.250000
vt 0.410714 0.281250
vt 0.410714 0.625000
vt 0.303571 0.625000
vt 0.428571 0.750000
vt 0.571429 0.250000
vt 0.571429 0.750000
vt 0.142857 0.750000
vt 0.017857 0.281250
vt 0.142857 0.250000
vt 0.125000 0.281250
vt 0.125000 0.625000
vt 0.000000 0.750000
vt 0.017857 0.625000
vt 0.000000 0.250000
vt 0.705357 0.625000
vt 0.705357 0.281250
vt 0.687500 0.281250
vt 0.580357 0.625000
vt 0.696429 0.281250
vt 0.696429 0.625000
vt 0.571429 0.281250
vt 0.571429 0.625000
vt 0.696429 0.625000
vt 0.696429 0.281250
vt 0.812500 0.265625
vt 0.705357 0.265625
vt 0.705357 0.640625
vt 0.812500 0.625000
vt 0.812500 0.640625
vt 0.428571 0.750000
vt 1.000000 0.875000
vt 0.428571 0.875000
vt 1.000000 0.750000
vn 0.0000 -1.0000 0.0000
vn 0.0000 1.0000 0.0000
vn 0.0000 0.0000 1.0000
vn 1.0000 0.0000 0.0000
vn 0.0000 0.0000 -1.0000
vn -1.0000 0.0000 0.0000
s off
f 13/1/1 6/2/1 14/3/1
f 10/4/2 15/5/2 16/6/2
f 12/7/1 24/8/1 9/9/1
f 4/10/3 18/11/3 2/12/3
f 5/13/2 21/14/2 8/15/2
f 1/16/4 7/17/4 2/18/4
f 4/19/4 10/4/4 3/20/4
f 4/19/4 7/17/4 11/21/4
f 3/20/4 6/2/4 1/16/4
f 11/21/5 13/22/5 15/23/5
f 16/24/3 6/2/3 10/4/3
f 8/25/6 14/26/6 5/27/6
f 9/28/6 15/29/6 12/30/6
f 12/30/6 13/31/6 8/25/6
f 9/28/6 14/26/6 16/24/6
f 18/11/4 19/32/4 17/33/4
f 22/34/6 23/35/6 21/14/6
f 1/36/5 19/32/5 3/37/5
f 8/38/3 23/35/3 12/39/3
f 9/40/5 22/34/5 5/41/5
f 2/42/2 17/33/2 1/43/2
f 3/44/1 20/45/1 4/46/1
f 26/47/4 27/48/4 25/49/4
f 13/1/1 7/17/1 6/2/1
f 10/4/2 11/21/2 15/5/2
f 12/7/1 23/35/1 24/8/1
f 4/10/3 20/45/3 18/11/3
f 5/13/2 22/34/2 21/14/2
f 1/16/4 6/2/4 7/17/4
f 4/19/4 11/21/4 10/4/4
f 4/19/4 2/18/4 7/17/4
f 3/20/4 10/4/4 6/2/4
f 11/21/5 7/17/5 13/22/5
f 16/24/3 14/26/3 6/2/3
f 8/25/6 13/31/6 14/26/6
f 9/28/6 16/24/6 15/29/6
f 12/30/6 15/29/6 13/31/6
f 9/28/6 5/27/6 14/26/6
f 18/11/4 20/45/4 19/32/4
f 22/34/6 24/8/6 23/35/6
f 1/36/5 17/33/5 19/32/5
f 8/38/3 21/14/3 23/35/3
f 9/40/5 24/8/5 22/34/5
f 2/42/2 18/11/2 17/33/2
f 3/44/1 19/32/1 20/45/1
f 26/47/4 28/50/4 27/48/4

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB