From b2cb1e1e1392644efb34892b545b6aef3489d600 Mon Sep 17 00:00:00 2001 From: Christian Mesh Date: Fri, 4 Aug 2023 20:18:10 -0400 Subject: [PATCH 1/8] New stock user interfaces --- .../immersiverailroading/ConfigGraphics.java | 7 + .../entity/Locomotive.java | 30 +- .../entity/LocomotiveSteam.java | 2 +- .../entity/physics/SimulationState.java | 2 +- .../gui/overlay/GuiBuilder.java | 335 ++++++++++++------ .../gui/overlay/Readouts.java | 27 +- .../gui/overlay/Stat.java | 82 ++++- .../model/SteamLocomotiveModel.java | 2 +- .../model/StockModel.java | 3 + .../model/part/Control.java | 68 +--- .../model/part/Whistle.java | 23 +- .../EntityRollingStockDefinition.java | 72 +++- .../tile/TileRailBase.java | 4 +- .../util/MergedBlocks.java | 10 +- .../immersiverailroading/util/Speed.java | 3 + .../gui/default/common/gear.png | Bin 0 -> 405 bytes .../gui/default/common/opacity.caml | 3 + .../gui/default/common/scale.caml | 5 + .../gui/default/common/settings.caml | 74 ++++ .../gui/default/common/settings_bar.png | Bin 0 -> 139 bytes .../gui/default/common/settings_panel.png | Bin 0 -> 435 bytes .../gui/default/common/settings_slider.png | Bin 0 -> 157 bytes .../gui/default/diesel.caml | 264 +++++++------- .../gui/default/diesel.json | 184 +--------- .../gui/default/diesel/brake_lever.png | Bin 0 -> 139 bytes .../gui/default/diesel/brake_stand.png | Bin 0 -> 261 bytes .../gui/default/diesel/button.png | Bin 0 -> 212 bytes .../gui/default/diesel/dial.png | Bin 0 -> 1353 bytes .../gui/default/diesel/dial_ticks_x10.caml | 60 ++++ .../gui/default/diesel/diesel_base.png | Bin 0 -> 587 bytes .../gui/default/diesel/diesel_dial.png | Bin 0 -> 540 bytes .../gui/default/diesel/gauge_brake_face.caml | 62 ++++ .../gui/default/diesel/gauge_speed_face.caml | 49 +++ .../diesel/gauge_temperature_face.caml | 49 +++ .../gui/default/diesel/horn.png | Bin 0 -> 136 bytes .../gui/default/diesel/reverser_base.png | Bin 0 -> 303 bytes .../gui/default/diesel/reverser_lever.png | Bin 0 -> 137 bytes .../gui/default/diesel/throttle_base.png | Bin 0 -> 355 bytes .../gui/default/diesel/throttle_lever.png | Bin 0 -> 136 bytes .../gui/default/diesel/tickmark.png | Bin 0 -> 135 bytes .../gui/default/steam.caml | 285 ++++++++------- .../gui/default/steam.json | 215 +---------- .../gui/default/steam/brake_lever.png | Bin 0 -> 143 bytes .../gui/default/steam/brake_stand.png | Bin 0 -> 251 bytes .../gui/default/steam/dial_down.caml | 2 + .../gui/default/steam/dial_left.caml | 7 + .../gui/default/steam/dial_left_down.caml | 7 + .../gui/default/steam/dial_right.caml | 8 + .../gui/default/steam/dial_steam.png | Bin 0 -> 16262 bytes .../gui/default/steam/dial_ticks_x10.caml | 60 ++++ .../gui/default/steam/dial_up.caml | 8 + .../gui/default/steam/gauge_boiler_face.caml | 49 +++ .../gui/default/steam/gauge_brake_face.caml | 62 ++++ .../gui/default/steam/gauge_speed_face.caml | 49 +++ .../default/steam/gauge_temperature_face.caml | 49 +++ .../gui/default/steam/gauge_water_left.caml | 14 + .../gui/default/steam/gauge_water_right.caml | 15 + .../gui/default/steam/reverser_base.png | Bin 0 -> 543 bytes .../gui/default/steam/reverser_harp_lever.png | Bin 0 -> 3256 bytes .../gui/default/steam/reverser_harp_stand.png | Bin 0 -> 10113 bytes .../gui/default/steam/reverser_lever.png | Bin 0 -> 170 bytes .../gui/default/steam/steam_dial.png | Bin 0 -> 540 bytes .../gui/default/steam/throttle.png | Bin 0 -> 6885 bytes .../gui/default/steam/throttle_backing.png | Bin 0 -> 7635 bytes .../gui/default/steam/tickmark.png | Bin 0 -> 135 bytes .../gui/default/steam/water_glass_fill.png | Bin 0 -> 5076 bytes .../gui/default/steam/water_glass_left.png | Bin 0 -> 14561 bytes .../gui/default/steam/water_glass_right.png | Bin 0 -> 14580 bytes .../gui/default/steam/whistle.png | Bin 0 -> 6371 bytes .../gui/default/steam/whistle_cord.png | Bin 0 -> 6232 bytes 70 files changed, 1370 insertions(+), 880 deletions(-) create mode 100644 src/main/resources/assets/immersiverailroading/gui/default/common/gear.png create mode 100644 src/main/resources/assets/immersiverailroading/gui/default/common/opacity.caml create mode 100644 src/main/resources/assets/immersiverailroading/gui/default/common/scale.caml create mode 100644 src/main/resources/assets/immersiverailroading/gui/default/common/settings.caml create mode 100644 src/main/resources/assets/immersiverailroading/gui/default/common/settings_bar.png create mode 100644 src/main/resources/assets/immersiverailroading/gui/default/common/settings_panel.png create mode 100644 src/main/resources/assets/immersiverailroading/gui/default/common/settings_slider.png create mode 100644 src/main/resources/assets/immersiverailroading/gui/default/diesel/brake_lever.png create mode 100644 src/main/resources/assets/immersiverailroading/gui/default/diesel/brake_stand.png create mode 100644 src/main/resources/assets/immersiverailroading/gui/default/diesel/button.png create mode 100644 src/main/resources/assets/immersiverailroading/gui/default/diesel/dial.png create mode 100644 src/main/resources/assets/immersiverailroading/gui/default/diesel/dial_ticks_x10.caml create mode 100644 src/main/resources/assets/immersiverailroading/gui/default/diesel/diesel_base.png create mode 100644 src/main/resources/assets/immersiverailroading/gui/default/diesel/diesel_dial.png create mode 100644 src/main/resources/assets/immersiverailroading/gui/default/diesel/gauge_brake_face.caml create mode 100644 src/main/resources/assets/immersiverailroading/gui/default/diesel/gauge_speed_face.caml create mode 100644 src/main/resources/assets/immersiverailroading/gui/default/diesel/gauge_temperature_face.caml create mode 100644 src/main/resources/assets/immersiverailroading/gui/default/diesel/horn.png create mode 100644 src/main/resources/assets/immersiverailroading/gui/default/diesel/reverser_base.png create mode 100644 src/main/resources/assets/immersiverailroading/gui/default/diesel/reverser_lever.png create mode 100644 src/main/resources/assets/immersiverailroading/gui/default/diesel/throttle_base.png create mode 100644 src/main/resources/assets/immersiverailroading/gui/default/diesel/throttle_lever.png create mode 100644 src/main/resources/assets/immersiverailroading/gui/default/diesel/tickmark.png create mode 100644 src/main/resources/assets/immersiverailroading/gui/default/steam/brake_lever.png create mode 100644 src/main/resources/assets/immersiverailroading/gui/default/steam/brake_stand.png create mode 100644 src/main/resources/assets/immersiverailroading/gui/default/steam/dial_down.caml create mode 100644 src/main/resources/assets/immersiverailroading/gui/default/steam/dial_left.caml create mode 100644 src/main/resources/assets/immersiverailroading/gui/default/steam/dial_left_down.caml create mode 100644 src/main/resources/assets/immersiverailroading/gui/default/steam/dial_right.caml create mode 100644 src/main/resources/assets/immersiverailroading/gui/default/steam/dial_steam.png create mode 100644 src/main/resources/assets/immersiverailroading/gui/default/steam/dial_ticks_x10.caml create mode 100644 src/main/resources/assets/immersiverailroading/gui/default/steam/dial_up.caml create mode 100644 src/main/resources/assets/immersiverailroading/gui/default/steam/gauge_boiler_face.caml create mode 100644 src/main/resources/assets/immersiverailroading/gui/default/steam/gauge_brake_face.caml create mode 100644 src/main/resources/assets/immersiverailroading/gui/default/steam/gauge_speed_face.caml create mode 100644 src/main/resources/assets/immersiverailroading/gui/default/steam/gauge_temperature_face.caml create mode 100644 src/main/resources/assets/immersiverailroading/gui/default/steam/gauge_water_left.caml create mode 100644 src/main/resources/assets/immersiverailroading/gui/default/steam/gauge_water_right.caml create mode 100644 src/main/resources/assets/immersiverailroading/gui/default/steam/reverser_base.png create mode 100644 src/main/resources/assets/immersiverailroading/gui/default/steam/reverser_harp_lever.png create mode 100644 src/main/resources/assets/immersiverailroading/gui/default/steam/reverser_harp_stand.png create mode 100644 src/main/resources/assets/immersiverailroading/gui/default/steam/reverser_lever.png create mode 100644 src/main/resources/assets/immersiverailroading/gui/default/steam/steam_dial.png create mode 100644 src/main/resources/assets/immersiverailroading/gui/default/steam/throttle.png create mode 100644 src/main/resources/assets/immersiverailroading/gui/default/steam/throttle_backing.png create mode 100644 src/main/resources/assets/immersiverailroading/gui/default/steam/tickmark.png create mode 100644 src/main/resources/assets/immersiverailroading/gui/default/steam/water_glass_fill.png create mode 100644 src/main/resources/assets/immersiverailroading/gui/default/steam/water_glass_left.png create mode 100644 src/main/resources/assets/immersiverailroading/gui/default/steam/water_glass_right.png create mode 100644 src/main/resources/assets/immersiverailroading/gui/default/steam/whistle.png create mode 100644 src/main/resources/assets/immersiverailroading/gui/default/steam/whistle_cord.png diff --git a/src/main/java/cam72cam/immersiverailroading/ConfigGraphics.java b/src/main/java/cam72cam/immersiverailroading/ConfigGraphics.java index 8540439af..24b6758d8 100644 --- a/src/main/java/cam72cam/immersiverailroading/ConfigGraphics.java +++ b/src/main/java/cam72cam/immersiverailroading/ConfigGraphics.java @@ -1,10 +1,14 @@ package cam72cam.immersiverailroading; import cam72cam.immersiverailroading.library.SpeedDisplayType; +import cam72cam.immersiverailroading.library.ValveGearConfig; import cam72cam.mod.config.ConfigFile.Comment; import cam72cam.mod.config.ConfigFile.Name; import cam72cam.mod.render.OptiFine; +import java.util.HashMap; +import java.util.Map; + import static cam72cam.mod.config.ConfigFile.*; @Comment("Configuration File") @@ -48,4 +52,7 @@ public class ConfigGraphics { @Comment("How likely a piece of stock is to sway (1 == always, 10 == infrequent)") public static int StockSwayChance = 1; + + @Comment("Settings used in the stock user interfaces") + public static Map settings = new HashMap<>(); } diff --git a/src/main/java/cam72cam/immersiverailroading/entity/Locomotive.java b/src/main/java/cam72cam/immersiverailroading/entity/Locomotive.java index 2a4a8af4d..45b3e69cb 100644 --- a/src/main/java/cam72cam/immersiverailroading/entity/Locomotive.java +++ b/src/main/java/cam72cam/immersiverailroading/entity/Locomotive.java @@ -48,10 +48,12 @@ public abstract class Locomotive extends FreightTank { @TagField("HORN") protected int hornTime = 0; - public static final UUID AUTOMATED_PLAYER = new UUID(0, 0); @TagSync @TagField(value = "HORN_PLAYER", mapper = StrictTagMapper.class) protected UUID hornPlayer = null; + @TagSync + @TagField(value = "HORN_PULL") + public float hornPull; @TagSync @TagField("BELL") @@ -325,6 +327,7 @@ public void onTick() { if (getWorld().isServer) { sync.setInterval(5); for (Control control : getDefinition().getModel().getControls()) { + // Logic duplicated in Readouts#setValue if (!getDefinition().isLinearBrakeControl() && control.part.type == ModelComponentType.TRAIN_BRAKE_X) { setTrainBrake(Math.max(0, Math.min(1, getTrainBrake() + (getControlPosition(control) - 0.5f) / 8))); } @@ -349,6 +352,9 @@ public void onTick() { } else if (hornPlayer != null) { hornPlayer = null; } + if (hornTime == 0) { + hornPull = 0; + } OptionalDouble control = this.getDefinition().getModel().getControls().stream() .filter(x -> x.part.type == ModelComponentType.BELL_CONTROL_X) .mapToDouble(this::getControlPosition) @@ -504,6 +510,11 @@ private void setRealReverser(float newReverser){ } public void setHorn(int val, UUID uuid) { + if (uuid == null) { + // Legacy API + hornPull = 1; + } + if (hornPlayer == null && uuid != null) { hornPlayer = uuid; } @@ -512,6 +523,11 @@ public void setHorn(int val, UUID uuid) { } } + public void setHorn(int time, float value) { + hornTime = time; + hornPull = value; + } + public int getHornTime() { return hornTime; } @@ -525,8 +541,16 @@ public Entity getHornPlayer() { return null; } - public boolean isAutomatedHorn() { - return AUTOMATED_PLAYER.equals(hornPlayer); + public float getHornPull() { + if (getHornPlayer() != null) { + return (getHornPlayer().getRotationPitch() + 90) / 180; + } + double control = this.getDefinition().getModel().getControls().stream() + .filter(x -> x.part.type == ModelComponentType.WHISTLE_CONTROL_X) + .mapToDouble(this::getControlPosition) + .max().orElse(0); + + return Math.max((float)control, hornPull); } @Deprecated diff --git a/src/main/java/cam72cam/immersiverailroading/entity/LocomotiveSteam.java b/src/main/java/cam72cam/immersiverailroading/entity/LocomotiveSteam.java index 0e692669e..00060f10a 100644 --- a/src/main/java/cam72cam/immersiverailroading/entity/LocomotiveSteam.java +++ b/src/main/java/cam72cam/immersiverailroading/entity/LocomotiveSteam.java @@ -102,7 +102,7 @@ public double getAppliedTractiveEffort(Speed speed) { // Cap the max "effective" reverser. At high speeds having a fully open reverser just damages equipment double reverser = getReverser(); - double reverserCap = 0.5; + double reverserCap = 0.25; double maxReverser = 1 - Math.abs(getCurrentSpeed().metric()) / getDefinition().getMaxSpeed(gauge).metric() * reverserCap; // This should probably be tuned... diff --git a/src/main/java/cam72cam/immersiverailroading/entity/physics/SimulationState.java b/src/main/java/cam72cam/immersiverailroading/entity/physics/SimulationState.java index 316771b8d..d5e0fe1ba 100644 --- a/src/main/java/cam72cam/immersiverailroading/entity/physics/SimulationState.java +++ b/src/main/java/cam72cam/immersiverailroading/entity/physics/SimulationState.java @@ -162,7 +162,7 @@ public boolean equals(Object o) { couplerEngagedRear == other.couplerEngagedRear && Math.abs(tractiveEffortFactors - other.tractiveEffortFactors) < 0.01 && Math.abs(massKg - other.massKg)/massKg < 0.01 && - (desiredBrakePressure == null || Math.abs(desiredBrakePressure - other.desiredBrakePressure) < 0.1) && + (desiredBrakePressure == null || Math.abs(desiredBrakePressure - other.desiredBrakePressure) < 0.001) && Math.abs(independentBrakePosition - other.independentBrakePosition) < 0.01; } return false; diff --git a/src/main/java/cam72cam/immersiverailroading/gui/overlay/GuiBuilder.java b/src/main/java/cam72cam/immersiverailroading/gui/overlay/GuiBuilder.java index 180da9e97..25b39d756 100644 --- a/src/main/java/cam72cam/immersiverailroading/gui/overlay/GuiBuilder.java +++ b/src/main/java/cam72cam/immersiverailroading/gui/overlay/GuiBuilder.java @@ -1,9 +1,14 @@ package cam72cam.immersiverailroading.gui.overlay; +import cam72cam.immersiverailroading.ConfigGraphics; import cam72cam.immersiverailroading.entity.EntityCoupleableRollingStock; import cam72cam.immersiverailroading.entity.EntityRollingStock; import cam72cam.immersiverailroading.library.GuiText; +import cam72cam.immersiverailroading.registry.EntityRollingStockDefinition; import cam72cam.immersiverailroading.util.DataBlock; +import cam72cam.immersiverailroading.util.MergedBlocks; +import cam72cam.mod.MinecraftClient; +import cam72cam.mod.entity.Entity; import cam72cam.mod.event.ClientEvents; import cam72cam.mod.gui.helpers.GUIHelpers; import cam72cam.mod.math.Vec3d; @@ -17,15 +22,17 @@ import util.Matrix4; import javax.imageio.ImageIO; +import java.awt.*; import java.awt.image.BufferedImage; import java.io.IOException; import java.util.*; +import java.util.List; public class GuiBuilder { private final float x; private final float y; - private final boolean centerx; - private final boolean centery; + private final Horizontal screen_x; + private final Vertical screen_y; private final Identifier image; private final int imageWidth; @@ -36,9 +43,12 @@ public class GuiBuilder { private final Readouts readout; private final String control; + private final String setting; + private final Float setting_default; private final boolean global; private final boolean invert; - private final boolean hide; + private final boolean translucent; + private final boolean toggle; private final float tlx; private final float tly; private final float rotx; @@ -49,20 +59,53 @@ public class GuiBuilder { private final Float scaley; private final Map colors = new HashMap<>(); + private final EntityRollingStockDefinition.ControlSoundsDefinition sound; private final List elements; + private float temporary_value = 0.5f; + + private enum Horizontal { + LEFT, + RIGHT, + MIDDLE, + ; + + public static Horizontal from(String pos_x) { + return pos_x == null ? LEFT : valueOf(pos_x.toUpperCase(Locale.ROOT)); + } + } + private enum Vertical { + TOP, + MIDDLE, + BOTTOM, + ; + public static Vertical from(String pos_y) { + return pos_y == null ? TOP : valueOf(pos_y.toUpperCase(Locale.ROOT)); + } + } + + private static DataBlock processImports(DataBlock data) throws IOException { + // This is kinda weird as it appends the import to the current block instead of inserting it in the current + // element list. It's definitely a bit of a footgun. The "correct" way to do this would be to make import part + // of parsing in CAML, which is it's own sort of weirdness due to JSON interop. + List imports = data.getValues("import"); + if (imports != null) { + for (DataBlock.Value imp : imports) { + data = new MergedBlocks(data, processImports(DataBlock.load(imp.asIdentifier()))); + } + } + return data; + } + protected GuiBuilder(DataBlock data) throws IOException { + data = processImports(data); + // common stuff this.x = data.getValue("x").asFloat(0f); this.y = data.getValue("y").asFloat(0f); - DataBlock centered = data.getBlock("centered"); - if (centered != null) { - this.centerx = centered.getValue("x").asBoolean(false); - this.centery = centered.getValue("y").asBoolean(false); - } else { - this.centerx = this.centery = false; - } + this.screen_x = Horizontal.from(data.getValue("screen_x").asString()); + this.screen_y = Vertical.from(data.getValue("screen_y").asString()); // Image stuff this.image = data.getValue("image").asIdentifier(null); @@ -89,9 +132,14 @@ protected GuiBuilder(DataBlock data) throws IOException { String readout = data.getValue("readout").asString(); this.readout = readout != null ? Readouts.valueOf(readout.toUpperCase(Locale.ROOT)) : null; this.control = data.getValue("control").asString(); + this.setting = data.getValue("setting").asString(); + this.setting_default = data.getValue("setting_default").asFloat(); + DataBlock soundBlock = data.getBlock("sound"); + this.sound = soundBlock != null ? new EntityRollingStockDefinition.ControlSoundsDefinition(soundBlock) : null; this.global = data.getValue("global").asBoolean(false); this.invert = data.getValue("invert").asBoolean(false); - this.hide = data.getValue("hide").asBoolean(false); + this.translucent = data.getValue("translucent").asBoolean(data.getValue("hide").asBoolean(false)); + this.toggle = data.getValue("toggle").asBoolean(false); DataBlock tl = data.getBlock("translate"); if (tl != null) { @@ -147,12 +195,6 @@ protected GuiBuilder(DataBlock data) throws IOException { elements.add(new GuiBuilder(element)); } } - List imports = data.getValues("import"); - if (imports != null) { - for (DataBlock.Value imp : imports) { - elements.add(parse(imp.asIdentifier())); - } - } } public static GuiBuilder parse(Identifier overlay) throws IOException { @@ -161,28 +203,43 @@ public static GuiBuilder parse(Identifier overlay) throws IOException { private void applyPosition(Matrix4 matrix, int maxx, int maxy) { matrix.translate(this.x, this.y, 0); - if (centerx) { - matrix.translate(maxx/2f, 0, 0); - } - if (centery) { - matrix.translate(0, maxy/2f, 0); - } - Vec3d offset = matrix.apply(Vec3d.ZERO); - if (offset.x < 0) { - matrix.translate(maxx, 0, 0); + switch (screen_x) { + case LEFT: + // NOP + break; + case MIDDLE: + matrix.translate(maxx/2f, 0, 0); + break; + case RIGHT: + matrix.translate(maxx, 0, 0); + break; } - if (offset.y < 0) { - matrix.translate(0, maxy, 0); + switch (screen_y) { + case TOP: + // NOP + break; + case MIDDLE: + matrix.translate(0, maxy/2f, 0); + break; + case BOTTOM: + matrix.translate(0, maxy, 0); + break; } } private float getValue(EntityRollingStock stock) { float value = 0; if (readout != null) { - value = readout.getValue(stock); + value = readout.getValue(stock, temporary_value); } else if (control != null) { value = stock.getControlPosition(control); + } else if (setting != null) { + if (!ConfigGraphics.settings.containsKey(setting) && setting_default != null) { + ConfigGraphics.settings.put(setting, setting_default); + } + + value = ConfigGraphics.settings.getOrDefault(setting, 0f); } if (invert) { @@ -192,7 +249,7 @@ private float getValue(EntityRollingStock stock) { return value; } - private void applyValue(Matrix4 matrix, float value, int maxx, int maxy) { + private void applyValue(Matrix4 matrix, float value) { if (tlx != 0 || tly != 0) { matrix.translate(tlx * value, tly * value, 0); } @@ -204,29 +261,25 @@ private void applyValue(Matrix4 matrix, float value, int maxx, int maxy) { if (scalex != null || scaley != null) { matrix.scale(scalex != null ? scalex * value : 1, scaley != null ? scaley * value : 1, 1); } - - Vec3d offset = matrix.apply(Vec3d.ZERO); - if (offset.x < 0) { - matrix.translate(maxx, 0, 0); - } - if (offset.y < 0) { - matrix.translate(0, maxy, 0); - } } public void render(RenderState state, EntityRollingStock stock) { - render(stock, state.clone().color(1, 1, 1, 1), GUIHelpers.getScreenWidth(), GUIHelpers.getScreenHeight()); + render(stock, state.clone().color(1, 1, 1, 1), GUIHelpers.getScreenWidth(), GUIHelpers.getScreenHeight(), 0xFFFFFFFF); } - private void render(EntityRollingStock stock, RenderState state, int maxx, int maxy) { + private void render(EntityRollingStock stock, RenderState state, int maxx, int maxy, int baseColor) { float value = getValue(stock); - if (hide && value != 1) { - return; + if (translucent) { + if (value == 0) { + return; + } + float alpha = (baseColor >> 24 & 255) / 255f * value; + baseColor = baseColor & 0x00FFFFFF | ((int)(alpha * 255f) << 24); } state = state.clone(); // TODO mem opt? applyPosition(state.model_view(), maxx, maxy); - applyValue(state.model_view(), value, maxx, maxy); + applyValue(state.model_view(), value); Float colorKey = null; for (float key : colors.keySet()) { @@ -235,9 +288,16 @@ private void render(EntityRollingStock stock, RenderState state, int maxx, int m } } - int col = colors.getOrDefault(colorKey, 0xFFFFFFFF); - if (colorKey != null) { - state.color((col >> 16 & 255) / 255.0f, (col >> 8 & 255) / 255.0f, (col & 255) / 255.0f, (col >> 24 & 255) / 255.0f); + if (colorKey != null && colors.containsKey(colorKey)) { + float oldAlpha = (baseColor >> 24 & 255) / 255f; + + int newColor = colors.get(colorKey); + float newAlpha = (newColor >> 24 & 255) / 255f; + baseColor = newColor & 0x00FFFFFF | ((int)(newAlpha * oldAlpha * 255f) << 24); + } + + if (colorKey != null || translucent) { + state.color((baseColor >> 16 & 255) / 255.0f, (baseColor >> 8 & 255) / 255.0f, (baseColor & 255) / 255.0f, (baseColor >> 24 & 255) / 255.0f); } if (image != null) { @@ -266,21 +326,21 @@ private void render(EntityRollingStock stock, RenderState state, int maxx, int m float scale = textHeight / 8f; Matrix4 mat = state.model_view().copy(); mat.scale(scale, scale, scale); - GUIHelpers.drawCenteredString(out, 0, 0, col, mat); + GUIHelpers.drawCenteredString(out, 0, 0, baseColor, mat); } for (GuiBuilder element : elements) { - element.render(stock, state, maxx, maxy); + element.render(stock, state, maxx, maxy, baseColor); } } private GuiBuilder find(EntityRollingStock stock, Matrix4 matrix, int maxx, int maxy, int x, int y) { float value = getValue(stock); - if (hide && value != 1) { + if (translucent && value == 0) { return null; } matrix = matrix.copy(); // TODO mem opt? applyPosition(matrix, maxx, maxy); - applyValue(matrix, value, maxx, maxy); + applyValue(matrix, value); for (GuiBuilder element : elements) { GuiBuilder found = element.find(stock, matrix, maxx, maxy, x, y); if (found != null) { @@ -288,8 +348,8 @@ private GuiBuilder find(EntityRollingStock stock, Matrix4 matrix, int maxx, int } } - if (image != null) { - if (control == null) { + if (image != null && interactable()) { + if (control == null && setting == null) { if (readout == null) { return null; } @@ -297,6 +357,7 @@ private GuiBuilder find(EntityRollingStock stock, Matrix4 matrix, int maxx, int case THROTTLE: case REVERSER: case TRAIN_BRAKE: + case TRAIN_BRAKE_LEVER: case INDEPENDENT_BRAKE: case COUPLER_FRONT: case COUPLER_REAR: @@ -311,18 +372,30 @@ private GuiBuilder find(EntityRollingStock stock, Matrix4 matrix, int maxx, int } int border = 2; Vec3d cornerA = matrix.apply(new Vec3d(-border, -border, 0)); - Vec3d cornerB = matrix.apply(new Vec3d(imageWidth + border, imageHeight + border, 0)); - if (x >= cornerA.x && x <= cornerB.x || x >= cornerB.x && x <= cornerA.x) { - if (y >= cornerA.y && y <= cornerB.y || y >= cornerB.y && y <= cornerA.y) { - return this; - } + Vec3d cornerB = matrix.apply(new Vec3d(-border, imageHeight + border, 0)); + Vec3d cornerC = matrix.apply(new Vec3d(imageWidth + border, -border, 0)); + Vec3d cornerD = matrix.apply(new Vec3d(imageWidth + border, imageHeight + border, 0)); + + Polygon poly = new Polygon( + new int[]{(int) cornerA.x, (int) cornerB.x, (int) cornerC.x, (int) cornerD.x}, + new int[]{(int) cornerA.y, (int) cornerB.y, (int) cornerC.y, (int) cornerD.y}, + 4 + ); + if (poly.getBounds2D().contains(x, y)) { + return this; } } return null; } - private boolean hasMovement() { - return tlx != 0 || tly != 0 || rotdeg != 0 || scalex != null || scaley != null; + private boolean interactable() { + return tlx != 0 || tly != 0 || rotdeg != 0 || scalex != null || scaley != null || toggle; + } + + private void onMouseClick(EntityRollingStock stock) { + if (sound != null) { + sound.effects(stock, true, getValue(stock), MinecraftClient.getPlayer().getPosition()); + } } private void onMouseMove(EntityRollingStock stock, Matrix4 matrix, GuiBuilder target, int maxx, int maxy, int x, int y) { @@ -330,7 +403,7 @@ private void onMouseMove(EntityRollingStock stock, Matrix4 matrix, GuiBuilder ta matrix = matrix.copy(); // TODO mem opt? applyPosition(matrix, maxx, maxy); Matrix4 preApply = matrix.copy(); - applyValue(matrix, value, maxx, maxy); + applyValue(matrix, value); if (target == this) { // 0 0 imageHeight imageWidth @@ -352,7 +425,7 @@ private void onMouseMove(EntityRollingStock stock, Matrix4 matrix, GuiBuilder ta temp.scale(scalex != null ? scalex * checkValue : 1, scaley != null ? scaley * checkValue : 1, 1); } - Vec3d checkMiddle = temp.apply(new Vec3d(1, 1, 0)); + Vec3d checkMiddle = temp.apply(new Vec3d(imageWidth/2f, imageHeight/2f, 0)); double delta = checkMiddle.distanceTo(new Vec3d(x, y, 0)); if (delta < closestDelta) { closestDelta = delta; @@ -361,7 +434,20 @@ private void onMouseMove(EntityRollingStock stock, Matrix4 matrix, GuiBuilder ta } if (closestValue != value) { - new ControlChangePacket(stock, readout, control, global, closestValue).sendToServer(); + float val = invert ? 1 - closestValue : closestValue; + temporary_value = val; + if (setting != null) { + ConfigGraphics.settings.put(setting, val); + //TODO ConfigFile.write(ConfigGraphics.class); + } else { + if (readout != Readouts.TRAIN_BRAKE_LEVER) { + new ControlChangePacket(stock, readout, control, global, val).sendToServer(); + } + } + } + + if (target.sound != null) { + target.sound.effects(stock, true, target.getValue(stock), MinecraftClient.getPlayer().getPosition()); } } else { for (GuiBuilder element : elements) { @@ -370,24 +456,51 @@ private void onMouseMove(EntityRollingStock stock, Matrix4 matrix, GuiBuilder ta } } + public void onMouseRelease(EntityRollingStock stock) { + float value = getValue(stock); + + if (sound != null) { + sound.effects(stock, false, value, MinecraftClient.getPlayer().getPosition()); + } + + if (toggle) { + value = 1 - value; + } + + if (readout == Readouts.HORN || readout == Readouts.WHISTLE) { + value = 0; + } + + if (setting != null) { + ConfigGraphics.settings.put(setting, value); + //TODO ConfigFile.write(ConfigGraphics.class); + } else { + new ControlChangePacket(stock, readout, control, global, value).sendToServer(); + } + + temporary_value = 0.5f; + } + private static GuiBuilder target = null; + public boolean click(ClientEvents.MouseGuiEvent event, EntityRollingStock stock) { switch (event.action) { case CLICK: target = find(stock, new Matrix4(), GUIHelpers.getScreenWidth(), GUIHelpers.getScreenHeight(), event.x, event.y); + if (target != null) { + target.onMouseClick(stock); + } return target == null; case RELEASE: if (target != null) { - if (!target.hasMovement()) { - new ControlChangePacket(stock, target.readout, target.control, target.global, target.invert ? target.getValue(stock) : 1 - target.getValue(stock)).sendToServer(); - } + target.onMouseRelease(stock); target = null; return false; } break; case MOVE: - if (target != null && target.hasMovement()) { - onMouseMove(stock, new Matrix4(), target, GUIHelpers.getScreenWidth(), GUIHelpers.getScreenHeight(), event.x, event.y); + if (target != null) { + this.onMouseMove(stock, new Matrix4(), target, GUIHelpers.getScreenWidth(), GUIHelpers.getScreenHeight(), event.x, event.y); return false; } break; @@ -395,6 +508,23 @@ public boolean click(ClientEvents.MouseGuiEvent event, EntityRollingStock stock) return true; } + static { + ClientEvents.TICK.subscribe(() -> { + if (target != null && (target.readout == Readouts.TRAIN_BRAKE_LEVER)) { + if (!MinecraftClient.isReady()) { + return ; + } + Entity riding = MinecraftClient.getPlayer().getRiding(); + if (!(riding instanceof EntityRollingStock)) { + return ; + } + EntityRollingStock stock = (EntityRollingStock) riding; + float value = target.invert ? target.getValue(stock) : 1 - target.getValue(stock); + new ControlChangePacket(stock, target.readout, target.control, target.global, value).sendToServer(); + } + }); + } + public static class ControlChangePacket extends Packet { @TagField private UUID stockUUID; @@ -417,46 +547,51 @@ public ControlChangePacket(EntityRollingStock stock, Readouts readout, String co this.controlGroup = controlGroup; this.global = global; this.value = value; + // Update the UI, server will resync once the update actually happens + update(stock); } @Override protected void handle() { EntityRollingStock stock = getWorld().getEntity(stockUUID, EntityRollingStock.class); if (stock != null) { - // TODO permissions! - if (controlGroup != null) { - switch (controlGroup) { - case "REVERSERFORWARD": - readout = Readouts.REVERSER; - value = 1; - break; - case "REVERSERNEUTRAL": - readout = Readouts.REVERSER; - value = 0.5f; - break; - case "REVERSERBACKWARD": - readout = Readouts.REVERSER; - value = 0; - break; - default: - if (global) { - ((EntityCoupleableRollingStock)stock).mapTrain((EntityCoupleableRollingStock) stock, false, target -> { - target.setControlPosition(controlGroup, value); - }); - } else { - stock.setControlPosition(controlGroup, value); - } - return; - } + update(stock); + } + } + public void update(EntityRollingStock stock) { + // TODO permissions! + if (controlGroup != null) { + switch (controlGroup) { + case "REVERSERFORWARD": + readout = Readouts.REVERSER; + value = 1; + break; + case "REVERSERNEUTRAL": + readout = Readouts.REVERSER; + value = 0.5f; + break; + case "REVERSERBACKWARD": + readout = Readouts.REVERSER; + value = 0; + break; + default: + if (global) { + ((EntityCoupleableRollingStock)stock).mapTrain((EntityCoupleableRollingStock) stock, false, target -> { + target.setControlPosition(controlGroup, value); + }); + } else { + stock.setControlPosition(controlGroup, value); + } + return; } - if (readout != null) { - if (global) { - ((EntityCoupleableRollingStock)stock).mapTrain((EntityCoupleableRollingStock) stock, false, target -> { - readout.setValue(target, value); - }); - } else { - readout.setValue(stock, value); - } + } + if (readout != null) { + if (global) { + ((EntityCoupleableRollingStock)stock).mapTrain((EntityCoupleableRollingStock) stock, false, target -> { + readout.setValue(target, value); + }); + } else { + readout.setValue(stock, value); } } } diff --git a/src/main/java/cam72cam/immersiverailroading/gui/overlay/Readouts.java b/src/main/java/cam72cam/immersiverailroading/gui/overlay/Readouts.java index d7899468f..10b0991df 100644 --- a/src/main/java/cam72cam/immersiverailroading/gui/overlay/Readouts.java +++ b/src/main/java/cam72cam/immersiverailroading/gui/overlay/Readouts.java @@ -13,6 +13,7 @@ public enum Readouts { THROTTLE, REVERSER, TRAIN_BRAKE, + TRAIN_BRAKE_LEVER, INDEPENDENT_BRAKE, BRAKE_PRESSURE, COUPLER_FRONT, @@ -31,6 +32,10 @@ public enum Readouts { ; public float getValue(EntityRollingStock stock) { + return getValue(stock, 0); + } + + public float getValue(EntityRollingStock stock, float lever) { switch (this) { case LIQUID: return stock instanceof FreightTank ? ((FreightTank) stock).getPercentLiquidFull() / 100f : 0; @@ -55,6 +60,8 @@ public float getValue(EntityRollingStock stock) { return stock instanceof Locomotive ? (((Locomotive) stock).getReverser() + 1) / 2 : 0; case TRAIN_BRAKE: return stock instanceof Locomotive ? ((Locomotive) stock).getTrainBrake() : 0; + case TRAIN_BRAKE_LEVER: + return stock.getDefinition().isLinearBrakeControl() ? TRAIN_BRAKE.getValue(stock) : lever; case INDEPENDENT_BRAKE: return stock instanceof EntityMoveableRollingStock ? ((EntityMoveableRollingStock) stock).getIndependentBrake() : 0; case BRAKE_PRESSURE: @@ -71,7 +78,7 @@ public float getValue(EntityRollingStock stock) { return stock instanceof Locomotive ? ((Locomotive) stock).getBell() > 0 ? 1 : 0 : 0; case WHISTLE: case HORN: - return stock instanceof Locomotive ? ((Locomotive) stock).getHornTime() > 0 ? 1 : 0 : 0; + return stock instanceof Locomotive ? ((Locomotive) stock).hornPull : 0; case ENGINE: return stock instanceof LocomotiveDiesel ? ((LocomotiveDiesel) stock).isTurnedOn() ? 1 : 0 : 0; case FRONT_BOGEY_ANGLE: @@ -119,6 +126,16 @@ public void setValue(EntityRollingStock stock, float value) { ((Locomotive) stock).setTrainBrake(value); } break; + case TRAIN_BRAKE_LEVER: + if (stock.getDefinition().isLinearBrakeControl()) { + TRAIN_BRAKE.setValue(stock, value); + } else { + if (stock instanceof Locomotive) { + // Logic duplicated in Locomotive#onTick + ((Locomotive) stock).setTrainBrake(Math.max(0, Math.min(1, ((Locomotive) stock).getTrainBrake() + (value - 0.5f) / 80))); + } + } + break; case INDEPENDENT_BRAKE: if (stock instanceof EntityMoveableRollingStock) { ((Locomotive) stock).setIndependentBrake(value); @@ -136,13 +153,17 @@ public void setValue(EntityRollingStock stock, float value) { break; case BELL: if (stock instanceof Locomotive) { - ((Locomotive) stock).setBell(10); + ((Locomotive) stock).setBell((int) (value * 10)); } break; case WHISTLE: case HORN: if (stock instanceof Locomotive) { - ((Locomotive) stock).setHorn(40, Locomotive.AUTOMATED_PLAYER); + if (value != 0) { + ((Locomotive) stock).setHorn(10000, value); + } else { + ((Locomotive) stock).setHorn(10, value); + } } break; case ENGINE: diff --git a/src/main/java/cam72cam/immersiverailroading/gui/overlay/Stat.java b/src/main/java/cam72cam/immersiverailroading/gui/overlay/Stat.java index f47981a1d..98128be76 100644 --- a/src/main/java/cam72cam/immersiverailroading/gui/overlay/Stat.java +++ b/src/main/java/cam72cam/immersiverailroading/gui/overlay/Stat.java @@ -9,13 +9,22 @@ public enum Stat { SPEED, + MAX_SPEED, + UNITS_SPEED, LIQUID, MAX_LIQUID, - BOILER_PSI, - MAX_BOILER_PSI, + UNITS_LIQUID, + BOILER_PRESSURE, + MAX_BOILER_PRESSURE, + UNITS_BOILER_PRESSURE, TEMPERATURE, MAX_TEMPERATURE, + UNITS_TEMPERATURE, + BRAKE_PRESSURE, + MAX_BRAKE_PRESSURE, + UNITS_BRAKE_PRESSURE, ; + public String getValue(EntityRollingStock stock) { switch (this) { case SPEED: @@ -23,51 +32,90 @@ public String getValue(EntityRollingStock stock) { Speed speed = ((EntityMoveableRollingStock) stock).getCurrentSpeed(); switch (ConfigGraphics.speedUnit) { case mph: - return String.format("%.2f mph", Math.abs(speed.imperial())); + return String.format("%.2f", Math.abs(speed.imperial())); + case ms: + return String.format("%.2f", Math.abs(speed.metersPerSecond())); + case kmh: + return String.format("%.2f", Math.abs(speed.metric())); + } + } + return ""; + case MAX_SPEED: + if (stock instanceof Locomotive) { + Speed speed = ((Locomotive)stock).getDefinition().getMaxSpeed(stock.gauge); + switch (ConfigGraphics.speedUnit) { + case mph: + return String.format("%.0f", Math.abs(speed.imperial())); case ms: - return String.format("%.2f m/s", Math.abs(speed.metric()) / 3.6); + return String.format("%.0f", Math.abs(speed.metersPerSecond())); case kmh: - default: - return String.format("%.2f km/h", Math.abs(speed.metric())); + return String.format("%.0f", Math.abs(speed.metric())); } } return ""; + case UNITS_SPEED: + switch (ConfigGraphics.speedUnit) { + case kmh: + return "km/h"; + case mph: + return "mph"; + case ms: + return "m/s"; + } + return ""; + case LIQUID: return stock instanceof FreightTank ? - String.format("%.1fB", + String.format("%.1f", ((FreightTank) stock).getLiquidAmount() / (float)Fluid.BUCKET_VOLUME) : ""; case MAX_LIQUID: return stock instanceof FreightTank ? - String.format("%.1fB", + String.format("%.1f", ((FreightTank)stock).getTankCapacity().MilliBuckets() / (float)Fluid.BUCKET_VOLUME) : ""; - case BOILER_PSI: + case UNITS_LIQUID: + return "B"; + + case BOILER_PRESSURE: return stock instanceof LocomotiveSteam ? - String.format("%.1fPSI", ((LocomotiveSteam) stock).getBoilerPressure()) : ""; - case MAX_BOILER_PSI: + String.format("%.1f", ((LocomotiveSteam) stock).getBoilerPressure()) : ""; + case MAX_BOILER_PRESSURE: return stock instanceof LocomotiveSteam ? - String.format("%.1fPSI", (float)((LocomotiveSteam) stock).getDefinition().getMaxPSI(stock.gauge)) + String.format("%.1f", (float)((LocomotiveSteam) stock).getDefinition().getMaxPSI(stock.gauge)) : ""; + case UNITS_BOILER_PRESSURE: + return "PSI"; + case TEMPERATURE: if (stock instanceof LocomotiveSteam) { - return String.format("%.1fC", ((LocomotiveSteam) stock).getBoilerTemperature()); + return String.format("%.1f", ((LocomotiveSteam) stock).getBoilerTemperature()); } if (stock instanceof LocomotiveDiesel) { - return String.format("%.1fC", ((LocomotiveDiesel) stock).getEngineTemperature()); + return String.format("%.1f", ((LocomotiveDiesel) stock).getEngineTemperature()); } return ""; case MAX_TEMPERATURE: if (stock instanceof LocomotiveSteam) { - return String.format("%.1fC", 100f); + return String.format("%.1f", 100f); } if (stock instanceof LocomotiveDiesel) { - return String.format("%.1fC", 150f); + return String.format("%.1f", 150f); } return ""; - default: + case UNITS_TEMPERATURE: + return "C"; + case BRAKE_PRESSURE: + if (stock instanceof EntityMoveableRollingStock) { + return String.format("%s", (int)(((EntityMoveableRollingStock) stock).getBrakePressure() * 100)); + } return ""; + case MAX_BRAKE_PRESSURE: + return "100"; + case UNITS_BRAKE_PRESSURE: + return "%"; } + return ""; } @Override public String toString() { diff --git a/src/main/java/cam72cam/immersiverailroading/model/SteamLocomotiveModel.java b/src/main/java/cam72cam/immersiverailroading/model/SteamLocomotiveModel.java index a8183e885..bf0ce989c 100644 --- a/src/main/java/cam72cam/immersiverailroading/model/SteamLocomotiveModel.java +++ b/src/main/java/cam72cam/immersiverailroading/model/SteamLocomotiveModel.java @@ -91,7 +91,7 @@ protected void effects(LocomotiveSteam stock) { } pressureValve.effects(stock, stock.isOverpressure() && Config.isFuelRequired(stock.gauge)); idleSounds.effects(stock, stock.getBoilerTemperature() > stock.ambientTemperature() + 5 ? 0.1f : 0); - whistle.effects(stock, stock.getBoilerPressure() > 0 || !Config.isFuelRequired(stock.gauge) ? stock.getHornTime() : 0, stock.isAutomatedHorn(), stock.getHornPlayer()); + whistle.effects(stock, stock.getBoilerPressure() > 0 || !Config.isFuelRequired(stock.gauge) ? stock.getHornTime() : 0, stock.getHornPull()); } @Override diff --git a/src/main/java/cam72cam/immersiverailroading/model/StockModel.java b/src/main/java/cam72cam/immersiverailroading/model/StockModel.java index b54d95678..6266804a7 100644 --- a/src/main/java/cam72cam/immersiverailroading/model/StockModel.java +++ b/src/main/java/cam72cam/immersiverailroading/model/StockModel.java @@ -228,6 +228,9 @@ public final void onClientRemoved(EntityMoveableRollingStock stock) { protected void removed(ENTITY stock) { headlights.forEach(x -> x.removed(stock)); + controls.forEach(c -> c.removed(stock)); + doors.forEach(c -> c.removed(stock)); + gauges.forEach(c -> c.removed(stock)); animations.forEach(c -> c.removed(stock)); } diff --git a/src/main/java/cam72cam/immersiverailroading/model/part/Control.java b/src/main/java/cam72cam/immersiverailroading/model/part/Control.java index 97235cedb..8e564f831 100644 --- a/src/main/java/cam72cam/immersiverailroading/model/part/Control.java +++ b/src/main/java/cam72cam/immersiverailroading/model/part/Control.java @@ -17,8 +17,6 @@ import cam72cam.mod.model.obj.OBJGroup; import cam72cam.mod.render.GlobalRender; import cam72cam.mod.render.opengl.RenderState; -import cam72cam.mod.resource.Identifier; -import cam72cam.mod.sound.ISound; import cam72cam.mod.util.Axis; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.text.WordUtils; @@ -47,9 +45,6 @@ public class Control extends Interactable< private final Map translations = new HashMap<>(); private final Map scales = new HashMap<>(); private final Map scaleRot = new HashMap<>(); - private final Map lastMoveSoundValue = new HashMap<>(); - private final Map wasSoundPressed = new HashMap<>(); - private final Map> sounds = new HashMap<>(); public static List> get(ComponentProvider provider, ModelState state, ModelComponentType type, ModelPosition pos) { return provider.parseAll(type, pos).stream().map(part1 -> new Control(part1, state)).collect(Collectors.toList()); @@ -379,65 +374,16 @@ public void stopClientDragging() { lastClientLook = null; } - public ControlSoundsDefinition getSounds(T stock) { - return stock.getDefinition().getControlSound(part.type.name().replace("_X", "_" + part.id)); - } - - private void createSound(T stock, Identifier sound, boolean repeats) { - if (sound == null) { - return; + public void effects(T stock) { + ControlSoundsDefinition sounds = stock.getDefinition().getControlSound(part.type.name().replace("_X", "_" + part.id)); + if (sounds != null) { + sounds.effects(stock, stock.getControlPressed(this), stock.getControlPosition(this), center(stock)); } - ISound snd = stock.createSound(sound, repeats, 10); - snd.setVelocity(stock.getVelocity()); - snd.setVolume(1); - snd.setPitch(1f); - snd.play(center(stock)); - sounds.computeIfAbsent(stock.getUUID(), k -> new ArrayList<>()).add(snd); } - - public void effects(T stock) { - ControlSoundsDefinition sounds = getSounds(stock); + public void removed(T stock) { + ControlSoundsDefinition sounds = stock.getDefinition().getControlSound(part.type.name().replace("_X", "_" + part.id)); if (sounds != null) { - if (this.sounds.containsKey(stock.getUUID())) { - for (ISound snd : new ArrayList<>(this.sounds.get(stock.getUUID()))) { - if (snd.isPlaying()) { - snd.setVelocity(stock.getVelocity()); - snd.setPosition(center(stock)); - } else { - this.sounds.get(stock.getUUID()).remove(snd); - } - } - } - - boolean isPressed = stock.getControlPressed(this); - Boolean wasPressed = wasSoundPressed.getOrDefault(stock.getUUID(), isPressed); - wasSoundPressed.put(stock.getUUID(), isPressed); - - float value = stock.getControlPosition(this); - float lastValue = lastMoveSoundValue.computeIfAbsent(stock.getUUID(), k -> value); - - if (!wasPressed && isPressed) { - // Start - createSound(stock, sounds.engage, false); - if (sounds.move != null && sounds.movePercent == null) { - // Start move repeat - createSound(stock, sounds.move, true); - } - } else if (wasPressed && !isPressed) { - // Release - if (this.sounds.containsKey(stock.getUUID())) { - for (ISound snd : this.sounds.get(stock.getUUID())) { - snd.stop(); - } - } - createSound(stock, sounds.disengage, false); - } else if (sounds.move != null && sounds.movePercent != null){ - // Move - if (Math.abs(lastValue - value) > sounds.movePercent) { - createSound(stock, sounds.move, false); - lastMoveSoundValue.put(stock.getUUID(), value); - } - } + sounds.removed(stock); } } } diff --git a/src/main/java/cam72cam/immersiverailroading/model/part/Whistle.java b/src/main/java/cam72cam/immersiverailroading/model/part/Whistle.java index c7904c5d7..f4e296a49 100644 --- a/src/main/java/cam72cam/immersiverailroading/model/part/Whistle.java +++ b/src/main/java/cam72cam/immersiverailroading/model/part/Whistle.java @@ -12,13 +12,11 @@ import cam72cam.immersiverailroading.render.ExpireableMap; import cam72cam.immersiverailroading.render.SmokeParticle; import cam72cam.immersiverailroading.util.VecUtil; -import cam72cam.mod.entity.Entity; import cam72cam.mod.math.Vec3d; import cam72cam.mod.sound.ISound; import java.util.ArrayList; import java.util.List; -import java.util.OptionalDouble; import java.util.UUID; public class Whistle { @@ -49,7 +47,7 @@ private SoundEffects(EntityMoveableRollingStock stock) { } } - public void update(EntityMoveableRollingStock stock, int hornTime, boolean isAutomatedHorn, Entity hornPlayer) { + public void update(EntityMoveableRollingStock stock, int hornTime, float hornPull) { if (hornTime < 1) { pullString = 0; soundDampener = 0; @@ -68,20 +66,7 @@ public void update(EntityMoveableRollingStock stock, int hornTime, boolean isAut if (soundDampener < 1) { soundDampener += 0.1; } - if (hornPlayer != null) { - float newString = (hornPlayer.getRotationPitch() + 90) / 180; - delta = newString - pullString; - } else { - OptionalDouble control = stock.getDefinition().getModel().getControls().stream() - .filter(x -> x.part.type == ModelComponentType.WHISTLE_CONTROL_X) - .mapToDouble(stock::getControlPosition) - .max(); - if (control.isPresent() && !isAutomatedHorn) { - delta = (float) control.getAsDouble() - pullString; - } else { - delta = (float) quilling.maxPull - pullString; - } - } + delta = hornPull - pullString; } else { if (soundDampener > 0) { soundDampener -= 0.07; @@ -145,7 +130,7 @@ public void onRemove(UUID key, SoundEffects value) { } }; - public void effects(EntityMoveableRollingStock stock, int hornTime, boolean isAutomatedHorn, Entity hornPlayer) { + public void effects(EntityMoveableRollingStock stock, int hornTime, float hornPull) { // Particles and Sound if (quilling != null) { @@ -157,7 +142,7 @@ public void effects(EntityMoveableRollingStock stock, int hornTime, boolean isAu sounds.put(stock.getUUID(), sound); } - sound.update(stock, hornTime, isAutomatedHorn, hornPlayer); + sound.update(stock, hornTime, hornPull); } } else { whistle.effects(stock, hornTime > 0); diff --git a/src/main/java/cam72cam/immersiverailroading/registry/EntityRollingStockDefinition.java b/src/main/java/cam72cam/immersiverailroading/registry/EntityRollingStockDefinition.java index 42f78c8b7..111cdc394 100644 --- a/src/main/java/cam72cam/immersiverailroading/registry/EntityRollingStockDefinition.java +++ b/src/main/java/cam72cam/immersiverailroading/registry/EntityRollingStockDefinition.java @@ -5,6 +5,7 @@ import cam72cam.immersiverailroading.ImmersiveRailroading; import cam72cam.immersiverailroading.entity.EntityBuildableRollingStock; import cam72cam.immersiverailroading.entity.EntityCoupleableRollingStock.CouplerType; +import cam72cam.immersiverailroading.entity.EntityMoveableRollingStock; import cam72cam.immersiverailroading.entity.EntityRollingStock; import cam72cam.immersiverailroading.util.CAML; import cam72cam.immersiverailroading.util.DataBlock; @@ -25,6 +26,7 @@ import cam72cam.mod.serialization.TagCompound; import cam72cam.mod.serialization.TagField; import cam72cam.mod.serialization.TagMapped; +import cam72cam.mod.sound.ISound; import cam72cam.mod.text.TextUtil; import cam72cam.mod.world.World; @@ -209,19 +211,85 @@ public static class ControlSoundsDefinition { public final Float movePercent; public final Identifier disengage; - protected ControlSoundsDefinition(Identifier engage, Identifier move, Float movePercent, Identifier disengage) { + private final Map> sounds = new HashMap<>(); + private final Map lastMoveSoundValue = new HashMap<>(); + private final Map wasSoundPressed = new HashMap<>(); + + public ControlSoundsDefinition(Identifier engage, Identifier move, Float movePercent, Identifier disengage) { this.engage = engage; this.move = move; this.movePercent = movePercent; this.disengage = disengage; } - private ControlSoundsDefinition(DataBlock data) { + public ControlSoundsDefinition(DataBlock data) { engage = data.getValue("engage").asIdentifier(); move = data.getValue("move").asIdentifier(); movePercent = data.getValue("movePercent").asFloat(); disengage = data.getValue("disengage").asIdentifier(); } + + private void createSound(EntityRollingStock stock, Identifier sound, Vec3d pos, boolean repeats) { + if (sound == null) { + return; + } + ISound snd = stock.createSound(sound, repeats, 10); + snd.setVelocity(stock.getVelocity()); + snd.setVolume(1); + snd.setPitch(1f); + snd.play(pos); + sounds.computeIfAbsent(stock.getUUID(), k -> new ArrayList<>()).add(snd); + } + + public void effects(EntityRollingStock stock, boolean isPressed, float value, Vec3d pos) { + if (this.sounds.containsKey(stock.getUUID())) { + for (ISound snd : new ArrayList<>(this.sounds.get(stock.getUUID()))) { + if (snd.isPlaying()) { + snd.setVelocity(stock.getVelocity()); + snd.setPosition(pos); + } else { + this.sounds.get(stock.getUUID()).remove(snd); + } + } + } + + Boolean wasPressed = wasSoundPressed.getOrDefault(stock.getUUID(), isPressed); + wasSoundPressed.put(stock.getUUID(), isPressed); + + float lastValue = lastMoveSoundValue.computeIfAbsent(stock.getUUID(), k -> value); + + if (!wasPressed && isPressed) { + // Start + createSound(stock, engage, pos, false); + if (move != null && movePercent == null) { + // Start move repeat + createSound(stock, move, pos, true); + } + } else if (wasPressed && !isPressed) { + // Release + if (this.sounds.containsKey(stock.getUUID())) { + for (ISound snd : this.sounds.get(stock.getUUID())) { + snd.stop(); + } + } + createSound(stock, disengage, pos, false); + } else if (move != null && movePercent != null){ + // Move + if (Math.abs(lastValue - value) > movePercent) { + createSound(stock, move, pos, false); + lastMoveSoundValue.put(stock.getUUID(), value); + } + } + } + + public void removed(T stock) { + List removed = this.sounds.remove(stock.getUUID()); + if (removed != null) { + for (ISound sound : removed) { + sound.stop(); + } + } + } } public EntityRollingStockDefinition(Class type, String defID, DataBlock data) throws Exception { diff --git a/src/main/java/cam72cam/immersiverailroading/tile/TileRailBase.java b/src/main/java/cam72cam/immersiverailroading/tile/TileRailBase.java index 31c94f13b..3e6e30082 100644 --- a/src/main/java/cam72cam/immersiverailroading/tile/TileRailBase.java +++ b/src/main/java/cam72cam/immersiverailroading/tile/TileRailBase.java @@ -37,8 +37,6 @@ import java.util.*; -import static cam72cam.immersiverailroading.entity.Locomotive.AUTOMATED_PLAYER; - public class TileRailBase extends BlockEntityTrackTickable implements IRedstoneProvider { @TagField("parent") private Vec3i parent; @@ -668,7 +666,7 @@ public void update() { loco.setTrainBrake(power / 15f); break; case HORN: - loco.setHorn(40, AUTOMATED_PLAYER); + loco.setHorn(40, power/15f); break; case BELL: loco.setBell(10 * power); diff --git a/src/main/java/cam72cam/immersiverailroading/util/MergedBlocks.java b/src/main/java/cam72cam/immersiverailroading/util/MergedBlocks.java index af63ce10e..901e605fe 100644 --- a/src/main/java/cam72cam/immersiverailroading/util/MergedBlocks.java +++ b/src/main/java/cam72cam/immersiverailroading/util/MergedBlocks.java @@ -21,8 +21,9 @@ public MergedBlocks(DataBlock base, DataBlock override) { override.getValuesMap().forEach((key, values) -> { if (primitiveSets.containsKey(key)) { // Merge into new list - values = new ArrayList<>(values); - values.addAll(primitiveSets.get(key)); + List tmp = new ArrayList<>(primitiveSets.get(key)); + tmp.addAll(values); + values = tmp; } primitiveSets.put(key, values); }); @@ -34,8 +35,9 @@ public MergedBlocks(DataBlock base, DataBlock override) { }); override.getBlocksMap().forEach((key, blocks) -> { if (blockSets.containsKey(key)) { - blocks = new ArrayList<>(blocks); - blocks.addAll(blockSets.get(key)); + List tmp = new ArrayList<>(blockSets.get(key)); + tmp.addAll(blocks); + blocks = tmp; } blockSets.put(key, blocks); }); diff --git a/src/main/java/cam72cam/immersiverailroading/util/Speed.java b/src/main/java/cam72cam/immersiverailroading/util/Speed.java index 5b846b401..1fada9ad4 100644 --- a/src/main/java/cam72cam/immersiverailroading/util/Speed.java +++ b/src/main/java/cam72cam/immersiverailroading/util/Speed.java @@ -31,6 +31,9 @@ public double minecraft() { public double metric() { return internalSpeed * speedRatio; } + public double metersPerSecond() { + return internalSpeed * speedRatio / 3.6; + } public double imperial() { return metric() * 0.621371; diff --git a/src/main/resources/assets/immersiverailroading/gui/default/common/gear.png b/src/main/resources/assets/immersiverailroading/gui/default/common/gear.png new file mode 100644 index 0000000000000000000000000000000000000000..c3257a8007eab731a8e2ec2ddcbd088b5e196169 GIT binary patch literal 405 zcmV;G0c!qpF8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H10U}96 zK~yM_jgT=*LU9zuf9L?L_y{!c{c;cq;gbMLuVc&gRvQa+zQk#y{(!)~|x+iJCjOd)_qqp@mc zuaefhZe})+)B&E%>^hF)&otx!k|Y^Qn)hx1Nm>B5fpbYOwOVaw9EzeS2*dCeSn=-Z zHUJwz5IofD^&&t}E|-sh%d~0+MM+7iRBGqUESe#Zata(cV0{+1zjayCo&yFmZb|ue zz?+|v$;5-&&dh!$)@M~qxz7Qck}^Y{XCUnN`*%6u4A_#i|BpEVZp`eW*Xvoc*-Qjb zsZ7=pod!}{U{Mi})8(XnR$TIctF}k%gEOu_VsCeLoKtc(J!KkoZ Y(QffC0|n>FVdQ&MBb@0K@}^dH?_b literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/immersiverailroading/gui/default/common/settings_slider.png b/src/main/resources/assets/immersiverailroading/gui/default/common/settings_slider.png new file mode 100644 index 0000000000000000000000000000000000000000..41e5206157db288d607a2afff207d845487d0fca GIT binary patch literal 157 zcmeAS@N?(olHy`uVBq!ia0vp^CxDoRgAGWYE-DBFQk(@Ik;M!Qe1}1p@p%4<6riAF ziEBhjaDG}zd16s2LwR|*US?i)adKios$PCk`s{Z$Qb0voo-U3d9-VJ58wxTQ@EqB& z?2lzV=aWs#np+NZYkTrt*maxngK_hL0}2KeqBSfmmX>mAKwS);u6{1-oD!M<`<*Zz literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/immersiverailroading/gui/default/diesel.caml b/src/main/resources/assets/immersiverailroading/gui/default/diesel.caml index f0631bbd0..9ef8cc910 100644 --- a/src/main/resources/assets/immersiverailroading/gui/default/diesel.caml +++ b/src/main/resources/assets/immersiverailroading/gui/default/diesel.caml @@ -1,135 +1,149 @@ element: - # left panel - x = 10 - y = -95 - image = "immersiverailroading:gui/overlay_diesel.png" - element: - # fuel level - x = 5 - y = 75 - image = "immersiverailroading:gui/fluid.png" - readout = "LIQUID" - translate = - x = 0 - y = -50 - scale = - y = 1 - color = - 0 = "0xAA79650c" - element: - # engine temp - x = 25 - y = 75 - image = "immersiverailroading:gui/fluid.png" - readout = "TEMPERATURE" - translate = - x = 0 - y = -50 - scale = - y = 1 - color = - 0 = "0x992a81af" - 0.5 = "0x992a933a" - 0.6666 = "0x99d16c15" - 0.9333 = "0x99a21010" - element: - # brake pressure - x = 41 - y = 74 - image = "immersiverailroading:gui/indicator.png" - readout = "BRAKE_PRESSURE" - translate = - x = 0 - y = -50 - color = - 0.0 = "0xDDa22020" - element: - # train brake - x = 41 - y = 74 - image = "immersiverailroading:gui/indicator.png" - readout = "TRAIN_BRAKE" - translate = - x = 0 - y = -50 - element: - # ind brake - x = 47 - y = 74 - image = "immersiverailroading:gui/indicator.png" - readout = "INDEPENDENT_BRAKE" - translate = - x = 0 - y = -50 - element: - # throttle - x = 61 - y = 74 - image = "immersiverailroading:gui/indicator.png" - readout = "THROTTLE" - translate = - x = 0 - y = -50 - element: - # reverser - x = 67 - y = 74 - image = "immersiverailroading:gui/indicator.png" - readout = "REVERSER" - translate = - x = 0 - y = -50 - element: - # speed readout - text = - value = "stat.speed" - height = 6 - x = 38 - y = 8 + import: "immersiverailroading:gui/default/common/settings.caml" + +element: + screen_x = RIGHT + screen_y = BOTTOM + + import: "immersiverailroading:gui/default/common/opacity.caml" + + element: + import: "immersiverailroading:gui/default/common/scale.caml" + element: - # top labels - y = 19 + image = "immersiverailroading:gui/default/diesel/diesel_base.png" + x = -120 + y = -190 + + element: + x = 55 element: - x = 10 - text = - value = "stat.liquid" - height = 4 + y = 2.5 + image = "immersiverailroading:gui/default/diesel/dial.png" + import: "immersiverailroading:gui/default/diesel/gauge_speed_face.caml" + element: - x = 30 - text = - value = "stat.temperature" - height = 4 + y = 65 + image = "immersiverailroading:gui/default/diesel/dial.png" + import: "immersiverailroading:gui/default/diesel/gauge_brake_face.caml" + element: - x = 48 - text = - value = "label.brake" - height = 4 + y = 127.5 + image = "immersiverailroading:gui/default/diesel/dial.png" + import: "immersiverailroading:gui/default/diesel/gauge_temperature_face.caml" + + + element: element: - x = 69 - text = - value = "label.throttle" - height = 4 - element: - # bottom labels - y = 78 + x = 5 + y = 80 + image = "immersiverailroading:gui/default/diesel/horn.png" + + readout = HORN + + rotate = + x = 0 + y = 10 + degrees = 10 + element: - x = 10 - text = - value = "stat.max_liquid" - height = 4 + x = 5 + y = 95 + image = "immersiverailroading:gui/default/diesel/button.png" + + readout = ENGINE + toggle = true + color = + 0.0 = 0xFF006000 + 1.0 = 0xFF008000 + + element: + x = 35 + y = 95 + image = "immersiverailroading:gui/default/diesel/button.png" + + readout = BELL + toggle = true + color = + 0.0 = 0xFFAAAA00 + 1.0 = 0xFFCCCC00 + element: - x = 30 - y = 0 - text = - value = "stat.max_temperature" - height = 4 + x = 5 + y = 115 + image = "immersiverailroading:gui/default/diesel/throttle_base.png" + + element: + x = 11.5 + y = 13 + image = "immersiverailroading:gui/default/diesel/throttle_lever.png" + readout = "THROTTLE" + rotate = + x = 1.5 + y = -13 + degrees = -90 + offset = 45 + sound = + engage = "immersiverailroading:sounds/default/lever_engage.ogg" + disengage = "immersiverailroading:sounds/default/lever_disengage.ogg" + element: - x = 48 - text = - value = "" - height = 4 + x = 34 + y = 115 + image = "immersiverailroading:gui/default/diesel/reverser_base.png" + + element: + x = 9 + y = 10 + image = "immersiverailroading:gui/default/diesel/reverser_lever.png" + readout = "REVERSER" + rotate = + x = 1 + y = -10 + degrees = -90 + offset = 45 + sound = + engage = "immersiverailroading:sounds/default/lever_engage.ogg" + disengage = "immersiverailroading:sounds/default/lever_disengage.ogg" + element: - x = 69 - text = - value = "label.reverser" - height = 4 + x = 12 + y = 145 + image = "immersiverailroading:gui/default/diesel/brake_stand.png" + + element: + x = 10 + y = 2.5 + image = "immersiverailroading:gui/default/diesel/brake_lever.png" + readout = "TRAIN_BRAKE_LEVER" + rotate = + x = -5 + y = 2.5 + degrees = 30 + offset = -15 + color = + 0.0 = 0xFF806600 + sound = + engage = "immersiverailroading:sounds/default/lever_engage.ogg" + move = "immersiverailroading:sounds/default/pressure.ogg" + disengage = "immersiverailroading:sounds/default/lever_disengage.ogg" + + element: + x = 12 + y = 165 + image = "immersiverailroading:gui/default/diesel/brake_stand.png" + element: + x = 10 + y = 2.5 + image = "immersiverailroading:gui/default/diesel/brake_lever.png" + readout = "INDEPENDENT_BRAKE" + rotate = + x = -5 + y = 2.5 + degrees = -15 + offset = 0 + color = + 0.0 = 0xFF800000 + sound = + engage = "immersiverailroading:sounds/default/lever_engage.ogg" + disengage = "immersiverailroading:sounds/default/lever_disengage.ogg" diff --git a/src/main/resources/assets/immersiverailroading/gui/default/diesel.json b/src/main/resources/assets/immersiverailroading/gui/default/diesel.json index 2c7fcbf97..a44242760 100644 --- a/src/main/resources/assets/immersiverailroading/gui/default/diesel.json +++ b/src/main/resources/assets/immersiverailroading/gui/default/diesel.json @@ -1,185 +1,3 @@ { - "elements": [ - { - "comment": "left panel", - "x": 10, - "y": -95, - "image": "immersiverailroading:gui/overlay_diesel.png", - "elements": [ - { - "comment": "fuel level", - "x": 5, - "y": 75, - "image": "immersiverailroading:gui/fluid.png", - "readout": "LIQUID", - "translate": { - "x": 0, - "y": -50 - }, - "scale": { - "y": 1 - }, - "color": { - "0": "0xAA79650c" - } - }, - { - "comment": "engine temp", - "x": 25, - "y": 75, - "image": "immersiverailroading:gui/fluid.png", - "readout": "TEMPERATURE", - "translate": { - "x": 0, - "y": -50 - }, - "scale": { - "y": 1 - }, - "color": { - "0": "0x992a81af", - "0.5": "0x992a933a", - "0.6666": "0x99d16c15", - "0.9333": "0x99a21010" - } - }, - { - "comment": "brake pressure", - "x": 41, - "y": 74, - "image": "immersiverailroading:gui/indicator.png", - "readout": "BRAKE_PRESSURE", - "translate": { - "x": 0, - "y": -50 - }, - "color": { - "0.0": "0xDDa22020" - } - }, - { - "comment": "train brake", - "x": 41, - "y": 74, - "image": "immersiverailroading:gui/indicator.png", - "readout": "TRAIN_BRAKE", - "translate": { - "x": 0, - "y": -50 - } - }, - { - "comment": "ind brake", - "x": 47, - "y": 74, - "image": "immersiverailroading:gui/indicator.png", - "readout": "INDEPENDENT_BRAKE", - "translate": { - "x": 0, - "y": -50 - } - }, - { - "comment": "throttle", - "x": 61, - "y": 74, - "image": "immersiverailroading:gui/indicator.png", - "readout": "THROTTLE", - "translate": { - "x": 0, - "y": -50 - } - }, - { - "comment": "reverser", - "x": 67, - "y": 74, - "image": "immersiverailroading:gui/indicator.png", - "readout": "REVERSER", - "translate": { - "x": 0, - "y": -50 - } - }, - { - "comment": "speed readout", - "text": { - "value": "stat.speed", - "height": 6 - }, - "x": 38, - "y": 8 - }, - { - "comment": "top labels", - "y": 19, - "elements": [ - { - "x": 10, - "text": { - "value": "stat.liquid", - "height": 4 - } - }, - { - "x": 30, - "text": { - "value": "stat.temperature", - "height": 4 - } - }, - { - "x": 48, - "text": { - "value": "label.brake", - "height": 4 - } - }, - { - "x": 69, - "text": { - "value": "label.throttle", - "height": 4 - } - } - ] - }, - { - "comment": "bottom labels", - "y": 78, - "elements": [ - { - "x": 10, - "text": { - "value": "stat.max_liquid", - "height": 4 - } - }, - { - "x": 30, - "y": 0, - "text": { - "value": "stat.max_temperature", - "height": 4 - } - }, - { - "x": 48, - "text": { - "value": "", - "height": 4 - } - }, - { - "x": 69, - "text": { - "value": "label.reverser", - "height": 4 - } - } - ] - } - ] - } - ] + "import": "immersiverailroading:gui/diesel.caml" } \ No newline at end of file diff --git a/src/main/resources/assets/immersiverailroading/gui/default/diesel/brake_lever.png b/src/main/resources/assets/immersiverailroading/gui/default/diesel/brake_lever.png new file mode 100644 index 0000000000000000000000000000000000000000..9704fb7d74bc763aa6179a8e07f9b1773af4e7c7 GIT binary patch literal 139 zcmeAS@N?(olHy`uVBq!ia0vp^VnEEw!3HE9zsc|cDb50q$YKTtzQZ8Qcszea3Q$n8 z#5JNMI6tkVJh3R1p}f3YFEcN@I61K(RWH9NefB#WDWD<=PZ!4!iK)qd{{R19&nCs7 f7A0YoWdOA7?L8JY#Q<+jpc)2GS3j3^P6f4aTa()7BevL9R^{>lpinR(g8$%zH2dih1^v)|cB0ToU1ba4!knA&^6kn50xi0j4Q z^#^z*Prq?E5#^Jtc*dZ)<7$Z6I_EX*nWlLM&em`rKR4mduLI5MX>1NX8_hS%UikD@ zQ|Y8-IkSMVa!Uc*w?FTn?6o&pb&GjAC(DJYL2*&fpGk>C=r^p@b6{9@U4@H;d|8R$9&Pgg&ebxsLQ E09#vOga7~l literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/immersiverailroading/gui/default/diesel/button.png b/src/main/resources/assets/immersiverailroading/gui/default/diesel/button.png new file mode 100644 index 0000000000000000000000000000000000000000..ecc3861a493cce7e83ab8c7c5a7ebd79799fdf81 GIT binary patch literal 212 zcmeAS@N?(olHy`uVBq!ia0vp^oFL4>1|%O$WD@{!3Opi<85sBugD~Uq{1qucLCF%= zh?3y^w370~qEv?R@^Zb*yzJuS#DY}4{G#;P?`))iiV{6t9780gwoce7*kB-VgoV-g zf{DNW6ef{Gr4@%JdYqG#P@SGpmM1P+8X*2Pr1(zc+t99pALrJ6b8+hJ>{vHZ%9L65 z{u8ks+Dr$oGQIBKB4ql#zkb`LbsdvuB`sglm%5xiOYz*#{d8T literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/immersiverailroading/gui/default/diesel/dial.png b/src/main/resources/assets/immersiverailroading/gui/default/diesel/dial.png new file mode 100644 index 0000000000000000000000000000000000000000..b95a7e3790187dd76effabb0ac5ce479d0306234 GIT binary patch literal 1353 zcmV-P1-AN$P)pF8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H11lCDJ zK~!jg<(pfK6IB?;f8XpBsVrMHQ6&1f5!{4kcV^c{E*l^i(Fa3_8i?UZiHUC#@J+lt zm>AG_i5lahHwa;k2??MED~MXt*=B39VF_=Va52$xU$&?He4s1cOX+PcVfj6E&YU^@ z{WFtx=A3UtRSAN8KEJ>ivkGujbqTNpxEHt^SWxM98Tc3Y8#n=siO8^R+dn6h$;&}_ ziJyTz&sz*^1~vkZ0I?Q_Dgqf*9a7b=2L=Yt_~qzJV9)c`0Na7bfsVEZoJt8eq^i67 z`}@=F4%SxSY&N^jvaB~$b$PoY4RTyWb~=u8q}2gh3ha5_Vi9>;RkyVI#cgt+TrR&f zFfj0Evj;XEIGfFG5|K}Vu4cc?XU+p#UDw^$%s~wYPN&oJySlpGQ`Hw+u?vuo6N$vO z?(XhujdX4(@aX7h=hdrMhk(c0wF`(eu)%fR^YuE`4V=&C?<*FIhk;&y+yWyPi^bL_ zlgV>8>vYq=nM|h3w(TDQ$8WoU_@kqvW3ackw@|BtRcl8&ot|&o_P5+RVF8wvN~Qg$ zPMx}=R)?B_J3Bky2UZ7R6){(rVz&I1p-u6w$=amFQWjCq$^KP*53*gd^*${EgPv+G3U`!KD7Mpf5TKXgnv z!y@u}*g_%jR`tgJfh&)AD?--^7!i3im&*-KZdae-?GgBdLb+VtG5J|kl}skH$hPgj z06RjjATh?62UDrkSqs3n?JdmRumHAg+neCVCA}d+nNU#GjQ}z-GP0mpEdB$`i`Xe> zCStMJJ(e+MC38P4z`TixiRBjHMr{%nBH~z;N%GqQRb66neMR7QVW|b&AGJkTh{z%f zSQxcPh;&&%XVexUvase9&s@aEPa=`*l*X=e(5Ro$$a58FgtdsAWQ6LPUPCT-Ut<{2sMU(1^&duIpZzyz%%Z zQkxJ^)gh=%@fc&i1Wcqx!D5Utd!Z6Il}eoj4o9jKAgX$>lJUm8w5rZnhnXu_mbGj0 z^OUtE&-0D}E2EGK3K99qah%nY+tb&UUJKnQSX4xIRyRxu>^RO55!n;2Q*emL7kzzw zN2}XrJP)htHm)xw-|7@frP3?Y8)pQrte9^NMJYg3^@Z80aA%$IsFd@461Y`xe9+(D zzkjwJHK%wIiNwo5%T=pUAtFD-7JgRnh!&b zmL47g`uw)@87~%#Jz1~xuJpM!~mAcSK=cWt9JAjyP*GA4k>?%98EWRBrYGWE*WFhxmzRpj z=Rh#;0M+M!F=kn_VWIt(JXVXyo4~4;2WbLTJ=)RH@#^&3#b)s3HJ)59H&`hRT95z5 zF-BDnT9&oTahzlA4(98NT9v=B&8oUlL{dP_y5%}t6OrFkbto2#?P=uKw^4izT#e^> zcdF_N5ph-ZAz-P9ECTN0#){=7prEQ}ffFKfQdK<>IbNAu^qv0zDzwH3F}AAo00000 LNkvXXu0mjfmgRe2 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/immersiverailroading/gui/default/diesel/dial_ticks_x10.caml b/src/main/resources/assets/immersiverailroading/gui/default/diesel/dial_ticks_x10.caml new file mode 100644 index 000000000..453e7a013 --- /dev/null +++ b/src/main/resources/assets/immersiverailroading/gui/default/diesel/dial_ticks_x10.caml @@ -0,0 +1,60 @@ +# Ticks +element: + x = 29.5 # 60/2 - rotate.x + y = 7 # 60/2 - rotate.y + color = + 0.0 = "0xAA282828" + element: + image = "immersiverailroading:gui/default/diesel/tickmark.png" + rotate = + x = 0.5 + y = 23 + offset = -100 + element: + image = "immersiverailroading:gui/default/diesel/tickmark.png" + rotate = + x = 0.5 + y = 23 + offset = -75 + element: + image = "immersiverailroading:gui/default/diesel/tickmark.png" + rotate = + x = 0.5 + y = 23 + offset = -50 + element: + image = "immersiverailroading:gui/default/diesel/tickmark.png" + rotate = + x = 0.5 + y = 23 + offset = -25 + element: + image = "immersiverailroading:gui/default/diesel/tickmark.png" + rotate = + x = 0.5 + y = 23 + offset = 0 + element: + image = "immersiverailroading:gui/default/diesel/tickmark.png" + rotate = + x = 0.5 + y = 23 + offset = 25 + element: + image = "immersiverailroading:gui/default/diesel/tickmark.png" + rotate = + x = 0.5 + y = 23 + offset = 50 + element: + image = "immersiverailroading:gui/default/diesel/tickmark.png" + rotate = + x = 0.5 + y = 23 + offset = 75 + element: + image = "immersiverailroading:gui/default/diesel/tickmark.png" + rotate = + x = 0.5 + y = 23 + offset = 100 \ No newline at end of file diff --git a/src/main/resources/assets/immersiverailroading/gui/default/diesel/diesel_base.png b/src/main/resources/assets/immersiverailroading/gui/default/diesel/diesel_base.png new file mode 100644 index 0000000000000000000000000000000000000000..c627a1cc43d94f1f84084329da4e1fdcede34301 GIT binary patch literal 587 zcmeAS@N?(olHy`uVBq!ia0vp^6+pa?gAGXjyL!4BNO2Z;L>4nJ@ErzW#^d=bQh9gP2NHH)l-tcsB45^s&_HrZV0R;h; zgEOXW`G3tvSTOO%$_MkF9>4cnaLv{p=elqAd`h>cVGw8;~~vBHmCT$+{EHv l2{Da@tj9zp!iOcJR=$v3K=rpwMnl7sn8Z%f06odbtEjxITP8Cs{zS zp1oD`-h&3;?v8{FQ97qwnr}W)zH#Ig_YEDkkF0AZmmWwF$Q9qhQ+~vtfVbIK;^nK0kE{x| zPP19|>RRKH(&?)fFS<(zL|nlG38oBCv9wc@s_n74X3o@#cBjEr5 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/immersiverailroading/gui/default/diesel/reverser_base.png b/src/main/resources/assets/immersiverailroading/gui/default/diesel/reverser_base.png new file mode 100644 index 0000000000000000000000000000000000000000..db347360706e7734e04c807d99d545c56ec89f1a GIT binary patch literal 303 zcmeAS@N?(olHy`uVBq!ia0vp^B0$W=!3HF^gw{O+Qk(@Ik;M!Qe1}1p@p%4<6riAF ziEBhjaDG}zd16s2LwR|*US?i)adKios$PCk`s{Z$Qb0v}JY5_^G$yV+XPC$2$kY1p ze(-`Vw+%N3`EAHuC)lYWt|;#KL?$u(iPTKz1ul<$x2v)qUEJt#!KJA%{q@hyx=XjP z%dH6h&CoN|;7ksW!#Y;4Q;ZT>jSlijJ@NBe zhUxwGq_k~i-+3J@`0v(Ma%ZKz4mx!^UjL=igrF+J&7vQE^ncaOeEIJ0(Z+0n1+RDA xH1Xw5NKEse^lW*4ooX$kbI-=}mh$V$`(;?%e_z-na0BQM22WQ%mvv4FO#p4WcbWhI literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/immersiverailroading/gui/default/diesel/reverser_lever.png b/src/main/resources/assets/immersiverailroading/gui/default/diesel/reverser_lever.png new file mode 100644 index 0000000000000000000000000000000000000000..608b3cc06673d851ce354f88054e143e3cf110eb GIT binary patch literal 137 zcmeAS@N?(olHy`uVBq!ia0vp^OhC-W!3HE>s{0!ODb50q$YKTtzQZ8Qcszea3Q$n8 z#5JNMI6tkVJh3R1p}f3YFEcN@I61K(RWH9NefB#WDWD=TPZ!4!j+x0h85uv$9{_?Q d46H{C7#cm8y4{X!5CW=T@O1TaS?83{1OUeZCnf*@ literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/immersiverailroading/gui/default/diesel/throttle_base.png b/src/main/resources/assets/immersiverailroading/gui/default/diesel/throttle_base.png new file mode 100644 index 0000000000000000000000000000000000000000..23779d8da0d11d0fce2412d5e02b2a628dd29a46 GIT binary patch literal 355 zcmeAS@N?(olHy`uVBq!ia0vp^QXn=58<5;Sv+gaB;w%&w)i+H@H%8*GscK zGd3`oTAar$6KE#Q7}hG-5Y9M_y&;ZehAcysn4sjeu%dJRm$to?kq|F&(_FfgVZ|%i z1*`(0SM3@Yjz}C|^(%rw>WGB8HOGQ=ucZ@qxu;~U-TSsn_Q~G+Qpr`-ht!VEnD@Qz zH!Bl|;+nNvrypqef7hpxJ^O7aN8;a&fh+6m&s&!B-3bpZ{pg;uWYynyYV$uUUs;+t xOIUCx+qs6|2R~{*i*8^@vwWWD_3}FZXSQp6Q68e_N=04iL3ws literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/immersiverailroading/gui/default/diesel/throttle_lever.png b/src/main/resources/assets/immersiverailroading/gui/default/diesel/throttle_lever.png new file mode 100644 index 0000000000000000000000000000000000000000..8ae8e7260e53044fea463db8ff52a6c1d5f501d5 GIT binary patch literal 136 zcmeAS@N?(olHy`uVBq!ia0vp^%s|Y+!3HF4-n-TSDb50q$YKTtzQZ8Qcszea3Q$n8 z#5JNMI6tkVJh3R1p}f3YFEcN@I61K(RWH9NefB#WDWD=zPZ!4!j+x0h85uv$GqC#b dcr@&iVPFVm>Q{B0-w9N};OXk;vd$@?2>^3IBozPv literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/immersiverailroading/gui/default/diesel/tickmark.png b/src/main/resources/assets/immersiverailroading/gui/default/diesel/tickmark.png new file mode 100644 index 0000000000000000000000000000000000000000..014434c3992855001efd41d382dd72b4f9812871 GIT binary patch literal 135 zcmeAS@N?(olHy`uVBq!ia0vp^j6lrF!3HE-TH59VDb50q$YKTtzQZ8Qcszea3Q$n8 z#5JNMI6tkVJh3R1p}f3YFEcN@I61K(RWH9NefB#WDWD<|PZ!4!j+x0HzJC3C{>Xs? d2O8WM7_>AQ-+Q}VDFVtfc)I$ztaD0e0s!CAC}scv literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/immersiverailroading/gui/default/steam.caml b/src/main/resources/assets/immersiverailroading/gui/default/steam.caml index db5298cee..0f7ae1f54 100644 --- a/src/main/resources/assets/immersiverailroading/gui/default/steam.caml +++ b/src/main/resources/assets/immersiverailroading/gui/default/steam.caml @@ -1,157 +1,154 @@ element: - # left panel - x = 10 - y = -95 - image = "immersiverailroading:gui/overlay_steam.png" + import: "immersiverailroading:gui/default/common/settings.caml" + +element: + screen_x = RIGHT + screen_y = BOTTOM + + import: "immersiverailroading:gui/default/common/opacity.caml" + + element: + import: "immersiverailroading:gui/default/common/scale.caml" + element: - # water level - x = 5 - y = 75 - image = "immersiverailroading:gui/fluid.png" - readout = "LIQUID" - translate = - x = 0 - y = -50 - scale = - y = 1 - color = - 0 = "0xAA0F0FFF" + y = -60 + element: + import: "immersiverailroading:gui/default/steam/dial_right.caml" + import: "immersiverailroading:gui/default/steam/gauge_speed_face.caml" + element: - # steam level - x = 25 - y = 75 - image = "immersiverailroading:gui/fluid.png" - readout = "BOILER_PRESSURE" - translate = - x = 0 - y = -50 - scale = - y = 1 - color = - 0 = "0x99DDDDDD" + y = -120 + element: + import: "immersiverailroading:gui/default/steam/dial_right.caml" + import: "immersiverailroading:gui/default/steam/gauge_brake_face.caml" + +element: + screen_x = RIGHT + screen_y = TOP + + import: "immersiverailroading:gui/default/common/opacity.caml" + + element: + import: "immersiverailroading:gui/default/common/scale.caml" + element: - # boiler temp - x = 45 - y = 75 - image = "immersiverailroading:gui/fluid.png" - readout = "TEMPERATURE" - translate = - x = 0 - y = -40 - scale = - y = 0.8 - color = - 0 = "0x99d1c715" - 0.95 = "0x99d16c15" - 1.05 = "0x99a21010" + x = -6 + y = -30 + image = "immersiverailroading:gui/default/steam/whistle_cord.png" element: - # brake pressure - x = 61 - y = 74 - image = "immersiverailroading:gui/indicator.png" - readout = "BRAKE_PRESSURE" - translate = - x = 0 - y = -50 - color = - 0.0 = "0xDDa22020" + x = -6 + y = 52 + image = "immersiverailroading:gui/default/steam/whistle.png" + readout = "WHISTLE" + translate = + y = 12 + + +element: + screen_x = RIGHT + screen_y = BOTTOM + + import: "immersiverailroading:gui/default/common/opacity.caml" + + element: + import: "immersiverailroading:gui/default/common/scale.caml" + element: - # train brake - x = 61 - y = 74 - image = "immersiverailroading:gui/indicator.png" - readout = "TRAIN_BRAKE" - translate = - x = 0 - y = -50 + x = -70 + element: + x = -25 + y = -70 + + image = "immersiverailroading:gui/default/steam/reverser_harp_lever.png" + readout = "REVERSER" + sound = + engage = "immersiverailroading:sounds/default/lever_engage.ogg" + move = "immersiverailroading:sounds/default/lever_move.ogg" + movePercent = 0.1 + disengage = "immersiverailroading:sounds/default/lever_disengage.ogg" + rotate = + x = 2 + y = 70 + degrees = 60 + offset = -30 + element: + x = -45 + y = -36 + image = "immersiverailroading:gui/default/steam/reverser_harp_stand.png" + element: - # ind brake - x = 67 - y = 74 - image = "immersiverailroading:gui/indicator.png" - readout = "INDEPENDENT_BRAKE" - translate = - x = 0 - y = -50 + y = -65 + element: + x = -8 + y = -4 + image = "immersiverailroading:gui/default/steam/brake_stand.png" + element: + x = -22.5 + y = -1.5 + image = "immersiverailroading:gui/default/steam/brake_lever.png" + readout = "TRAIN_BRAKE_LEVER" + sound = + engage = "immersiverailroading:sounds/default/lever_engage.ogg" + move = "immersiverailroading:sounds/default/pressure.ogg" + disengage = "immersiverailroading:sounds/default/lever_disengage.ogg" + rotate = + x = 18.5 + y = 1.5 + degrees = -30 + element: - # throttle - x = 81 - y = 74 - image = "immersiverailroading:gui/indicator.png" + y = -150 + element: + x = -10 + y = -5 + image = "immersiverailroading:gui/default/steam/throttle_backing.png" + element: + x = -80 + y = -4 + image = "immersiverailroading:gui/default/steam/throttle.png" readout = "THROTTLE" - translate = - x = 0 - y = -50 - element: - # reverser - x = 87 - y = 74 - image = "immersiverailroading:gui/indicator.png" - readout = "REVERSER" - translate = - x = 0 - y = -50 - element: - # speed readout - text = - value = "stat.speed" - height = 6 - x = 50 - y = 8 + sound = + engage = "immersiverailroading:sounds/default/lever_engage.ogg" + disengage = "immersiverailroading:sounds/default/lever_disengage.ogg" + rotate = + x = 75 + y = 4 + degrees = -30 + + +element: + #left panel + screen_x = LEFT + screen_y = MIDDLE + + import: "immersiverailroading:gui/default/common/opacity.caml" + + element: + import: "immersiverailroading:gui/default/common/scale.caml" + element: - # top labels - y = 19 - element: - x = 10 - text = - value = "stat.liquid" - height = 4 - element: - x = 30 - text = - value = "stat.boiler_psi" - height = 4 - element: - x = 50 - text = - value = "stat.temperature" - height = 4 - element: - x = 68 - text = - value = "label.brake" - height = 4 - element: - x = 89 - text = - value = "label.throttle" - height = 4 + y = -50 + x = -90 + import: "immersiverailroading:gui/default/steam/gauge_water_left.caml" + +element: + screen_x = LEFT + screen_y = TOP + + import: "immersiverailroading:gui/default/common/opacity.caml" + + element: + import: "immersiverailroading:gui/default/common/scale.caml" + element: - # bottom labels - y = 78 - element: - x = 10 - text = - value = "stat.max_liquid" - height = 4 - element: - x = 30 - text = - value = "stat.max_boiler_psi" - height = 4 - element: - x = 50 - y = 0 - text = - value = "stat.max_temperature" - height = 4 + element: + x = 0 element: - x = 68 - text = - value = "" - height = 4 + import: "immersiverailroading:gui/default/steam/dial_up.caml" + import: "immersiverailroading:gui/default/steam/gauge_boiler_face.caml" + + element: + x = 60 element: - x = 89 - text = - value = "label.reverser" - height = 4 + import: "immersiverailroading:gui/default/steam/dial_up.caml" + import: "immersiverailroading:gui/default/steam/gauge_temperature_face.caml" \ No newline at end of file diff --git a/src/main/resources/assets/immersiverailroading/gui/default/steam.json b/src/main/resources/assets/immersiverailroading/gui/default/steam.json index 4ecfdca58..84971d9ea 100644 --- a/src/main/resources/assets/immersiverailroading/gui/default/steam.json +++ b/src/main/resources/assets/immersiverailroading/gui/default/steam.json @@ -1,215 +1,4 @@ { - "elements": [ - { - "comment": "left panel", - "x": 10, - "y": -95, - "image": "immersiverailroading:gui/overlay_steam.png", - "elements": [ - { - "comment": "water level", - "x": 5, - "y": 75, - "image": "immersiverailroading:gui/fluid.png", - "readout": "LIQUID", - "translate": { - "x": 0, - "y": -50 - }, - "scale": { - "y": 1 - }, - "color": { - "0": "0xAA0F0FFF" - } - }, - { - "comment": "steam level", - "x": 25, - "y": 75, - "image": "immersiverailroading:gui/fluid.png", - "readout": "BOILER_PRESSURE", - "translate": { - "x": 0, - "y": -50 - }, - "scale": { - "y": 1 - }, - "color": { - "0": "0x99DDDDDD" - } - }, - { - "comment": "boiler temp", - "x": 45, - "y": 75, - "image": "immersiverailroading:gui/fluid.png", - "readout": "TEMPERATURE", - "translate": { - "x": 0, - "y": -40 - }, - "scale": { - "y": 0.8 - }, - "color": { - "0": "0x99d1c715", - "0.95": "0x99d16c15", - "1.05": "0x99a21010" - } - }, - { - "comment": "brake pressure", - "x": 61, - "y": 74, - "image": "immersiverailroading:gui/indicator.png", - "readout": "BRAKE_PRESSURE", - "translate": { - "x": 0, - "y": -50 - }, - "color": { - "0.0": "0xDDa22020" - } - }, - { - "comment": "train brake", - "x": 61, - "y": 74, - "image": "immersiverailroading:gui/indicator.png", - "readout": "TRAIN_BRAKE", - "translate": { - "x": 0, - "y": -50 - } - }, - { - "comment": "ind brake", - "x": 67, - "y": 74, - "image": "immersiverailroading:gui/indicator.png", - "readout": "INDEPENDENT_BRAKE", - "translate": { - "x": 0, - "y": -50 - } - }, - { - "comment": "throttle", - "x": 81, - "y": 74, - "image": "immersiverailroading:gui/indicator.png", - "readout": "THROTTLE", - "translate": { - "x": 0, - "y": -50 - } - }, - { - "comment": "reverser", - "x": 87, - "y": 74, - "image": "immersiverailroading:gui/indicator.png", - "readout": "REVERSER", - "translate": { - "x": 0, - "y": -50 - } - }, - { - "comment": "speed readout", - "text": { - "value": "stat.speed", - "height": 6 - }, - "x": 50, - "y": 8 - }, - { - "comment": "top labels", - "y": 19, - "elements": [ - { - "x": 10, - "text": { - "value": "stat.liquid", - "height": 4 - } - }, - { - "x": 30, - "text": { - "value": "stat.boiler_psi", - "height": 4 - } - }, - { - "x": 50, - "text": { - "value": "stat.temperature", - "height": 4 - } - }, - { - "x": 68, - "text": { - "value": "label.brake", - "height": 4 - } - }, - { - "x": 89, - "text": { - "value": "label.throttle", - "height": 4 - } - } - ] - }, - { - "comment": "bottom labels", - "y": 78, - "elements": [ - { - "x": 10, - "text": { - "value": "stat.max_liquid", - "height": 4 - } - }, - { - "x": 30, - "text": { - "value": "stat.max_boiler_psi", - "height": 4 - } - }, - { - "x": 50, - "y": 0, - "text": { - "value": "stat.max_temperature", - "height": 4 - } - }, - { - "x": 68, - "text": { - "value": "", - "height": 4 - } - }, - { - "x": 89, - "text": { - "value": "label.reverser", - "height": 4 - } - } - ] - } - ] - } - ] + "comment": "Legacy JSON wrapper", + "import": "immersiverailroading:gui/steam.caml" } \ No newline at end of file diff --git a/src/main/resources/assets/immersiverailroading/gui/default/steam/brake_lever.png b/src/main/resources/assets/immersiverailroading/gui/default/steam/brake_lever.png new file mode 100644 index 0000000000000000000000000000000000000000..f041ba1438745f7bc06f6b602743c8706819b128 GIT binary patch literal 143 zcmeAS@N?(olHy`uVBq!ia0vp^B0$W{!3HEJN9pSUDb50q$YKTtzQZ8Qcszea3Q$n8 z#5JNMI6tkVJh3R1p}f3YFEcN@I61K(RWH9NefB#WDWD=5PZ!4!j+x0{zJC3C-a)Zr l>ph-y{4;DW(7b literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/immersiverailroading/gui/default/steam/brake_stand.png b/src/main/resources/assets/immersiverailroading/gui/default/steam/brake_stand.png new file mode 100644 index 0000000000000000000000000000000000000000..dfab2c474f97d8018479b1c2c203345602a6886a GIT binary patch literal 251 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1|*O0@9PFqoCO|{#S9F5he4R}c>anMprB-l zYeY$Kep*R+Vo@qXd3m{BW?pu2a$-TMUVc&f>~}U&Kt&y%E{-7*Q)|yC@*Q#zVYx8# zym`bGuBx_46YeS>Xf)W%YW{$sfXU^r+Wmyr8<%j_eOGpH$oMrW&{%Spb&!{@=2MTN z_U-dh($koedwkb^WlLy{U)-^!Ti{UV6S4o(-tC*V{44$rjF6*2UngD9gTigHu literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/immersiverailroading/gui/default/steam/dial_down.caml b/src/main/resources/assets/immersiverailroading/gui/default/steam/dial_down.caml new file mode 100644 index 000000000..5a329711b --- /dev/null +++ b/src/main/resources/assets/immersiverailroading/gui/default/steam/dial_down.caml @@ -0,0 +1,2 @@ +element: + image = "immersiverailroading:gui/default/steam/dial_steam.png" \ No newline at end of file diff --git a/src/main/resources/assets/immersiverailroading/gui/default/steam/dial_left.caml b/src/main/resources/assets/immersiverailroading/gui/default/steam/dial_left.caml new file mode 100644 index 000000000..fcddeba89 --- /dev/null +++ b/src/main/resources/assets/immersiverailroading/gui/default/steam/dial_left.caml @@ -0,0 +1,7 @@ +element: + x = 60 # 68 + element: + image = "immersiverailroading:gui/default/steam/dial_steam.png" + rotate = + offset = 90 +x = 8 \ No newline at end of file diff --git a/src/main/resources/assets/immersiverailroading/gui/default/steam/dial_left_down.caml b/src/main/resources/assets/immersiverailroading/gui/default/steam/dial_left_down.caml new file mode 100644 index 000000000..4e81d390a --- /dev/null +++ b/src/main/resources/assets/immersiverailroading/gui/default/steam/dial_left_down.caml @@ -0,0 +1,7 @@ +element: + image = "immersiverailroading:gui/default/steam/dial_steam_right_base.png" + rotate = + offset = 180 + + x = 137 + y = 82 diff --git a/src/main/resources/assets/immersiverailroading/gui/default/steam/dial_right.caml b/src/main/resources/assets/immersiverailroading/gui/default/steam/dial_right.caml new file mode 100644 index 000000000..cf9d0a281 --- /dev/null +++ b/src/main/resources/assets/immersiverailroading/gui/default/steam/dial_right.caml @@ -0,0 +1,8 @@ +element: + y = 60 + element: + image = "immersiverailroading:gui/default/steam/dial_steam.png" + rotate = + offset = -90 + +x = -68 \ No newline at end of file diff --git a/src/main/resources/assets/immersiverailroading/gui/default/steam/dial_steam.png b/src/main/resources/assets/immersiverailroading/gui/default/steam/dial_steam.png new file mode 100644 index 0000000000000000000000000000000000000000..3e56743cef77aed9b55eb0262e7b2e647fc29364 GIT binary patch literal 16262 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 HMnV4vuSYSS literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/immersiverailroading/gui/default/steam/dial_ticks_x10.caml b/src/main/resources/assets/immersiverailroading/gui/default/steam/dial_ticks_x10.caml new file mode 100644 index 000000000..457260e0d --- /dev/null +++ b/src/main/resources/assets/immersiverailroading/gui/default/steam/dial_ticks_x10.caml @@ -0,0 +1,60 @@ +# Ticks +element: + x = 29.5 # 60/2 - rotate.x + y = 7 # 60/2 - rotate.y + color = + 0.0 = "0xAA282828" + element: + image = "immersiverailroading:gui/default/steam/tickmark.png" + rotate = + x = 0.5 + y = 23 + offset = -100 + element: + image = "immersiverailroading:gui/default/steam/tickmark.png" + rotate = + x = 0.5 + y = 23 + offset = -75 + element: + image = "immersiverailroading:gui/default/steam/tickmark.png" + rotate = + x = 0.5 + y = 23 + offset = -50 + element: + image = "immersiverailroading:gui/default/steam/tickmark.png" + rotate = + x = 0.5 + y = 23 + offset = -25 + element: + image = "immersiverailroading:gui/default/steam/tickmark.png" + rotate = + x = 0.5 + y = 23 + offset = 0 + element: + image = "immersiverailroading:gui/default/steam/tickmark.png" + rotate = + x = 0.5 + y = 23 + offset = 25 + element: + image = "immersiverailroading:gui/default/steam/tickmark.png" + rotate = + x = 0.5 + y = 23 + offset = 50 + element: + image = "immersiverailroading:gui/default/steam/tickmark.png" + rotate = + x = 0.5 + y = 23 + offset = 75 + element: + image = "immersiverailroading:gui/default/steam/tickmark.png" + rotate = + x = 0.5 + y = 23 + offset = 100 \ No newline at end of file diff --git a/src/main/resources/assets/immersiverailroading/gui/default/steam/dial_up.caml b/src/main/resources/assets/immersiverailroading/gui/default/steam/dial_up.caml new file mode 100644 index 000000000..5d3712a61 --- /dev/null +++ b/src/main/resources/assets/immersiverailroading/gui/default/steam/dial_up.caml @@ -0,0 +1,8 @@ +element: + x = 60 + y = 60 # 8 + element: + image = "immersiverailroading:gui/default/steam/dial_steam.png" + rotate = + offset = 180 +y = 8 \ No newline at end of file diff --git a/src/main/resources/assets/immersiverailroading/gui/default/steam/gauge_boiler_face.caml b/src/main/resources/assets/immersiverailroading/gui/default/steam/gauge_boiler_face.caml new file mode 100644 index 000000000..ddd32726e --- /dev/null +++ b/src/main/resources/assets/immersiverailroading/gui/default/steam/gauge_boiler_face.caml @@ -0,0 +1,49 @@ +element: + x = 30 + y = 19 + text = + value = "BOILER" + height = 4 + color = + 0.0 = "0xFF000000" + +element: + x = 30 + y = 47 + text = + value = stat.boiler_pressure stat.units_boiler_pressure + height = 4 + color = + 0.0 = "0xFF000000" + +element: + x = 14 + y = 40 + text = + value = "0" + height = 4 + color = + 0.0 = "0xFF000000" + +element: + x = 44 + y = 40 + text = + value = stat.max_boiler_pressure + height = 4 + color = + 0.0 = "0xFF000000" + + +element: + image = "immersiverailroading:gui/default/steam/steam_dial.png" + x = 27.5 # 60/2 - rotate.x + y = 16 # 60/2 - rotate.y + readout = BOILER_PRESSURE + rotate = + x = 2.5 # indicator width/2 + y = 14 # indicator height/2 + degrees = 250 + offset = -125 + +import: "immersiverailroading:gui/default/steam/dial_ticks_x10.caml" \ No newline at end of file diff --git a/src/main/resources/assets/immersiverailroading/gui/default/steam/gauge_brake_face.caml b/src/main/resources/assets/immersiverailroading/gui/default/steam/gauge_brake_face.caml new file mode 100644 index 000000000..ea5838d85 --- /dev/null +++ b/src/main/resources/assets/immersiverailroading/gui/default/steam/gauge_brake_face.caml @@ -0,0 +1,62 @@ +element: + x = 30 + y = 19 + text = + value = "BRAKE" + height = 4 + color = + 0.0 = "0xFF000000" + +element: + x = 30 + y = 47 + text = + value = stat.brake_pressure stat.units_brake_pressure + height = 4 + color = + 0.0 = "0xFF000000" + +element: + x = 14 + y = 40 + text = + value = "0" + height = 4 + color = + 0.0 = "0xFF000000" + +element: + x = 44 + y = 40 + text = + value = stat.max_brake_pressure + height = 4 + color = + 0.0 = "0xFF000000" + + +element: + image = "immersiverailroading:gui/default/steam/steam_dial.png" + x = 27.5 # 60/2 - rotate.x + y = 16 # 60/2 - rotate.y + readout = TRAIN_BRAKE + rotate = + x = 2.5 # indicator width/2 + y = 14 # indicator height/2 + degrees = 250 + offset = -125 + +element: + image = "immersiverailroading:gui/default/steam/steam_dial.png" + x = 27.5 # 60/2 - rotate.x + y = 16 # 60/2 - rotate.y + readout = BRAKE_PRESSURE + color = + 0 = "0xFFFF1010" + rotate = + x = 2.5 # indicator width/2 + y = 14 # indicator height/2 + degrees = 250 + offset = -125 + +import: "immersiverailroading:gui/default/steam/dial_ticks_x10.caml" \ No newline at end of file diff --git a/src/main/resources/assets/immersiverailroading/gui/default/steam/gauge_speed_face.caml b/src/main/resources/assets/immersiverailroading/gui/default/steam/gauge_speed_face.caml new file mode 100644 index 000000000..663ab4c0a --- /dev/null +++ b/src/main/resources/assets/immersiverailroading/gui/default/steam/gauge_speed_face.caml @@ -0,0 +1,49 @@ +element: + x = 30 + y = 19 + text = + value = "SPEED" + height = 4 + color = + 0.0 = "0xFF000000" + +element: + x = 30 + y = 47 + text = + value = stat.speed stat.units_speed + height = 4 + color = + 0.0 = "0xFF000000" + +element: + x = 14 + y = 40 + text = + value = "0" + height = 4 + color = + 0.0 = "0xFF000000" + +element: + x = 44 + y = 40 + text = + value = stat.max_speed + height = 4 + color = + 0.0 = "0xFF000000" + + +element: + image = "immersiverailroading:gui/default/steam/steam_dial.png" + x = 27.5 # 60/2 - rotate.x + y = 16 # 60/2 - rotate.y + readout = SPEED + rotate = + x = 2.5 # indicator width/2 + y = 14 # indicator height/2 + degrees = 250 + offset = -125 + +import: "immersiverailroading:gui/default/steam/dial_ticks_x10.caml" \ No newline at end of file diff --git a/src/main/resources/assets/immersiverailroading/gui/default/steam/gauge_temperature_face.caml b/src/main/resources/assets/immersiverailroading/gui/default/steam/gauge_temperature_face.caml new file mode 100644 index 000000000..c9b8eb2a9 --- /dev/null +++ b/src/main/resources/assets/immersiverailroading/gui/default/steam/gauge_temperature_face.caml @@ -0,0 +1,49 @@ +element: + x = 30 + y = 19 + text = + value = "FIREBOX" + height = 4 + color = + 0.0 = "0xFF000000" + +element: + x = 30 + y = 47 + text = + value = stat.temperature stat.units_temperature + height = 4 + color = + 0.0 = "0xFF000000" + +element: + x = 14 + y = 40 + text = + value = "0" + height = 4 + color = + 0.0 = "0xFF000000" + +element: + x = 44 + y = 40 + text = + value = stat.max_temperature + height = 4 + color = + 0.0 = "0xFF000000" + + +element: + image = "immersiverailroading:gui/default/steam/steam_dial.png" + x = 27.5 # 60/2 - rotate.x + y = 16 # 60/2 - rotate.y + readout = TEMPERATURE + rotate = + x = 2.5 # indicator width/2 + y = 14 # indicator height/2 + degrees = 250 + offset = -125 + +import: "immersiverailroading:gui/default/steam/dial_ticks_x10.caml" \ No newline at end of file diff --git a/src/main/resources/assets/immersiverailroading/gui/default/steam/gauge_water_left.caml b/src/main/resources/assets/immersiverailroading/gui/default/steam/gauge_water_left.caml new file mode 100644 index 000000000..ce8ac6d9d --- /dev/null +++ b/src/main/resources/assets/immersiverailroading/gui/default/steam/gauge_water_left.caml @@ -0,0 +1,14 @@ +element: + image = "immersiverailroading:gui/default/steam/water_glass_left.png" + + element: + x = 104 + y = 87 + image = "immersiverailroading:gui/default/steam/water_glass_fill.png" + + readout = "LIQUID" + translate = + x = 0 + y = -69 + scale = + y = 1 diff --git a/src/main/resources/assets/immersiverailroading/gui/default/steam/gauge_water_right.caml b/src/main/resources/assets/immersiverailroading/gui/default/steam/gauge_water_right.caml new file mode 100644 index 000000000..f37663b6a --- /dev/null +++ b/src/main/resources/assets/immersiverailroading/gui/default/steam/gauge_water_right.caml @@ -0,0 +1,15 @@ +element: + image = "immersiverailroading:gui/default/steam/water_glass_right.png" + x = -105 # base width from image + + element: + x = 6 + y = 87 + image = "immersiverailroading:gui/default/steam/water_glass_fill.png" + + readout = "LIQUID" + translate = + x = 0 + y = -69 + scale = + y = 1 diff --git a/src/main/resources/assets/immersiverailroading/gui/default/steam/reverser_base.png b/src/main/resources/assets/immersiverailroading/gui/default/steam/reverser_base.png new file mode 100644 index 0000000000000000000000000000000000000000..885427cd25264f4e553b35276fbc0490cfb5ee98 GIT binary patch literal 543 zcmV+)0^t3LP)pF8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H10jx$vlCXqMNy=Ao?ijD z24LR%Ohg<>hzRz=syZY&j-n{`-giXge@ELeNm11s5gEmCob7_1C)x)4{r;VZoW*gR zk=!4|Hms^QB+rs0$ttP!Y&@yzrHG7<1%dU>MNy=kPG^#)=}Fa&J8kEAe&w7?>VN`GK!+ws=!~{??l8kg-;J1!(Q0TUN_kn_~%F(!6Y9- z2#uA098(B8VKciAA^d3si^%PAx%|}xCIF|Z4{4gds(ssLwi1!Ai^bw+U1b4)>2&&S zwOY;VrY-<5nM{70*=RnWKh{Zpvt(J8w;_b_Y&Ls57Jkv5>*o9}A|I0^Iaq)%;x)5S zmgU8>gF_iqmHsD^W9MAjYPELucjde3cDqTt-ToXx_(Bri`?hl~A_+b*n literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/immersiverailroading/gui/default/steam/reverser_harp_lever.png b/src/main/resources/assets/immersiverailroading/gui/default/steam/reverser_harp_lever.png new file mode 100644 index 0000000000000000000000000000000000000000..4fbadbc38fd1bf4b9c1df81c3193f8e8522c8c43 GIT binary patch literal 3256 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 Gg#8D-d1AW& literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/immersiverailroading/gui/default/steam/reverser_lever.png b/src/main/resources/assets/immersiverailroading/gui/default/steam/reverser_lever.png new file mode 100644 index 0000000000000000000000000000000000000000..e38830003e75b6441fcac8d373487ee848ff7289 GIT binary patch literal 170 zcmeAS@N?(olHy`uVBq!ia0vp^x~}U&Kt<-BE{-7@6O(m%H~l^D!15xh zs$-%==@$l_BU&bghKi?(r!*L+pA!%@@Yzzvq04Yl(Kkoq0y6`H2@6+V*TN82|tP literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/immersiverailroading/gui/default/steam/steam_dial.png b/src/main/resources/assets/immersiverailroading/gui/default/steam/steam_dial.png new file mode 100644 index 0000000000000000000000000000000000000000..babb3673da5ea24446a4167c9335a090dcf49498 GIT binary patch literal 540 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/immersiverailroading/gui/default/steam/throttle_backing.png b/src/main/resources/assets/immersiverailroading/gui/default/steam/throttle_backing.png new file mode 100644 index 0000000000000000000000000000000000000000..38279e53a037aa504a2aa37d4d2f0b2e791b0789 GIT binary patch literal 7635 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/immersiverailroading/gui/default/steam/tickmark.png b/src/main/resources/assets/immersiverailroading/gui/default/steam/tickmark.png new file mode 100644 index 0000000000000000000000000000000000000000..014434c3992855001efd41d382dd72b4f9812871 GIT binary patch literal 135 zcmeAS@N?(olHy`uVBq!ia0vp^j6lrF!3HE-TH59VDb50q$YKTtzQZ8Qcszea3Q$n8 z#5JNMI6tkVJh3R1p}f3YFEcN@I61K(RWH9NefB#WDWD<|PZ!4!j+x0HzJC3C{>Xs? d2O8WM7_>AQ-+Q}VDFVtfc)I$ztaD0e0s!CAC}scv literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/immersiverailroading/gui/default/steam/water_glass_fill.png b/src/main/resources/assets/immersiverailroading/gui/default/steam/water_glass_fill.png new file mode 100644 index 0000000000000000000000000000000000000000..2bd3b975758cf73105479f754c5f31b13a25cfaf GIT binary patch literal 5076 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 z z4>?Wbzm>j}dLLY;DT$;yOtaJY5OkMUlfWr)38j+p;WndBRi?JmlY~^FhBsJ@s>0qv zqtLNv^+hXib(4#icEKhlkV0XCX!YS>k+##)7XP2Cpaed&Ui9>v;@%B?ySC3s!9Ogq; zeY2IMtz}oRt5cDjt7pI7cYw73<@$h}<^GbShoT)!?qW$Y`nAk~(GqE0MCmk@BT3Bg z13AgacMTRV+D>VjMFYnU%7ff2!+!G>W=P~_3g2a=X9NjD@ ziC!|9SnG?eWF~Hkj1d;aYapX1(rs^f%=Xly_!WX4oWD<7IUaA~_U(UpVlbgX>T`c% z?aQ81-It-494}N4iiC^bJq|cAaK4fIWa#z`HE}%lrKM2%lKp8`Wdq;5VyJe^-B^u# z8lR^gzWPb}y*1;!6sUDLe2MZRTiIRSK3&M`*GC!uVto$3$<5zG+2Xy)ZOb?x6Uld= zw1s{+G`j<<_WW6rMA+8Q&|uX`(GJLvj@;HZTEMzP@L`A?o36Pgfehi{GO`9+0@M1LpK zNKP(?N!?9y9g23<-LK*HR8@+XZOij2c;m%oAkIW52fmG`CpBrn4v zAdf#iyRe-0P4M0KC6elIGYOB16)XvDOUl`>6|7Rz$3t$dl??Mngd?6}FOmU$E6zBJ znen}$QH05l$`Xhj2~Ip7k)3Yd?Q0P^7^qvjPc-C`gV>5o`)YO=uUa$v9##VH^q{de zVCEVg>f(xi0-c(3i7yCzLD(NSFL*vjvS%3E6)j)JcHKYG4Ve1b>)19TH(bzH_DV}dM?%mOk2FsJ_TiOR* zpz(KO?5ACLh$4X`zHr5z%bElh5sv{XZ)eJ@=Bd_^78}5pLTiSMUcs)Cq2_#IB*XG| zs^i5YPRa?eo#w%Sa>yP30+5I~+dZSW#<8@(HYT~dahDZwR(iWX6}|^Qor;p7S)P&d z%j!%dbm_8uwph(!Wr&CC8uDRz{MQ%j%}w6KlavpPk4eK)uSIrW0A-BW7s$YBREI>k zb9hY|Y;piMZPKP!t=UxmUipGQW=@AE=agl8B$*c9IpYF~qkmW(XT{vPOOs%-5<``= zHX5liK;&_1?$F?o-a)5Z>Ii&=dXPQQT?wrZO!V;N|?|Djph zcThhxc9DH^5Zqa3eO^;2n9t_=ED5YDr?bV#8nJSW$$`X$bQA?!ZX%mWPTIyz7+p<8xAecfn7dItu;Gv@R_hr@qrt%dMc-6Uf}{F4ym@ zkjzliO};m#5~FSk_|KGYO2fV%RxZ`9gnHo>HW^+W^6WjxDr?6Pm!5(84LdQtxpLA8 z&g5JxKFUdXcvkpK^0SNDz(Caie--Vyp;wg`_k8hnKOSktBgMv%7H-@J7_Bk!g?bk$ zA`Y<+csr?;=a|+rt2ye3-Ur;rm6ur{ej+P%AzZ}&A;!+_P`1DZ+uc*i(pdhVsX`B}-MGS45u*uQB>-3@)A;A^JmZ@62ETt^Ol7>vX@ zw5*=6o$8ir;zTJta>)OMh!8IE-g^ttyS3HPx%E`KzObQpVIGUAbYPDeu^U!&#LI9> z;IXK9Q*2FSJ)~RNNM;n?@JVX~PAE&`y;hu}l{>`b@m2C$%sZD4wkVbp&8X&ii{via zPR+SF`_d+ZEy9Z1#KGRWzc4pk15@+e_^YnT-v|#P{)D=+QM#%Nv*-eO!Kh>>u2U{f5+a3)9wH4*W}zzjx6NvSJHN&RD&7ab(s=Z&a*mpFBg@$)B^~SP zQ*E}u*Ap)-r#sdsI4mdP^6CW$%W(0ZKPV<)A8iz>Ck*_GA66XsHa9ZitSxUG)El(s z+uVez_riDA=gp_MArOC|BKlStaNb!2be94829tHg^%H>;4Tn2iyqCJVW}5`YoSvh$ z07EiCm^1n0g$tCj zvfy!7XF2?eox9;_c;ckp(O=;&bT6!y_KMWk%{n2UDI-rE>$_J)Nh}3$4tGIi=t8Wv zwwk;28PIUcL%7MHO!gL6lQ_269w%zwSnjm=As}SUKLIi2Pv2( zLf+E}rs=7wW#(yZCTI?NE=DNgE`$cKg*ij%+-+^_kV5XFpg(Yh(C>dYbAagnFmbjP z1?j4&(@EJo!RUC{dDuC@GVTahZjcxuorse;Tu4J&_AdzZCsB~4v$KN`2Zx)R8@n5X z-QLN9gG*3Qkb{$(gPR+Swg4kN?3|(QU^^tkZ-_rJq+v)iC$yLm_I7l?F`=gRF3zGL z5W1i4ANkojsHprC-VXT}3urz#+@TH}Tx7-tzJA@CgXQdBB|Jd=M}Xj{pn|Cd=QA~-=Gxikj_v$ zGuUq^G&nl~jl&P;hI4UoLBV`nu-_1ZW?(@Wj1SDo4dH?Cz`0=jf@Xh%P;)|{RSC8E zdse@p%zs0f!C?ISyl}8NHx&9i8*Z?GAcW_4Hs%lk2p^P-Pv8$Ib2A}Xdna2cdN>ib zPzxA`gPp}69lr?|l2BI=1#z=;{;NgZ2I>q)JD`sNgq^v)8}eUWS_oU1rZe<6pIm%` zTwJ^YT%242ynH+YJpVF!33EcCwfGyAi<6!E&m?{ij1amUG_}y*N<{P*<4wAA!(*e|DK!LhUSI=p+0uN&TZ8@jncgAU_w3#|+8~7T^*P0P~n~@_|j^ zT)bdY7&n(WA2$!Lx%vMG9cd4Dc7r;>BrMRCqN_nm=#OgXp8iE9%fDyiW(oVv6ngx@ zoC07@h!z*85HGI~x_plR{qoGr;XF_?b3U*Dw;(qdT@61NYR1bA7UUA(gbVU>@pVPDB{^w~+u!%>mV&qF76J$PS4a$uyUxEKn9FclcXT7Zvx15Y z{uTg-1aN=$G0g_L??gdbLd$(&&rqe%c0l`PMz03dfNHsHG`=*F52bjduOK)6^hxv? zgiJd4iHf>3{-bdUT;>S|3CDzRZ2VE_C*g%*+0ySH|1iJD85WL^mm>(N##2_VOSWsg zTyh%|Y_cld$o6&JLmE>HED$$+b=nu*+V!!Tt-G ze&Qc)vYABdv3cj$vEITv`;4`qeIFD@RM6F_UECeF_C*P4VhAKQl;wtz?0S^;dha>y zrFB=Wck%j!e&^M3&-ob%JVcwF(Ld}FGxua@e)0`YdmFp>^>jgI^ZTE47(e~rs|RCi zmDL{dlXWb0NIM+$ZtbZJwuJ*o+Lwk@Sd7OKSV)dq!r;(vBy8qH;^%Eb1&2l+<({@J zu}wiK*;X57M=fgXp@Jq>z;A4Wiyzsvn)t~YQt4%J!+^=aX>kn6xaPTRS9=cRe%6@i zN|g`)7O76Y^=xC)n6!#@L~|Hf zGY}^S9Dxlx#DF9bBod$w)1G3l>Ndw7riC|#9@$kKSzmXmze@GI1Hun5aR5Mun=$9G zcYXrNEGMpna)9)8xSv)Ko9alS&et}F!^wqgBJI-a{aKn207Qd6neIqrd&RYq=_G|P zz}$2jkmHoG!AlR>55MLklNkB6uz`spC0`7H(mJ9}iK8SA70+E%Nu|CDXCCHG69~Zx z)AD(aqJ=}+9mc9^IwfQfTlgma;ut!k$qlc=CE`DFe3axh=8)bKI%*b3KPMl4+^1dC z*DNr6emPyDKt?HP=31o;NQ|wIAjcS>4mhe&4wS@yb;wC)VDwaIgl#2@wa+n=6r(#2 z&~3Z4ZOwMxP&yo)9Le*Pu4zlKq056QIJr`q6S*5pru|8k1scJkq-37kh_}tfI!sjb zDx|k^PJ3P3>R2-4#eF-p5K;pGfMEe;7EO(>y^bmJIMTs+A#^FGOM_hrp@XP*vI97s z3HPK-aLB?qO{Z$Vlb8VF+uG!TykOMul-7q>D<)|&S!KMOSNBo`OykaD_o`9^S9J0~u!a;gHEHrnpO2#xP>B zqhgY|$y8s^cM4F0p2#yL9C{jMDc37Ye=OnWl909&HV6Zd8mE7&8N!~Tez?UT83&@L zZT2BRmBpw%;jQ+gA){V2l>>0tYkST%DJw(?pSu zo*!Ax=_*x!=c^_L4YvOh@E~8#uG5x}tvI3@KYZo+?eP(ZeOj1Hw6X74(<#>v{^Z`i zA~FR!-3s9%8kHh^0V9!?%sNJcm9y{6Sxwxi*yS&@9H>5mP!_0PlmInDSqypE;7TZq zk3sP5(W3%8rTS6mChg_n?9HXm`-97mdX1aO@h?@cFZT5LeUHequImtMQ~dpB{SadZ zeqpUGB@=e9FC|CB?UA3FeZ9wPs0zxQo44eaeT4zfefe;J8!ct2i5d9lU=MXde>7ow zBaq)e3A0vdA{U#4#NS@`p3k2_vGDF$gt`4(xaL}Q^7Ce5x@=$i!vD$6I2}9qIMnaR zV@cdlO>1*2=wK{B94-bt=#oB`uW?Q))0wi>)u{5TudnbjOeTAK6Dcg#9KMoB1ozzD zoImqrRegX@C(SAMYte-)e3b*3A2;lmP5^{bGhYOBCHT5wyD)Z*oEO;`*W+kxnuLo7 z`b(&5T2AbyfAqBmx|+4mQ@BGCUh>4xTNwM;-a=M9PgsvtJ>k9eP2o#+I9&8S!Cpy8 z4$(-eaXJ) zvOau8+as<-ro(FLV@Szj75@MtW~a!d!wWbpVU@|JuG91JU^t91sMD%lgqdM#Px)q# z*Z|_s(r2Tk5-_4t589b+-g{-$sySrbNmS_NWlPfJ5$$)0w;h3(1Dl75x8&i1UGsT; zU^h!`#`H*|Oxy#Cw-{i<6{$ON0-2H zp<^Rc3J(QD>+?$?RI+Qi)rVaQrDr>EQ!Azv*(FNVZLwW!v)?IuGg;lD3`Ay+G_aEz z+ZqyjZL-Vorcxl`7A6)4aFa{cIhwPVTy@|rPjOgLTf_9XmurB?2rv7@n*y0ds={ZF zYRq{eKkHfUW#o>0_4tBf)4zL$w|x;JU+bOBQinJgT@mJ|#!xi~2TNfgr{LHwyxD|+ zcRgkF7Qz%lT~F_rpzJOFYO6jx%T#&4y1VZ?em~fLW`CiU z$2)%KYld}h$Wqbd%4YT%0uoC+rA_=v@92?wD`$))S;vp;6>-Cd=|ebGjQE+l^}#a6 zh1TzmYtx@$)(G6c_<9!j>Z8RFjJa;(8CBs9Rt;=9%S13ueuRT)IHz`#by3`qp>U|X zYFi;WgIWnFd(R=jF^M&7<`Am2Kd zc1o~ZSP*2O#ITf3y7RVOH$Pw|uWCo1di#x$5F=V)ygxC|BvTevUf(Q((^9j zsr$raST+di7neD&Cv%v5G~JA7ge-c{vxs=NxCqgIjq7?KULcnae?8n^3r0tQ+-Itz?|}KZFkj)5KZGIHLeVn$R?W&s!RqCdLz*5^NCQ>Lo<5bbb zx}i!;If>2{_CTOiZf^m{Mn9F;UQ=I5D0Qdr%gT`5a#ek-XxzeM^Z>Z zag*USml*utHecxFV7THi5q3_+HzLo#p)(6&)gi0N?Xo5yCJ;1t?`(V8(RwD5eYhuy z6MvxLL^4gS4#*%dMoFe|Xke~C=n_SHv0UdP9-Nh#8v}}>=fr~&b@?yZ1;hhoAe3*?3M(Ykd8>V9YeL|lX>(JP^otRCOpkdpel6fvfFmkpA$}3HF`gpb@gX+ zx`LgF>7oFYC3UA7Qw{X@%yIWe&v{s=U1meW<>I%8$Ot8m0Q#^8slG1$!|6Y1V^XL1 zG8#eA7rR{BZ#$W`W4utY_Av_f)vbyxAM|timDkayM}Cj}iv;&#f9zv+6HlE)Jzt_N zXv2QfC1HHCpwz~T=b@*~_)%iSc&kyVR+d1Z}G)w&< zml!v(n|wKr(J?V>6tmWSxyc?Q7r?ihyqjeX-;LKKMk9sd0f) zpmY)@opuz!;2ij;Zi+0g@tU+V!yjj#_x-3I$2yuoaiHn@A)DS|EKtRWC3i8A?+dnB zjT66oV%yG>0uMgj8M33MkiY?*19RyCqrKl|>;SRN*QG1JemuO{xxXafva$A&)@!ww z?pqN5SV*XqnT2+j9T9+SdTgjK@A>7$#y1p3b2-L|l$xkVADeJ|s08{vR9{6d1_03X z<59GZ(S5(?^W`DR(<-R$raGh{xLxs!CN!imXz1F>$!WAhp=dgo8NR)20mQUAr!P*i zZ;&CVch}h_qvgp;!*vs1qGvSdQ3k^E^3)-mWFG~Gi4DP#voVgyMukj{uxu;aNa# zYzV|}dbPZ!OpQe)nVq;G6K)3!>cGI@pmsqhgwS~KmBeaHgFD139+4Z(oRYP678t*P@C3dK{#Q+3z(eCOUN z7#7;z`!)do_2OYxgl-Xrp6!`2#$5D-05J=)A_b~Q_>5X9yM$0fvVlmU<}o#purSnY zF!)8g&dDx-_6hj|%%X~6Fmm`Fc^J3A08ywJYc5rCt|WNuvUeWiP33{Y8;w?cZB-5p z8wwmNK>~g0I8#ZDAe@jNY@?XfE+q8G#d|d$0wUydLeK9V9yv;z_5iU$=u`qZHQ(@& zxR%D3Y%^M9f{vJwyx~OX| zcqosRgN|de>86Hh6srS2$9AQHe*$`mNY;A;+-kf#8c7{p4%z9Z<&Mi%mfth5@1&rx ziYW|n1gf!&H8?3#B>&uFj>F!3xzNvuL#8WC0XP@z|E8L3ii7?;Mu==^ry`~%8H#1& zr89`L>g9ShRdBNczZ|duteh)Z?;>WZm{J&I{=*DI654N_z5HBRjoK`5yJY+^L{1C4 zdpO&Q6B!)$yia+AR8VG5x)2&cn4mBz zluF1C`Q{LXOrwOmi~SS2dtPB67`l1_{nV&ku@&_7Zs#$l?>bXqJme54}< zpai_Zap!Z@VEAfduNBVp=Bq5Hxf$mj63MCIfs~_48M`@OdcSFcuI6G0 z*1!^M@ecgvF(bn$TY(eOn+xP$Yq6`3w2NviK5O3mBE1;t1X~i7%nDVc?$kJ%Q$g4q zcB7&EfsqHVlU9Y>wqCfMy>&=U_qN~kLaxxh|M_--rhR()-YsEvoBK`iAGXqTqX&JL xQWrRp@jx)qSP2%c!rtLn=Jl$x=iV(=G%$zsz-T%dy(fmDAfqZ>CTSA*e*i@Q!LtAW literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/immersiverailroading/gui/default/steam/water_glass_right.png b/src/main/resources/assets/immersiverailroading/gui/default/steam/water_glass_right.png new file mode 100644 index 0000000000000000000000000000000000000000..6ced25b013dc1facc29174789e10af6269b42b3b GIT binary patch literal 14580 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 S=R+S1ASIB?`MxCY-FwdOp7XoEbM8yB zLV}iD8tT_@a2k7FbOS{N%+D97_CT_!f03} z!7hBD%URn399_@Rwbzoba(-!^x3)M?U_tXcE zOtGa@hO-YP$Ipy=q{b@r|%vr*C5 zHKN&vFUx-dL_dlg$%z~k_`jTfYxv1{rB~joFWbz<910J5V8{C|KINKapJscF-*$ED z5vV%ieareaUHt|9jqdaq-&&^~-Bma77A4OUe>RE?4gm*u8b9O&O+8^A5#4q7(& zY0iG8oemqG03({l$&tB%@^AKh>``Xca|$ml{gpb%viVxRkqA>b$-E$EDJyVMfUsUt8avSjfr3tmhCt(6d5h*jJ`abT^Zj|7+zEU zU3vU*?v>`k@|`D@0=!G%%?np9Tua^blX+;+S9j*T0x9x86J zE#Fb*P*xK444YZH%P-dG3_W7f%~=-eOLjZpz0r)^DeYBzU)g{&7Gzv4T{P9CJJ5C0 z^3sSDTa8m&UA4{(5tG+Uie_l_XoH!k_`gmglg2rQrTK8?36& zW)5xDl~d6^idf3AhQ$$&n{KX|t=IB7q{Pd8uB;A%&^f24Hk28Sbh$?mN{aF#4TqS@ zTF)r^Pn(ig#y<$_XPO=gAH_K~w_TmT=cMSun(qCYfSRZ3J-t1NQ1j=ps_eYdErr{F z%=(BMVfRIozj=mr8K@ejJ3Xr0RMP<0>Q!9KrKN)qgt7xrMOTW3If#-q@tAIfh%{+56cXaNtb+zgIZ|28e>KN^7DXNu}T{f-#5?gGN zIk%?YuTTEm?7LQso~zS+?l+3ntCWS9`E|HlWku)rD2LO%XBmUf#Gl6cDgBIdJN z*NB$J-fH2WY)b@c(S0+l$Qywsp1()Tf$mpob%+=fct&;>xM3tSX;eh5gwj&s_fSIdJHPUl=v56URu_=<%8ht7^^|&QbR_tR;$T-;h zT!GtCEDv_6hH?2c37Y+v3l0IFc#m&P+f;6?EH@zLcE$|kotT*u!E+%q4rKQdUYy}yT~Vg*=%(OB?LOZ=eM5?{yw~RM-g?G^rWqZHszu(0 z(~q^F?T)R#0`IYQ?Jd=)tNdsQr?+&O$1Iv={cs~N>^!gL{?sYb3b)kKA(D0LZNCxb z`lCPTcW0K)er)fHbv)r#XSho(sp-`tV%QWqN)0$OB+l$c27Y2uQujnj%|2`KMMcY-Kz9*>JLAGZqjV-F8*T0 z%5q5RY}ckPYWL*i?INso#athVD6l1_T3-eCWKKe%^wtY~eM30DzHd$vWQASnc^-aE zA=Vp$!)nF6)FR0o4q*0T;E9v52PmQ8VBH+TSU!%y^NTl+8JAOQ}B_qm!s3xRs zZfmc&$O)ab`ylqx{BX?p8rdhV>7pWQ^6cQ?CZGOIj6iZ=%vq3aKY2*fFlEm6xON;A9xWnHU zRvE`UL^%d4ujbmf1_!uI&v*`u>~|-mO__T5r2PIa&5+*t&WcXjnTEN9&r2UDwhh>A)@p)72K$jQDua48d#h;SfJ!i8}vkrWXH3gzyll7dh?tUz;N zfl%y$dGvif1})@yV4^62gg~h;94GWkk-=dpLE%tJJjCE(ygW_aRZIjx1S>$aN|Yd$ zGgTfK9WE2O)(+z_Xq|~7-UG8dFa+%@k-=y(j*KH<*(zZo3FB#sc9-$^%uv?i*AU2^ z2PRITkTUW3q@*NV5`dG)1b8BY!N3zpcoGSVSYYMJVg;zeisg=4hzSf9EQe%>l7$j6 zT8jyCB}#<{27}C_-^3@91_r)`7t3F>fbfA=fl@pXN5G3j_;)?z3U(p_@;abD^pJ-m z4@me>ST0e@Aefy9ixrOVLhzus{!*nZL6;5>!ovx$2r-o-s}kQ^atS9esVnMA=5dhsdk_*#U27?NTEL&EJRv{%KUr&w2kkzw<$gjrvVO8|XriLR;58F>6SoOM zT`8f_x}so$&_oJyFcIeI;zY0}rl2@bEP#>j@mj8L#)W?%1qO`>lOd3Tr4#9NESZaB z!sQbwST0N=@~9*-g~$6Hx?I9nB!M#6TY&J0aD~WI#}(QoN+{+@lH?2Ck0vP&*6ssB z8J0lD5`b_bfk~k-5pwZ=etKLA2*UsaDHec^C>@{?u^<3ISSmmS85EGn<^P_%|1v$Y zHa$cd0Pz1v51>LI1Q4lM03twGG6O=|0GUR@f)tPd5MTn00#6jhTW$Q8=~1=m0b23@ zKhlFiA_&k(NMnL|BqTjLvU@O5+ouRb5(VZ{xeWgMV*A(W>AJ|jm*(4QroFD_*8+9N zYY((H<>-$8r-Gd@c;^H}ggr5aoUF(}jemPmzg9f59e?KQb))z*mq4RGF7iSAex&Oo zT_4222N{1<*GIZOh=C6>{;0118C|Bo{nm%Y$Rm0Z@=Kl-jq^nQ1u)_UEMcJ*pxCH$ zi<=&jkdet!zZG&6%1WVq>s{wNsE|Qp1t*YgeA{HQHN#jBvz>1U(WJxgmA&#Qa!N$7~) di6Y92Nfuk@N8-|q`j7xn9Ci@vuup8}KLFB5a~l8v literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/immersiverailroading/gui/default/steam/whistle_cord.png b/src/main/resources/assets/immersiverailroading/gui/default/steam/whistle_cord.png new file mode 100644 index 0000000000000000000000000000000000000000..90be1df41adbfce1807d609b1d3f5a505fc40d79 GIT binary patch literal 6232 zcmeHLXH-+!77mJ`2o5NqB1i~>pb~PE+*An&Awtv$(rhT+BsY*DZBj@8v7wI03<^4; zfQpC;3aDU3M~Vd%6;Y`&prT;GilAb9H^Dk@-h0b6Z|0X-E9LI9&v*9z&bRl;y-9)7 z{LD>lO;IS6Imh2O2>Bj}yd#X!$R|#aKtiDmtKvdts)8U5My`+vMPeAEij~6{SSu2u zP}=70VRO<>QjA|Wj8zy8Oq*C$o4l(9Qx)J8Kj!G#H%%vt_K}C)tTjI)de^gar7LlQ@rNVqw>@R2 zUWkMiL`1!;l{u^$%^EtIfWANd>S%W5^8rlLKKuI6%)1R1r;oSQyd8yp9pur>$Qgd1 zDs%O_SxZ{S?R7do$KTz9{q(`&lL2Wevo4QOhMaMy*;X}fd)A=N1kirNkC-~TsKa6T z@eimR# zymF6O7ju&o?PDtMu_g2tk-0Xt=mv>(cpHdha4W25>p^IvF?b^XTngovz z<6bD3C2KL{mh5ldKP$RW)7W*XW#3U&_VWtXq|wg5FjS3E+X8ZH&X=5LsGG$l_<6;b zyu#*|iVxILCApU`9_=bsUy~l45Ol#gw)bXT-G$8O)aYmH9(2ALTz?1p<-1_VJXt-k zyD!MQcG_e)x>CN%VnjnW%H%>t6TFUOq^xMFyYw~}$a1S_%9(Fw*1-+Wn%Clde585D z+u<@!H?{Re88LlnX$0}PtaTwza*6X>=2E`tvn>=)P% zQOs-$QH43~nZg@sJ0ZnybKQvYfP2S7D$`0kd7Cmj_U^koJ3}DtN!`;}DhWI-O8z;L zQ1;9H*;_KM^>Nwu==HSlIWGG}_eUOoc%!zs#*L8F2zdvApx*mw|neh67zqR1fn7U!viw0Y1*A;kfoPce*z#`l{w5-xEXX~h% z4W5rZ;{8)6UnyKi3ob_)USDhwjjj;S+znQ&7C*i9!j@JcX=Hc{3Ilg!u6FU4qS`k` zzb=^){Y&-HMPQ+WA_BIPg{|)^v&`f{9d@a{;b-|`C7$Jm`) zyy?6rCp8OL=!R`=3;lICA>OJ9+Ol*23T3cN#9{?Q)w0VR(+Do5{@UPuAk%i6kH^o04zQVNc9I-*f zPOZeQ$9OMWiXN9&>5~<+$!ge0MnK#PKl?IY_vPW%fWj>sZC>1N^e(Zjgq$3_RvP4L z@~o_}w`zn%7;|`$MA)fdJ_anSi`JM%Y54;wV;|cBnULwJNK0~Qu#~>i@1Nze$$`IsV0X_&;%G3%z z%#MMjDyPpO1pJTlzxrVE|=je1aCZ@cL*;=mkwPNisC+7b z14$4K2N7X1PDrE@K(dg>r%{NXK?Ep7h$9F0x_ zbiV z(5+8e|0mL;639qZgLE8) zMnGB}S%CBv3esEv5(TEf0s)mrrF<#2|D2w_i~M(KeynC>1${NY7pMnbcc6VJM-TkJ z6>Pu5XD1*c?0y?^vLXjH{^LpgUh&9w{GG4&jpFZI0)zRs$XD_EjjnHWeH8;=<@`-u z-{|@(2ENMqo4WpQbeVp-*N3IZUvv#}CqKV4;xO_Q0L`1?=ZhMPVxy`iop?w@EGBaQ zSxOXYs7m)XXcanW5u=fc!(|(F8k<lp?Wzu{cL8PtlT6>P({#74r6#n^soT+WJmb5RJJd-rDJ7^Q zp`mQz;_C+WYG+n*PT-mDti~1B9qsR+so4xOR`EH&%4b=DK{+MDbhNSCpf1<87u_C7 tTimJ_{&?=nl7gc}POR$U*D-wtg;NsE9#;1ukIqmi4ttt!k Date: Mon, 28 Aug 2023 07:37:06 -0400 Subject: [PATCH 2/8] Fix #1394: Add texture_variant to GUI Builders Example: ``` element: image = "immersiverailroading:somebutton.png" translucent = true toggle = true texture_variant = "dark_blue" # Same as the model's texture variant folder ``` --- .../entity/EntityRollingStock.java | 4 +++- .../gui/overlay/GuiBuilder.java | 20 ++++++++++++++----- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/main/java/cam72cam/immersiverailroading/entity/EntityRollingStock.java b/src/main/java/cam72cam/immersiverailroading/entity/EntityRollingStock.java index 06b4037e4..a1441de2a 100644 --- a/src/main/java/cam72cam/immersiverailroading/entity/EntityRollingStock.java +++ b/src/main/java/cam72cam/immersiverailroading/entity/EntityRollingStock.java @@ -137,7 +137,9 @@ public ClickResult onClick(Player player, Player.Hand hand) { } public void setTexture(String variant) { - this.texture = variant; + if (getDefinition().textureNames.containsKey(variant)) { + this.texture = variant; + } } @Override diff --git a/src/main/java/cam72cam/immersiverailroading/gui/overlay/GuiBuilder.java b/src/main/java/cam72cam/immersiverailroading/gui/overlay/GuiBuilder.java index 25b39d756..ec55c3d2b 100644 --- a/src/main/java/cam72cam/immersiverailroading/gui/overlay/GuiBuilder.java +++ b/src/main/java/cam72cam/immersiverailroading/gui/overlay/GuiBuilder.java @@ -44,6 +44,7 @@ public class GuiBuilder { private final Readouts readout; private final String control; private final String setting; + private final String texture_variant; private final Float setting_default; private final boolean global; private final boolean invert; @@ -134,6 +135,7 @@ protected GuiBuilder(DataBlock data) throws IOException { this.control = data.getValue("control").asString(); this.setting = data.getValue("setting").asString(); this.setting_default = data.getValue("setting_default").asFloat(); + this.texture_variant = data.getValue("texture_variant").asString(); DataBlock soundBlock = data.getBlock("sound"); this.sound = soundBlock != null ? new EntityRollingStockDefinition.ControlSoundsDefinition(soundBlock) : null; this.global = data.getValue("global").asBoolean(false); @@ -240,6 +242,8 @@ private float getValue(EntityRollingStock stock) { } value = ConfigGraphics.settings.getOrDefault(setting, 0f); + } else if (texture_variant != null) { + value = texture_variant.equals(stock.getTexture()) ? 1 : 0; } if (invert) { @@ -349,7 +353,7 @@ private GuiBuilder find(EntityRollingStock stock, Matrix4 matrix, int maxx, int } if (image != null && interactable()) { - if (control == null && setting == null) { + if (control == null && setting == null && texture_variant == null) { if (readout == null) { return null; } @@ -441,7 +445,7 @@ private void onMouseMove(EntityRollingStock stock, Matrix4 matrix, GuiBuilder ta //TODO ConfigFile.write(ConfigGraphics.class); } else { if (readout != Readouts.TRAIN_BRAKE_LEVER) { - new ControlChangePacket(stock, readout, control, global, val).sendToServer(); + new ControlChangePacket(stock, readout, control, global, texture_variant, val).sendToServer(); } } } @@ -475,7 +479,7 @@ public void onMouseRelease(EntityRollingStock stock) { ConfigGraphics.settings.put(setting, value); //TODO ConfigFile.write(ConfigGraphics.class); } else { - new ControlChangePacket(stock, readout, control, global, value).sendToServer(); + new ControlChangePacket(stock, readout, control, global, texture_variant, value).sendToServer(); } temporary_value = 0.5f; @@ -520,7 +524,7 @@ public boolean click(ClientEvents.MouseGuiEvent event, EntityRollingStock stock) } EntityRollingStock stock = (EntityRollingStock) riding; float value = target.invert ? target.getValue(stock) : 1 - target.getValue(stock); - new ControlChangePacket(stock, target.readout, target.control, target.global, value).sendToServer(); + new ControlChangePacket(stock, target.readout, target.control, target.global, target.texture_variant, value).sendToServer(); } }); } @@ -537,15 +541,18 @@ public static class ControlChangePacket extends Packet { @TagField private float value; + @TagField + private String texture_variant; public ControlChangePacket() { super(); // Reflection } - public ControlChangePacket(EntityRollingStock stock, Readouts readout, String controlGroup, boolean global, float value) { + public ControlChangePacket(EntityRollingStock stock, Readouts readout, String controlGroup, boolean global, String texture_variant, float value) { this.stockUUID = stock.getUUID(); this.readout = readout; this.controlGroup = controlGroup; this.global = global; + this.texture_variant = texture_variant; this.value = value; // Update the UI, server will resync once the update actually happens update(stock); @@ -594,6 +601,9 @@ public void update(EntityRollingStock stock) { readout.setValue(stock, value); } } + if (texture_variant != null && value == 1) { + stock.setTexture(texture_variant); + } } } } From 0840f7aff50c6f9e411d0b504c1618f38c074bc9 Mon Sep 17 00:00:00 2001 From: Christian Mesh Date: Wed, 30 Aug 2023 08:13:25 -0400 Subject: [PATCH 3/8] Implement find/replace in GUI import blocks example: ``` import: source = immersiverailroading:gui/default/steam/gauge_common_face.caml replace = TITLE = TEMPERATURE MIN = 0 CURRENT = stat.speed stat.units_speed MAX = stat.max_speed ``` --- .../gui/overlay/GuiBuilder.java | 12 +++- .../immersiverailroading/util/DataBlock.java | 21 +++++- .../gui/default/steam/gauge_boiler_face.caml | 61 ++++------------ .../gui/default/steam/gauge_brake_face.caml | 72 ++++--------------- .../gui/default/steam/gauge_common_face.caml | 37 ++++++++++ .../gui/default/steam/gauge_dial.caml | 10 +++ .../gui/default/steam/gauge_speed_face.caml | 61 ++++------------ .../default/steam/gauge_temperature_face.caml | 61 ++++------------ 8 files changed, 126 insertions(+), 209 deletions(-) create mode 100644 src/main/resources/assets/immersiverailroading/gui/default/steam/gauge_common_face.caml create mode 100644 src/main/resources/assets/immersiverailroading/gui/default/steam/gauge_dial.caml diff --git a/src/main/java/cam72cam/immersiverailroading/gui/overlay/GuiBuilder.java b/src/main/java/cam72cam/immersiverailroading/gui/overlay/GuiBuilder.java index ec55c3d2b..30cd8d2b2 100644 --- a/src/main/java/cam72cam/immersiverailroading/gui/overlay/GuiBuilder.java +++ b/src/main/java/cam72cam/immersiverailroading/gui/overlay/GuiBuilder.java @@ -90,12 +90,18 @@ private static DataBlock processImports(DataBlock data) throws IOException { // This is kinda weird as it appends the import to the current block instead of inserting it in the current // element list. It's definitely a bit of a footgun. The "correct" way to do this would be to make import part // of parsing in CAML, which is it's own sort of weirdness due to JSON interop. - List imports = data.getValues("import"); - if (imports != null) { - for (DataBlock.Value imp : imports) { + List direct = data.getValues("import"); + if (direct != null) { + for (DataBlock.Value imp : direct) { data = new MergedBlocks(data, processImports(DataBlock.load(imp.asIdentifier()))); } } + List imports = data.getBlocks("import"); + if (imports != null) { + for (DataBlock imp : imports) { + data = new MergedBlocks(data, processImports(DataBlock.load(imp.getValue("source").asIdentifier(), imp.getBlock("replace")))); + } + } return data; } diff --git a/src/main/java/cam72cam/immersiverailroading/util/DataBlock.java b/src/main/java/cam72cam/immersiverailroading/util/DataBlock.java index 9e4e17bbc..0ec051a9a 100644 --- a/src/main/java/cam72cam/immersiverailroading/util/DataBlock.java +++ b/src/main/java/cam72cam/immersiverailroading/util/DataBlock.java @@ -2,9 +2,12 @@ import cam72cam.immersiverailroading.ImmersiveRailroading; import cam72cam.mod.resource.Identifier; +import org.apache.commons.io.IOUtils; import java.io.IOException; import java.io.InputStream; +import java.io.StringReader; +import java.nio.charset.Charset; import java.util.List; import java.util.Locale; import java.util.Map; @@ -99,11 +102,25 @@ public String asString() { static DataBlock load(Identifier ident) throws IOException { - return load(ident, false); + return load(ident, false, null); } - static DataBlock load(Identifier ident, boolean last) throws IOException { + static DataBlock load(Identifier ident, DataBlock parameters) throws IOException { + return load(ident, false, parameters); + } + + static DataBlock load(Identifier ident, boolean last, DataBlock parameters) throws IOException { InputStream stream = last ? ident.getLastResourceStream() : ident.getResourceStream(); + + if (parameters != null) { + String input = IOUtils.toString(stream, Charset.defaultCharset()); + for (String key : parameters.getValueMap().keySet()) { + input = input.replace(key, parameters.getValue(key).asString()); + } + stream = IOUtils.toInputStream(input, Charset.defaultCharset()); + } + + if (ident.getPath().toLowerCase(Locale.ROOT).endsWith(".caml")) { return CAML.parse(stream); } diff --git a/src/main/resources/assets/immersiverailroading/gui/default/steam/gauge_boiler_face.caml b/src/main/resources/assets/immersiverailroading/gui/default/steam/gauge_boiler_face.caml index ddd32726e..a026eb109 100644 --- a/src/main/resources/assets/immersiverailroading/gui/default/steam/gauge_boiler_face.caml +++ b/src/main/resources/assets/immersiverailroading/gui/default/steam/gauge_boiler_face.caml @@ -1,49 +1,12 @@ -element: - x = 30 - y = 19 - text = - value = "BOILER" - height = 4 - color = - 0.0 = "0xFF000000" - -element: - x = 30 - y = 47 - text = - value = stat.boiler_pressure stat.units_boiler_pressure - height = 4 - color = - 0.0 = "0xFF000000" - -element: - x = 14 - y = 40 - text = - value = "0" - height = 4 - color = - 0.0 = "0xFF000000" - -element: - x = 44 - y = 40 - text = - value = stat.max_boiler_pressure - height = 4 - color = - 0.0 = "0xFF000000" - - -element: - image = "immersiverailroading:gui/default/steam/steam_dial.png" - x = 27.5 # 60/2 - rotate.x - y = 16 # 60/2 - rotate.y - readout = BOILER_PRESSURE - rotate = - x = 2.5 # indicator width/2 - y = 14 # indicator height/2 - degrees = 250 - offset = -125 - -import: "immersiverailroading:gui/default/steam/dial_ticks_x10.caml" \ No newline at end of file +import: + source = immersiverailroading:gui/default/steam/gauge_common_face.caml + replace = + TITLE = BOILER + MIN = 0 + CURRENT = stat.boiler_pressure stat.units_boiler_pressure + MAX = stat.max_boiler_pressure + +import: + source = immersiverailroading:gui/default/steam/gauge_dial.caml + replace = + READOUT = BOILER_PRESSURE \ No newline at end of file diff --git a/src/main/resources/assets/immersiverailroading/gui/default/steam/gauge_brake_face.caml b/src/main/resources/assets/immersiverailroading/gui/default/steam/gauge_brake_face.caml index ea5838d85..c1ba76d00 100644 --- a/src/main/resources/assets/immersiverailroading/gui/default/steam/gauge_brake_face.caml +++ b/src/main/resources/assets/immersiverailroading/gui/default/steam/gauge_brake_face.caml @@ -1,62 +1,20 @@ -element: - x = 30 - y = 19 - text = - value = "BRAKE" - height = 4 - color = - 0.0 = "0xFF000000" - -element: - x = 30 - y = 47 - text = - value = stat.brake_pressure stat.units_brake_pressure - height = 4 - color = - 0.0 = "0xFF000000" - -element: - x = 14 - y = 40 - text = - value = "0" - height = 4 - color = - 0.0 = "0xFF000000" - -element: - x = 44 - y = 40 - text = - value = stat.max_brake_pressure - height = 4 - color = - 0.0 = "0xFF000000" +import: + source = immersiverailroading:gui/default/steam/gauge_common_face.caml + replace = + TITLE = BRAKE + MIN = 0 + CURRENT = stat.brake_pressure stat.units_brake_pressure + MAX = stat.max_brake_pressure +import: + source = immersiverailroading:gui/default/steam/gauge_dial.caml + replace = + READOUT = TRAIN_BRAKE element: - image = "immersiverailroading:gui/default/steam/steam_dial.png" - x = 27.5 # 60/2 - rotate.x - y = 16 # 60/2 - rotate.y - readout = TRAIN_BRAKE - rotate = - x = 2.5 # indicator width/2 - y = 14 # indicator height/2 - degrees = 250 - offset = -125 - -element: - image = "immersiverailroading:gui/default/steam/steam_dial.png" - x = 27.5 # 60/2 - rotate.x - y = 16 # 60/2 - rotate.y - readout = BRAKE_PRESSURE color = 0 = "0xFFFF1010" - rotate = - x = 2.5 # indicator width/2 - y = 14 # indicator height/2 - degrees = 250 - offset = -125 - -import: "immersiverailroading:gui/default/steam/dial_ticks_x10.caml" \ No newline at end of file + import: + source = immersiverailroading:gui/default/steam/gauge_dial.caml + replace = + READOUT = BRAKE_PRESSURE \ No newline at end of file diff --git a/src/main/resources/assets/immersiverailroading/gui/default/steam/gauge_common_face.caml b/src/main/resources/assets/immersiverailroading/gui/default/steam/gauge_common_face.caml new file mode 100644 index 000000000..744967b53 --- /dev/null +++ b/src/main/resources/assets/immersiverailroading/gui/default/steam/gauge_common_face.caml @@ -0,0 +1,37 @@ +element: + x = 30 + y = 19 + text = + value = TITLE + height = 4 + color = + 0.0 = "0xFF000000" + +element: + x = 30 + y = 47 + text = + value = CURRENT + height = 4 + color = + 0.0 = "0xFF000000" + +element: + x = 14 + y = 40 + text = + value = MIN + height = 4 + color = + 0.0 = "0xFF000000" + +element: + x = 44 + y = 40 + text = + value = MAX + height = 4 + color = + 0.0 = "0xFF000000" + +import: "immersiverailroading:gui/default/steam/dial_ticks_x10.caml" \ No newline at end of file diff --git a/src/main/resources/assets/immersiverailroading/gui/default/steam/gauge_dial.caml b/src/main/resources/assets/immersiverailroading/gui/default/steam/gauge_dial.caml new file mode 100644 index 000000000..5e5428c23 --- /dev/null +++ b/src/main/resources/assets/immersiverailroading/gui/default/steam/gauge_dial.caml @@ -0,0 +1,10 @@ +element: + image = "immersiverailroading:gui/default/steam/steam_dial.png" + x = 27.5 # 60/2 - rotate.x + y = 16 # 60/2 - rotate.y + readout = READOUT + rotate = + x = 2.5 # indicator width/2 + y = 14 # indicator height/2 + degrees = 250 + offset = -125 diff --git a/src/main/resources/assets/immersiverailroading/gui/default/steam/gauge_speed_face.caml b/src/main/resources/assets/immersiverailroading/gui/default/steam/gauge_speed_face.caml index 663ab4c0a..1b7c6d844 100644 --- a/src/main/resources/assets/immersiverailroading/gui/default/steam/gauge_speed_face.caml +++ b/src/main/resources/assets/immersiverailroading/gui/default/steam/gauge_speed_face.caml @@ -1,49 +1,12 @@ -element: - x = 30 - y = 19 - text = - value = "SPEED" - height = 4 - color = - 0.0 = "0xFF000000" - -element: - x = 30 - y = 47 - text = - value = stat.speed stat.units_speed - height = 4 - color = - 0.0 = "0xFF000000" - -element: - x = 14 - y = 40 - text = - value = "0" - height = 4 - color = - 0.0 = "0xFF000000" - -element: - x = 44 - y = 40 - text = - value = stat.max_speed - height = 4 - color = - 0.0 = "0xFF000000" - - -element: - image = "immersiverailroading:gui/default/steam/steam_dial.png" - x = 27.5 # 60/2 - rotate.x - y = 16 # 60/2 - rotate.y - readout = SPEED - rotate = - x = 2.5 # indicator width/2 - y = 14 # indicator height/2 - degrees = 250 - offset = -125 - -import: "immersiverailroading:gui/default/steam/dial_ticks_x10.caml" \ No newline at end of file +import: + source = immersiverailroading:gui/default/steam/gauge_common_face.caml + replace = + TITLE = SPEED + MIN = 0 + CURRENT = stat.speed stat.units_speed + MAX = stat.max_speed + +import: + source = immersiverailroading:gui/default/steam/gauge_dial.caml + replace = + READOUT = SPEED \ No newline at end of file diff --git a/src/main/resources/assets/immersiverailroading/gui/default/steam/gauge_temperature_face.caml b/src/main/resources/assets/immersiverailroading/gui/default/steam/gauge_temperature_face.caml index c9b8eb2a9..2fc5d819f 100644 --- a/src/main/resources/assets/immersiverailroading/gui/default/steam/gauge_temperature_face.caml +++ b/src/main/resources/assets/immersiverailroading/gui/default/steam/gauge_temperature_face.caml @@ -1,49 +1,12 @@ -element: - x = 30 - y = 19 - text = - value = "FIREBOX" - height = 4 - color = - 0.0 = "0xFF000000" - -element: - x = 30 - y = 47 - text = - value = stat.temperature stat.units_temperature - height = 4 - color = - 0.0 = "0xFF000000" - -element: - x = 14 - y = 40 - text = - value = "0" - height = 4 - color = - 0.0 = "0xFF000000" - -element: - x = 44 - y = 40 - text = - value = stat.max_temperature - height = 4 - color = - 0.0 = "0xFF000000" - - -element: - image = "immersiverailroading:gui/default/steam/steam_dial.png" - x = 27.5 # 60/2 - rotate.x - y = 16 # 60/2 - rotate.y - readout = TEMPERATURE - rotate = - x = 2.5 # indicator width/2 - y = 14 # indicator height/2 - degrees = 250 - offset = -125 - -import: "immersiverailroading:gui/default/steam/dial_ticks_x10.caml" \ No newline at end of file +import: + source = immersiverailroading:gui/default/steam/gauge_common_face.caml + replace = + TITLE = TEMPERATURE + MIN = 0 + CURRENT = stat.temperature stat.units_temperature + MAX = stat.max_temperature + +import: + source = immersiverailroading:gui/default/steam/gauge_dial.caml + replace = + READOUT = TEMPERATURE \ No newline at end of file From 064af9402895fa5400fd0b3219b115b02b74ac71 Mon Sep 17 00:00:00 2001 From: Christian Mesh Date: Tue, 5 Sep 2023 07:18:56 -0400 Subject: [PATCH 4/8] Side safety around ClientEvents --- .../ImmersiveRailroading.java | 2 ++ .../gui/overlay/GuiBuilder.java | 26 +++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/main/java/cam72cam/immersiverailroading/ImmersiveRailroading.java b/src/main/java/cam72cam/immersiverailroading/ImmersiveRailroading.java index 474f361ee..746e43acc 100644 --- a/src/main/java/cam72cam/immersiverailroading/ImmersiveRailroading.java +++ b/src/main/java/cam72cam/immersiverailroading/ImmersiveRailroading.java @@ -217,6 +217,8 @@ public void postRender(EntityMoveableRollingStock entity, RenderState state, flo return true; }); + ClientEvents.TICK.subscribe(GuiBuilder::onClientTick); + Particles.SMOKE = Particle.register(SmokeParticle::new, SmokeParticle::renderAll); ClientPartDragging.register(); diff --git a/src/main/java/cam72cam/immersiverailroading/gui/overlay/GuiBuilder.java b/src/main/java/cam72cam/immersiverailroading/gui/overlay/GuiBuilder.java index 30cd8d2b2..6204361cc 100644 --- a/src/main/java/cam72cam/immersiverailroading/gui/overlay/GuiBuilder.java +++ b/src/main/java/cam72cam/immersiverailroading/gui/overlay/GuiBuilder.java @@ -518,21 +518,19 @@ public boolean click(ClientEvents.MouseGuiEvent event, EntityRollingStock stock) return true; } - static { - ClientEvents.TICK.subscribe(() -> { - if (target != null && (target.readout == Readouts.TRAIN_BRAKE_LEVER)) { - if (!MinecraftClient.isReady()) { - return ; - } - Entity riding = MinecraftClient.getPlayer().getRiding(); - if (!(riding instanceof EntityRollingStock)) { - return ; - } - EntityRollingStock stock = (EntityRollingStock) riding; - float value = target.invert ? target.getValue(stock) : 1 - target.getValue(stock); - new ControlChangePacket(stock, target.readout, target.control, target.global, target.texture_variant, value).sendToServer(); + public static void onClientTick() { + if (target != null && (target.readout == Readouts.TRAIN_BRAKE_LEVER)) { + if (!MinecraftClient.isReady()) { + return ; } - }); + Entity riding = MinecraftClient.getPlayer().getRiding(); + if (!(riding instanceof EntityRollingStock)) { + return ; + } + EntityRollingStock stock = (EntityRollingStock) riding; + float value = target.invert ? target.getValue(stock) : 1 - target.getValue(stock); + new ControlChangePacket(stock, target.readout, target.control, target.global, target.texture_variant, value).sendToServer(); + } } public static class ControlChangePacket extends Packet { From de35b67dbda4ecc1b0aacfadf0e334d7ed57d53e Mon Sep 17 00:00:00 2001 From: Christian Mesh Date: Tue, 5 Sep 2023 14:05:20 -0400 Subject: [PATCH 5/8] Change detection of when trucks/bogies need custom pathing --- .../cam72cam/immersiverailroading/model/StockModel.java | 6 ++++-- .../cam72cam/immersiverailroading/model/part/Bogey.java | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/cam72cam/immersiverailroading/model/StockModel.java b/src/main/java/cam72cam/immersiverailroading/model/StockModel.java index 6266804a7..306aa57a0 100644 --- a/src/main/java/cam72cam/immersiverailroading/model/StockModel.java +++ b/src/main/java/cam72cam/immersiverailroading/model/StockModel.java @@ -120,12 +120,14 @@ public StockModel(DEFINITION def) throws Exception { rocking.include(remaining); this.allComponents = provider.components(); - if (bogeyFront != null && Math.abs(def.getBogeyFront(Gauge.from(Gauge.STANDARD)) + bogeyFront.center().x) > 0.5) { + float frontOffset = -def.getBogeyFront(Gauge.from(Gauge.STANDARD)); + if (bogeyFront != null && (frontOffset < bogeyFront.bogey.min.x || frontOffset > bogeyFront.bogey.max.x)) { frontTrackers = new TrackFollowers(s -> new TrackFollower(s, bogeyFront.center(), def.getBogeyFront(s.gauge))); } else { frontTrackers = null; } - if (bogeyRear != null && Math.abs(def.getBogeyRear(Gauge.from(Gauge.STANDARD)) + bogeyRear.center().x) > 0.5) { + float rearOffset = -def.getBogeyRear(Gauge.from(Gauge.STANDARD)); + if (bogeyRear != null && (rearOffset < bogeyRear.bogey.min.x || rearOffset > bogeyRear.bogey.max.x)) { rearTrackers = new TrackFollowers(s -> new TrackFollower(s, bogeyRear.center(), def.getBogeyRear(s.gauge))); } else { rearTrackers = null; diff --git a/src/main/java/cam72cam/immersiverailroading/model/part/Bogey.java b/src/main/java/cam72cam/immersiverailroading/model/part/Bogey.java index 41f11242d..6160740f0 100644 --- a/src/main/java/cam72cam/immersiverailroading/model/part/Bogey.java +++ b/src/main/java/cam72cam/immersiverailroading/model/part/Bogey.java @@ -11,7 +11,7 @@ import java.util.stream.Collectors; public class Bogey { - private final ModelComponent bogey; + public final ModelComponent bogey; public final WheelSet wheels; public static Bogey get(ComponentProvider provider, ModelState state, boolean unified, ModelPosition pos) { From efcb1c823b5a908915c3f59b99384a49e9ff8d04 Mon Sep 17 00:00:00 2001 From: Christian Mesh Date: Wed, 6 Sep 2023 07:27:12 -0400 Subject: [PATCH 6/8] Add pressure/temperature conversion options to ConfigGraphics --- .../immersiverailroading/ConfigGraphics.java | 14 ++++---- .../gui/overlay/Stat.java | 33 ++++++++----------- .../library/PressureDisplayType.java | 16 +++++++++ .../library/SpeedDisplayType.java | 14 +++++++- .../library/TemperatureDisplayType.java | 31 +++++++++++++++++ 5 files changed, 81 insertions(+), 27 deletions(-) create mode 100644 src/main/java/cam72cam/immersiverailroading/library/PressureDisplayType.java create mode 100644 src/main/java/cam72cam/immersiverailroading/library/TemperatureDisplayType.java diff --git a/src/main/java/cam72cam/immersiverailroading/ConfigGraphics.java b/src/main/java/cam72cam/immersiverailroading/ConfigGraphics.java index 24b6758d8..b1e86e81a 100644 --- a/src/main/java/cam72cam/immersiverailroading/ConfigGraphics.java +++ b/src/main/java/cam72cam/immersiverailroading/ConfigGraphics.java @@ -1,6 +1,8 @@ package cam72cam.immersiverailroading; +import cam72cam.immersiverailroading.library.PressureDisplayType; import cam72cam.immersiverailroading.library.SpeedDisplayType; +import cam72cam.immersiverailroading.library.TemperatureDisplayType; import cam72cam.immersiverailroading.library.ValveGearConfig; import cam72cam.mod.config.ConfigFile.Comment; import cam72cam.mod.config.ConfigFile.Name; @@ -15,12 +17,6 @@ @Name("general") @File("immersiverailroading_graphics.cfg") public class ConfigGraphics { - @Comment( "Place to draw the Train GUI as a % from the left of the screen" ) - public static int GUIPositionHorizontal = 2; - - @Comment( "Place to draw the Train GUI as a % from the top of the screen" ) - public static int GUIPositionVertical = 95; - @Comment("Enable Particles") public static boolean particlesEnabled = true; @@ -30,6 +26,12 @@ public class ConfigGraphics { @Comment( "What unit to use for speedometer. (kmh, mph or ms)" ) public static SpeedDisplayType speedUnit = SpeedDisplayType.kmh; + @Comment("What units to display pressure in (psi, bar)") + public static PressureDisplayType pressureUnit = PressureDisplayType.psi; + + @Comment("What units to display pressure in (psi, bar)") + public static TemperatureDisplayType temperatureUnit = TemperatureDisplayType.celcius; + @Comment( "How long to keep textures in memory after they have left the screen (higher numbers = smoother game play, lower numbers = less GPU memory used)") public static int textureCacheSeconds = 30; diff --git a/src/main/java/cam72cam/immersiverailroading/gui/overlay/Stat.java b/src/main/java/cam72cam/immersiverailroading/gui/overlay/Stat.java index 98128be76..cac3a7d0b 100644 --- a/src/main/java/cam72cam/immersiverailroading/gui/overlay/Stat.java +++ b/src/main/java/cam72cam/immersiverailroading/gui/overlay/Stat.java @@ -26,6 +26,8 @@ public enum Stat { ; public String getValue(EntityRollingStock stock) { + Float temp = null; + switch (this) { case SPEED: if (stock instanceof EntityMoveableRollingStock) { @@ -54,16 +56,7 @@ public String getValue(EntityRollingStock stock) { } return ""; case UNITS_SPEED: - switch (ConfigGraphics.speedUnit) { - case kmh: - return "km/h"; - case mph: - return "mph"; - case ms: - return "m/s"; - } - return ""; - + return ConfigGraphics.speedUnit.toUnitString(); case LIQUID: return stock instanceof FreightTank ? String.format("%.1f", @@ -79,32 +72,32 @@ public String getValue(EntityRollingStock stock) { case BOILER_PRESSURE: return stock instanceof LocomotiveSteam ? - String.format("%.1f", ((LocomotiveSteam) stock).getBoilerPressure()) : ""; + String.format("%.1f", ConfigGraphics.pressureUnit.convertFromPSI(((LocomotiveSteam) stock).getBoilerPressure())) : ""; case MAX_BOILER_PRESSURE: return stock instanceof LocomotiveSteam ? - String.format("%.1f", (float)((LocomotiveSteam) stock).getDefinition().getMaxPSI(stock.gauge)) + String.format("%.1f", ConfigGraphics.pressureUnit.convertFromPSI((float)((LocomotiveSteam) stock).getDefinition().getMaxPSI(stock.gauge))) : ""; case UNITS_BOILER_PRESSURE: - return "PSI"; + return ConfigGraphics.pressureUnit.toUnitString(); case TEMPERATURE: if (stock instanceof LocomotiveSteam) { - return String.format("%.1f", ((LocomotiveSteam) stock).getBoilerTemperature()); + temp = ((LocomotiveSteam) stock).getBoilerTemperature(); } if (stock instanceof LocomotiveDiesel) { - return String.format("%.1f", ((LocomotiveDiesel) stock).getEngineTemperature()); + temp = ((LocomotiveDiesel) stock).getEngineTemperature(); } - return ""; + return temp != null ? String.format("%.1f", ConfigGraphics.temperatureUnit.convertFromCelcius(temp)) : ""; case MAX_TEMPERATURE: if (stock instanceof LocomotiveSteam) { - return String.format("%.1f", 100f); + temp = 100f; } if (stock instanceof LocomotiveDiesel) { - return String.format("%.1f", 150f); + temp = 150f; } - return ""; + return temp != null ? String.format("%.1f", ConfigGraphics.temperatureUnit.convertFromCelcius(temp)) : ""; case UNITS_TEMPERATURE: - return "C"; + return ConfigGraphics.temperatureUnit.toUnitString(); case BRAKE_PRESSURE: if (stock instanceof EntityMoveableRollingStock) { return String.format("%s", (int)(((EntityMoveableRollingStock) stock).getBrakePressure() * 100)); diff --git a/src/main/java/cam72cam/immersiverailroading/library/PressureDisplayType.java b/src/main/java/cam72cam/immersiverailroading/library/PressureDisplayType.java new file mode 100644 index 000000000..8fd4be128 --- /dev/null +++ b/src/main/java/cam72cam/immersiverailroading/library/PressureDisplayType.java @@ -0,0 +1,16 @@ +package cam72cam.immersiverailroading.library; + +import java.util.Locale; + +public enum PressureDisplayType { + psi, + bar; + + public float convertFromPSI(float value) { + return this == psi ? value : value * 0.0689476f; + } + + public String toUnitString() { + return toString().toUpperCase(Locale.ROOT); + } +} diff --git a/src/main/java/cam72cam/immersiverailroading/library/SpeedDisplayType.java b/src/main/java/cam72cam/immersiverailroading/library/SpeedDisplayType.java index 1220dd534..e5ebb348a 100644 --- a/src/main/java/cam72cam/immersiverailroading/library/SpeedDisplayType.java +++ b/src/main/java/cam72cam/immersiverailroading/library/SpeedDisplayType.java @@ -3,5 +3,17 @@ public enum SpeedDisplayType { kmh, mph, - ms, + ms; + + public String toUnitString() { + switch (this) { + default: + case kmh: + return "km/h"; + case mph: + return "mph"; + case ms: + return "m/s"; + } + } } diff --git a/src/main/java/cam72cam/immersiverailroading/library/TemperatureDisplayType.java b/src/main/java/cam72cam/immersiverailroading/library/TemperatureDisplayType.java new file mode 100644 index 000000000..090646425 --- /dev/null +++ b/src/main/java/cam72cam/immersiverailroading/library/TemperatureDisplayType.java @@ -0,0 +1,31 @@ +package cam72cam.immersiverailroading.library; + +public enum TemperatureDisplayType { + celcius, + farenheit, + kelvin,; + + public float convertFromCelcius(float value) { + switch (this) { + default: + case celcius: + return value; + case farenheit: + return (value * 9f/5f) + 32f; + case kelvin: + return value + 270f; + } + } + + public String toUnitString() { + switch (this) { + default: + case celcius: + return "C"; + case farenheit: + return "F"; + case kelvin: + return "K"; + } + } +} From ee642802e1ce1c2615897159813a7120dbcb59ce Mon Sep 17 00:00:00 2001 From: Christian Mesh Date: Thu, 7 Sep 2023 11:06:06 -0400 Subject: [PATCH 7/8] Use ConfigFile.write to save GUI settings --- .../immersiverailroading/gui/overlay/GuiBuilder.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/cam72cam/immersiverailroading/gui/overlay/GuiBuilder.java b/src/main/java/cam72cam/immersiverailroading/gui/overlay/GuiBuilder.java index 6204361cc..3fe64cd3f 100644 --- a/src/main/java/cam72cam/immersiverailroading/gui/overlay/GuiBuilder.java +++ b/src/main/java/cam72cam/immersiverailroading/gui/overlay/GuiBuilder.java @@ -8,6 +8,7 @@ import cam72cam.immersiverailroading.util.DataBlock; import cam72cam.immersiverailroading.util.MergedBlocks; import cam72cam.mod.MinecraftClient; +import cam72cam.mod.config.ConfigFile; import cam72cam.mod.entity.Entity; import cam72cam.mod.event.ClientEvents; import cam72cam.mod.gui.helpers.GUIHelpers; @@ -448,7 +449,7 @@ private void onMouseMove(EntityRollingStock stock, Matrix4 matrix, GuiBuilder ta temporary_value = val; if (setting != null) { ConfigGraphics.settings.put(setting, val); - //TODO ConfigFile.write(ConfigGraphics.class); + ConfigFile.write(ConfigGraphics.class); } else { if (readout != Readouts.TRAIN_BRAKE_LEVER) { new ControlChangePacket(stock, readout, control, global, texture_variant, val).sendToServer(); @@ -483,7 +484,7 @@ public void onMouseRelease(EntityRollingStock stock) { if (setting != null) { ConfigGraphics.settings.put(setting, value); - //TODO ConfigFile.write(ConfigGraphics.class); + ConfigFile.write(ConfigGraphics.class); } else { new ControlChangePacket(stock, readout, control, global, texture_variant, value).sendToServer(); } From 7a7bf2a8be1d83d55dc0cf9d5ec6f9e1a17b0bcf Mon Sep 17 00:00:00 2001 From: Christian Mesh Date: Thu, 7 Sep 2023 11:25:28 -0400 Subject: [PATCH 8/8] Default speed readout to 200 km/h You can scale that however needed in your caml. Example: for 120 needle rotation degrees from 0 to 100 km/h you would set the rotation degrees to 240. Equation: degrees * 200 / desired_speed_kmh --- .../cam72cam/immersiverailroading/gui/overlay/Readouts.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/cam72cam/immersiverailroading/gui/overlay/Readouts.java b/src/main/java/cam72cam/immersiverailroading/gui/overlay/Readouts.java index 10b0991df..48a2f8c6a 100644 --- a/src/main/java/cam72cam/immersiverailroading/gui/overlay/Readouts.java +++ b/src/main/java/cam72cam/immersiverailroading/gui/overlay/Readouts.java @@ -40,9 +40,8 @@ public float getValue(EntityRollingStock stock, float lever) { case LIQUID: return stock instanceof FreightTank ? ((FreightTank) stock).getPercentLiquidFull() / 100f : 0; case SPEED: - return stock instanceof Locomotive ? (float) ( - Math.abs(((Locomotive) stock).getCurrentSpeed().metric()) / - ((Locomotive) stock).getDefinition().getMaxSpeed(stock.gauge).metric()) : 0; + return (float)Math.abs(((EntityMoveableRollingStock)stock).getCurrentSpeed().metric() / + (stock instanceof Locomotive ? ((Locomotive) stock).getDefinition().getMaxSpeed(stock.gauge).metric() : 200)); case TEMPERATURE: if (stock instanceof LocomotiveSteam) { return ((LocomotiveSteam) stock).getBoilerTemperature() / 100f;