Add in-game calculator

This commit is contained in:
MartinTheDragon 2022-01-29 16:16:15 +01:00
parent 081df1c8fe
commit fd940b59c8
No known key found for this signature in database
GPG Key ID: F03B4EA7AB5A6C23
5 changed files with 248 additions and 2 deletions

View File

@ -1,5 +1,7 @@
package com.hbm.handler;
import com.hbm.inventory.gui.GUICalculator;
import cpw.mods.fml.common.FMLCommonHandler;
import org.lwjgl.input.Keyboard;
import com.hbm.extprop.HbmPlayerProps;
@ -16,6 +18,7 @@ public class HbmKeybinds {
public static final String category = "hbm.key";
public static KeyBinding calculatorKey = new KeyBinding(category + ".calculator", Keyboard.KEY_N, category);
public static KeyBinding jetpackKey = new KeyBinding(category + ".toggleBack", Keyboard.KEY_C, category);
public static KeyBinding hudKey = new KeyBinding(category + ".toggleHUD", Keyboard.KEY_V, category);
public static KeyBinding reloadKey = new KeyBinding(category + ".reload", Keyboard.KEY_R, category);
@ -25,12 +28,13 @@ public class HbmKeybinds {
public static KeyBinding craneLeftKey = new KeyBinding(category + ".craneMoveLeft", Keyboard.KEY_LEFT, category);
public static KeyBinding craneRightKey = new KeyBinding(category + ".craneMoveRight", Keyboard.KEY_RIGHT, category);
public static KeyBinding craneLoadKey = new KeyBinding(category + ".craneLoad", Keyboard.KEY_RETURN, category);
public static void register() {
ClientRegistry.registerKeyBinding(calculatorKey);
ClientRegistry.registerKeyBinding(jetpackKey);
ClientRegistry.registerKeyBinding(hudKey);
ClientRegistry.registerKeyBinding(reloadKey);
ClientRegistry.registerKeyBinding(craneUpKey);
ClientRegistry.registerKeyBinding(craneDownKey);
ClientRegistry.registerKeyBinding(craneLeftKey);
@ -40,6 +44,9 @@ public class HbmKeybinds {
@SubscribeEvent
public void keyEvent(KeyInputEvent event) {
if (calculatorKey.getIsKeyPressed()) { // handle the calculator client-side only
FMLCommonHandler.instance().showGuiScreen(new GUICalculator());
}
HbmPlayerProps props = HbmPlayerProps.getData(MainRegistry.proxy.me());

View File

@ -0,0 +1,237 @@
package com.hbm.inventory.gui;
import com.hbm.lib.RefStrings;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.gui.GuiTextField;
import net.minecraft.util.ResourceLocation;
import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.GL11;
import java.math.BigDecimal;
import java.util.Locale;
import java.util.Stack;
public class GUICalculator extends GuiScreen {
private static final ResourceLocation texture = new ResourceLocation(RefStrings.MODID, "textures/gui/calculator.png");
private int xSize = 220;
private int ySize = 50;
private GuiTextField inputField;
private String latestResult = "?";
@Override
public void initGui() {
Keyboard.enableRepeatEvents(true);
int x = (width - xSize) / 2;
int y = (height - ySize) / 2;
inputField = new GuiTextField(fontRendererObj, x + 5, y + 5, 210, 13);
inputField.setTextColor(-1);
inputField.setCanLoseFocus(false);
inputField.setFocused(true);
inputField.setMaxStringLength(1000);
}
@Override
public void onGuiClosed() {
Keyboard.enableRepeatEvents(false);
}
@Override
protected void keyTyped(char p_73869_1_, int p_73869_2_) {
if (!inputField.textboxKeyTyped(p_73869_1_, p_73869_2_))
super.keyTyped(p_73869_1_, p_73869_2_);
if (p_73869_1_ == 13 || p_73869_1_ == 10) { // when pressing enter (CR or LF)
try {
double result = Double.parseDouble(latestResult);
String plainStringRepresentation = (new BigDecimal(result)).toPlainString();
GuiScreen.setClipboardString(plainStringRepresentation);
inputField.setText(plainStringRepresentation);
inputField.setCursorPositionEnd();
inputField.setSelectionPos(0);
} catch (Exception ignored) {}
return;
}
String input = inputField.getText().replaceAll("[^\\d+\\-*/^.()\\sA-Za-z]+", "");
if (input.isEmpty()) {
latestResult = "?";
return;
}
try {
latestResult = Double.toString(evaluateExpression(input));
} catch (Exception e) { latestResult = e.toString(); }
}
@Override
public void drawScreen(int mouseX, int mouseY, float partialTicks) {
super.drawScreen(mouseX, mouseY, partialTicks);
GL11.glColor4f(1F, 1F, 1F, 1F);
mc.getTextureManager().bindTexture(texture);
int x = (width - xSize) / 2;
int y = (height - ySize) / 2;
drawTexturedModalRect(x, y, 0, 0, xSize, ySize);
inputField.drawTextBox();
fontRendererObj.drawString("=" + latestResult, x + 5, y + 30, -1);
}
/**
* 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] >= '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)).toPlainString() + input.substring(exponentExpressionEnd);
} while (input.contains("^"));
return input;
}
}

View File

@ -438,6 +438,7 @@ hazard.particleFine=Feinstaub
hazard.sand=Augenreizstoffe
hbm.key=NTM Hotkeys
hbm.key.calculator=Taschenrechner
hbm.key.craneLoad=Kran laden/entladen
hbm.key.craneMoveDown=Kran rückwärts
hbm.key.craneMoveLeft=Kran nach links

View File

@ -575,6 +575,7 @@ hazard.particleFine=Particulates
hazard.sand=Eye Irritants
hbm.key=NTM Hotkeys
hbm.key.calculator=Calculator
hbm.key.craneLoad=Load/Unload Crane
hbm.key.craneMoveDown=Move Crane Backward
hbm.key.craneMoveLeft=Move Crane Left

Binary file not shown.

After

Width:  |  Height:  |  Size: 566 B