Skip to content

Commit

Permalink
Boilers (GregTechCEu#417)
Browse files Browse the repository at this point in the history
  • Loading branch information
serenibyss authored Dec 28, 2021
1 parent f9e9948 commit ddbc8d5
Show file tree
Hide file tree
Showing 64 changed files with 703 additions and 455 deletions.
2 changes: 2 additions & 0 deletions src/main/java/gregtech/api/capability/GregtechDataCodes.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ public class GregtechDataCodes {
public static final int NEEDS_VENTING = 2;
public static final int VENTING_SIDE = 3;
public static final int VENTING_STUCK = 4;
public static final int BOILER_HEAT = 15;
public static final int BOILER_LAST_TICK_STEAM = 16;

// Misc TEs (Transformer, World Accelerator)
public static final int SYNC_TILE_MODE = 100;
Expand Down
296 changes: 296 additions & 0 deletions src/main/java/gregtech/api/capability/impl/BoilerRecipeLogic.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,296 @@
package gregtech.api.capability.impl;

import gregtech.api.GTValues;
import gregtech.api.capability.IMultipleTankHandler;
import gregtech.api.recipes.MatchingMode;
import gregtech.api.recipes.ModHandler;
import gregtech.api.recipes.Recipe;
import gregtech.api.recipes.RecipeMaps;
import gregtech.api.util.GTLog;
import gregtech.common.ConfigHolder;
import gregtech.common.metatileentities.multi.MetaTileEntityLargeBoiler;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.network.PacketBuffer;
import net.minecraft.util.NonNullList;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.FluidTank;
import net.minecraftforge.fluids.FluidUtil;
import net.minecraftforge.fluids.IFluidTank;
import net.minecraftforge.items.IItemHandlerModifiable;

import javax.annotation.Nonnull;
import java.util.Collections;
import java.util.List;

import static gregtech.api.capability.GregtechDataCodes.BOILER_HEAT;
import static gregtech.api.capability.GregtechDataCodes.BOILER_LAST_TICK_STEAM;

public class BoilerRecipeLogic extends AbstractRecipeLogic {

private static final long STEAM_PER_WATER = 160;

private int currentHeat;
private int lastTickSteamOutput;
private int excessWater, excessFuel, excessProjectedEU;

public BoilerRecipeLogic(MetaTileEntityLargeBoiler tileEntity) {
super(tileEntity, null);
this.fluidOutputs = Collections.emptyList();
this.itemOutputs = NonNullList.create();
}

@Override
public void update() {
if ((!isActive() || !isWorkingEnabled()) && currentHeat > 0) {
setHeat(currentHeat - 1);
setLastTickSteam(0);
}
super.update();
}

@Override
protected void trySearchNewRecipe() {
MetaTileEntityLargeBoiler boiler = (MetaTileEntityLargeBoiler) metaTileEntity;
if (ConfigHolder.machines.enableMaintenance && boiler.hasMaintenanceMechanics() && boiler.getNumMaintenanceProblems() > 5) {
return;
}

// can optimize with an override of checkPreviousRecipe() and a check here

IMultipleTankHandler importFluids = boiler.getImportFluids();
List<ItemStack> dummyList = NonNullList.create();
boolean didStartRecipe = false;

for (IFluidTank fluidTank : importFluids.getFluidTanks()) {
FluidStack fuelStack = fluidTank.drain(Integer.MAX_VALUE, false);
if (fuelStack == null || ModHandler.isWater(fuelStack)) continue;

Recipe dieselRecipe = RecipeMaps.COMBUSTION_GENERATOR_FUELS.findRecipe(
GTValues.V[GTValues.MAX], dummyList, Collections.singletonList(fuelStack), Integer.MAX_VALUE, MatchingMode.IGNORE_ITEMS);
if (dieselRecipe != null) {
((FluidTank) fluidTank).drain(dieselRecipe.getFluidInputs().get(0), true);
// divide by 4, since we divide by 2 for the steam ratio, and by 2 again to half the duration of the fuel
setMaxProgress(adjustBurnTimeForThrottle(Math.max(1, boiler.boilerType.runtimeBoost((Math.abs(dieselRecipe.getEUt()) * dieselRecipe.getDuration()) / 4))));
didStartRecipe = true;
break;
}

Recipe denseFuelRecipe = RecipeMaps.SEMI_FLUID_GENERATOR_FUELS.findRecipe(
GTValues.V[GTValues.MAX], dummyList, Collections.singletonList(fuelStack), Integer.MAX_VALUE, MatchingMode.IGNORE_ITEMS);
if (denseFuelRecipe != null) {
((FluidTank) fluidTank).drain(denseFuelRecipe.getFluidInputs().get(0), true);
// leave as is, as it is 2x burntime for semi-fluid (so just skip the EU->Steam ratio)
setMaxProgress(adjustBurnTimeForThrottle(Math.max(1, boiler.boilerType.runtimeBoost((Math.abs(denseFuelRecipe.getEUt()) * denseFuelRecipe.getDuration())))));
didStartRecipe = true;
break;
}
}

if (!didStartRecipe) {
IItemHandlerModifiable importItems = boiler.getImportItems();
for (int i = 0; i < importItems.getSlots(); i++) {
ItemStack stack = importItems.getStackInSlot(i);
int fuelBurnTime = (int) Math.ceil(ModHandler.getFuelValue(stack));
if (fuelBurnTime / 80 > 0) { // try to ensure this fuel can burn for at least 1 tick
if (FluidUtil.getFluidHandler(stack) != null) continue;
this.excessFuel += fuelBurnTime % 80;
int excessProgress = this.excessFuel / 80;
setMaxProgress(excessProgress + adjustBurnTimeForThrottle(boiler.boilerType.runtimeBoost(fuelBurnTime / 80)));
stack.shrink(1);
didStartRecipe = true;
break;
}
}
}
if (didStartRecipe) {
this.progressTime = 1;
this.recipeEUt = adjustEUtForThrottle(boiler.boilerType.steamPerTick());
if (wasActiveAndNeedsUpdate) {
wasActiveAndNeedsUpdate = false;
} else {
setActive(true);
}
}
metaTileEntity.getNotifiedItemInputList().clear();
metaTileEntity.getNotifiedFluidInputList().clear();
}

@Override
protected void updateRecipeProgress() {
int generatedSteam = this.recipeEUt * getMaximumHeatFromMaintenance() / getMaximumHeat();
if (generatedSteam > 0) {
long amount = (generatedSteam + STEAM_PER_WATER) / STEAM_PER_WATER;
excessWater += amount * STEAM_PER_WATER - generatedSteam;
amount -= excessWater / STEAM_PER_WATER;
excessWater %= STEAM_PER_WATER;

FluidStack drainedWater = ModHandler.getBoilerFluidFromContainer(getInputTank(), (int) amount, true);
if (amount != 0 && (drainedWater == null || drainedWater.amount < amount)) {
getMetaTileEntity().explodeMultiblock();
} else {
setLastTickSteam(generatedSteam);
getOutputTank().fill(ModHandler.getSteam(generatedSteam), true);
}
}
if (currentHeat < getMaximumHeat()) {
setHeat(currentHeat + 1);
}

if (++progressTime > maxProgressTime) {
completeRecipe();
}
}

private int getMaximumHeatFromMaintenance() {
if (ConfigHolder.machines.enableMaintenance) {
return (int) Math.min(currentHeat, (1 - 0.1 * getMetaTileEntity().getNumMaintenanceProblems()) * getMaximumHeat());
} else return currentHeat;
}

private int adjustEUtForThrottle(int rawEUt) {
int throttle = ((MetaTileEntityLargeBoiler) metaTileEntity).getThrottle();
return Math.max(25, (int) (rawEUt * (throttle / 100.0)));
}

private int adjustBurnTimeForThrottle(int rawBurnTime) {
MetaTileEntityLargeBoiler boiler = (MetaTileEntityLargeBoiler) metaTileEntity;
int EUt = boiler.boilerType.steamPerTick();
int adjustedEUt = adjustEUtForThrottle(EUt);
int adjustedBurnTime = rawBurnTime * EUt / adjustedEUt;
this.excessProjectedEU += EUt * rawBurnTime - adjustedEUt * adjustedBurnTime;
adjustedBurnTime += this.excessProjectedEU / adjustedEUt;
this.excessProjectedEU %= adjustedEUt;
return adjustedBurnTime;
}

private int getMaximumHeat() {
return ((MetaTileEntityLargeBoiler) metaTileEntity).boilerType.getTicksToBoiling();
}

public int getHeatScaled() {
return (int) Math.round(currentHeat / (1.0 * getMaximumHeat()) * 100);
}

public void setHeat(int heat) {
if (heat != this.currentHeat && !metaTileEntity.getWorld().isRemote) {
writeCustomData(BOILER_HEAT, b -> b.writeVarInt(heat));
}
this.currentHeat = heat;
}

public int getLastTickSteam() {
return lastTickSteamOutput;
}

public void setLastTickSteam(int lastTickSteamOutput) {
if (lastTickSteamOutput != this.lastTickSteamOutput && !metaTileEntity.getWorld().isRemote) {
writeCustomData(BOILER_LAST_TICK_STEAM, b -> b.writeVarInt(lastTickSteamOutput));
}
this.lastTickSteamOutput = lastTickSteamOutput;
}

public void invalidate() {
progressTime = 0;
maxProgressTime = 0;
recipeEUt = 0;
setActive(false);
setLastTickSteam(0);
}

@Override
protected void completeRecipe() {
progressTime = 0;
setMaxProgress(0);
recipeEUt = 0;
wasActiveAndNeedsUpdate = true;
}

@Override
public MetaTileEntityLargeBoiler getMetaTileEntity() {
return (MetaTileEntityLargeBoiler) super.getMetaTileEntity();
}

@Override
protected void setActive(boolean active) {
if (active != this.isActive) {
getMetaTileEntity().replaceFireboxAsActive(active);
}
super.setActive(active);
}

@Override
public NBTTagCompound serializeNBT() {
NBTTagCompound compound = super.serializeNBT();
compound.setInteger("Heat", currentHeat);
compound.setInteger("ExcessFuel", excessFuel);
compound.setInteger("ExcessWater", excessWater);
compound.setInteger("ExcessProjectedEU", excessProjectedEU);
return compound;
}

@Override
public void deserializeNBT(@Nonnull NBTTagCompound compound) {
super.deserializeNBT(compound);
this.currentHeat = compound.getInteger("Heat");
this.excessFuel = compound.getInteger("ExcessFuel");
this.excessWater = compound.getInteger("ExcessWater");
this.excessProjectedEU = compound.getInteger("ExcessProjectedEU");
}

@Override
public void writeInitialData(@Nonnull PacketBuffer buf) {
super.writeInitialData(buf);
buf.writeVarInt(currentHeat);
buf.writeVarInt(lastTickSteamOutput);
}

@Override
public void receiveInitialData(@Nonnull PacketBuffer buf) {
super.receiveInitialData(buf);
this.currentHeat = buf.readVarInt();
this.lastTickSteamOutput = buf.readVarInt();
}

@Override
public void receiveCustomData(int dataId, PacketBuffer buf) {
super.receiveCustomData(dataId, buf);
if (dataId == BOILER_HEAT) {
this.currentHeat = buf.readVarInt();
} else if (dataId == BOILER_LAST_TICK_STEAM) {
this.lastTickSteamOutput = buf.readVarInt();
}
}

// Required overrides to use RecipeLogic, but all of them are redirected by the above overrides.

@Override
protected long getEnergyInputPerSecond() {
GTLog.logger.error("Large Boiler called getEnergyInputPerSecond(), this should not be possible!");
return 0;
}

@Override
protected long getEnergyStored() {
GTLog.logger.error("Large Boiler called getEnergyStored(), this should not be possible!");
return 0;
}

@Override
protected long getEnergyCapacity() {
GTLog.logger.error("Large Boiler called getEnergyCapacity(), this should not be possible!");
return 0;
}

@Override
protected boolean drawEnergy(int recipeEUt, boolean simulate) {
GTLog.logger.error("Large Boiler called drawEnergy(), this should not be possible!");
return false;
}

@Override
protected long getMaxVoltage() {
GTLog.logger.error("Large Boiler called getMaxVoltage(), this should not be possible!");
return 0;
}
}
18 changes: 14 additions & 4 deletions src/main/java/gregtech/api/recipes/ModHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import net.minecraft.item.crafting.FurnaceRecipes;
import net.minecraft.item.crafting.IRecipe;
import net.minecraft.item.crafting.Ingredient;
import net.minecraft.tileentity.TileEntityFurnace;
import net.minecraft.util.NonNullList;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
Expand Down Expand Up @@ -66,16 +67,21 @@ public static boolean isWater(@Nullable FluidStack fluid) {
return false;
}

public static FluidStack getWaterFromContainer(@Nonnull IFluidHandler fluidHandler, boolean doDrain) {
FluidStack drainedWater = fluidHandler.drain(Materials.Water.getFluid(1), doDrain);
public static FluidStack getBoilerFluidFromContainer(@Nonnull IFluidHandler fluidHandler, boolean doDrain) {
return getBoilerFluidFromContainer(fluidHandler, 1, doDrain);
}

public static FluidStack getBoilerFluidFromContainer(@Nonnull IFluidHandler fluidHandler, int amount, boolean doDrain) {
if (amount == 0) return null;
FluidStack drainedWater = fluidHandler.drain(Materials.Water.getFluid(amount), doDrain);
if (drainedWater == null || drainedWater.amount == 0) {
drainedWater = fluidHandler.drain(Materials.DistilledWater.getFluid(1), doDrain);
drainedWater = fluidHandler.drain(Materials.DistilledWater.getFluid(amount), doDrain);
}
if (drainedWater == null || drainedWater.amount == 0) {
for (String fluidName : ConfigHolder.machines.boilerFluids) {
Fluid f = FluidRegistry.getFluid(fluidName);
if (f != null) {
drainedWater = fluidHandler.drain(new FluidStack(f, 1), doDrain);
drainedWater = fluidHandler.drain(new FluidStack(f, amount), doDrain);
if (drainedWater != null && drainedWater.amount > 0) {
break;
}
Expand Down Expand Up @@ -117,6 +123,10 @@ public static boolean isMaterialWood(Material material) {
return material == Materials.Wood;
}

public static int getFuelValue(ItemStack stack) {
return TileEntityFurnace.getItemBurnTime(stack);
}

public static ItemStack getBurningFuelRemainder(ItemStack fuelStack) {
float remainderChance;
ItemStack remainder;
Expand Down
Loading

0 comments on commit ddbc8d5

Please sign in to comment.