diff --git a/src/generated/resources/assets/gtceu/lang/en_us.json b/src/generated/resources/assets/gtceu/lang/en_us.json index 6c764c0c0f1..c23b784fb97 100644 --- a/src/generated/resources/assets/gtceu/lang/en_us.json +++ b/src/generated/resources/assets/gtceu/lang/en_us.json @@ -2723,6 +2723,8 @@ "gtceu.machine.block_breaker.tooltip": "§7Mines block on front face and collects its drops", "gtceu.machine.boiler.info.cooling.down": "§9Cooling§r", "gtceu.machine.boiler.info.heating.up": "§cHeating§r", + "gtceu.machine.boiler.info.heated_up": "§cHeated Up§r", + "gtceu.machine.boiler.info.fuel_consumption_discount": "§eFuel Consumption Discount: §f%s%%", "gtceu.machine.boiler.info.production.data": "§aProducing %s§a mB/t", "gtceu.machine.buffer.tooltip": "A Small Buffer to store Items and Fluids", "gtceu.machine.canner.jei_description": "You can fill and empty any fluid containers with the Fluid Canner (e.g. Buckets or Fluid Cells)", diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/steam/SteamBoilerMachine.java b/src/main/java/com/gregtechceu/gtceu/api/machine/steam/SteamBoilerMachine.java index b7a556c807d..1722573ef9d 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/machine/steam/SteamBoilerMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/steam/SteamBoilerMachine.java @@ -9,6 +9,7 @@ import com.gregtechceu.gtceu.api.machine.mui.MachineUIPanelBuilder; import com.gregtechceu.gtceu.api.machine.trait.notifiable.NotifiableFluidTank; import com.gregtechceu.gtceu.api.machine.trait.recipe.RecipeLogic; +import com.gregtechceu.gtceu.api.recipe.ActionResult; import com.gregtechceu.gtceu.api.recipe.GTRecipe; import com.gregtechceu.gtceu.api.recipe.modifier.ModifierFunction; import com.gregtechceu.gtceu.api.recipe.modifier.RecipeModifier; @@ -74,12 +75,16 @@ public abstract class SteamBoilerMachine extends SteamWorkableMachine protected TickableSubscription temperatureSubs, autoOutputSubs; @Nullable protected ISubscription steamTankSubs; + @SaveField + @Getter + private int heatingTimeDebt; // amount of time the fuel should be burned for to account for the boiler cooling down public SteamBoilerMachine(BlockEntityCreationInfo info, boolean isHighPressure) { - super(info, isHighPressure, new RecipeLogic(), + super(info, isHighPressure, new SteamBoilerRecipeLogic(), new NotifiableFluidTank(1, 16 * FluidType.BUCKET_VOLUME, IO.OUT)); this.waterTank = attachTrait(createWaterTank()); this.waterTank.setFilter(fluid -> fluid.getFluid().is(GTMaterials.Water.getFluidTag())); + recipeLogic.setRegressWhenWaiting(false); } ////////////////////////////////////// @@ -157,18 +162,31 @@ protected void updateSteamSubscription() { } protected void updateCurrentTemperature() { - if (recipeLogic.isWorking()) { - if (getOffsetTimer() % 12 == 0) { - if (currentTemperature < getMaxTemperature()) - if (isHighPressure) { - currentTemperature++; - } else if (getOffsetTimer() % 24 == 0) { - currentTemperature++; - } + if (shouldDiscountFuelConsumptionAtMaxTemperature() && + currentTemperature >= getMaxTemperature() && + (recipeLogic.isWorking() || recipeLogic.isWaiting())) { + // We are fully heated up and are running a recipe. We want to simulate the cooling logic here, + // but instead of actually lowering the temperature, we instead manipulate the heating time debt + // to simulate the fuel burn cycle without actually affecting the temperature. + if (timeBeforeCoolingDown == 0) { + // Heating interval times cool down rate is the same amount of fuel recipe progress it would take + // to get the lowered temperature back up to the maximum temperature. + heatingTimeDebt += getHeatingTimeDebtAtMaxTemperature(); + timeBeforeCoolingDown = getCooldownInterval(); + } else { + --timeBeforeCoolingDown; + } + } else if (recipeLogic.isWorking()) { + this.timeBeforeCoolingDown = getCooldownInterval(); + if (getOffsetTimer() % getHeatingInterval() == 0) { + if (currentTemperature < getMaxTemperature()) { + currentTemperature++; + } } } else if (timeBeforeCoolingDown == 0) { if (currentTemperature > 0) { currentTemperature -= getCoolDownRate(); + heatingTimeDebt = 0; timeBeforeCoolingDown = getCooldownInterval(); } } else { @@ -229,6 +247,10 @@ protected int getCoolDownRate() { return 1; } + protected int getHeatingInterval() { + return isHighPressure ? 12 : 24; + } + public int getMaxTemperature() { return isHighPressure ? 1000 : 500; } @@ -267,6 +289,9 @@ public static ModifierFunction recipeModifier(MetaMachine machine, GTRecipe reci @Override public boolean onWorking() { + if (heatingTimeDebt > 0) { + --this.heatingTimeDebt; + } boolean value = super.onWorking(); if (currentTemperature < getMaxTemperature()) { currentTemperature = Math.max(1, currentTemperature); @@ -275,10 +300,28 @@ public boolean onWorking() { return value; } - @Override - public void afterWorking() { - super.afterWorking(); - this.timeBeforeCoolingDown = getCooldownInterval(); + /** Returns true if fuel consumption should be discounted at max temperature */ + protected boolean shouldDiscountFuelConsumptionAtMaxTemperature() { + return true; + } + + /** Returns true if we should suspend the currently running burning recipe due to the boiler being fully heated */ + protected boolean shouldSuspendRecipeDueToBeingFullyHeated() { + return shouldDiscountFuelConsumptionAtMaxTemperature() && + currentTemperature >= getMaxTemperature() && + heatingTimeDebt <= 0; + } + + protected int getHeatingTimeDebtAtMaxTemperature() { + return Math.min(getHeatingInterval() * getCoolDownRate(), getCooldownInterval()); + } + + /** Returns the effective fuel consumption rate, normalized */ + public float getEffectiveFuelConsumptionRate() { + if (shouldDiscountFuelConsumptionAtMaxTemperature() && currentTemperature >= getMaxTemperature()) { + return getHeatingTimeDebtAtMaxTemperature() * 1.0f / getCooldownInterval(); + } + return 1.0f; } ////////////////////////////////////// @@ -391,4 +434,16 @@ public List getDataInfo(PortableScannerBehavior.DisplayMode mode) { } return new ArrayList<>(); } + + private static class SteamBoilerRecipeLogic extends RecipeLogic { + + @Override + public ActionResult handleTickRecipe(GTRecipe recipe) { + SteamBoilerMachine boilerMachine = (SteamBoilerMachine) getMachine(); + if (boilerMachine.shouldSuspendRecipeDueToBeingFullyHeated()) { + return ActionResult.FAIL_NO_REASON; + } + return super.handleTickRecipe(recipe); + } + } } diff --git a/src/main/java/com/gregtechceu/gtceu/common/machine/steam/SteamSolarBoiler.java b/src/main/java/com/gregtechceu/gtceu/common/machine/steam/SteamSolarBoiler.java index bb58d1bf296..12d07c0563c 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/machine/steam/SteamSolarBoiler.java +++ b/src/main/java/com/gregtechceu/gtceu/common/machine/steam/SteamSolarBoiler.java @@ -70,6 +70,11 @@ protected int getCoolDownRate() { return 3; } + @Override + protected boolean shouldDiscountFuelConsumptionAtMaxTemperature() { + return false; // does not really make sense for solar boiler + } + @Override public void buildMainUI(ParentWidget mainWidget, PosGuiData guiData, PanelSyncManager syncManager, UISettings settings) { diff --git a/src/main/java/com/gregtechceu/gtceu/integration/jade/provider/SteamBoilerBlockProvider.java b/src/main/java/com/gregtechceu/gtceu/integration/jade/provider/SteamBoilerBlockProvider.java index 2622a4e5eaa..64b13842c82 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/jade/provider/SteamBoilerBlockProvider.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/jade/provider/SteamBoilerBlockProvider.java @@ -9,6 +9,7 @@ import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; +import net.minecraft.util.Mth; import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.block.entity.BlockEntity; @@ -25,11 +26,14 @@ public SteamBoilerBlockProvider() { @Override protected CompoundTag write(SteamBoilerMachine machine) { CompoundTag data = new CompoundTag(); - data.putBoolean("isBurning", machine.getTraitOptional(RecipeLogic.class).orElseThrow().isWorking()); + RecipeLogic recipeLogic = machine.getTraitOptional(RecipeLogic.class).orElseThrow(); + data.putBoolean("isBurning", recipeLogic.isWorking() || recipeLogic.isWaiting()); data.putBoolean("hasWater", !machine.isHasNoWater()); data.putLong("steamProduction", machine.getTotalSteamOutput()); data.putInt("currentTemperature", machine.getCurrentTemperature()); data.putInt("maxTemperature", machine.getMaxTemperature()); + int fuelConsumptionRate = Mth.ceil(machine.getEffectiveFuelConsumptionRate() * 100.0f); + data.putInt("fuelConsumptionRate", fuelConsumptionRate); return data; } @@ -41,12 +45,24 @@ protected void addTooltip(CompoundTag capData, ITooltip tooltip, Player player, long production = capData.getLong("steamProduction"); int temperature = capData.getInt("currentTemperature"); int maxTemperature = capData.getInt("maxTemperature"); + int fuelConsumptionRate = capData.getInt("fuelConsumptionRate"); boolean makingSteam = hasWater && temperature >= 100; + // Append fuel consumption discount first + if (fuelConsumptionRate < 100) { + // Round up to a nicer number. precision less than 5% is irrelevant here + int fuelConsumptionDiscount = Math.round((100.0f - fuelConsumptionRate) / 5.0f) * 5; + tooltip.add(Component.translatable("gtceu.machine.boiler.info.fuel_consumption_discount", + fuelConsumptionDiscount)); + } + // Determine the first section MutableComponent root; - if (isBurning && temperature < maxTemperature) { + if (isBurning && temperature >= maxTemperature) { + // Fully heated + root = Component.translatable("gtceu.machine.boiler.info.heated_up"); + } else if (isBurning) { // Heating up root = Component.translatable("gtceu.machine.boiler.info.heating.up"); } else if (!isBurning && temperature > 0) {