diff --git a/addon.gradle b/addon.gradle
index 49206f7f..a774badd 100644
--- a/addon.gradle
+++ b/addon.gradle
@@ -3,3 +3,44 @@ tasks.named("jar", Jar).configure {
attributes("Main-class": "kubatech.standalone")
}
}
+
+
+SourceSet functionalTestSet = null
+
+sourceSets {
+ functionalTestSet = create("functionalTest") {
+ java {
+ srcDir("src/functionalTest/java")
+ compileClasspath += sourceSets.patchedMc.output + sourceSets.main.output
+ }
+ }
+}
+
+configurations { configs ->
+ // Keep all dependencies from the main mod in the functional test mod
+ named(functionalTestSet.compileClasspathConfigurationName).configure {it.extendsFrom(named("compileClasspath").get())}
+ named(functionalTestSet.runtimeClasspathConfigurationName).configure {it.extendsFrom(named("runtimeClasspath").get())}
+ named(functionalTestSet.annotationProcessorConfigurationName).configure {it.extendsFrom(named("annotationProcessor").get())}
+}
+
+tasks.register(functionalTestSet.jarTaskName, Jar) {
+ from(functionalTestSet.output)
+ archiveClassifier.set("functionalTests")
+ // we don't care about the version number here, keep it stable to avoid polluting the tmp directory
+ archiveVersion.set("1.0")
+ destinationDirectory.set(new File(buildDir, "tmp"))
+}
+tasks.named("assemble").configure {
+ dependsOn(functionalTestSet.jarTaskName)
+}
+
+// Run tests in the default runServer/runClient configurations
+tasks.named("runServer", JavaExec).configure {
+ dependsOn(functionalTestSet.jarTaskName)
+ classpath(configurations.named(functionalTestSet.runtimeClasspathConfigurationName), tasks.named(functionalTestSet.jarTaskName))
+}
+
+tasks.named("runClient", JavaExec).configure {
+ dependsOn(functionalTestSet.jarTaskName)
+ classpath(configurations.named(functionalTestSet.runtimeClasspathConfigurationName), tasks.named(functionalTestSet.jarTaskName))
+}
diff --git a/dependencies.gradle b/dependencies.gradle
index c2ae58c6..b7ee4687 100644
--- a/dependencies.gradle
+++ b/dependencies.gradle
@@ -100,4 +100,11 @@ dependencies {
//runtimeOnly("thaumcraft:Thaumcraft:1.7.10-4.2.3.5:dev")
//runtimeOnly("com.github.GTNewHorizons:BloodMagic:1.5.1:dev")
//api("curse.maven:witchery-69673:2234410")
+
+ testImplementation('junit:junit:4.12')
+ functionalTestImplementation(platform('org.junit:junit-bom:5.9.2'))
+ functionalTestImplementation('org.junit.jupiter:junit-jupiter')
+ functionalTestImplementation('org.junit.platform:junit-platform-engine')
+ functionalTestImplementation('org.junit.platform:junit-platform-launcher')
+ functionalTestImplementation('org.junit.platform:junit-platform-reporting')
}
diff --git a/settings.gradle b/settings.gradle
index 16a5b4ca..c52d518d 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -17,7 +17,7 @@ pluginManagement {
}
plugins {
- id 'com.gtnewhorizons.gtnhsettingsconvention' version '1.0.14'
+ id 'com.gtnewhorizons.gtnhsettingsconvention' version '1.0.19'
}
diff --git a/src/functionalTest/java/kubatech/test/EIGTests.java b/src/functionalTest/java/kubatech/test/EIGTests.java
new file mode 100644
index 00000000..1bb88814
--- /dev/null
+++ b/src/functionalTest/java/kubatech/test/EIGTests.java
@@ -0,0 +1,342 @@
+/*
+ * spotless:off
+ * KubaTech - Gregtech Addon
+ * Copyright (C) 2022 - 2024 kuba6000
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see .
+ * spotless:on
+ */
+
+package kubatech.test;
+
+import static gregtech.api.util.GT_RecipeBuilder.MINUTES;
+import static gregtech.api.util.GT_RecipeBuilder.SECONDS;
+import static kubatech.tileentity.gregtech.multiblock.GT_MetaTileEntity_ExtremeIndustrialGreenhouse.EIG_BALANCE_IC2_ACCELERATOR_TIER;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Map;
+
+import net.minecraft.block.Block;
+import net.minecraft.init.Blocks;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.util.ChatComponentText;
+import net.minecraft.world.MinecraftException;
+import net.minecraft.world.World;
+import net.minecraft.world.WorldProvider;
+import net.minecraft.world.WorldProviderSurface;
+import net.minecraft.world.WorldServer;
+import net.minecraft.world.WorldSettings;
+import net.minecraft.world.WorldType;
+import net.minecraft.world.chunk.storage.IChunkLoader;
+import net.minecraft.world.storage.IPlayerFileData;
+import net.minecraft.world.storage.ISaveHandler;
+import net.minecraft.world.storage.WorldInfo;
+import net.minecraftforge.common.DimensionManager;
+
+import org.junit.jupiter.api.Test;
+
+import com.gtnewhorizon.gtnhlib.util.map.ItemStackMap;
+
+import gregtech.api.GregTech_API;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.common.blocks.GT_Item_Machines;
+import ic2.api.crops.CropCard;
+import ic2.api.crops.Crops;
+import ic2.core.Ic2Items;
+import ic2.core.crop.TileEntityCrop;
+import kubatech.tileentity.gregtech.multiblock.GT_MetaTileEntity_ExtremeIndustrialGreenhouse;
+
+public class EIGTests {
+
+ private static final int EIG_CONTROLLER_METADATA = 12_792;
+ private static final int NUMBER_OF_CROPS_PER_TEST = 90;
+ private static final int NUMBER_OF_TESTS_TO_DO = 30;
+
+ static World myWorld;
+
+ public EIGTests() {
+ if (!DimensionManager.isDimensionRegistered(256)) {
+ DimensionManager.registerProviderType(256, WorldProviderSurface.class, false);
+ DimensionManager.registerDimension(256, 256);
+ }
+ if (myWorld == null) {
+ myWorld = new WorldServer(MinecraftServer.getServer(), new ISaveHandler() {
+
+ @Override
+ public WorldInfo loadWorldInfo() {
+ return null;
+ }
+
+ @Override
+ public void checkSessionLock() throws MinecraftException {
+
+ }
+
+ @Override
+ public IChunkLoader getChunkLoader(WorldProvider p_75763_1_) {
+ return null;
+ }
+
+ @Override
+ public void saveWorldInfoWithPlayer(WorldInfo p_75755_1_, NBTTagCompound p_75755_2_) {
+
+ }
+
+ @Override
+ public void saveWorldInfo(WorldInfo p_75761_1_) {
+
+ }
+
+ @Override
+ public IPlayerFileData getSaveHandler() {
+ return null;
+ }
+
+ @Override
+ public void flush() {
+
+ }
+
+ @Override
+ public File getWorldDirectory() {
+ return null;
+ }
+
+ @Override
+ public File getMapFileFromName(String p_75758_1_) {
+ return null;
+ }
+
+ @Override
+ public String getWorldDirectoryName() {
+ return "dummy";
+ }
+ },
+ "DummyTestWorld",
+ 256,
+ new WorldSettings(256, WorldSettings.GameType.SURVIVAL, false, false, WorldType.DEFAULT),
+ MinecraftServer.getServer().theProfiler) {
+
+ @Override
+ public File getChunkSaveLocation() {
+ return new File("ignoreme");
+ }
+
+ @Override
+ public int getBlockLightValue(int p_72957_1_, int p_72957_2_, int p_72957_3_) {
+ return 4;
+ }
+ };
+ }
+ }
+
+ private static int leftOverTicksFromRealRun = 0;
+
+ ItemStackMap getRealDrops(TileEntityCrop cropTile, CropCard cc, int growth, int gain, int resistance) {
+ cropTile.setCrop(cc);
+ cropTile.setGrowth((byte) growth);
+ cropTile.setGain((byte) gain);
+ cropTile.setResistance((byte) resistance);
+ cropTile.tick();
+
+ ItemStackMap expected = new ItemStackMap<>();
+
+ // run for 30 minutes
+ for (int k = 0; k < NUMBER_OF_CROPS_PER_TEST; k++) {
+ cropTile.ticker = 1;
+ cropTile.setSize((byte) cc.maxSize());
+ cropTile.setSize(cc.getSizeAfterHarvest(cropTile));
+ cropTile.growthPoints = 0;
+ int lastHarvestedAt = 0;
+ int i;
+ for (i = 0; i < (30 * MINUTES) * (1 << EIG_BALANCE_IC2_ACCELERATOR_TIER);) {
+ i += TileEntityCrop.tickRate;
+ cropTile.tick();
+ if (!cc.canGrow(cropTile)) {
+ lastHarvestedAt = i;
+ ItemStack[] stacks = cropTile.harvest_automated(false);
+ for (ItemStack stack : stacks) {
+ expected.merge(stack, stack.stackSize, Integer::sum);
+ }
+ }
+ }
+ leftOverTicksFromRealRun += i - lastHarvestedAt;
+ }
+
+ return expected;
+ }
+
+ ItemStackMap getEIGDrops(GT_MetaTileEntity_ExtremeIndustrialGreenhouse EIG, ItemStack stack) {
+ ItemStackMap generated = new ItemStackMap<>();
+ int imax = (30 * MINUTES) / (5 * SECONDS);
+ double ticks_to_ignore_per_operation = Math
+ .ceil((double) leftOverTicksFromRealRun / (NUMBER_OF_CROPS_PER_TEST * imax));
+ for (int j = 0; j < NUMBER_OF_CROPS_PER_TEST; j++) {
+ GT_MetaTileEntity_ExtremeIndustrialGreenhouse.GreenHouseSlot slot = new GT_MetaTileEntity_ExtremeIndustrialGreenhouse.GreenHouseSlot(
+ EIG,
+ stack.copy(),
+ true,
+ false);
+ if (slot.isValid) {
+ for (int i = 0; i < imax; i++) {
+ int ticks_to_ignore = (int) Math.min(ticks_to_ignore_per_operation, leftOverTicksFromRealRun);
+ leftOverTicksFromRealRun -= ticks_to_ignore;
+ for (ItemStack ic2Drop : slot.getIC2Drops(
+ EIG,
+ (5 * SECONDS * (1 << EIG_BALANCE_IC2_ACCELERATOR_TIER)) - (double) ticks_to_ignore)) {
+ generated.merge(ic2Drop, ic2Drop.stackSize, Integer::sum);
+ }
+ }
+ }
+ }
+
+ return generated;
+ }
+
+ @Test
+ void EIGDrops() {
+
+ myWorld.setBlock(10, 80, 0, Blocks.farmland, 0, 0);
+ myWorld.setBlock(10, 81, 0, Block.getBlockFromItem(Ic2Items.crop.getItem()), 0, 0);
+ CropCard cc = Crops.instance.getCropCard("gregtech", "Indigo");
+ TileEntityCrop cropTile = (TileEntityCrop) myWorld.getTileEntity(10, 81, 0);
+ ItemStack ccStack = cropTile.generateSeeds(cc, (byte) 10, (byte) 10, (byte) 10, (byte) 1);
+ for (int i = 0; i < TileEntityCrop.tickRate; i++) {
+ cropTile.waterStorage = 200;
+ cropTile.updateEntity();
+ }
+
+ GT_Item_Machines itemMachines = (GT_Item_Machines) Item.getItemFromBlock(GregTech_API.sBlockMachines);
+ itemMachines.placeBlockAt(
+ new ItemStack(itemMachines, 1, EIG_CONTROLLER_METADATA),
+ null,
+ myWorld,
+ 0,
+ 81,
+ 0,
+ 2,
+ 0,
+ 0,
+ 0,
+ EIG_CONTROLLER_METADATA);
+ IGregTechTileEntity te = (IGregTechTileEntity) myWorld.getTileEntity(0, 81, 0);
+ GT_MetaTileEntity_ExtremeIndustrialGreenhouse EIG = (GT_MetaTileEntity_ExtremeIndustrialGreenhouse) te
+ .getMetaTileEntity();
+
+ int[] abc = new int[] { 0, -2, 3 };
+ int[] xyz = new int[] { 0, 0, 0 };
+ EIG.getExtendedFacing()
+ .getWorldOffset(abc, xyz);
+ xyz[0] += te.getXCoord();
+ xyz[1] += te.getYCoord();
+ xyz[2] += te.getZCoord();
+
+ myWorld.setBlock(xyz[0], xyz[1] - 2, xyz[2], GregTech_API.sBlockCasings4, 1, 0);
+ myWorld.setBlock(xyz[0], xyz[1] - 1, xyz[2], Blocks.farmland, 0, 0);
+
+ ItemStack stackToTest = null;
+
+ for (int n = 0; n < 5; n++) {
+
+ int[] x = new int[NUMBER_OF_TESTS_TO_DO];
+ int[] y = new int[NUMBER_OF_TESTS_TO_DO];
+
+ // MinecraftServer.getServer()
+ // .addChatMessage(new ChatComponentText("[EIGTest results]"));
+
+ for (int i = 0; i < NUMBER_OF_TESTS_TO_DO; i++) {
+ leftOverTicksFromRealRun = 0;
+ ItemStackMap expected = getRealDrops(cropTile, cc, 10, 10, 10);
+ ItemStackMap generated = getEIGDrops(EIG, ccStack);
+
+ // MinecraftServer.getServer()
+ // .addChatMessage(new ChatComponentText("[TEST" + i + "]Real crop drops:"));
+ // for (Map.Entry entry : expected.entrySet()) {
+ // MinecraftServer.getServer()
+ // .addChatMessage(new ChatComponentText("- " + entry.getKey().getDisplayName() + " x" +
+ // entry.getValue()));
+ // }
+
+ // MinecraftServer.getServer()
+ // .addChatMessage(new ChatComponentText("[TEST" + i + "]EIG crop drops:"));
+ // for (Map.Entry entry : generated.entrySet()) {
+ // MinecraftServer.getServer()
+ // .addChatMessage(new ChatComponentText("- " + entry.getKey().getDisplayName() + " x" +
+ // entry.getValue()));
+ // }
+
+ // we are only comparing one item from drops
+ if (stackToTest == null) {
+ stackToTest = expected.entrySet()
+ .stream()
+ .max(Comparator.comparingInt(Map.Entry::getValue))
+ .get()
+ .getKey();
+ }
+
+ int expectedValue = expected.getOrDefault(stackToTest, 0);
+ int generatedValue = generated.getOrDefault(stackToTest, 0);
+
+ x[i] = expectedValue;
+ y[i] = generatedValue;
+ }
+
+ double real_average = Arrays.stream(x)
+ .average()
+ .getAsDouble();
+ double eig_average = Arrays.stream(y)
+ .average()
+ .getAsDouble();
+
+ double real_variance = 0d;
+ double a = 0d;
+ for (int i : x) {
+ a += (i - real_average) * (i - real_average);
+ }
+ a /= NUMBER_OF_TESTS_TO_DO;
+ real_variance = a;
+
+ double eig_variance = 0d;
+ a = 0d;
+ for (int i : y) {
+ a += (i - eig_average) * (i - eig_average);
+ }
+ a /= NUMBER_OF_TESTS_TO_DO;
+ eig_variance = a;
+
+ double u = (real_average - eig_average)
+ / Math.sqrt((real_variance / NUMBER_OF_TESTS_TO_DO) + (eig_variance / NUMBER_OF_TESTS_TO_DO));
+ MinecraftServer.getServer()
+ .addChatMessage(
+ new ChatComponentText(
+ "real average = " + Math
+ .round(real_average) + " eig average = " + Math.round(eig_average) + " u = " + u));
+ double test_critical_value = 1.959964d;
+ boolean passed = Math.abs(u) < test_critical_value;
+ boolean instafail = Math.abs(u) > test_critical_value * 2;
+ if (passed) return;
+ assertFalse(instafail);
+ }
+ fail();
+
+ }
+
+}
diff --git a/src/functionalTest/java/kubatech/test/kubatechTestMod.java b/src/functionalTest/java/kubatech/test/kubatechTestMod.java
new file mode 100644
index 00000000..bc68d3f2
--- /dev/null
+++ b/src/functionalTest/java/kubatech/test/kubatechTestMod.java
@@ -0,0 +1,101 @@
+package kubatech.test;
+
+import java.io.File;
+import java.io.PrintWriter;
+import java.nio.file.FileSystems;
+import java.nio.file.Path;
+
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.util.ChatComponentText;
+
+import org.apache.commons.io.output.CloseShieldOutputStream;
+import org.junit.platform.engine.discovery.DiscoverySelectors;
+import org.junit.platform.launcher.Launcher;
+import org.junit.platform.launcher.LauncherDiscoveryRequest;
+import org.junit.platform.launcher.LauncherSession;
+import org.junit.platform.launcher.TestPlan;
+import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder;
+import org.junit.platform.launcher.core.LauncherFactory;
+import org.junit.platform.launcher.listeners.SummaryGeneratingListener;
+import org.junit.platform.launcher.listeners.TestExecutionSummary;
+import org.junit.platform.reporting.legacy.xml.LegacyXmlReportGeneratingListener;
+
+import cpw.mods.fml.common.FMLCommonHandler;
+import cpw.mods.fml.common.Mod;
+import cpw.mods.fml.common.Mod.EventHandler;
+import cpw.mods.fml.common.event.FMLPreInitializationEvent;
+import cpw.mods.fml.common.event.FMLServerStartedEvent;
+import gregtech.GT_Mod;
+
+@Mod(
+ modid = "kubatech-tests",
+ name = "KubaTech Dev Tests",
+ version = "1.0",
+ dependencies = "required-after:kubatech;required-after:gregtech;after:berriespp;")
+public class kubatechTestMod {
+
+ @EventHandler
+ public void preInit(FMLPreInitializationEvent ev) {
+ // Disable GT5u messing with vanilla recipes for unit tests
+ GT_Mod.gregtechproxy.mNerfedWoodPlank = false;
+ GT_Mod.gregtechproxy.mNerfedVanillaTools = false;
+ }
+
+ @EventHandler
+ public void onServerStarted(FMLServerStartedEvent startedEv) {
+ MinecraftServer.getServer()
+ .addChatMessage(new ChatComponentText("Running KT unit tests..."));
+ runTests();
+ MinecraftServer.getServer()
+ .addChatMessage(new ChatComponentText("Running KT unit tests finished"));
+ }
+
+ public void runTests() {
+ // https://junit.org/junit5/docs/current/user-guide/#launcher-api
+ System.setProperty("junit.platform.reporting.open.xml.enabled", "false");
+ final Path testsXmlOutDir = FileSystems.getDefault()
+ .getPath("./junit-out/")
+ .toAbsolutePath();
+ final File testsXmlOutDirFile = testsXmlOutDir.toFile();
+ testsXmlOutDirFile.mkdirs();
+ {
+ File[] fileList = testsXmlOutDirFile.listFiles();
+ if (fileList != null) {
+ for (File child : fileList) {
+ if (child.isFile() && child.getName()
+ .endsWith(".xml")) {
+ child.delete();
+ }
+ }
+ }
+ }
+ final LauncherDiscoveryRequest discovery = LauncherDiscoveryRequestBuilder.request()
+ .selectors(DiscoverySelectors.selectPackage("kubatech.test"))
+ .build();
+ final SummaryGeneratingListener summaryGenerator = new SummaryGeneratingListener();
+ final TestExecutionSummary summary;
+ try (PrintWriter stderrWriter = new PrintWriter(new CloseShieldOutputStream(System.err), true)) {
+ final LegacyXmlReportGeneratingListener xmlGenerator = new LegacyXmlReportGeneratingListener(
+ testsXmlOutDir,
+ stderrWriter);
+ try (LauncherSession session = LauncherFactory.openSession()) {
+ final Launcher launcher = session.getLauncher();
+ final TestPlan plan = launcher.discover(discovery);
+ launcher.registerTestExecutionListeners(summaryGenerator, xmlGenerator);
+ launcher.execute(plan);
+ }
+ summary = summaryGenerator.getSummary();
+
+ summary.printFailuresTo(stderrWriter, 32);
+ summary.printTo(stderrWriter);
+ stderrWriter.flush();
+ }
+ // Throw an exception if running via `runServer`
+ if (summary.getTotalFailureCount() > 0 && FMLCommonHandler.instance()
+ .getSide()
+ .isServer()) {
+ throw new RuntimeException("Some of the unit tests failed to execute, check the log for details");
+ }
+ }
+
+}
diff --git a/src/functionalTest/resources/mcmod.info b/src/functionalTest/resources/mcmod.info
new file mode 100644
index 00000000..e80179bb
--- /dev/null
+++ b/src/functionalTest/resources/mcmod.info
@@ -0,0 +1,15 @@
+[
+ {
+ "modid":"kubatech-tests",
+ "name":"KubaTech Dev Tests",
+ "description":"KubaTech Tests to run in the development environment",
+ "version":"1.0",
+ "mcversion":"1.7.10",
+ "url":"https://github.com/GTNewHorizons/KubaTech",
+ "updateUrl":"",
+ "authorList":[],
+ "credits":"",
+ "logoFile":"",
+ "screenshots":[]
+ }
+]
diff --git a/src/main/java/kubatech/tileentity/gregtech/multiblock/GT_MetaTileEntity_ExtremeIndustrialGreenhouse.java b/src/main/java/kubatech/tileentity/gregtech/multiblock/GT_MetaTileEntity_ExtremeIndustrialGreenhouse.java
index f43309f7..b94ceca8 100644
--- a/src/main/java/kubatech/tileentity/gregtech/multiblock/GT_MetaTileEntity_ExtremeIndustrialGreenhouse.java
+++ b/src/main/java/kubatech/tileentity/gregtech/multiblock/GT_MetaTileEntity_ExtremeIndustrialGreenhouse.java
@@ -140,7 +140,37 @@
public class GT_MetaTileEntity_ExtremeIndustrialGreenhouse
extends KubaTechGTMultiBlockBase {
+ /***
+ * BALANCE OF THE IC2 MODE:
+ * (let T = EIG_BALANCE_IC2_ACCELERATOR_TIER)
+ * All IC2 crops are simulated and all drops are generated based on the real crop drops.
+ * T is a tick accelerator tier for the IC2 crops,
+ * Each crop in the EIG is accelerated using T tier accelerator
+ * (Accelerators in the game are defined as 2^T acceleration, 8*(4^T) voltage, 6 amps)
+ * IC2 mode is unlocked at T+1 tier (glass and power)
+ * And each amp of T gives one crop slot, EIG only consumes 1 AMP of a tier that it is at
+ * (EIG starts at 4 crops (T+1 tier) and each tier quadruples the amount of slots)
+ * Each crop is accelerated 2^T times
+ * Summary:
+ * Accelerators in EIG are a bit cheaper than on the crop field (4 amps instead of 6 amps)
+ * There are 4 crops touching the accelerator (1 AMP for 1 accelerated crop)
+ *
+ * Changing T one number down will buff the EIG twice, as well as changing it up will nerf the EIG twice
+ * (That is because accelerators are imperfectly scaled in game LV = 2x, MV = 4x, ...)
+ */
+ public static final int EIG_BALANCE_IC2_ACCELERATOR_TIER = 5; // IV
+
+ /***
+ * All crops above this threshold will die if WEED-EX 9000 isn't supplied.
+ * Consider changing this value after making changes to the EIG_BALANCE_IC2_ACCELERATOR_TIER.
+ */
+ private static final int EIG_CROP_LIMIT_FOR_WEEDEX9000_REQUIREMENT = 1000;
+
private static final boolean debug = false;
+
+ /***
+ * Changing this variable will cause ALL EIGs in the world to regenerate their drop tables.
+ */
private static final int EIG_MATH_VERSION = 0;
private static final int CONFIGURATION_WINDOW_ID = 999;
@@ -288,7 +318,9 @@ protected GT_Multiblock_Tooltip_Builder createTooltip() {
.addInfo("Use screwdriver while sneaking to enable/disable IC2 mode")
.addInfo("Use wire cutters to give incoming IC2 crops 0 humidity")
.addInfo("Uses 1000L of water per crop per operation")
- .addInfo("If there are >= 1000 crops -> Uses 1L of Weed-EX 9000 per crop per second")
+ .addInfo(
+ "If there are >= " + EIG_CROP_LIMIT_FOR_WEEDEX9000_REQUIREMENT
+ + " crops -> Uses 1L of Weed-EX 9000 per crop per second")
.addInfo("Otherwise, around 1% of crops will die each operation")
.addInfo("You can insert fertilizer each operation to get more drops (max +400%)")
.addInfo("-------------------- SETUP MODE --------------------")
@@ -309,13 +341,15 @@ protected GT_Multiblock_Tooltip_Builder createTooltip() {
.addInfo("Will automatically craft seeds if they are not dropped")
.addInfo("1 Fertilizer per 1 crop +200%")
.addInfo("-------------------- IC2 CROPS --------------------")
- .addInfo("Minimal tier: " + voltageTooltipFormatted(6))
- .addInfo("Need " + voltageTooltipFormatted(6) + " glass tier")
+ .addInfo("Minimal tier: " + voltageTooltipFormatted(EIG_BALANCE_IC2_ACCELERATOR_TIER + 1))
+ .addInfo("Need " + voltageTooltipFormatted(EIG_BALANCE_IC2_ACCELERATOR_TIER + 1) + " glass tier")
.addInfo("Starting with 4 slots")
.addInfo("Every slot gives 1 crop")
- .addInfo("Every tier past " + voltageTooltipFormatted(6) + ", slots are multiplied by 4")
+ .addInfo(
+ "Every tier past " + voltageTooltipFormatted(EIG_BALANCE_IC2_ACCELERATOR_TIER + 1)
+ + ", slots are multiplied by 4")
.addInfo("Process time: 5 sec")
- .addInfo("All crops are accelerated by x32 times")
+ .addInfo("All crops are accelerated by x" + (1 << EIG_BALANCE_IC2_ACCELERATOR_TIER) + " times")
.addInfo("1 Fertilizer per 1 crop +10%")
.addInfo(StructureHologram)
.addSeparator()
@@ -401,8 +435,8 @@ public void construct(ItemStack itemStack, boolean b) {
private void updateMaxSlots() {
int tier = getVoltageTier();
- if (tier < (isIC2Mode ? 6 : 4)) mMaxSlots = 0;
- else if (isIC2Mode) mMaxSlots = 4 << (2 * (tier - 6));
+ if (tier < (isIC2Mode ? (EIG_BALANCE_IC2_ACCELERATOR_TIER + 1) : 4)) mMaxSlots = 0;
+ else if (isIC2Mode) mMaxSlots = 4 << (2 * (tier - (EIG_BALANCE_IC2_ACCELERATOR_TIER + 1)));
else mMaxSlots = 1 << (tier - 4);
}
@@ -418,7 +452,7 @@ public CheckRecipeResult checkProcessing() {
}
if (setupphase > 0) {
- if ((mStorage.size() >= mMaxSlots && setupphase == 1) || (mStorage.size() == 0 && setupphase == 2))
+ if ((mStorage.size() >= mMaxSlots && setupphase == 1) || (mStorage.isEmpty() && setupphase == 2))
return CheckRecipeResultRegistry.NO_RECIPE;
if (setupphase == 1) {
@@ -449,7 +483,7 @@ public CheckRecipeResult checkProcessing() {
waterusage = 0;
weedexusage = 0;
for (GreenHouseSlot s : mStorage) waterusage += s.input.stackSize;
- if (waterusage >= 1000) weedexusage = waterusage;
+ if (waterusage >= EIG_CROP_LIMIT_FOR_WEEDEX9000_REQUIREMENT) weedexusage = waterusage;
waterusage *= 1000;
List fluids = mInputHatches;
@@ -513,12 +547,15 @@ public CheckRecipeResult checkProcessing() {
double multiplier = 1.d + (((double) boost / (double) maxboost) * 4d);
if (isIC2Mode) {
- if (glasTier < 6) return SimpleCheckRecipeResult.ofFailure("EIG_ic2glass");
+ if (glasTier < (EIG_BALANCE_IC2_ACCELERATOR_TIER + 1))
+ return SimpleCheckRecipeResult.ofFailure("EIG_ic2glass");
this.mMaxProgresstime = 100;
List outputs = new ArrayList<>();
for (int i = 0; i < Math.min(mMaxSlots, mStorage.size()); i++) outputs.addAll(
mStorage.get(i)
- .getIC2Drops(this, ((double) this.mMaxProgresstime * 32d) * multiplier));
+ .getIC2Drops(
+ this,
+ ((double) this.mMaxProgresstime * (1 << EIG_BALANCE_IC2_ACCELERATOR_TIER)) * multiplier));
this.mOutputItems = outputs.toArray(new ItemStack[0]);
} else {
this.mMaxProgresstime = Math.max(20, 100 / (tier - 3)); // Min 1 s
@@ -552,7 +589,7 @@ public boolean checkMachine(IGregTechTileEntity iGregTechTileEntity, ItemStack i
for (GT_MetaTileEntity_Hatch_Energy hatchEnergy : this.mEnergyHatches)
if (this.glasTier < hatchEnergy.mTier) return false;
- boolean valid = this.mMaintenanceHatches.size() == 1 && this.mEnergyHatches.size() >= 1 && this.mCasing >= 70;
+ boolean valid = this.mMaintenanceHatches.size() == 1 && !this.mEnergyHatches.isEmpty() && this.mCasing >= 70;
if (valid) updateMaxSlots();
@@ -980,14 +1017,16 @@ private ItemStack addCrop(ItemStack input) {
final Map dropprogress = new HashMap<>();
- private static class GreenHouseSlot extends InventoryCrafting {
+ public static class GreenHouseSlot extends InventoryCrafting {
+
+ private static final int NUMBER_OF_GENERATIONS_TO_MAKE = 10;
final ItemStack input;
Block crop;
ArrayList customDrops = null;
ItemStack undercrop = null;
List drops;
- boolean isValid;
+ public boolean isValid;
boolean isIC2Crop;
boolean noHumidity;
int growthticks;
@@ -1259,6 +1298,14 @@ public void recalculate(GT_MetaTileEntity_ExtremeIndustrialGreenhouse tileEntity
te.setGain(ga);
te.setResistance(re);
+ if (noHumidity) te.humidity = 0;
+ else {
+ te.waterStorage = 200;
+ te.humidity = te.updateHumidity();
+ }
+ te.airQuality = te.updateAirQuality();
+ te.nutrients = te.updateNutrients();
+
ItemStack tobeused = null;
if (undercrop != null) setBlock(undercrop, xyz[0], xyz[1] - 2, xyz[2], world);
@@ -1290,9 +1337,11 @@ public void recalculate(GT_MetaTileEntity_ExtremeIndustrialGreenhouse tileEntity
// GENERATE DROPS
generations = new ArrayList<>();
- out: for (int i = 0; i < 10; i++) // get 10 generations
+ int afterHarvestCropSize = 0;
+ out: for (int i = 0; i < NUMBER_OF_GENERATIONS_TO_MAKE; i++) // get 10 generations
{
ItemStack[] st = te.harvest_automated(false);
+ afterHarvestCropSize = te.getSize();
te.setSize((byte) cc.maxSize());
if (st == null) continue;
if (st.length == 0) continue;
@@ -1303,15 +1352,21 @@ public void recalculate(GT_MetaTileEntity_ExtremeIndustrialGreenhouse tileEntity
rn = new Random();
// CHECK GROWTH SPEED
- te.humidity = (byte) (noHumidity ? 0 : 12); // humidity with full water storage or 0 humidity
- te.airQuality = 6; // air quality when sky is seen
- te.nutrients = 8; // nutrients with full nutrient storage
-
- int dur = cc.growthDuration(te);
- int rate = te.calcGrowthRate();
- if (rate == 0) return; // should not be possible with those stats
- growthticks = (int) Math.ceil(
- ((double) dur / (double) rate) * (double) cc.maxSize() * (double) TileEntityCrop.tickRate);
+
+ growthticks = 0;
+
+ for (int i = afterHarvestCropSize; i < cc.maxSize(); i++) {
+ te.setSize((byte) i);
+ int grown = 0;
+ do {
+ int rate = te.calcGrowthRate();
+ if (rate == 0) return;
+ growthticks++;
+ grown += rate;
+ } while (grown < cc.growthDuration(te));
+ }
+
+ growthticks *= TileEntityCrop.tickRate;
if (growthticks < 1) growthticks = 1;
if (tobeused != null) tobeused.stackSize--;
@@ -1337,7 +1392,7 @@ public List getDrops() {
public List getIC2Drops(GT_MetaTileEntity_ExtremeIndustrialGreenhouse tileEntity,
double timeelapsed) {
- int r = rn.nextInt(10);
+ int r = rn.nextInt(NUMBER_OF_GENERATIONS_TO_MAKE);
if (generations.size() <= r) return new ArrayList<>();
double growthPercent = (timeelapsed / (double) growthticks);
List generation = generations.get(r);
@@ -1365,7 +1420,7 @@ public List getIC2Drops(GT_MetaTileEntity_ExtremeIndustrialGreenhouse
public int addDrops(World world, int count) {
if (drops == null) drops = new ArrayList<>();
- if (customDrops != null && customDrops.size() > 0) {
+ if (customDrops != null && !customDrops.isEmpty()) {
@SuppressWarnings("unchecked")
ArrayList d = (ArrayList) customDrops.clone();
for (ItemStack x : drops) {