diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/Contraption.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/Contraption.java index b49ec4883..2b6d884f1 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/Contraption.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/Contraption.java @@ -1242,6 +1242,10 @@ public abstract class Contraption { return storage.getItems(); } + public IItemHandlerModifiable getSharedFuelInventory() { + return storage.getFuelItems(); + } + public IFluidHandler getSharedFluidTanks() { return storage.getFluids(); } diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/MountedStorage.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/MountedStorage.java index 0196ce6af..f9d8f2813 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/MountedStorage.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/MountedStorage.java @@ -26,16 +26,16 @@ public class MountedStorage { private static final ItemStackHandler dummyHandler = new ItemStackHandler(); ItemStackHandler handler; + boolean noFuel; boolean valid; + private BlockEntity te; public static boolean canUseAsStorage(BlockEntity te) { if (te == null) return false; - if (te instanceof MechanicalCrafterTileEntity) return false; - if (AllTileEntities.CREATIVE_CRATE.is(te)) return true; if (te instanceof ShulkerBoxBlockEntity) @@ -55,6 +55,7 @@ public class MountedStorage { public MountedStorage(BlockEntity te) { this.te = te; handler = dummyHandler; + noFuel = te instanceof ItemVaultTileEntity; } public void removeStorageFromWorld() { @@ -87,7 +88,7 @@ public class MountedStorage { valid = true; return; } - + // te uses ItemStackHandler if (teHandler instanceof ItemStackHandler) { handler = (ItemStackHandler) teHandler; @@ -124,7 +125,7 @@ public class MountedStorage { te.load(tag); return; } - + if (te instanceof ItemVaultTileEntity) { ((ItemVaultTileEntity) te).applyInventoryToBlock(handler); return; @@ -147,14 +148,16 @@ public class MountedStorage { public CompoundTag serialize() { if (!valid) return null; + CompoundTag tag = handler.serializeNBT(); + if (noFuel) + NBTHelper.putMarker(tag, "NoFuel"); + if (!(handler instanceof BottomlessItemHandler)) + return tag; - if (handler instanceof BottomlessItemHandler) { - NBTHelper.putMarker(tag, "Bottomless"); - tag.put("ProvidedStack", handler.getStackInSlot(0) - .serializeNBT()); - } - + NBTHelper.putMarker(tag, "Bottomless"); + tag.put("ProvidedStack", handler.getStackInSlot(0) + .serializeNBT()); return tag; } @@ -164,6 +167,7 @@ public class MountedStorage { if (nbt == null) return storage; storage.valid = true; + storage.noFuel = nbt.contains("NoFuel"); if (nbt.contains("Bottomless")) { ItemStack providedStack = ItemStack.of(nbt.getCompound("ProvidedStack")); @@ -178,5 +182,9 @@ public class MountedStorage { public boolean isValid() { return valid; } + + public boolean canUseForFuel() { + return !noFuel; + } } diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/MountedStorageManager.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/MountedStorageManager.java index c7af7ba38..4f95e0175 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/MountedStorageManager.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/MountedStorageManager.java @@ -1,6 +1,8 @@ package com.simibubi.create.content.contraptions.components.structureMovement; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -29,6 +31,7 @@ import net.minecraftforge.items.IItemHandlerModifiable; public class MountedStorageManager { private ContraptionInvWrapper inventory; + private ContraptionInvWrapper fuelInventory; private CombinedTankWrapper fluidInventory; private Map storage; private Map fluidStorage; @@ -43,12 +46,16 @@ public class MountedStorageManager { } public void createHandlers() { - List list = storage.values() - .stream() + Collection itemHandlers = storage.values(); + + inventory = wrap(itemHandlers.stream() .map(MountedStorage::getItemHandler) - .collect(Collectors.toList()); - inventory = - new ContraptionInvWrapper(Arrays.copyOf(list.toArray(), list.size(), IItemHandlerModifiable[].class)); + .toList()); + + fuelInventory = wrap(itemHandlers.stream() + .filter(MountedStorage::canUseForFuel) + .map(MountedStorage::getItemHandler) + .toList()); List fluidHandlers = fluidStorage.values() .stream() @@ -58,6 +65,10 @@ public class MountedStorageManager { Arrays.copyOf(fluidHandlers.toArray(), fluidHandlers.size(), IFluidHandler[].class)); } + private ContraptionInvWrapper wrap(List list) { + return new ContraptionInvWrapper(Arrays.copyOf(list.toArray(), list.size(), IItemHandlerModifiable[].class)); + } + public void addBlock(BlockPos localPos, BlockEntity te) { if (te != null && MountedStorage.canUseAsStorage(te)) storage.put(localPos, new MountedStorage(te)); @@ -88,17 +99,22 @@ public class MountedStorageManager { mfs.assignTileEntity(tank); }); - IItemHandlerModifiable[] handlers = new IItemHandlerModifiable[storage.size()]; - int index = 0; - for (MountedStorage mountedStorage : storage.values()) - handlers[index++] = mountedStorage.getItemHandler(); + List handlers = new ArrayList<>(); + List fuelHandlers = new ArrayList<>(); + for (MountedStorage mountedStorage : storage.values()) { + IItemHandlerModifiable itemHandler = mountedStorage.getItemHandler(); + handlers.add(itemHandler); + if (mountedStorage.canUseForFuel()) + fuelHandlers.add(itemHandler); + } + int index = 0; IFluidHandler[] fluidHandlers = new IFluidHandler[fluidStorage.size()]; - index = 0; for (MountedFluidStorage mountedStorage : fluidStorage.values()) fluidHandlers[index++] = mountedStorage.getFluidHandler(); - inventory = new ContraptionInvWrapper(handlers); + inventory = wrap(handlers); + fuelInventory = wrap(fuelHandlers); fluidInventory = new CombinedTankWrapper(fluidHandlers); } @@ -164,15 +180,20 @@ public class MountedStorageManager { if (mountedFluidStorage != null) mountedFluidStorage.updateFluid(containedFluid); } - + public void attachExternal(IItemHandlerModifiable externalStorage) { inventory = new ContraptionInvWrapper(externalStorage, inventory); + fuelInventory = new ContraptionInvWrapper(externalStorage, fuelInventory); } public IItemHandlerModifiable getItems() { return inventory; } + public IItemHandlerModifiable getFuelItems() { + return fuelInventory; + } + public IFluidHandler getFluids() { return fluidInventory; } diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/entity/CarriageContraptionEntity.java b/src/main/java/com/simibubi/create/content/logistics/trains/entity/CarriageContraptionEntity.java index a6d4808e7..7a476f601 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/entity/CarriageContraptionEntity.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/entity/CarriageContraptionEntity.java @@ -496,6 +496,9 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity { targetSteer *= -1; } + if (targetSpeed != 0) + carriage.train.burnFuel(); + boolean slow = inverted ^ targetSpeed < 0; boolean spaceDown = heldControls.contains(4); GlobalStation currentStation = carriage.train.getCurrentStation(); @@ -551,7 +554,7 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity { carriage.train.manualSteer = targetSteer < 0 ? SteerDirection.RIGHT : targetSteer > 0 ? SteerDirection.LEFT : SteerDirection.NONE; - double topSpeed = AllConfigs.SERVER.trains.getTopSpeedMPT(); + double topSpeed = carriage.train.maxSpeed(); carriage.train.targetSpeed = topSpeed * targetSpeed; if (slow) carriage.train.targetSpeed /= 8; diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/entity/Navigation.java b/src/main/java/com/simibubi/create/content/logistics/trains/entity/Navigation.java index b07cf1a56..e94f9d8fc 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/entity/Navigation.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/entity/Navigation.java @@ -95,8 +95,7 @@ public class Navigation { destination.reserveFor(train); - double acceleration = AllConfigs.SERVER.trains.getAccelerationMPTT(); - double turnTopSpeed = AllConfigs.SERVER.trains.getTurningTopSpeedMPT(); + double acceleration = train.acceleration(); double brakingDistance = (train.speed * train.speed) / (2 * acceleration); double speedMod = destinationBehindTrain ? -1 : 1; double preDepartureLookAhead = train.getCurrentStation() != null ? 4.5 : 0; @@ -240,7 +239,11 @@ public class Navigation { return; } - double topSpeed = AllConfigs.SERVER.trains.getTopSpeedMPT(); + train.burnFuel(); + + double topSpeed = train.maxSpeed(); + double turnTopSpeed = train.maxTurnSpeed(); + if (targetDistance < 10) { double target = topSpeed * ((targetDistance) / 10); if (target < Math.abs(train.speed)) { @@ -505,7 +508,7 @@ public class Navigation { return null; MutableObject result = new MutableObject<>(null); - double acceleration = AllConfigs.SERVER.trains.getAccelerationMPTT(); + double acceleration = train.acceleration(); double minDistance = .75f * (train.speed * train.speed) / (2 * acceleration); double maxDistance = Math.max(32, 1.5f * (train.speed * train.speed) / (2 * acceleration)); diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/entity/Train.java b/src/main/java/com/simibubi/create/content/logistics/trains/entity/Train.java index 8f0cd54d3..bc5aeb9c8 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/entity/Train.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/entity/Train.java @@ -56,9 +56,13 @@ import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.util.Mth; import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Explosion.BlockInteraction; import net.minecraft.world.level.Level; import net.minecraft.world.phys.Vec3; +import net.minecraftforge.common.ForgeHooks; +import net.minecraftforge.items.IItemHandlerModifiable; +import net.minecraftforge.items.ItemHandlerHelper; import net.minecraftforge.network.PacketDistributor; public class Train { @@ -76,7 +80,7 @@ public class Train { public TrainStatus status; public boolean invalid; - + public SteerDirection manualSteer; public boolean manualTick; @@ -96,6 +100,8 @@ public class Train { public int migrationCooldown; public boolean derailed; + public int fuelTicks; + int tickOffset; double[] stress; @@ -387,7 +393,7 @@ public class Train { private void tickPassiveSlowdown() { if (!manualTick && navigation.destination == null && speed != 0) { - double acceleration = AllConfigs.SERVER.trains.getAccelerationMPTT(); + double acceleration = acceleration(); if (speed > 0) { speed = Math.max(speed - acceleration, 0); } else @@ -541,7 +547,7 @@ public class Train { if (entity == null) return false; - if (entity.getContraption() instanceof CarriageContraption cc) + if (entity.getContraption()instanceof CarriageContraption cc) cc.returnStorageForDisassembly(carriage.storage); entity.setPos(Vec3 .atLowerCornerOf(pos.relative(assemblyDirection, backwards ? offset + carriage.bogeySpacing : offset))); @@ -747,7 +753,7 @@ public class Train { return; if (manualTick) leaveStation(); - double acceleration = AllConfigs.SERVER.trains.getAccelerationMPTT(); + double acceleration = acceleration(); if (speed < targetSpeed) speed = Math.min(speed + acceleration * accelerationMod, targetSpeed); else if (speed > targetSpeed) @@ -854,6 +860,50 @@ public class Train { return Penalties.ANY_TRAIN; } + public void burnFuel() { + if (fuelTicks > 0) { + fuelTicks--; + return; + } + + boolean iterateFromBack = speed < 0; + int carriageCount = carriages.size(); + + for (int index = 0; index < carriageCount; index++) { + int i = iterateFromBack ? carriageCount - 1 - index : index; + Carriage carriage = carriages.get(i); + IItemHandlerModifiable fuelItems = carriage.storage.getFuelItems(); + + for (int slot = 0; slot < fuelItems.getSlots(); slot++) { + ItemStack stack = fuelItems.extractItem(slot, 1, true); + int burnTime = ForgeHooks.getBurnTime(stack, null); + if (burnTime <= 0) + continue; + + stack = fuelItems.extractItem(slot, 1, false); + fuelTicks += burnTime * stack.getCount(); + ItemStack containerItem = stack.getContainerItem(); + if (!containerItem.isEmpty()) + ItemHandlerHelper.insertItemStacked(fuelItems, containerItem, false); + } + } + } + + public float maxSpeed() { + return (fuelTicks > 0 ? AllConfigs.SERVER.trains.poweredTrainTopSpeed.getF() + : AllConfigs.SERVER.trains.trainTopSpeed.getF()) / 20; + } + + public float maxTurnSpeed() { + return (fuelTicks > 0 ? AllConfigs.SERVER.trains.poweredTrainTurningTopSpeed.getF() + : AllConfigs.SERVER.trains.trainTurningTopSpeed.getF()) / 20; + } + + public float acceleration() { + return (fuelTicks > 0 ? AllConfigs.SERVER.trains.poweredTrainAcceleration.getF() + : AllConfigs.SERVER.trains.trainAcceleration.getF()) / 400; + } + public CompoundTag write(DimensionPalette dimensions) { CompoundTag tag = new CompoundTag(); tag.putUUID("Id", id); @@ -864,6 +914,7 @@ public class Train { tag.putIntArray("CarriageSpacing", carriageSpacing); tag.putBoolean("DoubleEnded", doubleEnded); tag.putDouble("Speed", speed); + tag.putInt("Fuel", fuelTicks); tag.putDouble("TargetSpeed", targetSpeed); tag.putString("IconType", icon.id.toString()); tag.putString("Name", Component.Serializer.toJson(name)); @@ -917,6 +968,7 @@ public class Train { train.heldForAssembly = tag.getBoolean("StillAssembling"); train.derailed = tag.getBoolean("Derailed"); train.updateSignalBlocks = tag.getBoolean("UpdateSignals"); + train.fuelTicks = tag.getInt("Fuel"); NBTHelper.iterateCompoundList(tag.getList("SignalBlocks", Tag.TAG_COMPOUND), c -> train.occupiedSignalBlocks .put(c.getUUID("Id"), c.contains("Boundary") ? c.getUUID("Boundary") : null)); diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/ScheduleRuntime.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/ScheduleRuntime.java index 035eb0568..b4a8dcd9c 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/ScheduleRuntime.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/ScheduleRuntime.java @@ -15,8 +15,6 @@ import com.simibubi.create.content.logistics.trains.management.schedule.conditio import com.simibubi.create.content.logistics.trains.management.schedule.destination.ChangeTitleInstruction; import com.simibubi.create.content.logistics.trains.management.schedule.destination.DestinationInstruction; import com.simibubi.create.content.logistics.trains.management.schedule.destination.ScheduleInstruction; -import com.simibubi.create.foundation.config.AllConfigs; -import com.simibubi.create.foundation.config.CTrains; import com.simibubi.create.foundation.utility.NBTHelper; import net.minecraft.nbt.CompoundTag; @@ -239,8 +237,7 @@ public class ScheduleRuntime { } else { GlobalStation destination = train.navigation.destination; if (destination != null) { - CTrains conf = AllConfigs.SERVER.trains; - double speed = (conf.getTopSpeedMPT() + conf.getTurningTopSpeedMPT()) / 2; + double speed = (train.maxSpeed() + train.maxTurnSpeed()) / 2; int timeRemaining = (int) (train.navigation.distanceToDestination / speed) * 2; if (predictionTicks.size() > current && train.navigation.distanceStartedAt != 0) { diff --git a/src/main/java/com/simibubi/create/foundation/config/CTrains.java b/src/main/java/com/simibubi/create/foundation/config/CTrains.java index f30b31e97..5abad255d 100644 --- a/src/main/java/com/simibubi/create/foundation/config/CTrains.java +++ b/src/main/java/com/simibubi/create/foundation/config/CTrains.java @@ -3,36 +3,34 @@ package com.simibubi.create.foundation.config; public class CTrains extends ConfigBase { public final ConfigBool trainsCauseDamage = b(true, "trainsCauseDamage", Comments.trainsCauseDamage); - public final ConfigFloat trainTopSpeed = f(36, 0, "trainTopSpeed", Comments.mps, Comments.trainTopSpeed); - public final ConfigFloat trainTurningTopSpeed = - f(18, 0, "trainTurningTopSpeed", Comments.mps, Comments.trainTurningTopSpeed); - public final ConfigFloat trainAcceleration = f(4, 0, "trainAcceleration", Comments.acc, Comments.trainAcceleration); public final ConfigInt maxAssemblyLength = i(128, 5, "maxAssemblyLength", Comments.maxAssemblyLength); public final ConfigInt maxBogeyCount = i(20, 1, "maxBogeyCount", Comments.maxBogeyCount); + + public final ConfigGroup trainStats = group(1, "trainStats", "Standard Trains"); + public final ConfigFloat trainTopSpeed = f(28, 0, "trainTopSpeed", Comments.mps, Comments.trainTopSpeed); + public final ConfigFloat trainTurningTopSpeed = f(14, 0, "trainTurningTopSpeed", Comments.mps, Comments.trainTurningTopSpeed); + public final ConfigFloat trainAcceleration = f(3, 0, "trainAcceleration", Comments.acc, Comments.trainAcceleration); + + public final ConfigGroup poweredTrainStats = group(1, "poweredTrainStats", "Powered Trains"); + public final ConfigFloat poweredTrainTopSpeed = f(40, 0, "poweredTrainTopSpeed", Comments.mps, Comments.poweredTrainTopSpeed); + public final ConfigFloat poweredTrainTurningTopSpeed = f(20, 0, "poweredTrainTurningTopSpeed", Comments.mps, Comments.poweredTrainTurningTopSpeed); + public final ConfigFloat poweredTrainAcceleration = f(3, 0, "poweredTrainAcceleration", Comments.acc, Comments.poweredTrainAcceleration); + @Override public String getName() { return "trains"; } - public double getTopSpeedMPT() { - return trainTopSpeed.getF() / 20; - } - - public double getTurningTopSpeedMPT() { - return trainTurningTopSpeed.getF() / 20; - } - - public double getAccelerationMPTT() { - return trainAcceleration.getF() / 400; - } - private static class Comments { static String mps = "[in Blocks/Second]"; static String acc = "[in Blocks/SecondĀ²]"; static String trainTopSpeed = "The top speed of any assembled Train."; static String trainTurningTopSpeed = "The top speed of Trains during a turn."; static String trainAcceleration = "The acceleration of any assembled Train."; + static String poweredTrainTopSpeed = "The top speed of powered Trains."; + static String poweredTrainTurningTopSpeed = "The top speed of powered Trains during a turn."; + static String poweredTrainAcceleration = "The acceleration of powered Trains."; static String trainsCauseDamage = "Whether moving Trains can hurt colliding mobs and players."; static String maxAssemblyLength = "Maximum length of a Train Stations' assembly track."; static String maxBogeyCount = "Maximum amount of bogeys assembled as a single Train.";