diff --git a/CHANGELOG.md b/CHANGELOG.md index cd7534c..806a552 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,19 @@ -# 0.5 The Performance Update +# 0.6 The Tilting Update -## **Now Supports Create 6!** +**Now requires dragonlib > 3.0.14** +**Always backup your save before updating!** -## The Performance part -- Improved rendering performance so no more fps drops -- Massively improved ETCS server side perfomance. -- Small performance improvements to compat code +## New Features -## The New Things Part -- ETCS now treats end of track as a red signal \ No newline at end of file +- Added settings menu for trains. + - Added tilt customization with options including None, Passive, Active, and Custom. + - Enabled custom per train acceleration settings with None and Custom modes. + +- Added train banking/tilting +- Added "Time Of Day Realistic" Condition. Which enhances Time Of Day Condition by factoring in train delays. +- language support + + +## Bug Fixes +- Fixed a bug where pressing shift would cause a crash. +- Some other things i forgot to write down. \ No newline at end of file diff --git a/build.gradle b/build.gradle index 6f8f2ef..b059cd6 100644 --- a/build.gradle +++ b/build.gradle @@ -26,6 +26,12 @@ subprojects { maven { url = "https://maven.blamejared.com/" } // JEI maven { url = "https://maven.parchmentmc.org" } // Parchment mappings maven { url = "https://maven.quiltmc.org/repository/release" } // Quilt Mappings + maven { url = "https://raw.githubusercontent.com/MisterJulsen/modsrepo/main/maven/" } // DragonLib + maven { url = "https://cursemaven.com" + content{ + includeGroup("curse.maven") + } + } maven { // Flywheel url = "https://maven.tterrag.com/" content { @@ -83,6 +89,7 @@ allprojects { maven { url = "https://maven.terraformersmc.com/releases/" } maven { url = "https://raw.githubusercontent.com/Fuzss/modresources/main/maven/" } maven { url = "https://purplecreate.github.io/maven/"} + maven { url = "https://maven.lounode.top/releases" } } @@ -110,6 +117,8 @@ tasks.create("publishRealism") { dependsOn tasks.build, ":fabric:publishMods", ":forge:publishMods" break case "fabric": + dependsOn "${platform}:build", "${platform}:publishMods" + break case "forge": dependsOn "${platform}:build", "${platform}:publishMods" break diff --git a/common/build.gradle b/common/build.gradle index 62ede32..e7cbf53 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -8,15 +8,14 @@ loom { dependencies { modImplementation("net.fabricmc:fabric-loader:${fabric_loader_version}") - modCompileOnly("com.simibubi.create:create-fabric-${minecraft_version}:${create_fabric_version}") - + modCompileOnly("com.simibubi.create:create-fabric:${create_fabric_version}-mc${minecraft_version}") modImplementation "dev.architectury:architectury:$rootProject.architectury_api_version" implementation("javazoom:jlayer:${jlayer_version}") annotationProcessor(implementation("io.github.llamalad7:mixinextras-common:${mixin_extras_version}")) modCompileOnly("purplecreate.tramways:tramways-common:$tramways_version-mc1.20.1-common") modRuntimeOnly("purplecreate.tramways:tramways-common:$tramways_version-mc1.20.1-common") - + modImplementation("de.mrjulsen.mcdragonlib:dragonlib-fabric:${minecraft_version}-${dragonlib_version}") } sourceSets { diff --git a/common/src/main/java/net/Realism/CommonEvents.java b/common/src/main/java/net/Realism/CommonEvents.java new file mode 100644 index 0000000..8df2182 --- /dev/null +++ b/common/src/main/java/net/Realism/CommonEvents.java @@ -0,0 +1,10 @@ +package net.Realism; + +import net.minecraft.server.level.ServerPlayer; + +public class CommonEvents { + public static void onPlayerJoin(ServerPlayer player) { + RNetworking.onPlayerJoin(player); + } + +} diff --git a/common/src/main/java/net/Realism/Interfaces/IOrientedContraptionEntity.java b/common/src/main/java/net/Realism/Interfaces/IOrientedContraptionEntity.java new file mode 100644 index 0000000..3330288 --- /dev/null +++ b/common/src/main/java/net/Realism/Interfaces/IOrientedContraptionEntity.java @@ -0,0 +1,38 @@ +package net.Realism.Interfaces; + +import java.util.UUID; + +/** + * Interface for adding banking (roll) functionality to OrientedContraptionEntity + */ +public interface IOrientedContraptionEntity { + /** + * Get the current roll angle + */ + float realism$getRoll(); + + /** + * Set the current roll angle + */ + void realism$setRoll(float roll); + + /** + * Get the previous tick's roll angle (for interpolation) + */ + float realism$getPrevRoll(); + + /** + * Set the previous tick's roll angle (for interpolation) + */ + void realism$setPrevRoll(float prevRoll); + + /** + * Get the interpolated roll angle for rendering + * @param partialTicks The partial tick time + * @return Interpolated roll angle + */ + float realism$getViewRoll(float partialTicks); + + UUID getuid(); +} + diff --git a/common/src/main/java/net/Realism/Interfaces/IRealismScreens.java b/common/src/main/java/net/Realism/Interfaces/IRealismScreens.java new file mode 100644 index 0000000..2f975ec --- /dev/null +++ b/common/src/main/java/net/Realism/Interfaces/IRealismScreens.java @@ -0,0 +1,5 @@ +package net.Realism.Interfaces; + +public interface IRealismScreens { + void init(ITrainInterface Rtrain); +} diff --git a/common/src/main/java/net/Realism/Interfaces/IScheduleRuntimeMixin.java b/common/src/main/java/net/Realism/Interfaces/IScheduleRuntimeMixin.java new file mode 100644 index 0000000..fb7ce21 --- /dev/null +++ b/common/src/main/java/net/Realism/Interfaces/IScheduleRuntimeMixin.java @@ -0,0 +1,11 @@ +package net.Realism.Interfaces; + +public interface IScheduleRuntimeMixin { + long getDepartureDate(); + long getExpectedArrivalDate(); + void setExpectedArrivalDate(long date); + int getLastScheduledDepartureDate(); + void setLastScheduledDepartureDate(int date); + boolean dontCheck(); + void setDontCheck(boolean dontCheck); +} diff --git a/common/src/main/java/net/Realism/Interfaces/ITrainInterface.java b/common/src/main/java/net/Realism/Interfaces/ITrainInterface.java index c957e82..78e7ec8 100644 --- a/common/src/main/java/net/Realism/Interfaces/ITrainInterface.java +++ b/common/src/main/java/net/Realism/Interfaces/ITrainInterface.java @@ -1,9 +1,12 @@ package net.Realism.Interfaces; +import net.Realism.trains.TrainSettings; import net.Realism.trains.etcs.ETCS; public interface ITrainInterface{ ETCS realism$getETCS(); void realism$setETCS(ETCS etcs); + TrainSettings realism$getSettings(); + void realism$setSettings(TrainSettings tiltSetting); } diff --git a/common/src/main/java/net/Realism/NBT/RNBTHelper.java b/common/src/main/java/net/Realism/NBT/RNBTHelper.java new file mode 100644 index 0000000..9c158ca --- /dev/null +++ b/common/src/main/java/net/Realism/NBT/RNBTHelper.java @@ -0,0 +1,29 @@ +package net.Realism.NBT; + +import net.minecraft.nbt.CompoundTag; + +import java.util.List; +import java.util.UUID; + +public class RNBTHelper { + public static CompoundTag writeUUIDList(List list, String key) { + int iter = 0; + CompoundTag tag = new CompoundTag(); + for (UUID id : list) { + tag.putUUID(String.join("_", key, String.valueOf(iter++)), id); + } + return tag; + } + + public static List readUUIDList(CompoundTag tag, String key) { + List list = new java.util.ArrayList<>(List.of()); + for (int i = 0; i < tag.size(); i++) { + list.add(tag.getUUID(String.join("_", key, String.valueOf(i)))); + } + return list; + } + + + + +} diff --git a/common/src/main/java/net/Realism/RExtras.java b/common/src/main/java/net/Realism/RExtras.java new file mode 100644 index 0000000..e7b4c46 --- /dev/null +++ b/common/src/main/java/net/Realism/RExtras.java @@ -0,0 +1,26 @@ +package net.Realism; + +import com.simibubi.create.content.trains.schedule.condition.ScheduleWaitCondition; +import com.simibubi.create.content.trains.schedule.destination.ScheduleInstruction; +import net.Realism.trains.schedule.TimeOfDayRealistic; +import net.createmod.catnip.data.Pair; + +import java.util.function.Supplier; + +import static com.simibubi.create.content.trains.schedule.Schedule.CONDITION_TYPES; +import static com.simibubi.create.content.trains.schedule.Schedule.INSTRUCTION_TYPES; + +public class RExtras { + public static class Schedule { + private static void registerInstruction(String path, Supplier factory) { + INSTRUCTION_TYPES.add(Pair.of(RealismMod.id(path), factory)); + } + private static void registerCondition(String path, Supplier clazz) { + CONDITION_TYPES.add(Pair.of(RealismMod.id(path), clazz)); + } + + public static void register() { + registerCondition("time_of_day_realistic", TimeOfDayRealistic::new); + } + } +} diff --git a/common/src/main/java/net/Realism/RNetworking.java b/common/src/main/java/net/Realism/RNetworking.java index 8d37cb8..ab968e2 100644 --- a/common/src/main/java/net/Realism/RNetworking.java +++ b/common/src/main/java/net/Realism/RNetworking.java @@ -1,16 +1,17 @@ package net.Realism; +import com.simibubi.create.Create; import dev.architectury.injectables.annotations.ExpectPlatform; import io.netty.buffer.Unpooled; -import net.Realism.network.ETCSStartStopPacket; -import net.Realism.network.ETCSSyncPacket; -import net.Realism.network.SteerDirectionPacket; +import net.Realism.Interfaces.ITrainInterface; +import net.Realism.network.*; import net.Realism.util.C2SPacket; import net.Realism.util.S2CPacket; import net.minecraft.client.Minecraft; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceKey; +import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.level.Level; import net.minecraft.world.phys.Vec3; @@ -46,9 +47,10 @@ public void handle(Minecraft mc) { return; mc.getConnection().onDisconnect( - Component.literal( - "Create: Tramways network versions do not match! Server expected %s, client has %s" - .formatted(serverVersion, RNetworking.VERSION) + Component.translatable( + "realism.network.version_mismatch", + serverVersion, + RNetworking.VERSION ) ); } @@ -104,6 +106,15 @@ public static void handleInternal(FriendlyByteBuf buf, ServerPlayer player) { public static void onPlayerJoin(ServerPlayer player) { sendToPlayer(new CheckVersionS2CPacket(RNetworking.VERSION), player); + MinecraftServer server = player.server; + server.getAllLevels().forEach((level) -> { + Create.RAILWAYS.sided(level).trains.forEach((uuid, train) -> { + if (train == null) return; + if (train instanceof ITrainInterface Rtrain) { + RNetworking.sendToPlayer(new TrainSettingsUpdatePacket(Rtrain.realism$getSettings(), train.id), player); + } + }); + }); } @ExpectPlatform @@ -145,6 +156,17 @@ public static void register() { ETCSStartStopPacket::read ); - + registerS2C( + RollSyncPacket.class, + RollSyncPacket::read + ); + registerC2S( + TrainSettingsSavePacket.class, + TrainSettingsSavePacket::read + ); + registerS2C( + TrainSettingsUpdatePacket.class, + TrainSettingsUpdatePacket::read + ); } } diff --git a/common/src/main/java/net/Realism/RealismBlocks.java b/common/src/main/java/net/Realism/RealismBlocks.java deleted file mode 100644 index b800837..0000000 --- a/common/src/main/java/net/Realism/RealismBlocks.java +++ /dev/null @@ -1,16 +0,0 @@ -package net.Realism; - -import com.simibubi.create.foundation.data.CreateRegistrate; -import com.tterrag.registrate.util.entry.BlockEntry; -import net.minecraft.world.level.block.Block; - -public class RealismBlocks { - public static final CreateRegistrate REGISTRATE = CreateRegistrate.create(RealismMod.MOD_ID); - - public static final BlockEntry EXAMPLE_BLOCK = REGISTRATE.block("example_block", Block::new).register(); - - public static void init() { - // load the class and register everything - RealismMod.LOGGER.info("Registering blocks for " + RealismMod.NAME); - } -} diff --git a/common/src/main/java/net/Realism/RealismMod.java b/common/src/main/java/net/Realism/RealismMod.java index 9735e2c..b4ea0f8 100644 --- a/common/src/main/java/net/Realism/RealismMod.java +++ b/common/src/main/java/net/Realism/RealismMod.java @@ -1,5 +1,7 @@ package net.Realism; + +import com.simibubi.create.foundation.data.CreateRegistrate; import net.minecraft.resources.ResourceLocation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -8,14 +10,15 @@ public class RealismMod { public static final String MOD_ID = "realism"; public static final String NAME = "Create Realism"; public static final Logger LOGGER = LoggerFactory.getLogger(NAME); + public static final CreateRegistrate REGISTRATE = CreateRegistrate.create(MOD_ID); public static void init() { - RealismBlocks.init(); RealismSounds.SOUND_EVENTS.register(); } public static void commonSetup() { RNetworking.register(); + RExtras.Schedule.register(); } diff --git a/common/src/main/java/net/Realism/compat/TramwaysCompat.java b/common/src/main/java/net/Realism/compat/TramwaysCompat.java index 94e49ff..d4cb86a 100644 --- a/common/src/main/java/net/Realism/compat/TramwaysCompat.java +++ b/common/src/main/java/net/Realism/compat/TramwaysCompat.java @@ -4,7 +4,7 @@ import net.Realism.Interfaces.ITramSignPoint; import net.Realism.RealismExpectPlatform; import net.Realism.RealismMod; -import net.Realism.mixinaccesors.TramSignDataAccessor; +import net.Realism.mixin.mixinaccesors.TramSignDataAccessor; import net.Realism.trains.SignalFinder; import net.Realism.trains.etcs.ETCS; import net.createmod.catnip.data.Couple; @@ -13,7 +13,6 @@ import purplecreate.tramways.content.signs.demands.SignDemand; import purplecreate.tramways.content.signs.demands.TemporaryEndSignDemand; import purplecreate.tramways.content.signs.demands.TemporarySpeedSignDemand; -import purplecreate.tramways.mixinInterfaces.ISpeedLimitableTrain; import java.util.*; @@ -62,6 +61,7 @@ public static List processTramSignsImpl(SignalFinder.SignalScan CompoundTag tag = null; SignDemand demand = null; + //noinspection ReassignedVariable if (sides.get(sign.getPrimary()) == null) continue; for (TramSignPoint.SignData signD : new HashSet<>(sides.get(sign.getPrimary()))) { if (signD == null) continue; @@ -89,24 +89,27 @@ public static List processTramSignsImpl(SignalFinder.SignalScan double signSpeedLimit = tag.getInt("Throttle"); signSpeedLimit = (signSpeedLimit / 100) * maxSpeed; cachedSpeedLimits.add(new ETCS.SpeedLimit(sign.getDistance(), signSpeedLimit)); - if (demand instanceof TemporarySpeedSignDemand) { - LastLimit = (int) cachedSpeedLimits.get(cachedSpeedLimits.size()-1).speedLimit(); + if (demand instanceof TemporarySpeedSignDemand && !first) { + LastLimit = (int) cachedSpeedLimits.get(cachedSpeedLimits.size()-2).speedLimit(); + } else if (demand instanceof TemporarySpeedSignDemand) { +// + double speed = train.maxSpeed()*20; + LastLimit = (int) (train.throttle*speed*3.6); } - first= false;} + first= false;} if (demand instanceof TemporaryEndSignDemand && !first) { cachedSpeedLimits.add(new ETCS.SpeedLimit(sign.getDistance(), LastLimit));} - else if (demand instanceof TemporarySpeedSignDemand && first) { - ISpeedLimitableTrain Ttrain = (ISpeedLimitableTrain) train; - double tempSpeedLimit = 300.0; + else if (demand instanceof TemporaryEndSignDemand && first) { + double tempSpeedLimit = 300.0; try { - java.lang.reflect.Field tempSPeedLimitField = ISpeedLimitableTrain.class.getDeclaredField("tempSpeedLimit$actual"); + java.lang.reflect.Field tempSPeedLimitField = train.getClass().getDeclaredField("tramways$storedPermanent"); tempSPeedLimitField.setAccessible(true); - tempSpeedLimit = (double) tempSPeedLimitField.get(Ttrain); + tempSpeedLimit = (double) tempSPeedLimitField.get(train); } catch (Exception e) { RealismMod.LOGGER.error("Error accessing tempSpeedLimit field: " + e.getMessage()); } - cachedSpeedLimits.add(new ETCS.SpeedLimit(sign.getDistance(),tempSpeedLimit)); + cachedSpeedLimits.add(new ETCS.SpeedLimit(sign.getDistance(),tempSpeedLimit*3.6*train.maxSpeed()*20)); } first = false; } diff --git a/common/src/main/java/net/Realism/config/RealismConfig.java b/common/src/main/java/net/Realism/config/RealismConfig.java index 839cff2..3a348f8 100644 --- a/common/src/main/java/net/Realism/config/RealismConfig.java +++ b/common/src/main/java/net/Realism/config/RealismConfig.java @@ -6,16 +6,24 @@ public class RealismConfig { // Common Config public static class Common { public final ForgeConfigSpec.BooleanValue GlobalETCSEnable; + public final ForgeConfigSpec.BooleanValue GlobalBankingEnable; public final ForgeConfigSpec.BooleanValue EnableCustomTrainAcceleration; - public final ForgeConfigSpec.DoubleValue CustomTrainAccelerationMultiplyer; + public final ForgeConfigSpec.DoubleValue CustomTrainAccelerationMultiplyer; + public final ForgeConfigSpec.BooleanValue AllowBiggerValuesTrains; Common(ForgeConfigSpec.Builder builder) { builder.push("general"); builder.push("Custom train acceleration"); - EnableCustomTrainAcceleration = builder.comment("Enable custom train acceleration based on the number of carriages") + EnableCustomTrainAcceleration = builder.comment("Enable custom train acceleration(Custom and Standard)") .define("Enable Custom Train Acceleration", true); - CustomTrainAccelerationMultiplyer = builder.comment("Multiplier for custom train acceleration(Higher = slower acceleration per Carrige)") - .defineInRange("Custom Train Acceleration Multiplyer", 1.0, 0.1, 5.0); + CustomTrainAccelerationMultiplyer = builder.comment("Multiplier for custom train acceleration(Higher = slower acceleration per Carriage)(For trains set on Standard)") + .defineInRange("Custom Train Acceleration Multiplayer", 1.0, 0.1, 5.0); + AllowBiggerValuesTrains = builder.comment("Allow players to set custom acceleration values larger than default(For trains set on Custom)") + .define("Allow Large Acceleration", true); + builder.pop(); + builder.push("Banking"); + GlobalBankingEnable = builder.comment("Global enable of ALL train banking") + .define("Global Banking Enable", true); builder.pop(); builder.push("ETCS"); GlobalETCSEnable = builder.comment("Enable ETCS for all trains") @@ -32,6 +40,9 @@ public static class Client { public final ForgeConfigSpec.DoubleValue ETCSSize; public final ForgeConfigSpec.BooleanValue ETCSSounds; + // Banking Configuration + public final ForgeConfigSpec.BooleanValue enableBanking; + public final ForgeConfigSpec.BooleanValue enablePlayerTilt; Client(ForgeConfigSpec.Builder builder) { builder.push("general"); @@ -43,7 +54,14 @@ public static class Client { ETCSSounds = builder.comment("Enable ETCS sounds") .define("ETCS Sounds", true); builder.pop(); - debugMode = builder.comment("Enable debug mode to see the modified acceleration") + + builder.push("Banking"); + enableBanking = builder.comment("Enable banking rendering (roll rotation) on curved tracks") + .define("Enable Banking", true); + enablePlayerTilt = builder.comment("Rotate player camera with the train(Forge only)") + .define("Enable Player Tilt", true); + builder.pop(); + debugMode = builder.comment("Enable debug mode") .define("debugMode", false); builder.pop(); } diff --git a/common/src/main/java/net/Realism/datagen/DataGen.java b/common/src/main/java/net/Realism/datagen/DataGen.java new file mode 100644 index 0000000..c6df7f3 --- /dev/null +++ b/common/src/main/java/net/Realism/datagen/DataGen.java @@ -0,0 +1,11 @@ +package net.Realism.datagen; + +import com.tterrag.registrate.providers.ProviderType; +import net.Realism.RealismMod; + +public class DataGen { + public static void register() { + RealismMod.REGISTRATE.addDataGenerator(ProviderType.LANG, GenLang::generator); + } + +} diff --git a/common/src/main/java/net/Realism/datagen/GenLang.java b/common/src/main/java/net/Realism/datagen/GenLang.java new file mode 100644 index 0000000..e9c9f12 --- /dev/null +++ b/common/src/main/java/net/Realism/datagen/GenLang.java @@ -0,0 +1,26 @@ +package net.Realism.datagen; + +import com.google.gson.JsonElement; +import com.simibubi.create.foundation.utility.FilesHelper; +import com.tterrag.registrate.providers.RegistrateLangProvider; +import net.Realism.RealismMod; +import net.createmod.ponder.foundation.PonderIndex; + +import java.util.Map; + +public class GenLang { + public static void generator(RegistrateLangProvider provider) { + PonderIndex.getLangAccess().provideLang(RealismMod.MOD_ID, provider::add); + + // defaults + JsonElement elem = FilesHelper.loadJsonResource("assets/realism/lang/defaults.json"); + + if (elem == null) { + throw new RuntimeException("Couldn't find lang/defaults.json"); + } + + for (Map.Entry entry : elem.getAsJsonObject().entrySet()) { + provider.add(entry.getKey(), entry.getValue().getAsString()); + } + } +} diff --git a/common/src/main/java/net/Realism/debug/RealismDebuger.java b/common/src/main/java/net/Realism/debug/RealismDebuger.java index 3317ae8..11b20c8 100644 --- a/common/src/main/java/net/Realism/debug/RealismDebuger.java +++ b/common/src/main/java/net/Realism/debug/RealismDebuger.java @@ -1,14 +1,15 @@ package net.Realism.debug; -import net.minecraft.client.Minecraft; import net.Realism.config.RealismConfig; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; +import net.minecraft.client.Minecraft; + +import java.util.HashSet; import java.util.LinkedList; import java.util.Queue; -import java.util.HashSet; import java.util.Set; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; public class RealismDebuger { private static final RealismDebuger INSTANCE = new RealismDebuger(); @@ -30,7 +31,7 @@ public static RealismDebuger getInstance() { public void startLogging() { if (!isRunning) { isRunning = true; - scheduler.scheduleAtFixedRate(this::logDebugInfo, 0, 5, TimeUnit.SECONDS); + scheduler.scheduleAtFixedRate(this::logDebugInfo, 0, 1, TimeUnit.SECONDS); } } @@ -41,7 +42,7 @@ public void stopLogging() { } } - public void addDebugInfo(float acceleration, int carriageCount, int locomotiveCount) { + public void addAccelerationDebugInfo(float acceleration, int carriageCount, int locomotiveCount) { // Only collect debug info if debug mode is enabled if (RealismConfig.CLIENT.debugMode.get()) { String message = String.format("Train acceleration: %.5f | Carriages: %d | PowerCars: %d", @@ -66,6 +67,20 @@ public void addDebugInfo(float acceleration, int carriageCount, int locomotiveCo } } + public void addDebugMessage(String message) { + // Only collect debug info if debug mode is enabled + if (RealismConfig.CLIENT.debugMode.get()) { + synchronized (pendingMessages) { + if (!knownMessages.contains(message)) { + // This is a new message we haven't seen before + pendingMessages.offer(message); + knownMessages.add(message); + hasNewData = true; + } + } + } + } + private void logDebugInfo() { // Check conditions: debug mode is on, there's a world loaded, and we have new data if (!RealismConfig.CLIENT.debugMode.get() || Minecraft.getInstance().level == null || !hasNewData) { diff --git a/common/src/main/java/net/Realism/gui/RealismScreens.java b/common/src/main/java/net/Realism/gui/RealismScreens.java new file mode 100644 index 0000000..51b1bae --- /dev/null +++ b/common/src/main/java/net/Realism/gui/RealismScreens.java @@ -0,0 +1,55 @@ +package net.Realism.gui; + + +import de.mrjulsen.mcdragonlib.client.gui.widgets.base.DLScreen; +import de.mrjulsen.mcdragonlib.client.gui.widgets.base.DLWindowManager; +import de.mrjulsen.mcdragonlib.client.gui.widgets.base.WindowBuilder; +import net.Realism.Interfaces.IRealismScreens; +import net.Realism.Interfaces.ITrainInterface; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.Component; + +public class RealismScreens extends Screen implements IRealismScreens { + private DLScreen wrapper; + + public RealismScreens() { + super(Component.translatable("realism.screen.title")); + } + + @Override + public void init(ITrainInterface Rtrain) { + // Create a WindowBuilder that constructs your main window + WindowBuilder builder = (manager) -> new TrainSettingsGui(manager, Rtrain); + + // DLScreen creates and manages the DLWindowManager internally + wrapper = new DLScreen( + null, + builder + ); + } + + @Override + public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { + this.renderBackground(graphics); + wrapper.render(graphics, mouseX, mouseY, partialTicks); + super.render(graphics, mouseX, mouseY, partialTicks); + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int button) { + return wrapper.mouseClicked(mouseX, mouseY, button) + || super.mouseClicked(mouseX, mouseY, button); + } + + @Override + public boolean keyPressed(int keyCode, int scanCode, int modifiers) { + return wrapper.keyPressed(keyCode, scanCode, modifiers) + || super.keyPressed(keyCode, scanCode, modifiers); + } + + // Access the manager directly if needed: + public DLWindowManager getManager() { + return wrapper.getWindowManager(); + } +} \ No newline at end of file diff --git a/common/src/main/java/net/Realism/gui/TrainSettingsGui.java b/common/src/main/java/net/Realism/gui/TrainSettingsGui.java new file mode 100644 index 0000000..35edacf --- /dev/null +++ b/common/src/main/java/net/Realism/gui/TrainSettingsGui.java @@ -0,0 +1,361 @@ +package net.Realism.gui; + + +import com.simibubi.create.content.trains.entity.Train; +import com.simibubi.create.infrastructure.config.AllConfigs; +import de.mrjulsen.mcdragonlib.client.gui.events.DLGuiStandardEvents; +import de.mrjulsen.mcdragonlib.client.gui.widgets.base.DLWindow; +import de.mrjulsen.mcdragonlib.client.gui.widgets.base.DLWindowManager; +import de.mrjulsen.mcdragonlib.client.gui.widgets.components.DLButton; +import de.mrjulsen.mcdragonlib.client.gui.widgets.components.DLCycleButton; +import de.mrjulsen.mcdragonlib.client.gui.widgets.components.DLNumberPicker; +import de.mrjulsen.mcdragonlib.client.gui.widgets.components.DLRichTextLabel; +import de.mrjulsen.mcdragonlib.client.gui.widgets.util.INumberFormatAdapter; +import de.mrjulsen.mcdragonlib.client.gui.widgets.util.ITextFormatter; +import de.mrjulsen.mcdragonlib.client.util.DLGuiGraphics; +import de.mrjulsen.mcdragonlib.client.util.GuiUtils; +import de.mrjulsen.mcdragonlib.util.TextUtils; +import de.mrjulsen.mcdragonlib.util.math.Rectangle; +import net.Realism.Interfaces.ITrainInterface; +import net.Realism.RNetworking; +import net.Realism.config.RealismConfig; +import net.Realism.network.TrainSettingsSavePacket; +import net.Realism.trains.TrainSettings; +import net.minecraft.network.chat.Component; + +import java.util.List; + +public class TrainSettingsGui extends DLWindow { + public TrainSettingsGui(DLWindowManager manager, ITrainInterface Rtrain) { + super(manager); + + TrainSettings cs = Rtrain.realism$getSettings(); + // Set window size and position + setSize(500, 300); + setPosition(0, 0); + + // Add components + DLRichTextLabel title = addComponent( + new DLRichTextLabel(140, 10, 200, 20) {} + ); + title.text.get().set(Component.translatable("realism.gui.train_settings.title").getString()); + + + + DLCycleButton Tiltbutton = addComponent( + new DLCycleButton(70, 40, 80, 20){ + + + } + ); + Tiltbutton.items.add(Component.translatable("realism.gui.tilt.none").getString()); + Tiltbutton.items.add(Component.translatable("realism.gui.tilt.passive").getString()); + Tiltbutton.items.add(Component.translatable("realism.gui.tilt.active").getString()); + Tiltbutton.items.add(Component.translatable("realism.gui.tilt.custom").getString()); + Tiltbutton.text.set(Component.literal("")); + Tiltbutton.textFormat.set((ITextFormatter>) (src -> + TextUtils.text(src.selectedItem.get().map(Object::toString).orElse("")) + .withStyle(src.text.get().getStyle()) + )); + + Tiltbutton.selectedIndex.set(cs.ts.ordinal()); + + DLRichTextLabel tiltLabel = addComponent( + new DLRichTextLabel(15, 45, 60, 20) { + @Override + public void renderFrontLayer(DLGuiGraphics g,double mousex, double mousey, Rectangle bounds){ + super.renderFrontLayer(g,mousex,mousey,bounds); + int screenW = (int)getWindowManager().getScreenWidth(); + if (isMouseOver(mousex,mousey)) { + GuiUtils.drawTooltip(g,g.defaultFont(), (int) mousex, (int) mousey, + List.of( + TextUtils.text(Component.translatable("realism.gui.tilt.tooltip.header").getString()), + TextUtils.text(Component.translatable("realism.gui.tilt.tooltip.none").getString()), + TextUtils.text(Component.translatable("realism.gui.tilt.tooltip.passive").getString()), + TextUtils.text(Component.translatable("realism.gui.tilt.tooltip.active").getString()), + TextUtils.text(Component.translatable("realism.gui.tilt.tooltip.custom").getString()) + ), + screenW + ); + } + } + } + ); + tiltLabel.text.get().set(Component.translatable("realism.gui.tilt.label").getString()); + + DLNumberPicker minSpeedPicker = addComponent( + new DLNumberPicker(75, 70, 80, 20){ + @Override + public void renderFrontLayer(DLGuiGraphics g,double mousex, double mousey, Rectangle bounds){ + super.renderFrontLayer(g,mousex,mousey,bounds); + int screenW = (int)getWindowManager().getScreenWidth(); + if (isMouseOver(mousex,mousey)) { + GuiUtils.drawTooltip(g,g.defaultFont(), (int) mousex, (int) mousey, + List.of( + TextUtils.text(Component.translatable("realism.gui.tilt.min_speed.tooltip").getString()) + ), + screenW + ); + } + } + } + ); + minSpeedPicker.min.set((double) 0); + minSpeedPicker.max.set(300.0); + minSpeedPicker.value.set(cs.customMinSpeed); + minSpeedPicker.visible.set(cs.ts == TrainSettings.tiltSetting.CUSTOM); + + + DLRichTextLabel minSpeedLabel = addComponent( + new DLRichTextLabel(19, 75, 60, 20) + ); + minSpeedLabel.text.get().set(Component.translatable("realism.gui.tilt.min_speed.label").getString()); + minSpeedLabel.visible.set(cs.ts == TrainSettings.tiltSetting.CUSTOM); + + DLNumberPicker maxTiltPicker = addComponent( + new DLNumberPicker(75, 95, 80, 20){ + @Override + public void renderFrontLayer(DLGuiGraphics g,double mousex, double mousey, Rectangle bounds){ + super.renderFrontLayer(g,mousex,mousey,bounds); + int screenW = (int)getWindowManager().getScreenWidth(); + if (isMouseOver(mousex,mousey)) { + GuiUtils.drawTooltip(g,g.defaultFont(), (int) mousex, (int) mousey, + List.of( + TextUtils.text(Component.translatable("realism.gui.tilt.max_tilt.tooltip").getString()) + ), + screenW + ); + } + } + } + ); + maxTiltPicker.min.set((double) 0); + maxTiltPicker.max.set(90.0); + maxTiltPicker.format.set(new INumberFormatAdapter.DecimalNumberFormat(1)); + maxTiltPicker.value.set((double) cs.customMaxTilt); + maxTiltPicker.visible.set(cs.ts == TrainSettings.tiltSetting.CUSTOM); + maxTiltPicker.step.set(0.1); + + + DLRichTextLabel maxTiltLabel = addComponent( + new DLRichTextLabel(29, 100, 60, 20) + ); + maxTiltLabel.text.get().set(Component.translatable("realism.gui.tilt.max_tilt.label").getString()); + maxTiltLabel.visible.set(cs.ts == TrainSettings.tiltSetting.CUSTOM); + + DLNumberPicker IntensityPicker = addComponent( + new DLNumberPicker(75, 120, 80, 20){ + @Override + public void renderFrontLayer(DLGuiGraphics g,double mousex, double mousey, Rectangle bounds){ + super.renderFrontLayer(g,mousex,mousey,bounds); + int screenW = (int)getWindowManager().getScreenWidth(); + if (isMouseOver(mousex,mousey)) { + GuiUtils.drawTooltip(g,g.defaultFont(), (int) mousex, (int) mousey, + List.of( + TextUtils.text(Component.translatable("realism.gui.tilt.intensity.tooltip").getString()) + ), + screenW + ); + } + } + + } + ); + IntensityPicker.min.set((double) 0); + IntensityPicker.max.set(20.0); + IntensityPicker.format.set(new INumberFormatAdapter.DecimalNumberFormat(1)); + IntensityPicker.value.set((double) cs.customTiltIntensity); + IntensityPicker.visible.set(cs.ts == TrainSettings.tiltSetting.CUSTOM); + IntensityPicker.step.set(0.1); + + + DLRichTextLabel IntensityLabel = addComponent( + new DLRichTextLabel(5, 125, 72, 20){ + + } + ); + IntensityLabel.text.get().set(Component.translatable("realism.gui.tilt.intensity.label").getString()); + IntensityLabel.visible.set(cs.ts == TrainSettings.tiltSetting.CUSTOM); + + DLRichTextLabel TiltDirectionLabel = addComponent( + new DLRichTextLabel(5, 155, 72, 20){}); + TiltDirectionLabel.text.get().set(Component.translatable("realism.gui.tilt.direction.label").getString()); + TiltDirectionLabel.visible.set(cs.ts == TrainSettings.tiltSetting.CUSTOM); + + + DLCycleButton DirectionButton = addComponent( + new DLCycleButton(75, 150, 72, 20){ + + } + ); + DirectionButton.items.add(Component.translatable("realism.gui.tilt.direction.inside").getString()); + DirectionButton.items.add(Component.translatable("realism.gui.tilt.direction.outside").getString()); + DirectionButton.textFormat.set((ITextFormatter>) (src -> + TextUtils.text(src.selectedItem.get().map(Object::toString).orElse("")) + .withStyle(src.text.get().getStyle()) + )); + DirectionButton.selectedIndex.set(cs.Inside?0:1); + DirectionButton.visible.set(cs.ts == TrainSettings.tiltSetting.CUSTOM); + + Tiltbutton.addEventListener(DLCycleButton.SelectedItemChanged.class, (src, e) -> { + // e.item() is Optional; e.index() is the selected index (or -1) + DLCycleButton.SelectedItemChanged te = (DLCycleButton.SelectedItemChanged) e; + if (te.index() == 3) { // Custom selected + minSpeedPicker.visible.set(true); + maxTiltPicker.visible.set(true); + IntensityPicker.visible.set(true); + minSpeedLabel.visible.set(true); + maxTiltLabel.visible.set(true); + IntensityLabel.visible.set(true); + DirectionButton.visible.set(true); + TiltDirectionLabel.visible.set(true); + } + else { + minSpeedPicker.visible.set(false); + maxTiltPicker.visible.set(false); + IntensityPicker.visible.set(false); + minSpeedLabel.visible.set(false); + maxTiltLabel.visible.set(false); + IntensityLabel.visible.set(false); + DirectionButton.visible.set(false); + TiltDirectionLabel.visible.set(false); + } + + return false; // return true to stop further propagation if needed + }); + + DLCycleButton AccelerationSettingbutton = addComponent( + new DLCycleButton(275, 40, 80, 20){ + + } + ); + AccelerationSettingbutton.items.add(Component.translatable("realism.gui.accel.setting.none").getString()); + AccelerationSettingbutton.items.add(Component.translatable("realism.gui.accel.setting.standard").getString()); + AccelerationSettingbutton.items.add(Component.translatable("realism.gui.accel.setting.custom").getString()); + + AccelerationSettingbutton.textFormat.set((ITextFormatter>) (src -> + TextUtils.text(src.selectedItem.get().map(Object::toString).orElse("")) + .withStyle(src.text.get().getStyle()) + )); + + AccelerationSettingbutton.selectedIndex.set(cs.as.ordinal()); + + DLRichTextLabel AccelerationSettingLabel = addComponent( + new DLRichTextLabel(165, 45, 105, 20){ + @Override + public void renderFrontLayer(DLGuiGraphics g,double mousex, double mousey, Rectangle bounds){ + super.renderFrontLayer(g,mousex,mousey,bounds); + int screenW = (int)getWindowManager().getScreenWidth(); + if (isMouseOver(mousex,mousey)) { + GuiUtils.drawTooltip(g,g.defaultFont(), (int) mousex, (int) mousey, + List.of( + TextUtils.text(Component.translatable("realism.gui.accel.tooltip.header").getString()), + TextUtils.text(Component.translatable("realism.gui.accel.tooltip.none").getString()), + TextUtils.text(Component.translatable("realism.gui.accel.tooltip.standard1").getString()), + TextUtils.text(Component.translatable("realism.gui.accel.tooltip.standard2").getString()), + TextUtils.text(Component.translatable("realism.gui.accel.tooltip.custom").getString()) + ), + screenW + ); + } + } + } + ); + AccelerationSettingLabel.text.get().set(Component.translatable("realism.gui.accel.label").getString()); + + DLNumberPicker AccelerationPicker = addComponent( + new DLNumberPicker(275, 70, 80, 20) + ); + AccelerationPicker.min.set((double) 0); + if(RealismConfig.COMMON.AllowBiggerValuesTrains.get()){ + AccelerationPicker.max.set(50.0); + } + else { + AccelerationPicker.max.set((double) AllConfigs.server().trains.trainAcceleration.getF()); + } + AccelerationPicker.format.set(new INumberFormatAdapter.DecimalNumberFormat(1)); + AccelerationPicker.value.set((double) cs.customAcceleration); + AccelerationPicker.visible.set(cs.as == TrainSettings.accelerationSetting.CUSTOM); + AccelerationPicker.step.set(0.1); + + + + DLRichTextLabel AccelerationLabel = addComponent( + new DLRichTextLabel(195, 75, 105, 20) + ); + AccelerationLabel.text.get().set(Component.translatable("realism.gui.accel.value.label").getString()); + AccelerationLabel.visible.set(cs.as == TrainSettings.accelerationSetting.CUSTOM); + + DLRichTextLabel CurrentAccelerationLabel = addComponent( + new DLRichTextLabel(195, 100, 200, 20) + ); + CurrentAccelerationLabel.text.get().set(Component.translatable("realism.gui.accel.default", AllConfigs.server().trains.trainAcceleration.get().toString()).getString()); + CurrentAccelerationLabel.visible.set(cs.as == TrainSettings.accelerationSetting.CUSTOM); + + + + + + AccelerationSettingbutton.addEventListener(DLCycleButton.SelectedItemChanged.class, (src, e) -> { + // e.item() is Optional; e.index() is the selected index (or -1) + DLCycleButton.SelectedItemChanged te = (DLCycleButton.SelectedItemChanged) e; + if (te.index() == 2) { // Custom selected + AccelerationPicker.visible.set(true); + AccelerationLabel.visible.set(true); + CurrentAccelerationLabel.visible.set(true); + } + else{ + AccelerationPicker.visible.set(false); + AccelerationLabel.visible.set(false); + CurrentAccelerationLabel.visible.set(false); + } + return false; // return true to stop further propagation if needed + + }); + DLButton ConfirmButton = addComponent( + new DLButton(180, 175, 80, 20) + ); + ConfirmButton.text.set(Component.translatable("realism.button.confirm")); + ConfirmButton.visible.set(true); + ConfirmButton.addEventListener(DLGuiStandardEvents.ClickEvent.class, (evt,s) -> { + TrainSettings ts = new TrainSettings(); + int o = (int) Tiltbutton.selectedIndex.get(); + if (o == 0) { + ts.ts = TrainSettings.tiltSetting.NONE; + } else if (o == 1) { + ts.ts = TrainSettings.tiltSetting.PASSIVE; + } else if (o == 2) { + ts.ts = TrainSettings.tiltSetting.ACTIVE; + } else if (o == 3) { + ts.ts = TrainSettings.tiltSetting.CUSTOM; + ts.customMinSpeed = minSpeedPicker.value.get().doubleValue(); + ts.customMaxTilt = maxTiltPicker.value.get().floatValue(); + ts.customTiltIntensity = IntensityPicker.value.get().floatValue(); + } + + int object = (int) AccelerationSettingbutton.selectedIndex.get(); + if (object == 0) { + ts.as = TrainSettings.accelerationSetting.NONE; + } else if (object == 1) { + ts.as = TrainSettings.accelerationSetting.REALISTIC; + } else if (object == 2) { + ts.as = TrainSettings.accelerationSetting.CUSTOM; + ts.customAcceleration = AccelerationPicker.value.get().doubleValue(); + } + if (((int) DirectionButton.selectedIndex.get())==0){ + ts.Inside = true; + }else{ + ts.Inside = false; + } + Rtrain.realism$setSettings(ts); + RNetworking.sendToServer(new TrainSettingsSavePacket(ts,((Train)Rtrain).id)); + closeWindow(); + + return false; + }); + + + + } + +} diff --git a/common/src/main/java/net/Realism/mixin/AbstractContraptionEntityMixin.java b/common/src/main/java/net/Realism/mixin/AbstractContraptionEntityMixin.java deleted file mode 100644 index 0b66df2..0000000 --- a/common/src/main/java/net/Realism/mixin/AbstractContraptionEntityMixin.java +++ /dev/null @@ -1,29 +0,0 @@ -package net.Realism.mixin; - -import com.simibubi.create.content.contraptions.AbstractContraptionEntity; -import com.simibubi.create.content.trains.entity.Carriage; -import com.simibubi.create.content.trains.entity.CarriageContraptionEntity; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -@Mixin(value = AbstractContraptionEntity.class,remap = false) -public class AbstractContraptionEntityMixin { - - - - @Inject(method = "stopControlling", at = @At("TAIL")) - private void AfterStopControlling(CallbackInfo ci) { - //noinspection ConstantValue - if(this.getClass().equals(CarriageContraptionEntity.class)){ - CarriageContraptionEntity carriageContraptionEntity = (CarriageContraptionEntity)(Object)this; - Carriage carriage = carriageContraptionEntity.getCarriage(); - if(carriage != null && carriage.train instanceof net.Realism.Interfaces.ITrainInterface RTrain){ - if(RTrain.realism$getETCS() != null){ - RTrain.realism$getETCS().stop(); - } - } - } - } -} diff --git a/common/src/main/java/net/Realism/mixin/CarriageDimensionalEntityMixin.java b/common/src/main/java/net/Realism/mixin/CarriageDimensionalEntityMixin.java new file mode 100644 index 0000000..3793ffb --- /dev/null +++ b/common/src/main/java/net/Realism/mixin/CarriageDimensionalEntityMixin.java @@ -0,0 +1,217 @@ +package net.Realism.mixin; + +import com.simibubi.create.content.trains.entity.Carriage; +import com.simibubi.create.content.trains.entity.CarriageBogey; +import com.simibubi.create.content.trains.entity.CarriageContraptionEntity; +import com.simibubi.create.content.trains.entity.TravellingPoint; +import com.simibubi.create.content.trains.graph.TrackEdge; +import com.simibubi.create.content.trains.track.BezierConnection; +import com.simibubi.create.infrastructure.config.AllConfigs; +import net.Realism.Interfaces.IOrientedContraptionEntity; +import net.Realism.Interfaces.ITrainInterface; +import net.Realism.RNetworking; +import net.Realism.config.RealismConfig; +import net.Realism.network.RollSyncPacket; +import net.createmod.catnip.data.Couple; +import net.createmod.catnip.math.VecHelper; +import net.minecraft.util.Mth; +import net.minecraft.world.phys.Vec3; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.Objects; + +/** + * Mixin for Carriage.DimensionalCarriageEntity to calculate banking angles + */ +@Mixin(targets = "com.simibubi.create.content.trains.entity.Carriage$DimensionalCarriageEntity", remap = false) +public class CarriageDimensionalEntityMixin { + + + @Shadow + @Final + Carriage this$0; // The outer Carriage instance + + @Shadow + public Couple rotationAnchors; + + /** + * Inject banking calculation into alignEntity method + */ + @Inject(method = "alignEntity", at = @At("TAIL")) + private void calculateAndApplyBanking(CarriageContraptionEntity entity, CallbackInfo ci) { + // Check if banking is enabled + if (!RealismConfig.CLIENT.enableBanking.get() || !RealismConfig.COMMON.GlobalBankingEnable.get()) { + return; + } + + // Ensure we have valid rotation anchors + if (rotationAnchors.either(Objects::isNull)) { + return; + } + + // Update previous roll + IOrientedContraptionEntity orientedEntity = (IOrientedContraptionEntity) entity; + orientedEntity.realism$setPrevRoll(orientedEntity.realism$getRoll()); + + // Calculate banking + if (!(this$0.train instanceof ITrainInterface Rtrain)){ + return; + } + float banking = realism$calculateBanking(entity,Rtrain); + orientedEntity.realism$setRoll(banking); + if(!entity.level().isClientSide) { + RNetworking.sendToAll(new RollSyncPacket(orientedEntity.realism$getRoll(), orientedEntity.realism$getPrevRoll(), orientedEntity.getuid())); + } + // For first position update, ensure prevRoll matches roll + if (entity.firstPositionUpdate) { + orientedEntity.realism$setPrevRoll(banking); + if(!entity.level().isClientSide){ + RNetworking.sendToAll(new RollSyncPacket(orientedEntity.realism$getRoll(),orientedEntity.realism$getPrevRoll(),orientedEntity.getuid())); + } + } + + } + + /** + * Calculate banking angle based on track curvature and train speed + */ + @Unique + private float realism$calculateBanking(CarriageContraptionEntity entity,ITrainInterface Rtrain) { + // Get the leading bogey + CarriageBogey leadingBogey = this$0.leadingBogey(); + if (leadingBogey == null) { + return 0f; + } + + // Get the leading travelling point + TravellingPoint leadingPoint = leadingBogey.leading(); + if (leadingPoint == null || leadingPoint.edge == null) { + return 0f; + } + + TrackEdge edge = leadingPoint.edge; + + // Only apply banking on curves (BezierConnections) + if (!edge.isTurn()) { + return(0f); + } + + BezierConnection turn = edge.getTurn(); + if (turn == null || turn.normals == null) { + return 0f; + } + + // Calculate normalized position along the curve (0.0 to 1.0) + double edgeLength = edge.getLength(); + double t = edgeLength == 0 ? 0.5 : leadingPoint.position / edgeLength; + t = Mth.clamp(t, 0.0, 1.0); + + // Get interpolated track normal at current position + Vec3 trackNormal = realism$getTrackNormalAt(turn, t); + if (trackNormal == null) { + return 0f; + } + + // Get train's forward direction + Vec3 positionVec = rotationAnchors.getFirst(); + Vec3 coupledVec = rotationAnchors.getSecond(); + + double diffX = positionVec.x - coupledVec.x; + double diffY = positionVec.y - coupledVec.y; + double diffZ = positionVec.z - coupledVec.z; + + Vec3 forward = new Vec3(diffX, diffY, diffZ).normalize(); + Vec3 worldUp = new Vec3(0, 1, 0); + Vec3 right = forward.cross(worldUp).normalize(); + + // Project track normal onto right vector to get banking + double bankingDot = trackNormal.dot(right); + bankingDot = Mth.clamp(bankingDot, -1.0, 1.0); + float bankingAngle = (float) Math.toDegrees(Math.asin(bankingDot)); + + // Apply intensity multiplier + float intensity = 1; + bankingAngle *= 0.02f; + bankingAngle *= intensity; + if (Rtrain.realism$getSettings().isTiltPassive()){ + bankingAngle *= 2f; + } + if (Rtrain.realism$getSettings().isTiltActive()){ + bankingAngle *= 4.5f; + } + if (Rtrain.realism$getSettings().isTiltCustom()){ + bankingAngle *= Rtrain.realism$getSettings().customTiltIntensity; + } + + double speed = this$0.train.speed*20*3.6f; + float minSpeed = 80f; + if(Rtrain.realism$getSettings().isTiltCustom()){ + minSpeed = Rtrain.realism$getSettings().customMinSpeed.floatValue(); + } + if(Rtrain.realism$getSettings().isTiltActive()){ + minSpeed = 1/2 *minSpeed; + } + float maxSpeed = AllConfigs.server().trains.trainTopSpeed.getF(); + float speedFactor; + if (Math.abs(speed) <= minSpeed) { + speedFactor = 0f; + } else if (speed >= maxSpeed) { + speedFactor = 1f; + } else { + float range = maxSpeed - minSpeed; + if (range <= 0f) { + speedFactor = 1f; + } else { + speedFactor =(float) ((Math.abs(speed) - minSpeed) / range); + speedFactor = Mth.clamp(speedFactor, 0f, 1f); + } + } + + + float targetBanking = bankingAngle * speedFactor; + + // Clamp to maximum banking angle + float maxAngle = 15; + if(Rtrain.realism$getSettings().isTiltCustom()){ + maxAngle = Rtrain.realism$getSettings().customMaxTilt; + } + targetBanking = Mth.clamp(targetBanking, -maxAngle, maxAngle); + if(Rtrain.realism$getSettings().isTiltNone() || !(Rtrain.realism$getSettings().isInside())){ + return targetBanking; + } + else{ + return -targetBanking; + } + } + + /** + * Interpolate track normal at position t along the curve + */ + @Unique + private Vec3 realism$getTrackNormalAt(BezierConnection turn, double t) { + if (turn.normals == null) { + return null; + } + + Vec3 normal1 = turn.axes.getFirst(); + Vec3 normal2 = turn.axes.getSecond(); + + // Linear interpolation between start and end normals + Vec3 interpolated = VecHelper.lerp((float) t, normal1, normal2); + + // Normalize to ensure it's a unit vector + double length = interpolated.length(); + if (length < 0.001) { + return new Vec3(0, 1, 0); // Default to up + } + + return interpolated.normalize(); + } +} + diff --git a/common/src/main/java/net/Realism/mixin/OrientedContraptionEntityMixin.java b/common/src/main/java/net/Realism/mixin/OrientedContraptionEntityMixin.java new file mode 100644 index 0000000..e3ef660 --- /dev/null +++ b/common/src/main/java/net/Realism/mixin/OrientedContraptionEntityMixin.java @@ -0,0 +1,92 @@ +package net.Realism.mixin; + +import com.simibubi.create.content.contraptions.OrientedContraptionEntity; +import net.Realism.Interfaces.IOrientedContraptionEntity; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.util.Mth; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.UUID; + +/** + * Mixin to add roll (banking) functionality to OrientedContraptionEntity + */ +@Mixin(value = OrientedContraptionEntity.class, remap = false) +public abstract class OrientedContraptionEntityMixin implements IOrientedContraptionEntity { + + @Unique + private float realism$roll = 0; + + @Unique + private float realism$prevRoll = 0; + + @Unique + public UUID getuid(){ + return ((net.minecraft.world.entity.Entity)(Object)this).getUUID(); + } + + @Override + @Unique + public float realism$getRoll() { + return realism$roll; + } + + @Override + @Unique + public void realism$setRoll(float roll) { + this.realism$roll = roll; + } + + @Override + @Unique + public float realism$getPrevRoll() { + return realism$prevRoll; + } + + @Override + @Unique + public void realism$setPrevRoll(float prevRoll) { + this.realism$prevRoll = prevRoll; + } + + @Override + @Unique + public float realism$getViewRoll(float partialTicks) { + return Mth.lerp(partialTicks, realism$prevRoll, realism$roll); + } + + /** + * Save roll to NBT + */ + @Inject(method = "writeAdditional", at = @At("TAIL")) + protected void writeRollToNBT(CompoundTag compound, boolean spawnPacket, CallbackInfo ci) { + compound.putFloat("Roll", realism$roll); + } + + /** + * Load roll from NBT + */ + @Inject(method = "readAdditional", at = @At("TAIL")) + protected void readRollFromNBT(CompoundTag compound, boolean spawnPacket, CallbackInfo ci) { + realism$roll = compound.getFloat("Roll"); + realism$prevRoll = realism$roll; + } + + /** + * Inject roll into ContraptionRotationState + */ +// @Inject(method = "getRotationState", at = @At("RETURN"), cancellable = true) +// public void injectRollIntoRotationState(@NotNull CallbackInfoReturnable cir) { +// AbstractContraptionEntity.ContraptionRotationState crs = cir.getReturnValue(); +// +// // Set xRotation to our roll value +// ((ContraptionRotationStateAccessor) crs).setXRotation(realism$roll); +// +// cir.setReturnValue(crs); +// } +} + diff --git a/common/src/main/java/net/Realism/mixin/ScheduleRuntimeMixin.java b/common/src/main/java/net/Realism/mixin/ScheduleRuntimeMixin.java new file mode 100644 index 0000000..2348abc --- /dev/null +++ b/common/src/main/java/net/Realism/mixin/ScheduleRuntimeMixin.java @@ -0,0 +1,173 @@ +package net.Realism.mixin; + + +import com.simibubi.create.content.trains.entity.Train; +import com.simibubi.create.content.trains.graph.DiscoveredPath; +import com.simibubi.create.content.trains.schedule.Schedule; +import com.simibubi.create.content.trains.schedule.ScheduleRuntime; +import net.Realism.Interfaces.IScheduleRuntimeMixin; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.level.Level; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.List; + +@Mixin(value = ScheduleRuntime.class, remap = false) +public abstract class ScheduleRuntimeMixin implements IScheduleRuntimeMixin { + @Shadow + private static final int TBD = -1; + @Shadow + Schedule schedule; + @Shadow + public boolean paused; + @Shadow + public Train train; + @Shadow + public int ticksInTransit; + @Shadow + public List predictionTicks; + @Shadow + public int currentEntry; + + + @Shadow + protected abstract boolean checkEndOfScheduleReached(); + + @Unique + public long departureDate; + @Unique + public boolean shouldUpdateDepartureDate; + @Unique + public long expectedArrivalDate; + @Unique + public int lastScheduledDepartureDate = 0; + @Unique + public boolean dontCheck = false; + + + @Inject(method = "tick",at=@At("HEAD")) + public void tickDate(Level level, CallbackInfo ci) { + if (schedule == null) + return; + if (paused) + return; + if (train.derailed) + return; + if (train.navigation.destination != null) { + ticksInTransit++; + if(shouldUpdateDepartureDate){ + long x = level.getGameTime(); + departureDate = x; + shouldUpdateDepartureDate = false; + } + return; + } + + if(checkEndOfScheduleReached() || shouldUpdateDepartureDate){ + long x = level.getGameTime(); + departureDate = x; + shouldUpdateDepartureDate = false; + } + + } + + @Inject(method = "setSchedule", at=@At("TAIL")) + public void setSchedule(Schedule schedule, boolean auto, CallbackInfo ci) { + shouldUpdateDepartureDate = true; + setDontCheck(false); + } + + @Inject(method = "reset", at=@At("TAIL")) + public void reset(CallbackInfo ci) { + shouldUpdateDepartureDate = true; + dontCheck = false; + } + + @Override + public long getDepartureDate() { + return departureDate; + } + + @Override + public long getExpectedArrivalDate() { + return expectedArrivalDate; + } + + @Override + public int getLastScheduledDepartureDate() { + return lastScheduledDepartureDate; + } + + @Override + public void setLastScheduledDepartureDate(int date) { + lastScheduledDepartureDate = date; + } + + @Override + public void setExpectedArrivalDate(long date) { + expectedArrivalDate = date; + } + + @Override + public boolean dontCheck() { + return dontCheck; + } + @Override + public void setDontCheck(boolean dontCheck) { + this.dontCheck = dontCheck; + } + + + @Inject(method = "startCurrentInstruction", at=@At("RETURN")) + public void startCurrentInstruction(Level level, CallbackInfoReturnable cir) { + DiscoveredPath nextPath = cir.getReturnValue(); + setDontCheck(false); + shouldUpdateDepartureDate = true; + int etaTicksToDestination; + int learned = (predictionTicks != null && predictionTicks.size() > currentEntry) + ? predictionTicks.get(currentEntry) + : TBD; // TBD == -1 + + if (learned >= 0) { + // Use learned transit time for this entry + etaTicksToDestination = learned; + } else if (nextPath != null) { + // Fallback: estimate from path distance and speed heuristic (same as submitPredictions) + double speed = Math.min(train.throttle * train.maxSpeed(), (train.maxSpeed() + train.maxTurnSpeed()) / 2.0); + // Protect against divide-by-zero; if the speed is ~0, report TBD + if (speed > 1e-6) { + etaTicksToDestination = (int) (Math.abs(nextPath.distance) / speed) * 2; // matches submitPredictions() + } else { + etaTicksToDestination = TBD; // can’t estimate right now + } + } else { + etaTicksToDestination = TBD; // no path available + } + expectedArrivalDate = (etaTicksToDestination + level.getGameTime()); + } + + @Inject(method = "write", at=@At("RETURN"), cancellable = true) + public void write(CallbackInfoReturnable cir) { + CompoundTag tag = cir.getReturnValue(); + tag.putLong("departureDate", departureDate); + tag.putLong("expectedArrivalDate", expectedArrivalDate); + tag.putInt("lastScheduledDepartureDate", lastScheduledDepartureDate); + tag.putBoolean("dontCheck", dontCheck); + cir.setReturnValue(tag); + + } + + @Inject(method = "read", at=@At("TAIL")) + public void read(CompoundTag tag, CallbackInfo ci) { + departureDate = tag.getLong("departureDate"); + expectedArrivalDate = tag.getLong("expectedArrivalDate"); + lastScheduledDepartureDate = tag.getInt("lastScheduledDepartureDate"); + dontCheck = tag.getBoolean("dontCheck"); + } +} diff --git a/common/src/main/java/net/Realism/mixin/TrainMixin.java b/common/src/main/java/net/Realism/mixin/TrainMixin.java index 9e0dc95..4230c15 100644 --- a/common/src/main/java/net/Realism/mixin/TrainMixin.java +++ b/common/src/main/java/net/Realism/mixin/TrainMixin.java @@ -8,8 +8,10 @@ import com.simibubi.create.content.trains.schedule.ScheduleRuntime; import com.simibubi.create.infrastructure.config.AllConfigs; import net.Realism.Interfaces.ITrainInterface; +import net.Realism.RNetworking; import net.Realism.config.RealismConfig; -import net.Realism.debug.RealismDebuger; +import net.Realism.network.TrainSettingsUpdatePacket; +import net.Realism.trains.TrainSettings; import net.Realism.trains.etcs.ETCS; import net.minecraft.nbt.CompoundTag; import org.spongepowered.asm.mixin.Mixin; @@ -37,6 +39,14 @@ public abstract class TrainMixin implements ITrainInterface { @Shadow public ScheduleRuntime runtime; @Shadow public TrainStatus status; @Shadow public boolean manualTick; + + @Unique + TrainSettings realism$Settings = new TrainSettings(); + @Unique + public void realism$setSettings(TrainSettings tiltSetting) {this.realism$Settings = tiltSetting;} + @Unique + public TrainSettings realism$getSettings() {return this.realism$Settings;} + @Unique public ETCS realism$etcs = null; @@ -60,13 +70,18 @@ private void tick(CallbackInfo ci) { } @Inject(method = "write", at = @At(value = "RETURN"), cancellable = true) private void write(DimensionPalette dimensions, CallbackInfoReturnable cir) { + CompoundTag tag = cir.getReturnValue(); if (this.realism$etcs != null) { - CompoundTag tag = cir.getReturnValue(); tag.put("ETCS", this.realism$etcs.saveToNBT()); - cir.setReturnValue(tag); + } + if (this.realism$Settings != null) { + cir.getReturnValue().put("trainSettings", this.realism$Settings.savetoNBT()); + } + cir.setReturnValue(tag); } + @Inject(method = "read", at = @At("RETURN"), cancellable = true) private static void onTrainRead(CompoundTag tag, Map trackNetworks, DimensionPalette dimensions, CallbackInfoReturnable cir) { @@ -76,6 +91,12 @@ private static void onTrainRead(CompoundTag tag, Map trackNetw ((ITrainInterface)original).realism$setETCS(new ETCS(original)); ((ITrainInterface)original).realism$getETCS().loadFromNBT(customDataTag); } + if (tag.contains("trainSettings")) { + CompoundTag tiltTag = tag.getCompound("trainSettings"); + ((ITrainInterface)original).realism$setSettings(TrainSettings.fromNBT(tiltTag)); + RNetworking.sendToAll(new TrainSettingsUpdatePacket(((ITrainInterface)original).realism$getSettings(), original.id)); + } + } /** @@ -85,28 +106,33 @@ private static void onTrainRead(CompoundTag tag, Map trackNetw @Overwrite() public float acceleration() { - if (!RealismConfig.COMMON.EnableCustomTrainAcceleration.get()) { + if (!RealismConfig.COMMON.EnableCustomTrainAcceleration.get() || this.realism$Settings.isAccelerationNone()) { return (fuelTicks > 0 ? AllConfigs.server().trains.poweredTrainAcceleration.getF() : AllConfigs.server().trains.trainAcceleration.getF()) / 400; } - float ac = (fuelTicks > 0 ? AllConfigs.server().trains.poweredTrainAcceleration.getF() : AllConfigs.server().trains.trainAcceleration.getF()) / 400; - int locomotives = 0; - for (Carriage carriage : carriages) { - if (carriage.presentConductors.either(b -> b)) { - locomotives++; + + if (this.realism$Settings.isAccelerationRealistic()) { + float ac = (fuelTicks > 0 ? AllConfigs.server().trains.poweredTrainAcceleration.getF() : AllConfigs.server().trains.trainAcceleration.getF()) / 400; + int locomotives = 0; + for (Carriage carriage : carriages) { + if (carriage.presentConductors.either(b -> b)) { + locomotives++; + } } + if (locomotives == 0) locomotives = 1; + float reduce = (float) ((carriages.size() * 0.0002f * RealismConfig.COMMON.CustomTrainAccelerationMultiplyer.get()) / locomotives); + ac -= reduce; + if (ac < 0) ac = 0.0001f; + + + + return ac; } - if (locomotives == 0) locomotives = 1; - float reduce = (float) ((carriages.size() * 0.0002f * RealismConfig.COMMON.CustomTrainAccelerationMultiplyer.get() ) / locomotives); - ac -= reduce; - if (ac < 0) ac = 0.0001f; - - if(RealismConfig.CLIENT.debugMode.get()) { - // Send data to the debug logger instead of printing directly - RealismDebuger.getInstance().addDebugInfo(ac*400, carriages.size(), locomotives ); + else{ + return (float) this.realism$Settings.customAcceleration/400; } - return ac; + } diff --git a/common/src/main/java/net/Realism/mixin/CarriageContraptionEntityMixin.java b/common/src/main/java/net/Realism/mixin/client/CarriageContraptionEntityMixin.java similarity index 94% rename from common/src/main/java/net/Realism/mixin/CarriageContraptionEntityMixin.java rename to common/src/main/java/net/Realism/mixin/client/CarriageContraptionEntityMixin.java index c823677..5af8001 100644 --- a/common/src/main/java/net/Realism/mixin/CarriageContraptionEntityMixin.java +++ b/common/src/main/java/net/Realism/mixin/client/CarriageContraptionEntityMixin.java @@ -1,4 +1,4 @@ -package net.Realism.mixin; +package net.Realism.mixin.client; import com.simibubi.create.content.trains.entity.Carriage; import com.simibubi.create.content.trains.entity.CarriageContraptionEntity; @@ -15,7 +15,8 @@ @Mixin(value = CarriageContraptionEntity.class) public class CarriageContraptionEntityMixin { - @Shadow private Carriage carriage; + @Shadow + private Carriage carriage; @Inject(method = "startControlling", at = @At("RETURN"), cancellable = true) private void afterStartControlling(BlockPos controlsLocalPos, Player player, CallbackInfoReturnable cir) { @@ -30,6 +31,4 @@ private void afterStartControlling(BlockPos controlsLocalPos, Player player, Cal } } - - -} +} \ No newline at end of file diff --git a/common/src/main/java/net/Realism/mixin/client/StationScreenMixin.java b/common/src/main/java/net/Realism/mixin/client/StationScreenMixin.java new file mode 100644 index 0000000..4d83b50 --- /dev/null +++ b/common/src/main/java/net/Realism/mixin/client/StationScreenMixin.java @@ -0,0 +1,71 @@ +package net.Realism.mixin.client; + +import com.simibubi.create.content.trains.station.AbstractStationScreen; +import com.simibubi.create.content.trains.station.GlobalStation; +import com.simibubi.create.content.trains.station.StationBlockEntity; +import com.simibubi.create.content.trains.station.StationScreen; +import com.simibubi.create.foundation.gui.widget.IconButton; +import de.mrjulsen.mcdragonlib.client.gui.widgets.base.DLWindow; +import net.Realism.Interfaces.ITrainInterface; +import net.Realism.gui.TrainSettingsGui; +import net.Realism.mixin.mixinaccesors.ScreenAccessor; +import net.Realism.util.AllRealismIcons; +import net.minecraft.client.gui.components.Button; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(value = StationScreen.class, remap = false) +public abstract class StationScreenMixin extends AbstractStationScreen { + + @Shadow + private IconButton disassembleTrainButton; + + @Unique + private IconButton realism$settingsButton; + + @Unique + private Button realism$disassembleButton; + + @Unique + private boolean realism$updaterAdded = false; + + + + public StationScreenMixin(StationBlockEntity be, GlobalStation station) { + super(be, station); + } + + @Inject(method = "init", at = @At(value = "INVOKE", target = "Lcom/simibubi/create/content/trains/station/StationScreen;tickTrainDisplay()V"), remap = true) + private void realism$onInit(CallbackInfo ci) { + // Create settings button with placeholder position; will be positioned next to Disassemble when found + realism$settingsButton = new IconButton(this.disassembleTrainButton.getX()+this.disassembleTrainButton.getWidth()+2, this.disassembleTrainButton.getY(), 16, 16,AllRealismIcons.SETTINGS_ICON) + .withCallback(this::onSettingsPressed); + realism$settingsButton.visible = false; // hidden until we find a train/disassemble button + ((ScreenAccessor)(Object)this).realism$invokeAddRenderableWidget(realism$settingsButton); + + // Add a lightweight per-frame updater without injecting into render() + if (!realism$updaterAdded) { + ((ScreenAccessor)(Object)this).realism$invokeAddRenderableOnly((graphics, mouseX, mouseY, partialTicks) -> alignAndToggleSettings()); + realism$updaterAdded = true; + } + + alignAndToggleSettings(); + } + + + + @Unique + private void alignAndToggleSettings() { + realism$settingsButton.visible = this.disassembleTrainButton.visible && this.disassembleTrainButton.active; + } + @Unique + private void onSettingsPressed() { + this.onClose(); + if(displayedTrain.get() instanceof ITrainInterface Rtrain) + DLWindow.openWindow((manager) -> new TrainSettingsGui(manager, Rtrain)); + } +} diff --git a/common/src/main/java/net/Realism/mixin/TrackPlacementMixin.java b/common/src/main/java/net/Realism/mixin/client/TrackPlacementMixin.java similarity index 92% rename from common/src/main/java/net/Realism/mixin/TrackPlacementMixin.java rename to common/src/main/java/net/Realism/mixin/client/TrackPlacementMixin.java index be11e67..1e3fbb8 100644 --- a/common/src/main/java/net/Realism/mixin/TrackPlacementMixin.java +++ b/common/src/main/java/net/Realism/mixin/client/TrackPlacementMixin.java @@ -1,10 +1,10 @@ // common/src/main/java/net/Realism/mixin/TrackPlacementMixin.java -package net.Realism.mixin; +package net.Realism.mixin.client; import com.simibubi.create.content.trains.track.BezierConnection; import com.simibubi.create.content.trains.track.TrackPlacement; import net.Realism.Interfaces.ITrackPlacementInterface; -import net.Realism.mixinaccesors.PlacementInfoAccessor; +import net.Realism.mixin.mixinaccesors.PlacementInfoAccessor; import net.minecraft.world.phys.Vec3; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; diff --git a/common/src/main/java/net/Realism/mixinaccesors/PlacementInfoAccessor.java b/common/src/main/java/net/Realism/mixin/mixinaccesors/PlacementInfoAccessor.java similarity index 90% rename from common/src/main/java/net/Realism/mixinaccesors/PlacementInfoAccessor.java rename to common/src/main/java/net/Realism/mixin/mixinaccesors/PlacementInfoAccessor.java index 1f174b5..84c78eb 100644 --- a/common/src/main/java/net/Realism/mixinaccesors/PlacementInfoAccessor.java +++ b/common/src/main/java/net/Realism/mixin/mixinaccesors/PlacementInfoAccessor.java @@ -1,4 +1,4 @@ -package net.Realism.mixinaccesors; +package net.Realism.mixin.mixinaccesors; import com.simibubi.create.content.trains.track.BezierConnection; import com.simibubi.create.content.trains.track.TrackPlacement; diff --git a/common/src/main/java/net/Realism/mixin/mixinaccesors/ScreenAccessor.java b/common/src/main/java/net/Realism/mixin/mixinaccesors/ScreenAccessor.java new file mode 100644 index 0000000..80fb7fa --- /dev/null +++ b/common/src/main/java/net/Realism/mixin/mixinaccesors/ScreenAccessor.java @@ -0,0 +1,17 @@ +package net.Realism.mixin.mixinaccesors; + +import net.minecraft.client.gui.components.Renderable; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.client.gui.narration.NarratableEntry; +import net.minecraft.client.gui.screens.Screen; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +@Mixin(value = Screen.class) +public interface ScreenAccessor { + @Invoker("addRenderableWidget") + T realism$invokeAddRenderableWidget(T widget); + + @Invoker("addRenderableOnly") + Renderable realism$invokeAddRenderableOnly(Renderable renderable); +} diff --git a/common/src/main/java/net/Realism/mixinaccesors/TramSignDataAccessor.java b/common/src/main/java/net/Realism/mixin/mixinaccesors/TramSignDataAccessor.java similarity index 93% rename from common/src/main/java/net/Realism/mixinaccesors/TramSignDataAccessor.java rename to common/src/main/java/net/Realism/mixin/mixinaccesors/TramSignDataAccessor.java index 663a1fe..9014cc1 100644 --- a/common/src/main/java/net/Realism/mixinaccesors/TramSignDataAccessor.java +++ b/common/src/main/java/net/Realism/mixin/mixinaccesors/TramSignDataAccessor.java @@ -1,4 +1,4 @@ -package net.Realism.mixinaccesors; +package net.Realism.mixin.mixinaccesors; import net.minecraft.core.BlockPos; diff --git a/common/src/main/java/net/Realism/network/RollSyncPacket.java b/common/src/main/java/net/Realism/network/RollSyncPacket.java new file mode 100644 index 0000000..ca74cfb --- /dev/null +++ b/common/src/main/java/net/Realism/network/RollSyncPacket.java @@ -0,0 +1,47 @@ +package net.Realism.network; + +import net.Realism.util.S2CPacket; +import net.minecraft.client.Minecraft; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.world.entity.Entity; + +import java.util.UUID; + + +public class RollSyncPacket implements S2CPacket { + private final float roll; + private final float prevRoll; + private final UUID UUID; + + public RollSyncPacket(float roll, float prevRoll, UUID UUID) { + this.roll = roll; + this.prevRoll = prevRoll; + this.UUID = UUID; + } + public static RollSyncPacket read(FriendlyByteBuf buf) { + return new RollSyncPacket(buf.readFloat(), buf.readFloat(),buf.readUUID()); + } + @Override + public void write(FriendlyByteBuf buf) { + buf.writeFloat(roll); + buf.writeFloat(prevRoll); + buf.writeUUID(UUID); + } + + @Override + public void handle(Minecraft mc) { + mc.execute(() -> { + if (mc.level == null) return; + for (Entity entity : mc.level.entitiesForRendering()) { + if (entity.getUUID().equals(UUID)) { + if (entity instanceof net.Realism.Interfaces.IOrientedContraptionEntity orientedEntity) { + orientedEntity.realism$setRoll(roll); + orientedEntity.realism$setPrevRoll(prevRoll); + } + break; + } + } + }); + + } +} diff --git a/common/src/main/java/net/Realism/network/TrainSettingsSavePacket.java b/common/src/main/java/net/Realism/network/TrainSettingsSavePacket.java new file mode 100644 index 0000000..7b3f8ae --- /dev/null +++ b/common/src/main/java/net/Realism/network/TrainSettingsSavePacket.java @@ -0,0 +1,50 @@ +package net.Realism.network; + +import com.simibubi.create.Create; +import com.simibubi.create.content.trains.entity.Train; +import net.Realism.Interfaces.ITrainInterface; +import net.Realism.RNetworking; +import net.Realism.trains.TrainSettings; +import net.Realism.util.C2SPacket; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.server.level.ServerPlayer; + +import java.util.UUID; + +public class TrainSettingsSavePacket implements C2SPacket { + public final TrainSettings trains; + public final UUID uuid; + + public TrainSettingsSavePacket(TrainSettings trains, UUID uuid) { + this.trains = trains; + this.uuid = uuid; + } + + + @Override + public void write(FriendlyByteBuf buf) { + buf.writeNbt(this.trains.savetoNBT()); + buf.writeUUID(this.uuid); + + } + + public static TrainSettingsSavePacket read(FriendlyByteBuf buf) { + return new TrainSettingsSavePacket(TrainSettings.fromNBT(buf.readNbt()), buf.readUUID()); + } + + @Override + public void handle(ServerPlayer player) { + player.getServer().execute(() -> { + player.getServer().levelKeys().iterator().forEachRemaining(levelResourceKey -> { + if (player.getServer().getLevel(levelResourceKey) == null) return; + Train train = Create.RAILWAYS.sided(player.getServer().getLevel(levelResourceKey)).trains.get(this.uuid); + if (train != null) { + ITrainInterface trainInterface = (ITrainInterface) train; + trainInterface.realism$setSettings(TrainSettings.fromNBT(this.trains.savetoNBT())); + RNetworking.sendToAll(new TrainSettingsUpdatePacket(trainInterface.realism$getSettings(), train.id)); + + } + }); + }); + } +} diff --git a/common/src/main/java/net/Realism/network/TrainSettingsUpdatePacket.java b/common/src/main/java/net/Realism/network/TrainSettingsUpdatePacket.java new file mode 100644 index 0000000..6abd0dc --- /dev/null +++ b/common/src/main/java/net/Realism/network/TrainSettingsUpdatePacket.java @@ -0,0 +1,44 @@ +package net.Realism.network; + +import com.simibubi.create.Create; +import com.simibubi.create.content.trains.entity.Train; +import net.Realism.Interfaces.ITrainInterface; +import net.Realism.trains.TrainSettings; +import net.Realism.util.S2CPacket; +import net.minecraft.client.Minecraft; +import net.minecraft.network.FriendlyByteBuf; + +import java.util.UUID; + +public class TrainSettingsUpdatePacket implements S2CPacket { + + public final TrainSettings settings; + public final UUID uuid; + + public TrainSettingsUpdatePacket(TrainSettings settings, UUID UUID) { + this.settings = settings; + this.uuid = UUID; + } + + @Override + public void write(FriendlyByteBuf buf) { + buf.writeNbt(settings.savetoNBT()); + buf.writeUUID(uuid); + } + + public static TrainSettingsUpdatePacket read(FriendlyByteBuf buf) { + return new TrainSettingsUpdatePacket(TrainSettings.fromNBT(buf.readNbt()), buf.readUUID()); + } + + + @Override + public void handle(Minecraft mc) { + mc.execute(() -> { + if (mc.level == null) return; + Train tr = Create.RAILWAYS.sided(Minecraft.getInstance().level).trains.get(uuid); + if(tr instanceof ITrainInterface trainInterface){ + trainInterface.realism$setSettings(settings); + } + }); + } +} diff --git a/common/src/main/java/net/Realism/trains/TrainSettings.java b/common/src/main/java/net/Realism/trains/TrainSettings.java new file mode 100644 index 0000000..cf3198e --- /dev/null +++ b/common/src/main/java/net/Realism/trains/TrainSettings.java @@ -0,0 +1,96 @@ +package net.Realism.trains; + +import net.minecraft.nbt.CompoundTag; + + +public class TrainSettings { + public enum tiltSetting{ + NONE,PASSIVE,ACTIVE,CUSTOM; + } + public enum accelerationSetting{ + NONE,REALISTIC, CUSTOM; + } + public TrainSettings trainSettings(tiltSetting ts, accelerationSetting as){ + this.ts = ts; + this.as = as; + return this; + } + + public tiltSetting ts = tiltSetting.NONE; + public accelerationSetting as = accelerationSetting.REALISTIC; + + public double customAcceleration = 1.0; + + public float customMaxTilt = 5.0f; + + public Double customMinSpeed = 40.0; + + public float customTiltIntensity = 1.0f; + + public boolean Inside = true; + + public boolean isInside() { + return Inside; + } + + public boolean isTiltActive() { + return ts == tiltSetting.ACTIVE; + } + + public boolean isTiltPassive() { + return ts == tiltSetting.PASSIVE; + } + + public boolean isTiltCustom() { + return ts == tiltSetting.CUSTOM; + } + + public boolean isTiltNone() { + return ts == tiltSetting.NONE; + } + + public boolean isAccelerationRealistic() { + return as == accelerationSetting.REALISTIC; + } + + public boolean isAccelerationCustom() { + return as == accelerationSetting.CUSTOM; + } + + public boolean isAccelerationNone() { + return as == accelerationSetting.NONE; + } + + public static tiltSetting tiltFromString(String s) { + if (s == null) return tiltSetting.NONE; + try { + return tiltSetting.valueOf(s.trim().toUpperCase()); + } catch (IllegalArgumentException e) { + return tiltSetting.NONE; + } + } + + public CompoundTag savetoNBT(){ + CompoundTag tag = new CompoundTag(); + tag.putString("TiltSetting", this.ts.toString()); + tag.putString("AccelerationSetting", this.as.toString()); + tag.putDouble("CustomAcceleration", this.customAcceleration); + tag.putFloat("CustomMaxTilt", this.customMaxTilt); + tag.putDouble("CustomMinSpeed", this.customMinSpeed); + tag.putFloat("CustomTiltIntensity", this.customTiltIntensity); + tag.putBoolean("Inside", this.Inside); + return tag; + } + + public static TrainSettings fromNBT(CompoundTag tag){ + TrainSettings settings = new TrainSettings(); + settings.ts = tiltFromString(tag.getString("TiltSetting")); + settings.as = accelerationSetting.valueOf(tag.getString("AccelerationSetting")); + settings.customAcceleration = tag.getDouble("CustomAcceleration"); + settings.customMaxTilt = tag.getFloat("CustomMaxTilt"); + settings.customMinSpeed = tag.getDouble("CustomMinSpeed"); + settings.customTiltIntensity = tag.getFloat("CustomTiltIntensity"); + settings.Inside = tag.getBoolean("Inside"); + return settings; + } +} \ No newline at end of file diff --git a/common/src/main/java/net/Realism/trains/schedule/TimeOfDayRealistic.java b/common/src/main/java/net/Realism/trains/schedule/TimeOfDayRealistic.java new file mode 100644 index 0000000..46d5603 --- /dev/null +++ b/common/src/main/java/net/Realism/trains/schedule/TimeOfDayRealistic.java @@ -0,0 +1,117 @@ +package net.Realism.trains.schedule; + +import com.simibubi.create.content.trains.entity.Train; +import com.simibubi.create.content.trains.schedule.condition.TimeOfDayCondition; +import com.simibubi.create.foundation.gui.ModularGuiLineBuilder; +import com.simibubi.create.foundation.gui.widget.Label; +import com.simibubi.create.foundation.gui.widget.ScrollInput; +import com.simibubi.create.foundation.utility.CreateLang; +import net.Realism.Interfaces.IScheduleRuntimeMixin; +import net.Realism.RealismMod; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.Level; +import org.apache.commons.lang3.mutable.MutableObject; + +public class TimeOfDayRealistic extends TimeOfDayCondition { + public TimeOfDayRealistic() { + data.putInt("Hour", 8); + data.putInt("Minute", 0); + data.putInt("Rotation", 0); + } + @Override + public ResourceLocation getId() { + return RealismMod.id("time_of_day_realistic"); + } + + @Override + public boolean tickCompletion(Level level, Train train, CompoundTag context) { + int maxTickDiff = 10; + IScheduleRuntimeMixin runtime = (IScheduleRuntimeMixin) train.runtime; + int targetHour = intData("Hour"); + int targetMinute = intData("Minute"); + long gtime = level.getGameTime(); + long depDate = runtime.getDepartureDate(); + long wtme = level.getDayTime(); + int wctime = (int) (wtme+6000)%24000; + int gctime = (int) (gtime+6000)%24000; + int discrepancy = 6000+ (wctime - gctime); + int date = (int) Math.floor((double) (gtime+discrepancy)/24000); + int expectedTargetDate = (int) Math.floor((double) (runtime.getExpectedArrivalDate()+discrepancy) /24000); + int targetTicks = (int)((expectedTargetDate * 24000) + (targetHour * 1000) + Math.ceil(targetMinute / 60f * 1000)); + int diff = (int) (targetTicks - (gtime+discrepancy)); + int targetDayTime = (int) ((targetHour * 1000) + Math.ceil(targetMinute / 60f * 1000)); + int depDayTime = runtime.getLastScheduledDepartureDate(); + //Handle same station, same day rollover + if (diff < 0 && Math.abs(depDate -gtime) < maxTickDiff && !(runtime.dontCheck())){ + long x = runtime.getExpectedArrivalDate()+ 24000; + runtime.setExpectedArrivalDate(x); + targetTicks = (int) (((expectedTargetDate+1) * 24000) + (targetHour * 1000) + Math.ceil(targetMinute / 60f * 1000)); + diff = (int) (targetTicks - (gtime+discrepancy)); + } + //Handle same day rollover with travel + else if (diff < 0 && targetDayTime < depDayTime &&!(runtime.dontCheck())){ + long x = runtime.getExpectedArrivalDate()+24000; + runtime.setExpectedArrivalDate(x); + targetTicks = (int) (((expectedTargetDate+1) * 24000) + (targetHour * 1000) + Math.ceil(targetMinute / 60f * 1000)); + diff = (int) (targetTicks - (gtime+discrepancy)); + } + + runtime.setDontCheck(true); + + if (diff<=0) { + runtime.setLastScheduledDepartureDate(targetDayTime); + return diff <= 0; + } + return false; + } + + @Override + public void initConfigurationWidgets(ModularGuiLineBuilder builder) { + MutableObject minuteInput = new MutableObject<>(); + MutableObject hourInput = new MutableObject<>(); + MutableObject