From 558471237a005f4963c32b5db053dcbd53d4a1d5 Mon Sep 17 00:00:00 2001 From: AnimeGitB Date: Wed, 12 Oct 2022 21:57:26 +1030 Subject: [PATCH] Add DynamicFloat class --- .../grasscutter/data/common/DynamicFloat.java | 74 +++++++++++++++++++ .../java/emu/grasscutter/utils/JsonUtils.java | 42 ++++++++++- 2 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 src/main/java/emu/grasscutter/data/common/DynamicFloat.java diff --git a/src/main/java/emu/grasscutter/data/common/DynamicFloat.java b/src/main/java/emu/grasscutter/data/common/DynamicFloat.java new file mode 100644 index 000000000..65c1be88f --- /dev/null +++ b/src/main/java/emu/grasscutter/data/common/DynamicFloat.java @@ -0,0 +1,74 @@ +package emu.grasscutter.data.common; + +import java.util.List; + +import it.unimi.dsi.fastutil.floats.FloatArrayList; +import it.unimi.dsi.fastutil.objects.Object2FloatArrayMap; +import it.unimi.dsi.fastutil.objects.Object2FloatMap; +import lombok.val; + +public class DynamicFloat { + public static class StackOp { + enum Op {CONSTANT, KEY, ADD, SUB, MUL, DIV}; + public Op op; + public float fValue; + public String sValue; + + public StackOp(String s) { + switch (s.toUpperCase()) { + case "ADD" -> this.op = Op.ADD; + case "SUB" -> this.op = Op.SUB; + case "MUL" -> this.op = Op.MUL; + case "DIV" -> this.op = Op.DIV; + default -> { + this.op = Op.KEY; + this.sValue = s; + } + } + } + + public StackOp(float f) { + this.op = Op.CONSTANT; + this.fValue = f; + } + } + private List ops; + private boolean dynamic = false; + private float constant = 0f; + + public DynamicFloat(float constant) { + this.constant = constant; + } + + public DynamicFloat(String key) { + this.dynamic = true; + this.ops = List.of(new StackOp(key)); + } + + public DynamicFloat(List ops) { + this.dynamic = true; + this.ops = ops; + } + + public float get() { + return this.get(new Object2FloatArrayMap()); + } + + public float get(Object2FloatMap props) { + if (!dynamic) + return constant; + + val fl = new FloatArrayList(); + for (var op : this.ops) { + switch (op.op) { + case CONSTANT -> fl.push(op.fValue); + case KEY -> fl.push(props.getOrDefault(op.sValue, 0f)); + case ADD -> fl.push(fl.popFloat() + fl.popFloat()); + case SUB -> fl.push(-fl.popFloat() + fl.popFloat()); // [f0, f1, f2] -> [f0, f1-f2] (opposite of RPN order) + case MUL -> fl.push(fl.popFloat() * fl.popFloat()); + case DIV -> fl.push((1f/fl.popFloat()) * fl.popFloat()); // [f0, f1, f2] -> [f0, f1/f2] + } + } + return fl.popFloat(); // well-formed data will always have only one value left at this point + } +} diff --git a/src/main/java/emu/grasscutter/utils/JsonUtils.java b/src/main/java/emu/grasscutter/utils/JsonUtils.java index 5742b87f1..bcd95fb9b 100644 --- a/src/main/java/emu/grasscutter/utils/JsonUtils.java +++ b/src/main/java/emu/grasscutter/utils/JsonUtils.java @@ -7,6 +7,7 @@ import java.io.Reader; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -14,10 +15,49 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonElement; import com.google.gson.JsonSyntaxException; +import com.google.gson.TypeAdapter; import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; + +import emu.grasscutter.data.common.DynamicFloat; +import lombok.val; public final class JsonUtils { - static final Gson gson = new GsonBuilder().setPrettyPrinting().create(); + // Adapters + static class DynamicFloatAdapter extends TypeAdapter { + @Override + public DynamicFloat read(JsonReader reader) throws IOException { + switch (reader.peek()) { + case STRING: + return new DynamicFloat(reader.nextString()); + case NUMBER: + return new DynamicFloat((float) reader.nextDouble()); + case BEGIN_ARRAY: + reader.beginArray(); + val opStack = new ArrayList(); + while (reader.hasNext()) { + opStack.add(switch (reader.peek()) { + case STRING -> new DynamicFloat.StackOp(reader.nextString()); + case NUMBER -> new DynamicFloat.StackOp((float) reader.nextDouble()); + default -> throw new IOException("Invalid DynamicFloat definition - " + reader.peek().name()); + }); + } + reader.endArray(); + return new DynamicFloat(opStack); + default: + throw new IOException("Invalid DynamicFloat definition - " + reader.peek().name()); + } + } + + @Override + public void write(JsonWriter writer, DynamicFloat f) {}; + } + + static final Gson gson = new GsonBuilder() + .setPrettyPrinting() + .registerTypeAdapter(DynamicFloat.class, new DynamicFloatAdapter()) + .create(); @Deprecated(forRemoval = true) public static Gson getGsonFactory() {