From 775b888722574c621ef3c6b779bbf6bbbd20046e Mon Sep 17 00:00:00 2001 From: Alvaro Martinez Date: Tue, 1 Sep 2026 19:32:56 +0200 Subject: [PATCH] Beeyonds Home: working multiblock apiary with Production/Breed modes The controller has been sitting in the tree as an empty shell (structure only, no recipe or tick logic, quest even said "not yet finished"). This gives it an actual job, split into two modes picked from a mode-cycle button in the custom GUI (also settable from the parameter panel): - Production: Alveary-style. Slot count scales with the highest energy tier plugged in (4 queens per named GT tier). Each queen ages and produces on its own simple cycle instead of going through Forestry's IBeekeepingLogic, so the machine never plants real flowers or triggers bee special effects in the world - it's meant to be a clean, self- contained production line. - Breed: drop two parent bees in an input bus, pick the target species and every trait from the panel. Same-species pairs breed as a pure line even with no registered mutation; a pairing with no valid mutation holds the parents instead of ejecting them every tick. 20s cycle, output is either a mated queen or a batch of up to 64 drones at 3x yield, always homozygous on the chosen alleles. GUI: a status line plus either the queen grid or the two parent slots + species picker live inside GT5's own terminal status box (as an extra row in its scrollable list), instead of a separate panel that would need the window itself to grow - keeps the layout in line with how every other multiblock in the pack sizes its own GUI. Also fixes a structure offset bug (hologram rendered away from the real controller), a bee-duplication bug in the input-pulling logic, an output void when a produced comb/queen couldn't fit because the output bus was full, and translates the questline (including a new bilingual quest entry for this multiblock) to English. --- .gitignore | 4 + .../newmaa/othtech/machine/OTEBeeyonds.java | 1282 ++++++++++++++++- .../othtech/machine/OTHBeeyondsMode.java | 6 + .../machine/OTHBeeyondsOutputKind.java | 7 + .../othtech/machine/gui/OTHBeeyondsGui.java | 331 +++++ .../newmaa/othtech/recipe/RecipesMain.java | 16 + .../assets/123technology/lang/en_US.lang | 34 + .../assets/123technology/lang/zh_CN.lang | 34 + .../QuestLine.json | 2 +- .../ActiveTransform-7092635789920456480.json | 4 +- .../BBPlasmaForge-2376869744404875678.json | 4 +- .../Beeyonds--3725891889802621959.json | 4 +- .../BlastFurnace--2865328750978580233.json | 4 +- .../ChemReactor-2224328208122200209.json | 4 +- .../Computer-487024529263250003.json | 4 +- .../DiracTide-2351942955227433758.json | 4 +- .../EnderIO--7210356133696356332.json | 4 +- .../EpicCokeOven--2222461257957685751.json | 4 +- .../FishFactory--1156493011789461005.json | 4 +- .../FoodGen-7097361839427765517.json | 4 +- .../GraveDragon--1204911824894343714.json | 4 +- .../HeatExchanger-3066769644673976920.json | 4 +- .../IsaFactory--2618974253404830901.json | 4 +- .../IsaForge-944708431315027684.json | 4 +- .../LargeBin--8940520277995012008.json | 4 +- .../LargeCircuitAss--8560569534669306506.json | 4 +- .../MegaBlastFurnace-3771014811916846052.json | 4 +- .../MegaCircuitLine--3684854234097562609.json | 4 +- .../MegaFactory--4046027631891429286.json | 4 +- .../MegaFreezer-8443499639976053383.json | 4 +- .../NQFuelEngine--5655652267192334289.json | 4 +- .../NQFuelRefinery--1250477325629895402.json | 4 +- .../RocketAssembler-2203467160277829399.json | 4 +- .../Sinopec--1316793397220718578.json | 4 +- .../SoulPrison--3428309261842625794.json | 4 +- .../SteamNineInOne-998481993715303752.json | 4 +- .../SunFactory--8646128165033130074.json | 4 +- .../TangshanSteel--717211484021964706.json | 4 +- .../Welcome-7722944585155103069.json | 4 +- .../WoodFusion-171878863013233495.json | 4 +- 40 files changed, 1767 insertions(+), 73 deletions(-) create mode 100644 src/main/java/com/newmaa/othtech/machine/OTHBeeyondsMode.java create mode 100644 src/main/java/com/newmaa/othtech/machine/OTHBeeyondsOutputKind.java create mode 100644 src/main/java/com/newmaa/othtech/machine/gui/OTHBeeyondsGui.java diff --git a/.gitignore b/.gitignore index 4a5697b..a938a64 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,9 @@ .gradle .settings +/HANDOUT.md +/docs/beeyonds-gui-terminal-box.md +/docs/mui2-gui-debugging.md +/docs/mui2-two-mode-gui-patterns.md /.idea/ /tmp/ /.vscode/ diff --git a/src/main/java/com/newmaa/othtech/machine/OTEBeeyonds.java b/src/main/java/com/newmaa/othtech/machine/OTEBeeyonds.java index 2c78d57..d965c26 100644 --- a/src/main/java/com/newmaa/othtech/machine/OTEBeeyonds.java +++ b/src/main/java/com/newmaa/othtech/machine/OTEBeeyonds.java @@ -8,35 +8,166 @@ import static gregtech.api.util.GTStructureUtility.buildHatchAdder; import static net.minecraft.util.StatCollector.translateToLocal; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.List; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.init.Blocks; import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.util.ChunkCoordinates; import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.Vec3; +import net.minecraft.world.World; +import net.minecraft.world.biome.BiomeGenBase; import net.minecraftforge.common.util.ForgeDirection; +import org.jetbrains.annotations.NotNull; + import com.gtnewhorizon.structurelib.alignment.constructable.IConstructable; import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable; import com.gtnewhorizon.structurelib.structure.IItemSource; import com.gtnewhorizon.structurelib.structure.IStructureDefinition; +import com.mojang.authlib.GameProfile; +import com.newmaa.othtech.machine.gui.OTHBeeyondsGui; import com.newmaa.othtech.machine.machineclass.OTHTTMultiMachineBaseEM; +import forestry.api.apiculture.BeeManager; +import forestry.api.apiculture.EnumBeeChromosome; +import forestry.api.apiculture.EnumBeeType; +import forestry.api.apiculture.IAlleleBeeEffect; +import forestry.api.apiculture.IAlleleBeeSpecies; +import forestry.api.apiculture.IBee; +import forestry.api.apiculture.IBeeGenome; +import forestry.api.apiculture.IBeeHousing; +import forestry.api.apiculture.IBeeHousingInventory; +import forestry.api.apiculture.IBeeListener; +import forestry.api.apiculture.IBeeModifier; +import forestry.api.apiculture.IBeeMutation; +import forestry.api.apiculture.IBeeRoot; +import forestry.api.apiculture.IBeekeepingLogic; +import forestry.api.core.EnumHumidity; +import forestry.api.core.EnumTemperature; +import forestry.api.core.IErrorLogic; +import forestry.api.genetics.AlleleManager; +import forestry.api.genetics.IAllele; +import forestry.api.genetics.IAlleleFlowers; +import forestry.core.errors.EnumErrorCode; +import forestry.core.errors.ErrorLogic; +import forestry.core.genetics.alleles.AlleleHelper; +import forestry.core.genetics.alleles.EnumAllele; import gregtech.api.enums.SoundResource; import gregtech.api.interfaces.ITexture; import gregtech.api.interfaces.metatileentity.IMetaTileEntity; import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.metatileentity.implementations.MTEHatchEnergy; +import gregtech.api.recipe.check.CheckRecipeResult; +import gregtech.api.recipe.check.CheckRecipeResultRegistry; import gregtech.api.render.TextureFactory; import gregtech.api.structure.error.StructureError; +import gregtech.api.util.GTUtility; import gregtech.api.util.MultiblockTooltipBuilder; +import gregtech.common.gui.modularui.multiblock.base.MTEMultiBlockBaseGui; import tectech.thing.metaTileEntity.multi.base.TTMultiblockBase; +import tectech.thing.metaTileEntity.multi.base.parameter.BooleanParameter; +import tectech.thing.metaTileEntity.multi.base.parameter.EnumParameter; +import tectech.thing.metaTileEntity.multi.base.parameter.IParametrized; +import tectech.thing.metaTileEntity.multi.base.parameter.Parameter; +import tectech.thing.metaTileEntity.multi.base.parameter.StringParameter; + +/** + * Beeyonds Home / Beeyonds 之家. + *

+ * Two operation modes, switchable from the machine's parameter panel: + *

+ */ +public class OTEBeeyonds extends OTHTTMultiMachineBaseEM + implements IConstructable, ISurvivalConstructable, IParametrized { + + private static final int QUEEN_SLOTS_PER_TIER = 4; + private static final int BREED_TIME_TICKS = 20 * 20; // 20 seconds + private static final float BREED_YIELD_MULTIPLIER = 3.0F; // 300% + private static final int MAX_DRONES_PER_CYCLE = 64; + // Fixed EU/t cost per queen actively held in Production mode. Not itself doubled per tier - the + // "overclock" comes for free from queenSlotCount() already doubling per named GT tier, so total + // consumption (cost-per-queen x held queens) scales exactly the same way capacity does: twice the + // tier, twice the queens, twice the EU/t, without needing a second multiplier layered on top. + private static final long EU_COST_PER_QUEEN = 8L; + // Same pacing Forestry's own BeekeepingLogic uses internally (queenWorkCycleThrottle >= 550, + // ~27.5s) - kept so production timing feels the same as before, now driven by our own minimal + // tick instead of Forestry's doWork() (see tickProduction()). + private static final int QUEEN_WORK_CYCLE_TICKS = 550; + + // region parameters (machine settings panel) + // NOTE: Parameter(value, langKey, nbtKey, ...) - langKey MUST be the translateToLocal() key + // (the "otht.bee.param.xxx" strings, see en_US.lang) and nbtKey is the short internal save id. + // These two used to be swapped on every field below, which made the settings panel show raw + // untranslated keys ("mode", "traitSpeed", ...) instead of the proper labels. + // + // These fields are declared WITHOUT inline initializers on purpose: TTMultiblockBase's own + // constructor calls initParameters() (see `if (this instanceof IParametrized parametrized) + // parametrized.initParameters();` in TTMultiblockBase(...)) before ANY of this subclass's own + // field initializers have run - a plain `= new EnumParameter<>(...)` here would still be null + // at that point. That was silently building mParameters out of 15 nulls, which blew up as a + // NullPointerException the moment loadNBTData()/saveNBTData() iterated it (every chunk + // load/save), and the whole controller/simulation effectively never worked. Constructing the + // Parameter objects inside initParameters() itself (called below) sidesteps the ordering issue + // entirely, since it doesn't depend on this class's own field-initializer timing. + private EnumParameter pMode; + private BooleanParameter pEjectHeldBees; + private StringParameter pTargetSpecies; + private EnumParameter pOutputKind; + private EnumParameter pSpeed; + private EnumParameter pFertility; + private EnumParameter pLifespan; + private EnumParameter pFlowering; + private EnumParameter pTerritory; + private EnumParameter pTempTolerance; + private EnumParameter pHumidTolerance; + private BooleanParameter pNocturnal; + private BooleanParameter pTolerantFlyer; + private BooleanParameter pCaveDwelling; + private StringParameter pEffect; + private StringParameter pFlowerProvider; -public class OTEBeeyonds extends OTHTTMultiMachineBaseEM implements IConstructable, ISurvivalConstructable { + private List> mParameters; + // subset of mParameters shown in TecTech's generic settings panel - see initParameters() + private List> mSettingsPanelParameters; + // endregion + + // internal state, not part of the GT structure - purely virtual bookkeeping + private final List mQueenCells = new ArrayList<>(); + private ItemStack mParent0; + private ItemStack mParent1; + private int mBreedProgress = 0; + private GameProfile mCachedOwner; + // throttles the "no mutation" log in tickBreed() to once per distinct species pairing instead + // of every tick, now that pairing without a mutation holds the parents instead of ejecting them. + // the mode the machine is actually operating as; lags pMode until the bees held by the mode + // being left have been fully ejected, so a mode switch never silently destroys them + private OTHBeeyondsMode mActiveMode = OTHBeeyondsMode.PRODUCTION; @Override public void checkMachine(IGregTechTileEntity iGregTechTileEntity, ItemStack itemStack, List errors) { - if (!checkPiece("main", 0, 1, 0, errors)) return; + // offset must match the '~' hint cell in STRUCTURE_DEFINITION (x=11, y=20, z=1) - same + // value survivalConstruct() below already uses. This was previously (0, 1, 0), which made + // StructureLib think the controller sat far away from where it actually was, so the + // hologram/structure rendered wildly offset from the real controller block. + if (!checkPiece("main", 11, 20, 1, errors)) { + return; + } checkHasEnergyHatch(errors); checkHasMaintenanceHatch(errors); checkHasOutputBus(errors); @@ -49,6 +180,18 @@ public void onFirstTick_EM(IGregTechTileEntity aBaseMetaTileEntity) { } } + /** + * {@code CommonMetaTileEntity.openGui()} only takes the MUI2 path (the one that calls + * {@link #getGui()} and therefore ever shows {@link OTHBeeyondsGui}) when + * {@code GTGuis.GLOBAL_SWITCH_MUI2 && useMui2() || forceUseMui2()} is true. Since + * {@code GTGuis.GLOBAL_SWITCH_MUI2} isn't guaranteed to be enabled globally, forcing it here + * (independent of that switch) is what makes the custom panel appear for this controller. + */ + @Override + protected boolean forceUseMui2() { + return true; + } + // region structure private static final String[] description = new String[] { EnumChatFormatting.AQUA + translateToLocal("otht.con") + ":", translateToLocal("ote.cm.bee.0"), }; @@ -120,17 +263,94 @@ public IStructureDefinition getStructure_EM() { public OTEBeeyonds(int aID, String aName, String aNameRegional) { super(aID, aName, aNameRegional); - eDismantleBoom = true; } public OTEBeeyonds(String aName) { super(aName); - eDismantleBoom = true; + } + + /** + * Deliberately does NOT call {@code super.onRemoval()}: {@link OTHTTMultiMachineBaseEM#onRemoval()} + * detonates the controller ({@code explodeMultiblockOTH()}) whenever {@code ePowerPass && getEUVar() + * > V[3]} - i.e. whenever "power pass" is on and the buffered EU in the energy hatches exceeds the + * HV threshold - independently of {@code eDismantleBoom} (which this class no longer sets anyway). + * That's a reactor-style safety mechanic inherited from the shared base class; Beeyonds Home is a + * bee farm, not a reactor, and must never explode when its controller is broken, no matter how much + * EU happens to be buffered at the time. + */ + @Override + public void onRemoval() {} + + @Override + public void saveNBTData(NBTTagCompound aNBT) { + super.saveNBTData(aNBT); + NBTTagCompound paramTag = new NBTTagCompound(); + // mParameters (the FULL list), not getParameters() (the settings-panel subset) - pTargetSpecies + // is excluded from getParameters() on purpose (see initParameters()) but must still persist. + for (Parameter parameter : mParameters) { + parameter.saveNBT(paramTag); + } + aNBT.setTag("othBeeyondsParams", paramTag); + + aNBT.setInteger("othBeeyondsActiveMode", mActiveMode.ordinal()); + aNBT.setInteger("othBeeyondsBreedProgress", mBreedProgress); + if (mParent0 != null) aNBT.setTag("othBeeyondsParent0", mParent0.writeToNBT(new NBTTagCompound())); + if (mParent1 != null) aNBT.setTag("othBeeyondsParent1", mParent1.writeToNBT(new NBTTagCompound())); + + NBTTagList queenList = new NBTTagList(); + for (QueenCell cell : mQueenCells) { + NBTTagCompound cellTag = new NBTTagCompound(); + if (cell.queenStack != null) { + cellTag.setTag("queen", cell.queenStack.writeToNBT(new NBTTagCompound())); + } + cellTag.setInteger("workCycleThrottle", cell.workCycleThrottle); + NBTTagList pendingList = new NBTTagList(); + for (ItemStack product : cell.pendingProducts) { + pendingList.appendTag(product.writeToNBT(new NBTTagCompound())); + } + cellTag.setTag("pendingProducts", pendingList); + queenList.appendTag(cellTag); + } + aNBT.setTag("othBeeyondsQueens", queenList); } @Override - public void onRemoval() { - super.onRemoval(); + public void loadNBTData(NBTTagCompound aNBT) { + super.loadNBTData(aNBT); + if (mParameters == null) initParameters(); + NBTTagCompound paramTag = aNBT.getCompoundTag("othBeeyondsParams"); + for (Parameter parameter : mParameters) { + parameter.loadNBT(paramTag); + } + + OTHBeeyondsMode[] modes = OTHBeeyondsMode.values(); + int activeModeOrdinal = aNBT.getInteger("othBeeyondsActiveMode"); + mActiveMode = activeModeOrdinal >= 0 && activeModeOrdinal < modes.length ? modes[activeModeOrdinal] + : OTHBeeyondsMode.PRODUCTION; + + mBreedProgress = aNBT.getInteger("othBeeyondsBreedProgress"); + mParent0 = aNBT.hasKey("othBeeyondsParent0") + ? ItemStack.loadItemStackFromNBT(aNBT.getCompoundTag("othBeeyondsParent0")) + : null; + mParent1 = aNBT.hasKey("othBeeyondsParent1") + ? ItemStack.loadItemStackFromNBT(aNBT.getCompoundTag("othBeeyondsParent1")) + : null; + + mQueenCells.clear(); + NBTTagList queenList = aNBT.getTagList("othBeeyondsQueens", 10); + for (int i = 0; i < queenList.tagCount(); i++) { + NBTTagCompound cellTag = queenList.getCompoundTagAt(i); + QueenCell cell = new QueenCell(); + if (cellTag.hasKey("queen")) { + cell.queenStack = ItemStack.loadItemStackFromNBT(cellTag.getCompoundTag("queen")); + } + cell.workCycleThrottle = cellTag.getInteger("workCycleThrottle"); + NBTTagList pendingList = cellTag.getTagList("pendingProducts", 10); + for (int j = 0; j < pendingList.tagCount(); j++) { + cell.pendingProducts.add(ItemStack.loadItemStackFromNBT(pendingList.getCompoundTagAt(j))); + } + mQueenCells.add(cell); + } } @Override @@ -144,6 +364,8 @@ public MultiblockTooltipBuilder createTooltip() { tt.addMachineType(translateToLocal("ote.tm.bee.0")) .addInfo(translateToLocal("ote.tm.bee.1")) .addInfo(translateToLocal("ote.tm.bee.2")) + .addInfo(translateToLocal("ote.tm.bee.3")) + .addInfo(translateToLocal("ote.tm.bee.4")) .addTecTechHatchInfo() .addSeparator() .toolTipFinisher("§a123Technology - Beeyonds"); @@ -176,19 +398,1059 @@ protected SoundResource getActivitySoundLoop() { return SoundResource.TECTECH_MACHINES_NOISE; } + // region parametrized (settings panel) + @Override + public void initParameters() { + pMode = new EnumParameter<>(OTHBeeyondsMode.class, OTHBeeyondsMode.PRODUCTION, "otht.bee.param.mode", "mode"); + pEjectHeldBees = new BooleanParameter(false, "otht.bee.param.eject", "ejectHeldBees"); + pTargetSpecies = new StringParameter("", "otht.bee.param.species", "targetSpecies"); + pOutputKind = new EnumParameter<>( + OTHBeeyondsOutputKind.class, + OTHBeeyondsOutputKind.QUEEN, + "otht.bee.param.outputKind", + "outputKind"); + pSpeed = new EnumParameter<>( + EnumAllele.Speed.class, + EnumAllele.Speed.NORMAL, + "otht.bee.param.speed", + "traitSpeed"); + pFertility = new EnumParameter<>( + EnumAllele.Fertility.class, + EnumAllele.Fertility.NORMAL, + "otht.bee.param.fertility", + "traitFertility"); + pLifespan = new EnumParameter<>( + EnumAllele.Lifespan.class, + EnumAllele.Lifespan.NORMAL, + "otht.bee.param.lifespan", + "traitLifespan"); + pFlowering = new EnumParameter<>( + EnumAllele.Flowering.class, + EnumAllele.Flowering.AVERAGE, + "otht.bee.param.flowering", + "traitFlowering"); + pTerritory = new EnumParameter<>( + EnumAllele.Territory.class, + EnumAllele.Territory.AVERAGE, + "otht.bee.param.territory", + "traitTerritory"); + pTempTolerance = new EnumParameter<>( + EnumAllele.Tolerance.class, + EnumAllele.Tolerance.NONE, + "otht.bee.param.tempTolerance", + "traitTempTolerance"); + pHumidTolerance = new EnumParameter<>( + EnumAllele.Tolerance.class, + EnumAllele.Tolerance.NONE, + "otht.bee.param.humidTolerance", + "traitHumidTolerance"); + pNocturnal = new BooleanParameter(false, "otht.bee.param.nocturnal", "traitNocturnal"); + pTolerantFlyer = new BooleanParameter(false, "otht.bee.param.tolerantFlyer", "traitTolerantFlyer"); + pCaveDwelling = new BooleanParameter(false, "otht.bee.param.caveDwelling", "traitCaveDwelling"); + pEffect = new StringParameter("", "otht.bee.param.effect", "traitEffect"); + pFlowerProvider = new StringParameter("", "otht.bee.param.flowerProvider", "traitFlowerProvider"); + + mParameters = Arrays.asList( + pMode, + pEjectHeldBees, + pOutputKind, + pTargetSpecies, + pSpeed, + pFertility, + pLifespan, + pFlowering, + pTerritory, + pTempTolerance, + pHumidTolerance, + pNocturnal, + pTolerantFlyer, + pCaveDwelling, + pEffect, + pFlowerProvider); + + // Everything EXCEPT pTargetSpecies - that one is driven entirely by the cycle-button picker + // in OTHBeeyondsGui (only ever one of the currently valid options for the held parents), so + // letting the player free-type an arbitrary species UID into the generic TecTech settings + // panel would just offer a second, unvalidated way to set the same value. saveNBTData()/ + // loadNBTData() persist the FULL mParameters list directly (not this one), so pTargetSpecies + // still saves/loads correctly - only its visibility in the settings panel is affected. + mSettingsPanelParameters = Arrays.asList( + pMode, + pEjectHeldBees, + pOutputKind, + pSpeed, + pFertility, + pLifespan, + pFlowering, + pTerritory, + pTempTolerance, + pHumidTolerance, + pNocturnal, + pTolerantFlyer, + pCaveDwelling, + pEffect, + pFlowerProvider); + } + + @Override + public void loadLegacyParameters(NBTTagCompound aNBT) { + // no legacy save format to migrate from - this machine's parameter system is new + } + + /** + * Used by TecTech's generic settings panel (and by {@code TTMultiblockBase}'s own redundant + * parameter persistence) - deliberately NOT the same list {@link #saveNBTData}/ + * {@link #loadNBTData} persist ({@code mParameters}, the full list): see the comment on + * {@link #mSettingsPanelParameters} in {@link #initParameters()} for why {@code pTargetSpecies} + * is excluded here specifically. + */ + @Override + public List> getParameters() { + return mSettingsPanelParameters; + } + + private OTHBeeyondsMode getMode() { + return pMode.getValue() == null ? OTHBeeyondsMode.PRODUCTION : pMode.getValue(); + } + + /** Exposed so {@link OTHBeeyondsGui} can drive its own mode-cycle button off the same state. */ + public EnumParameter getModeParameter() { + return pMode; + } + + /** + * The mode actually in effect right now, as opposed to {@link #pMode} (what the player last + * requested - see {@link #mActiveMode}'s own field comment for why the two can briefly + * disagree). Unlike {@code pMode} (a TecTech {@link Parameter}, whose client-side copy only + * gets its real value once the GUI that's opening registers its own sync handlers - AFTER the + * GUI's constructor already ran), {@code mActiveMode} is a plain field loaded through the + * TileEntity's normal NBT/description-packet sync, so it's already correct by the time a GUI + * is being built. {@link #getStatusText()} already relies on this same field for exactly that + * reason - {@link OTHBeeyondsGui} should pick its content (queen grid vs. parent slots) off + * this, not {@code getModeParameter().getValue()}, or the two can show different modes. + */ + public OTHBeeyondsMode getActiveMode() { + return mActiveMode; + } + + /** + * Drives {@link OTHBeeyondsGui}'s single mode-content trigger ({@code activeModeSync} there) - + * not a valid {@link OTHBeeyondsMode} ordinal by itself, only a value that changes whenever the + * panel needs a rebuild: on a genuine mode switch, or (in Breed mode) whenever the parent species + * pairing changes, since {@link OTHBeeyondsGui#createTargetSpeciesWidget} needs a full rebuild to + * pick up a new option count. Folded into this SAME int rather than a second sync-registered + * value, since registering a second top-level sync value on that panel breaks it from opening. + */ + public int getModeContentTrigger() { + if (mActiveMode != OTHBeeyondsMode.BREED) return 0; + IAlleleBeeSpecies species0 = parentSpecies(mParent0); + IAlleleBeeSpecies species1 = parentSpecies(mParent1); + int hash0 = species0 == null ? 0 + : species0.getUID() + .hashCode(); + int hash1 = species1 == null ? 0 + : species1.getUID() + .hashCode(); + return 1 + 31 * hash0 + hash1; + } + + /** Exposed so {@link OTHBeeyondsGui} can drive its own eject button off the same state. */ + public BooleanParameter getEjectParameter() { + return pEjectHeldBees; + } + + /** + * Live one-line status text shown directly on the main controller panel (like GTNH's own Industrial + * Apiary / Mega Industrial Apiary and Space Elevator controllers show their own live state) - since + * this machine never uses GT5's normal recipe map, the generic "Processing recipe / Progress" box + * has nothing real to show otherwise. Called from the server side only (wrapped in a + * {@code StringSyncValue} by {@link OTHBeeyondsGui}, which syncs the returned text to the client). + */ + public String getStatusText() { + if (mActiveMode == OTHBeeyondsMode.PRODUCTION) { + int held = 0; + for (QueenCell cell : mQueenCells) { + if (cell.queenStack != null) held++; + } + return String.format(translateToLocal("otht.bee.gui.status.production"), held, mQueenCells.size()); + } + if (mParent0 == null || mParent1 == null) { + return translateToLocal("otht.bee.gui.status.breed.waiting"); + } + if (targetSpeciesOptions().isEmpty()) { + return translateToLocal("otht.bee.gui.status.breed.noMutation"); + } + int percent = BREED_TIME_TICKS <= 0 ? 100 : Math.min(100, mBreedProgress * 100 / BREED_TIME_TICKS); + return String.format(translateToLocal("otht.bee.gui.status.breed.progress"), percent); + } + + // region effect/flower pickers (Breed mode) + // pEffect/pFlowerProvider still store a raw allele UID string (so NBT/settings-panel text entry + // keeps working for anyone who already knows one), but typing UIDs blind isn't a usable way to + // pick a trait - these index-based helpers back a proper cycle-button picker in OTHBeeyondsGui + // that lists every effect/flower provider actually registered in this Forestry instance (so the + // options always match whatever bee mods are loaded) instead of leaving it to guesswork. + private List mEffectOptions; + private List mFlowerOptions; + + private List effectOptions() { + if (mEffectOptions == null) { + mEffectOptions = new ArrayList<>(); + mEffectOptions.add(null); // index 0 - keep the mutation's own default effect + for (IAllele allele : AlleleManager.alleleRegistry.getRegisteredAlleles(EnumBeeChromosome.EFFECT)) { + if (allele instanceof IAlleleBeeEffect) mEffectOptions.add((IAlleleBeeEffect) allele); + } + } + return mEffectOptions; + } + + private List flowerOptions() { + if (mFlowerOptions == null) { + mFlowerOptions = new ArrayList<>(); + mFlowerOptions.add(null); // index 0 - keep the mutation's own default flower provider + for (IAllele allele : AlleleManager.alleleRegistry + .getRegisteredAlleles(EnumBeeChromosome.FLOWER_PROVIDER)) { + if (allele instanceof IAlleleFlowers) mFlowerOptions.add((IAlleleFlowers) allele); + } + } + return mFlowerOptions; + } + + public int getEffectOptionCount() { + return effectOptions().size(); + } + + public int getEffectSelectionIndex() { + List options = effectOptions(); + String current = pEffect.getValue(); + if (current == null || current.isEmpty()) return 0; + for (int i = 1; i < options.size(); i++) { + if (options.get(i) + .getUID() + .equals(current)) return i; + } + return 0; + } + + public void setEffectSelectionIndex(int index) { + List options = effectOptions(); + if (index < 0 || index >= options.size()) return; + IAlleleBeeEffect option = options.get(index); + pEffect.setValue(option == null ? "" : option.getUID()); + } + + public String getEffectOptionName(int index) { + List options = effectOptions(); + if (index < 0 || index >= options.size()) return ""; + IAlleleBeeEffect option = options.get(index); + return option == null ? translateToLocal("otht.bee.gui.effect.none") : option.getName(); + } + + public int getFlowerOptionCount() { + return flowerOptions().size(); + } + + public int getFlowerSelectionIndex() { + List options = flowerOptions(); + String current = pFlowerProvider.getValue(); + if (current == null || current.isEmpty()) return 0; + for (int i = 1; i < options.size(); i++) { + if (options.get(i) + .getUID() + .equals(current)) return i; + } + return 0; + } + + public void setFlowerSelectionIndex(int index) { + List options = flowerOptions(); + if (index < 0 || index >= options.size()) return; + IAlleleFlowers option = options.get(index); + pFlowerProvider.setValue(option == null ? "" : option.getUID()); + } + + public String getFlowerOptionName(int index) { + List options = flowerOptions(); + if (index < 0 || index >= options.size()) return ""; + IAlleleFlowers option = options.get(index); + return option == null ? translateToLocal("otht.bee.gui.flower.none") : option.getName(); + } + // endregion + + // region target species picker (Breed mode) + // Same index-based picker pattern as effect/flower above, but the option list itself is NOT + // static: it's recomputed from whichever two bees are currently held as Breed-mode parents (see + // tickBreed()) - "target species" only ever makes sense relative to that specific pairing, either + // the shared species itself (purebred pairing) or one of the DIFFERENT species Forestry has an + // actual registered mutation for between those two exact species. Nothing else can ever be + // produced by findMutation()/tickBreed() regardless of what pTargetSpecies holds, so restricting + // the picker to this list keeps it from ever offering (or silently accepting) an impossible target. + private IAlleleBeeSpecies parentSpecies(ItemStack parentStack) { + if (parentStack == null) return null; + IBee bee = BeeManager.beeRoot.getMember(parentStack); + return bee == null ? null + : bee.getGenome() + .getPrimary(); + } + + private List targetSpeciesOptions() { + List options = new ArrayList<>(); + IAlleleBeeSpecies species0 = parentSpecies(mParent0); + IAlleleBeeSpecies species1 = parentSpecies(mParent1); + if (species0 == null || species1 == null) return options; + + if (species0.getUID() + .equals(species1.getUID())) { + options.add(species0); + return options; + } + + for (IBeeMutation mutation : BeeManager.beeRoot.getMutations(false)) { + boolean matchesPair = (mutation.getAllele0() == species0 && mutation.getAllele1() == species1) + || (mutation.getAllele0() == species1 && mutation.getAllele1() == species0); + if (!matchesPair) continue; + IAllele resultAllele = mutation.getTemplate()[EnumBeeChromosome.SPECIES.ordinal()]; + if (resultAllele instanceof IAlleleBeeSpecies && !options.contains(resultAllele)) { + options.add((IAlleleBeeSpecies) resultAllele); + } + } + return options; + } + + /** Always at least 1 so a picker widget never has to handle a zero-state count. */ + public int getTargetSpeciesOptionCount() { + return Math.max(1, targetSpeciesOptions().size()); + } + + public int getTargetSpeciesSelectionIndex() { + List options = targetSpeciesOptions(); + String current = pTargetSpecies.getValue(); + if (current == null || current.isEmpty() || options.isEmpty()) return 0; + for (int i = 0; i < options.size(); i++) { + if (options.get(i) + .getUID() + .equals(current)) return i; + } + return 0; + } + + public void setTargetSpeciesSelectionIndex(int index) { + List options = targetSpeciesOptions(); + if (index < 0 || index >= options.size()) return; + pTargetSpecies.setValue( + options.get(index) + .getUID()); + } + + public String getTargetSpeciesOptionName(int index) { + List options = targetSpeciesOptions(); + if (index < 0 || index >= options.size()) { + return translateToLocal("otht.bee.gui.species.none"); + } + return options.get(index) + .getName(); + } + // endregion + + /** + * Read-only queen slot grid on the main panel - like GTNH's own Industrial Apiary/Mega Industrial + * Apiary, which shows every queen it's currently working instead of hiding them entirely. The GUI + * caps the visible grid at a fixed size; at higher tiers than that grid can show, the extra queens + * still work normally, they just aren't individually pictured. + */ + public ItemStack getQueenDisplayStack(int index) { + if (index < 0 || index >= mQueenCells.size()) return null; + ItemStack stack = mQueenCells.get(index).queenStack; + // MUST be a defensive copy: the GUI's GenericSyncValue caches whatever this getter returns + // (with ICopy.immutable(), meaning it keeps the exact reference, no copy of its own) and + // pushes it through ModularUI2's network sync every tick the panel is open. Handing out the + // SAME live ItemStack the production tick is actively reading/writing (age(), NBT rewrites on + // cycle completion) let the GUI layer race with and corrupt the real simulation state - a held + // queen was observed vanishing mid-cycle (well before her 550-tick work cycle or any aging) + // only while her slot was visible in this grid. Returning a copy here keeps the display fully + // decoupled from the actual bee being simulated. + return stack == null ? null : stack.copy(); + } + + /** + * Read-only display of a held Breed-mode parent (0 or 1) for the GUI - same copy-safety rule as + * {@link #getQueenDisplayStack}: never hand out the live {@code mParent0}/{@code mParent1} + * reference to the GUI sync layer. + */ + public ItemStack getParentDisplayStack(int index) { + ItemStack stack = index == 0 ? mParent0 : index == 1 ? mParent1 : null; + return stack == null ? null : stack.copy(); + } + + /** + * Global production preview tooltip for the main panel - one "Producing X xN in Ts" + * line per distinct product any currently-held queen can make, quantities summed across every queen + * that can produce it and the time being the soonest of theirs (same idea as GTNH's Steam Space + * Elevator showing one countdown for the whole machine, not a separate one per component). Not a + * per-queen tooltip - deliberately aggregated across all of {@link #mQueenCells}, since each + * individual queen's own countdown/products aren't meant to be inspected one by one here. Not every + * product actually lands each cycle (each has its own independent chance) - this is "what the + * machine is working towards producing next", not a guarantee. Lines are newline-separated; the GUI + * splits on {@code \n} into separate tooltip lines. Computed server-side (like + * {@link #getStatusText()}) since the client's own {@code mQueenCells} isn't ticked/populated. + */ + public String getProductionPreviewTooltip() { + // LinkedHashMap: first-seen order (roughly cell order) is a stable, deterministic tooltip + // ordering instead of shuffling every time products get re-aggregated. + java.util.Map aggregate = new java.util.LinkedHashMap<>(); + for (QueenCell cell : mQueenCells) { + if (cell.queenStack == null) continue; + IBee bee = BeeManager.beeRoot.getMember(cell.queenStack); + if (bee == null) continue; + int ticksRemaining = Math.max(0, QUEEN_WORK_CYCLE_TICKS - cell.workCycleThrottle); + float secondsRemaining = ticksRemaining / 20.0F; + for (ItemStack product : bee.getProduceList()) { + aggregateProduct(aggregate, product, secondsRemaining); + } + for (ItemStack specialty : bee.getSpecialtyList()) { + aggregateProduct(aggregate, specialty, secondsRemaining); + } + } + + if (aggregate.isEmpty()) return translateToLocal("otht.bee.gui.queen.tooltip.noProducts"); + StringBuilder text = new StringBuilder(); + for (Object[] entry : aggregate.values()) { + if (text.length() > 0) text.append('\n'); + text.append( + String.format(translateToLocal("otht.bee.gui.queen.tooltip.producing"), entry[0], entry[1], entry[2])); + } + return text.toString(); + } + + /** entry = {displayName (String), summed quantity (Integer), soonest seconds remaining (Float)} */ + private void aggregateProduct(java.util.Map aggregate, ItemStack product, + float secondsRemaining) { + if (product == null) return; + String key = product.getUnlocalizedName() + ":" + product.getItemDamage(); + Object[] entry = aggregate.get(key); + if (entry == null) { + aggregate.put(key, new Object[] { product.getDisplayName(), product.stackSize, secondsRemaining }); + } else { + entry[1] = (Integer) entry[1] + product.stackSize; + entry[2] = Math.min((Float) entry[2], secondsRemaining); + } + } + + @Override + protected @NotNull MTEMultiBlockBaseGui getGui() { + return new OTHBeeyondsGui(this); + } + // endregion + + // region tick logic + /** + * {@code onRunningTick()} is only ever invoked by the base class while {@code mMaxProgresstime > 0} + * (see {@code TTMultiblockBase#onPostTick}) - i.e. while a recipe is "in progress". Beeyonds has no + * real recipe map, so without this override {@code checkProcessing_EM()} would fall back to the + * default no-op {@code checkRecipe_EM(ItemStack)} (always {@code false}), {@code mMaxProgresstime} + * would stay 0 forever, and {@code onRunningTick} - and therefore the whole bee simulation - would + * never run at all (machine sits idle showing "No valid recipe found", input hatches never drained). + * Forcing a permanent 1-tick "recipe" here restores the simple every-tick semantics the rest of + * this class is written against. {@code checkProcessing()} itself is {@code final} on + * {@link TTMultiblockBase} - {@code checkProcessing_EM()} is the actual override point it delegates to. + */ + @Override + protected CheckRecipeResult checkProcessing_EM() { + mMaxProgresstime = 1; + mEUt = 0; + return CheckRecipeResultRegistry.SUCCESSFUL; + } + @Override public boolean onRunningTick(ItemStack aStack) { + if (getBaseMetaTileEntity().isServerSide()) { + OTHBeeyondsMode desired = getMode(); + if (mActiveMode != desired) { + // Switching modes auto-ejects whatever the mode being left is holding. If the + // output can't take it all right now, the switch stays blocked (and nothing is + // destroyed) until there's room - drainActiveMode() is safe to call every tick. + if (drainActiveMode()) { + mActiveMode = desired; + } + } + if (mActiveMode == desired) { + if (mActiveMode == OTHBeeyondsMode.PRODUCTION) { + tickProduction(); + } else { + tickBreed(); + } + } + handleManualEjectRequest(); + } return true; } - @Override - public boolean doRandomMaintenanceDamage() { - return false; + /** + * Named GT voltage tier (LV=1, MV=2, HV=3, EV=4, ...) of the single highest-voltage energy hatch + * present - deliberately NOT based on {@link #getMaxInputVoltageSum()} (which adds up every + * hatch's voltage together): two HV hatches summing past the HV threshold must still count as + * "HV tier", not silently promote the machine to EV-tier slot counts just because more amperage is + * plugged in. Hatch count should only affect throughput/EU-t, never which tier this machine reads + * as. + */ + private int currentTier() { + long highestVoltage = 0; + for (MTEHatchEnergy hatch : mEnergyHatches) { + highestVoltage = Math.max(highestVoltage, hatch.maxEUInput()); + } + return Math.max(1, GTUtility.getTier(highestVoltage)); + } + + /** + * Doubles per named GT tier (LV=4, MV=8, HV=16, EV=32, IV=64, ...) rather than scaling linearly - + * matches how most GT5 multiblocks reward higher voltage tiers with a step change in capability. + */ + private int queenSlotCount() { + return QUEEN_SLOTS_PER_TIER << (currentTier() - 1); } + /** + * Ejects everything held by {@link #mActiveMode} (the mode being left on a mode switch, or the + * currently active mode for a manual eject). Never destroys a bee: a cell/parent slot is only + * cleared once {@code addOutputAtomic} confirms the whole stack made it into an output bus. + */ + private boolean drainActiveMode() { + return mActiveMode == OTHBeeyondsMode.PRODUCTION ? ejectAllQueens() : ejectParents(); + } + + private void handleManualEjectRequest() { + if (!pEjectHeldBees.getValue()) return; + if (drainActiveMode()) { + // fully drained - release the button so it doesn't look permanently pressed + pEjectHeldBees.setValue(false); + } + // otherwise leave it "pressed": it keeps retrying every tick until there's room, without + // losing any bee in the meantime. + } + + private boolean ejectAllQueens() { + boolean allClear = true; + for (QueenCell cell : mQueenCells) { + if (cell.queenStack == null) continue; + if (addOutputAtomic(cell.queenStack)) { + cell.queenStack = null; + } else { + allClear = false; + } + } + if (allClear) mQueenCells.clear(); + return allClear; + } + + private boolean ejectParents() { + boolean clear0 = mParent0 == null || addOutputAtomic(mParent0); + if (clear0) mParent0 = null; + boolean clear1 = mParent1 == null || addOutputAtomic(mParent1); + if (clear1) mParent1 = null; + if (clear0 && clear1) mBreedProgress = 0; + return clear0 && clear1; + } + + private void tickProduction() { + int wanted = queenSlotCount(); + while (mQueenCells.size() < wanted) { + mQueenCells.add(new QueenCell()); + } + // Shrink from the end, but only drop a cell once its bee (if any) safely made it into an + // output bus - if the output is full, stop shrinking for now instead of voiding the bee. + while (mQueenCells.size() > wanted) { + QueenCell last = mQueenCells.get(mQueenCells.size() - 1); + flushPendingProducts(last); + if ((last.queenStack != null && !addOutputAtomic(last.queenStack)) || !last.pendingProducts.isEmpty()) { + break; + } + last.queenStack = null; + mQueenCells.remove(mQueenCells.size() - 1); + } + + IBeeRoot root = BeeManager.beeRoot; + + int heldQueens = 0; + for (QueenCell cell : mQueenCells) { + // Retried every tick regardless of whether this cell currently holds a live queen - a + // comb that couldn't fit in the output bus the tick it was produced must never be voided, + // even if her queen already died or got ejected in the meantime. + flushPendingProducts(cell); + if (cell.queenStack == null) { + cell.queenStack = pullQueenFromInputs(root); + } + if (cell.queenStack != null) heldQueens++; + } + // Fixed EU/t cost per held queen, drawn straight from the buffered EU the energy hatches keep + // topped up (same store getEUVar()/setEUVar() already track) - if there isn't enough to pay + // every queen this tick, none of them work (no partial-payment/undercharging), so a + // brownout is visible instead of silently rationing production. + long powerCost = EU_COST_PER_QUEEN * heldQueens; + boolean hasPower = powerCost <= 0 || getEUVar() >= powerCost; + if (hasPower && powerCost > 0) { + setEUVar(getEUVar() - powerCost); + } + + if (!hasPower) return; + + for (QueenCell cell : mQueenCells) { + if (cell.queenStack == null) continue; + IBee bee = root.getMember(cell.queenStack); + tickQueenCell(cell, bee); + } + } + + /** + * Minimal, self-written replacement for Forestry's own {@code BeekeepingLogic#doWork()} - that + * bundles honey/comb production together with {@code IBee.doEffect()} (bee special effects, fired + * every single tick) and {@code IBee.plantFlowerRandom()} (placing real flower blocks in the world) + * with no way to opt out of either individually. Beeyonds Home is meant to be a clean, self- + * contained production line, not something that spontaneously alters the world around it or fires + * unpredictable bee effects (explosions, ignition, mycophilic mushroom spawns, etc.) - so this only + * calls the parts of Forestry's public {@link IBee} API needed for production and aging + * ({@link IBee#produceStacks}, {@link IBee#age}), skipping doEffect/plantFlowerRandom/pollination + * entirely. {@code bee.getCanWork(cell)} is still consulted, but only for + * {@code NO_SPACE_INVENTORY} - everything else it might report (biome, temperature, flowers, ...) + * is a real-world requirement this virtual apiary ignores by design. + */ + private void tickQueenCell(QueenCell cell, IBee bee) { + if (bee == null) return; + if (!bee.isAlive()) { + // no death products beyond whatever her own production already sent to output - matches + // "no side effects/no spawns", just plain combs, and frees the cell for a fresh queen. + cell.queenStack = null; + return; + } + if (bee.getCanWork(cell) + .contains(EnumErrorCode.NO_SPACE_INVENTORY)) { + return; // output full - retry next tick, never voids the queen or her progress + } + + cell.workCycleThrottle++; + if (cell.workCycleThrottle < QUEEN_WORK_CYCLE_TICKS) return; + cell.workCycleThrottle = 0; + + for (ItemStack product : bee.produceStacks(cell)) { + if (product != null) cell.pendingProducts.add(product); + } + flushPendingProducts(cell); + bee.age(getBaseMetaTileEntity().getWorld(), 1.0F); + NBTTagCompound agedNbt = new NBTTagCompound(); + bee.writeToNBT(agedNbt); + cell.queenStack.setTagCompound(agedNbt); + } + + /** + * Retries every product a queen has ever produced but couldn't yet fit into an output bus, + * oldest first, stopping at the first one that still doesn't fit (so ordering is preserved and a + * later, smaller product can't jump the queue past an earlier one still waiting for space). Mirrors + * the same "never void a bee, only ever retry" rule {@link #ejectAllQueens()}/{@link #ejectParents()} + * already apply to held bees - {@code addOutputAtomic}'s return value being silently ignored for + * produced combs was the actual reason production could look like it "never comes out": a comb + * produced while the output bus happened to be full was previously discarded on the spot instead of + * being retried. + */ + private void flushPendingProducts(QueenCell cell) { + while (!cell.pendingProducts.isEmpty()) { + ItemStack product = cell.pendingProducts.get(0); + if (!addOutputAtomic(product)) break; + cell.pendingProducts.remove(0); + } + } + + private ItemStack pullQueenFromInputs(IBeeRoot root) { + for (ItemStack stack : getStoredInputs()) { + // getStoredInputs() checks for null but not for an already-drained (stackSize <= 0) slot - + // vanilla ItemStack.splitStack(1) has no bounds check either, it happily decrements below + // zero and hands back a brand new size-1 stack regardless. Skipping depleted stacks here + // (BEFORE splitting, not after) is what stops a single real queen from getting "split" over + // and over into a full grid of phantom duplicates whenever the hatch doesn't null out the + // slot the instant it empties. + if (stack == null || stack.stackSize <= 0) continue; + if (!root.isMember(stack)) continue; + if (root.getType(stack) != EnumBeeType.QUEEN) continue; + return stack.splitStack(1); + } + return null; + } + + private void tickBreed() { + IBeeRoot root = BeeManager.beeRoot; + + if (mParent0 == null) { + mParent0 = pullParentFromInputs(root); + } + if (mParent1 == null) { + mParent1 = pullParentFromInputs(root); + } + + if (mParent0 == null || mParent1 == null) { + mBreedProgress = 0; + return; + } + + IBee bee0 = root.getMember(mParent0); + IBee bee1 = root.getMember(mParent1); + if (bee0 == null || bee1 == null) { + returnParents(); + return; + } + + IAlleleBeeSpecies species0 = bee0.getGenome() + .getPrimary(); + IAlleleBeeSpecies species1 = bee1.getGenome() + .getPrimary(); + boolean sameSpecies = species0.getUID() + .equals(species1.getUID()); + + // Two bees of the SAME species is plain purebred breeding, not a mutation - Forestry never + // registers a mutation entry for a species paired with itself (getMutations() only lists + // actual cross-species mutations), so findMutation() below always returned null for this, + // the by far most common case (e.g. Forest Princess x Forest Drone), and the pair was + // ejected the instant a mutation lookup happened rather than ever breeding. Pull the base + // template straight from the species' own registered template instead of a mutation's. + IAllele[] baseTemplate; + if (sameSpecies) { + baseTemplate = BeeManager.beeRoot.getTemplate(species0.getUID()); + if (baseTemplate == null) { + returnParents(); + return; + } + } else { + IBeeMutation mutation = findMutation(species0, species1); + if (mutation == null) { + // No registered mutation between these two species - HOLD the parents instead of + // auto-ejecting every tick (that used to make the pair vanish the instant an + // unsupported combo was inserted, with no chance to swap the target species picker + // or even see what's wrong). Auto-eject is reserved for a genuinely unreadable + // parent stack (bee0/bee1 == null above) or an explicit mode switch/manual eject + // (drainActiveMode()) - never "just no mutation yet". + mBreedProgress = 0; + return; + } + baseTemplate = mutation.getTemplate(); + } + + mBreedProgress++; + if (mBreedProgress < BREED_TIME_TICKS) { + return; + } + + // Deliberately NOT clearing mBreedProgress yet, and NOT clearing the parents until the + // result actually makes it into an output bus (checked below) - both used to happen + // unconditionally right after building the result stack, regardless of whether + // addOutputAtomic() succeeded. With the output bus full, that silently voided the freshly + // bred queen/drones AND consumed the parents for nothing, every single tick the output + // stayed full. The template is fully deterministic (pristine homozygous, no randomness), so + // recomputing the same result stack next tick and retrying is safe. + IAllele[] template = buildCustomTemplate(baseTemplate); + ItemStack resultStack; + if (pOutputKind.getValue() == OTHBeeyondsOutputKind.DRONES || sameSpecies) { + int fertility = pFertility.getValue() + .getValue(); + int amount = Math.min(MAX_DRONES_PER_CYCLE, Math.max(1, Math.round(fertility * BREED_YIELD_MULTIPLIER))); + IBee droneIndividual = (IBee) root.templateAsIndividual(template); + resultStack = root.getMemberStack(droneIndividual, EnumBeeType.DRONE.ordinal()); + resultStack.stackSize = amount; + } else { + IBee mate = (IBee) root.templateAsIndividual(template); + IBee queen = root.getBee(getBaseMetaTileEntity().getWorld(), mate.getGenome(), mate); + resultStack = root.getMemberStack(queen, EnumBeeType.QUEEN.ordinal()); + } + + if (!addOutputAtomic(resultStack)) { + return; // output full - retry next tick, never voids the result or the parents + } + + mBreedProgress = 0; + mParent0 = null; + mParent1 = null; + } + + /** + * Returns both parents to the output when one of them turns out to be an unreadable/corrupted + * bee stack (not simply "no mutation between them" - see {@link #tickBreed()}, which holds the + * pair instead of ejecting for that case). Uses + * the same safe eject as {@link #ejectParents()} - if the output is full, the parents stay held + * (and are retried next tick) instead of being voided. + */ + private void returnParents() { + ejectParents(); + } + + private ItemStack pullParentFromInputs(IBeeRoot root) { + for (ItemStack stack : getStoredInputs()) { + // see pullQueenFromInputs() - must skip an already-drained stack BEFORE splitting from it. + if (stack == null || stack.stackSize <= 0) continue; + if (!root.isMember(stack)) continue; + EnumBeeType type = root.getType(stack); + if (type != EnumBeeType.PRINCESS && type != EnumBeeType.QUEEN && type != EnumBeeType.DRONE) continue; + return stack.splitStack(1); + } + return null; + } + + /** + * Finds a registered mutation whose two parent species match the given pair (either order), ignoring + * chance/resource/biome requirements entirely - only the species pairing is checked, per design. + */ + private IBeeMutation findMutation(IAlleleBeeSpecies species0, IAlleleBeeSpecies species1) { + String wantedUid = pTargetSpecies.getValue(); + IBeeMutation fallback = null; + for (IBeeMutation mutation : BeeManager.beeRoot.getMutations(false)) { + boolean matchesPair = (mutation.getAllele0() == species0 && mutation.getAllele1() == species1) + || (mutation.getAllele0() == species1 && mutation.getAllele1() == species0); + if (!matchesPair) continue; + if (fallback == null) fallback = mutation; + if (wantedUid != null && !wantedUid.isEmpty()) { + IAllele resultSpecies = mutation.getTemplate()[EnumBeeChromosome.SPECIES.ordinal()]; + if (wantedUid.equalsIgnoreCase(resultSpecies.getUID()) + || wantedUid.equalsIgnoreCase(resultSpecies.getUnlocalizedName())) { + return mutation; + } + } + } + return fallback; + } + + /** + * Builds a fully homozygous (both chromosome halves identical -> pristine, no genetic decay) template from + * the given base template (a mutation's result template, or a plain species template for purebred + * pairings - see {@link #tickBreed()}), overriding every chromosome with the player's chosen alleles + * from the settings panel. + */ + private IAllele[] buildCustomTemplate(IAllele[] baseTemplate) { + IAllele[] template = baseTemplate.clone(); + + AlleleHelper.instance.set(template, EnumBeeChromosome.SPEED, pSpeed.getValue()); + AlleleHelper.instance.set(template, EnumBeeChromosome.FERTILITY, pFertility.getValue()); + AlleleHelper.instance.set(template, EnumBeeChromosome.LIFESPAN, pLifespan.getValue()); + AlleleHelper.instance.set(template, EnumBeeChromosome.FLOWERING, pFlowering.getValue()); + AlleleHelper.instance.set(template, EnumBeeChromosome.TERRITORY, pTerritory.getValue()); + AlleleHelper.instance.set(template, EnumBeeChromosome.TEMPERATURE_TOLERANCE, pTempTolerance.getValue()); + AlleleHelper.instance.set(template, EnumBeeChromosome.HUMIDITY_TOLERANCE, pHumidTolerance.getValue()); + AlleleHelper.instance.set(template, EnumBeeChromosome.NOCTURNAL, boolAllele(pNocturnal.getValue())); + AlleleHelper.instance.set(template, EnumBeeChromosome.TOLERANT_FLYER, boolAllele(pTolerantFlyer.getValue())); + AlleleHelper.instance.set(template, EnumBeeChromosome.CAVE_DWELLING, boolAllele(pCaveDwelling.getValue())); + + IAlleleBeeEffect effect = resolveEffect(pEffect.getValue()); + if (effect != null) { + AlleleHelper.instance.set(template, EnumBeeChromosome.EFFECT, effect); + } + IAlleleFlowers flowers = resolveFlowers(pFlowerProvider.getValue()); + if (flowers != null) { + AlleleHelper.instance.set(template, EnumBeeChromosome.FLOWER_PROVIDER, flowers); + } + + return template; + } + + private IAllele boolAllele(boolean value) { + IAllele allele = AlleleManager.alleleRegistry.getAllele("forestry.boolean" + (value ? "Yes" : "No")); + return allele; + } + + private IAlleleBeeEffect resolveEffect(String uidOrName) { + if (uidOrName == null || uidOrName.isEmpty()) return null; + IAllele allele = AlleleManager.alleleRegistry.getAllele(uidOrName); + return allele instanceof IAlleleBeeEffect ? (IAlleleBeeEffect) allele : null; + } + + private IAlleleFlowers resolveFlowers(String uidOrName) { + if (uidOrName == null || uidOrName.isEmpty()) return null; + IAllele allele = AlleleManager.alleleRegistry.getAllele(uidOrName); + return allele instanceof IAlleleFlowers ? (IAlleleFlowers) allele : null; + } + // endregion + + // region IBeeHousing cell used to drive Forestry's own apiary simulation per production-mode queen + private final class QueenCell implements IBeeHousing, IBeeHousingInventory { + + private ItemStack queenStack; + private IBeekeepingLogic logic; + private final IErrorLogic errorLogic = new ErrorLogic(); + // mirrors Forestry's own BeekeepingLogic#queenWorkCycleThrottle pacing (550 ticks, ~27.5s) + private int workCycleThrottle = 0; + // combs produced but not yet delivered to an output bus (retried every tick by + // flushPendingProducts() until they fit) - never voided, even if this cell's queen dies or + // gets ejected before they're delivered. + private final List pendingProducts = new ArrayList<>(); + + public IBeekeepingLogic getBeekeepingLogic() { + if (logic == null) { + logic = BeeManager.beeRoot.createBeekeepingLogic(this); + } + return logic; + } + + @Override + public Iterable getBeeModifiers() { + return Collections.singletonList(new IBeeModifier() { + + @Override + public float getTerritoryModifier(IBeeGenome genome, float currentModifier) { + return 1.0F; + } + + @Override + public float getMutationModifier(IBeeGenome genome, IBeeGenome mate, float currentModifier) { + return 0.0F; + } + + @Override + public float getLifespanModifier(IBeeGenome genome, IBeeGenome mate, float currentModifier) { + return 1.0F; + } + + @Override + public float getProductionModifier(IBeeGenome genome, float currentModifier) { + return 1.0F; + } + + @Override + public float getFloweringModifier(IBeeGenome genome, float currentModifier) { + return 1.0F; + } + + @Override + public float getGeneticDecay(IBeeGenome genome, float currentModifier) { + return 0.0F; + } + + @Override + public boolean isSealed() { + return true; + } + + @Override + public boolean isSelfLighted() { + return true; + } + + @Override + public boolean isSunlightSimulated() { + return true; + } + + @Override + public boolean isHellish() { + return false; + } + }); + } + + @Override + public Iterable getBeeListeners() { + return Collections.emptyList(); + } + + @Override + public IBeeHousingInventory getBeeInventory() { + return this; + } + + @Override + public int getBlockLightValue() { + return 15; + } + + @Override + public boolean canBlockSeeTheSky() { + return true; + } + + @Override + public World getWorld() { + return getBaseMetaTileEntity().getWorld(); + } + + @Override + public GameProfile getOwner() { + if (mCachedOwner == null) { + mCachedOwner = new GameProfile(null, getBaseMetaTileEntity().getOwnerName()); + } + return mCachedOwner; + } + + @Override + public Vec3 getBeeFXCoordinates() { + return Vec3.createVectorHelper( + getBaseMetaTileEntity().getXCoord(), + getBaseMetaTileEntity().getYCoord(), + getBaseMetaTileEntity().getZCoord()); + } + + @Override + public ChunkCoordinates getCoordinates() { + return new ChunkCoordinates( + getBaseMetaTileEntity().getXCoord(), + getBaseMetaTileEntity().getYCoord(), + getBaseMetaTileEntity().getZCoord()); + } + + @Override + public BiomeGenBase getBiome() { + return getWorld() + .getBiomeGenForCoords(getBaseMetaTileEntity().getXCoord(), getBaseMetaTileEntity().getZCoord()); + } + + @Override + public EnumTemperature getTemperature() { + return EnumTemperature.NORMAL; + } + + @Override + public EnumHumidity getHumidity() { + return EnumHumidity.NORMAL; + } + + @Override + public IErrorLogic getErrorLogic() { + return errorLogic; + } + + @Override + public ItemStack getQueen() { + return queenStack; + } + + @Override + public ItemStack getDrone() { + return null; + } + + @Override + public void setQueen(ItemStack stack) { + queenStack = stack; + } + + @Override + public void setDrone(ItemStack stack) { + // Alveary-style housing does not keep a drone slot - queens are pre-mated. + } + + @Override + public boolean addProduct(ItemStack product, boolean allowPartial) { + return addOutputAtomic(product); + } + } + // endregion + + // doRandomMaintenanceDamage() intentionally left at MTEMultiBlockBase's default - it used to be + // overridden here to unconditionally `return false`, presumably meaning "don't wear down over + // time". But GT5's own tick loop (MTEMultiBlockBase#runMachine / TTMultiblockBase#onPostTick) + // dual-purposes this same boolean as the master gate for whether onRunningTick() gets called AT + // ALL this tick (`if (mMaxProgresstime > 0 && doRandomMaintenanceDamage()) onRunningTick(...)`). + // Returning false unconditionally therefore silently disabled the entire bee simulation forever - + // no queen/parent was ever pulled from the input bus and nothing was ever produced, regardless of + // structure/power/mode state. The default implementation already skips real wear/repair + // requirements here (gated by shouldCheckMaintenance(), which getDefaultHasMaintenanceChecks() + // below keeps false) while still returning true so the tick actually runs. + @Override public void construct(ItemStack stackSize, boolean hintsOnly) { - buildPiece("main", stackSize, hintsOnly, 0, 1, 0); + // same (11, 20, 1) offset as checkMachine()/survivalConstruct() - see the comment there. + buildPiece("main", stackSize, hintsOnly, 11, 20, 1); } @Override diff --git a/src/main/java/com/newmaa/othtech/machine/OTHBeeyondsMode.java b/src/main/java/com/newmaa/othtech/machine/OTHBeeyondsMode.java new file mode 100644 index 0000000..bc5a1c3 --- /dev/null +++ b/src/main/java/com/newmaa/othtech/machine/OTHBeeyondsMode.java @@ -0,0 +1,6 @@ +package com.newmaa.othtech.machine; + +public enum OTHBeeyondsMode { + PRODUCTION, + BREED +} diff --git a/src/main/java/com/newmaa/othtech/machine/OTHBeeyondsOutputKind.java b/src/main/java/com/newmaa/othtech/machine/OTHBeeyondsOutputKind.java new file mode 100644 index 0000000..6406683 --- /dev/null +++ b/src/main/java/com/newmaa/othtech/machine/OTHBeeyondsOutputKind.java @@ -0,0 +1,7 @@ +package com.newmaa.othtech.machine; + +/** Only used in {@link OTHBeeyondsMode#BREED}: what the finished breeding cycle produces. */ +public enum OTHBeeyondsOutputKind { + QUEEN, + DRONES +} diff --git a/src/main/java/com/newmaa/othtech/machine/gui/OTHBeeyondsGui.java b/src/main/java/com/newmaa/othtech/machine/gui/OTHBeeyondsGui.java new file mode 100644 index 0000000..623d78a --- /dev/null +++ b/src/main/java/com/newmaa/othtech/machine/gui/OTHBeeyondsGui.java @@ -0,0 +1,331 @@ +package com.newmaa.othtech.machine.gui; + +import net.minecraft.item.ItemStack; + +import com.cleanroommc.modularui.api.drawable.IKey; +import com.cleanroommc.modularui.api.widget.IWidget; +import com.cleanroommc.modularui.screen.ModularPanel; +import com.cleanroommc.modularui.screen.RichTooltip; +import com.cleanroommc.modularui.utils.ICopy; +import com.cleanroommc.modularui.utils.serialization.ByteBufAdapters; +import com.cleanroommc.modularui.value.sync.DynamicSyncHandler; +import com.cleanroommc.modularui.value.sync.GenericSyncValue; +import com.cleanroommc.modularui.value.sync.IntSyncValue; +import com.cleanroommc.modularui.value.sync.PanelSyncManager; +import com.cleanroommc.modularui.value.sync.StringSyncValue; +import com.cleanroommc.modularui.widget.EmptyWidget; +import com.cleanroommc.modularui.widgets.CycleButtonWidget; +import com.cleanroommc.modularui.widgets.DynamicSyncedWidget; +import com.cleanroommc.modularui.widgets.ItemDisplayWidget; +import com.cleanroommc.modularui.widgets.ListWidget; +import com.cleanroommc.modularui.widgets.TextWidget; +import com.cleanroommc.modularui.widgets.layout.Flow; +import com.newmaa.othtech.machine.OTEBeeyonds; +import com.newmaa.othtech.machine.OTHBeeyondsMode; + +import gregtech.api.modularui2.GTGuiTextures; +import gregtech.common.gui.modularui.multiblock.base.TTMultiblockBaseGui; + +/** + * MUI2 GUI for the {@link OTEBeeyonds} controller - same pattern as {@code OTEBBPlasmaForgeGui}. + *

+ * Adds a mode-cycle button (Production <-> Breed) to the right button column, above the + * standard TecTech row (power pass / edit parameters / power switch) - same single-button addition + * spot {@code OTEBBPlasmaForgeGui} uses for its wireless toggle. That column sits in a hardcoded + * 76px-tall row ({@code MTEMultiBlockBaseGui#createInventoryRow}), so only one extra icon is added + * here; the eject action stays in the settings panel (wrench icon) instead of also living here, to + * avoid overflowing that fixed-height row. + *

+ * Bound directly to the same {@link tectech.thing.metaTileEntity.multi.base.parameter.Parameter} + * sync handler the generic settings panel already uses for mode, so there's a single source of + * truth and no risk of the two UIs disagreeing. + *

+ * Everything this mod adds (status line, queen grid / parent slots) lives INSIDE the stock + * "Running perfectly." box, as an extra row appended to GT5's own terminal {@code ListWidget} - + * same pattern kubatech's {@code MTEMegaIndustrialApiaryGui} uses for its own bee grid: never grow + * the root panel or the terminal box, just fit the content inside the existing fixed budget. The + * content here (one status line, plus either a 4x8 queen grid or 2 parent slots + a species picker) + * comfortably fits the 174px the stock terminal row already reserves, and GT5's own + * {@code ListWidget} scrolls internally if it ever doesn't. + */ +public class OTHBeeyondsGui extends TTMultiblockBaseGui { + + public OTHBeeyondsGui(OTEBeeyonds multiblock) { + super(multiblock); + } + + @Override + protected Flow createButtonColumn(ModularPanel panel, PanelSyncManager syncManager) { + return super.createButtonColumn(panel, syncManager).child(createModeButton()); + } + + /** + * Appends our content as one more child of GT5's own terminal {@code ListWidget} (it auto-stacks + * its children and scrolls if they don't fit) instead of adding a whole separate panel that + * would need the root window itself to grow. + */ + @Override + protected ListWidget createTerminalTextWidget(PanelSyncManager syncManager, ModularPanel panel) { + return super.createTerminalTextWidget(syncManager, panel).child(createBeeyondsContent(syncManager)); + } + + /** + * Content differs by mode: Production shows the queen grid, Breed shows the two held parent + * bees + the target species picker. No border/theme/padding of its own - it's just extra rows + * inside the box GT5 already draws and themes for us. + */ + private IWidget createBeeyondsContent(PanelSyncManager syncManager) { + return Flow.column() + .coverChildren() + .marginTop(4) + .child(createStatusWidget(syncManager)) + .child(createModeContentWidget(syncManager)) + .child( + // Kept registered but hidden (zero-sized) rather than removed: dropping these two + // sync-registered widgets from the tree entirely destabilizes PanelSyncManager for + // this GUI, so they stay registered and just take no visible space. Neither is wired + // to anything useful yet, so there's nothing worth showing the player right now. + Flow.row() + .size(0, 0) + .child(createEffectPicker(syncManager)) + .child(createFlowerPicker(syncManager))); + } + + // region effect/flower pickers - kept registered but hidden, see the comment above. + // + // Both the row AND each button need their own .size(0, 0): a button still renders its overlay + // text at its own explicit size regardless of what box its parent claims, since Flow doesn't + // clip children larger than their parent. + protected IWidget createEffectPicker(PanelSyncManager syncManager) { + IntSyncValue effectSync = new IntSyncValue( + multiblock::getEffectSelectionIndex, + multiblock::setEffectSelectionIndex).allowC2S(); + syncManager.syncValue("beeyondsEffect", effectSync); + + int optionCount = multiblock.getEffectOptionCount(); + CycleButtonWidget effectButton = new CycleButtonWidget().value(effectSync) + .stateCount(optionCount) + .size(0, 0); + for (int i = 0; i < optionCount; i++) { + String name = multiblock.getEffectOptionName(i); + effectButton.stateOverlay(i, IKey.str(name)); + effectButton.addTooltip(i, name); + } + return effectButton; + } + + protected IWidget createFlowerPicker(PanelSyncManager syncManager) { + IntSyncValue flowerSync = new IntSyncValue( + multiblock::getFlowerSelectionIndex, + multiblock::setFlowerSelectionIndex).allowC2S(); + syncManager.syncValue("beeyondsFlower", flowerSync); + + int optionCount = multiblock.getFlowerOptionCount(); + CycleButtonWidget flowerButton = new CycleButtonWidget().value(flowerSync) + .stateCount(optionCount) + .size(0, 0); + for (int i = 0; i < optionCount; i++) { + String name = multiblock.getFlowerOptionName(i); + flowerButton.stateOverlay(i, IKey.str(name)); + flowerButton.addTooltip(i, name); + } + return flowerButton; + } + // endregion + + /** + * The mode content (queen grid vs. parent slots) is rebuilt reactively through a + * {@link DynamicSyncHandler} instead of being decided once from a plain getter at GUI + * construction time - same {@code widgetProvider}/{@code notifyUpdate} idiom GT5 itself uses for + * {@code createStructureErrorWidget}/{@code createRecipeInfoWidget}. A raw Java getter read + * during construction has no guarantee the client's copy has caught up with the server, so + * anything that decides which widget to show has to go through the sync framework, not a direct + * field read. + *

+ * {@code activeModeSync} mirrors {@link OTEBeeyonds#getActiveMode()} for the packet payload (the + * content builder needs the raw mode ordinal), but its supplier is + * {@link OTEBeeyonds#getModeContentTrigger()}, which folds the Breed-mode parent species pairing + * into the same int the automatic per-tick change detector already watches - so swapping a + * parent bee still triggers a content rebuild without needing a second top-level sync + * registration. + */ + private IWidget createModeContentWidget(PanelSyncManager syncManager) { + IntSyncValue activeModeSync = new IntSyncValue(multiblock::getModeContentTrigger); + syncManager.syncValue("beeyondsActiveMode", activeModeSync); + + DynamicSyncHandler modeContentHandler = new DynamicSyncHandler().widgetProvider( + (sm, packet) -> packet == null ? new EmptyWidget() + : createModeContent(sm, OTHBeeyondsMode.values()[packet.readInt()])); + syncManager.syncValue("beeyondsModeContent", modeContentHandler); + + if (!syncManager.isClient()) { + Runnable pushContent = () -> { + // NOT activeModeSync.getValue() - that carries the combined mode+species trigger + // (see the doc above), not a valid OTHBeeyondsMode ordinal by itself. + int ordinal = multiblock.getActiveMode() + .ordinal(); + modeContentHandler.notifyUpdate(packet -> packet.writeInt(ordinal)); + }; + pushContent.run(); + activeModeSync.setChangeListener(pushContent); + } + + return new DynamicSyncedWidget<>().syncHandler(modeContentHandler) + .coverChildren(); + } + + private IWidget createModeContent(PanelSyncManager syncManager, OTHBeeyondsMode mode) { + return mode == OTHBeeyondsMode.BREED ? createBreedContent(syncManager) : createQueenGrid(syncManager); + } + + private IWidget createBreedContent(PanelSyncManager syncManager) { + return Flow.column() + .coverChildren() + .child(createParentSlotsRow(syncManager)) + .child(createTargetSpeciesWidget(syncManager)); + } + + /** + * Cycle button listing every result species this exact parent pairing can actually produce - + * the shared species itself (purebred pairing, see {@link OTEBeeyonds#tickBreed()}) or, for two + * different species, whichever different-species mutations Forestry has registered for that + * pair. Only ever rebuilt (not just value-updated) as part of {@link #createBreedContent}, since + * a {@link CycleButtonWidget}'s state count can't change live once built. Registered with + * {@code getOrCreateSyncHandler(...)}, not {@code syncValue(...)}: this runs inside a + * {@link DynamicSyncHandler}'s {@code widgetProvider}, which only allows the former. + */ + @SuppressWarnings("unchecked") + private IWidget createTargetSpeciesWidget(PanelSyncManager syncManager) { + int optionCount = multiblock.getTargetSpeciesOptionCount(); + IntSyncValue speciesIndexSync = syncManager.getOrCreateSyncHandler( + "beeyondsTargetSpeciesIndex", + 0, + IntSyncValue.class, + () -> new IntSyncValue( + multiblock::getTargetSpeciesSelectionIndex, + multiblock::setTargetSpeciesSelectionIndex).allowC2S()); + + CycleButtonWidget speciesButton = new CycleButtonWidget().marginTop(4) + .size(getTerminalRowWidth() - 8, 18) + .value(speciesIndexSync) + .stateCount(optionCount); + // Deliberately no .addTooltip(...) here: with no parents held (optionCount falls back to 1, + // the button's default state count), the tooltip array never grows past its initial empty + // state and the first addTooltip(0, ...) throws. The button's own text (stateChild below) + // already shows the full species name, so a tooltip isn't needed anyway. + for (int i = 0; i < optionCount; i++) { + int index = i; + speciesButton + .stateChild(i, new TextWidget<>(IKey.dynamic(() -> multiblock.getTargetSpeciesOptionName(index)))); + } + return speciesButton; + } + + // region parent slots (Breed mode) + private IWidget createParentSlotsRow(PanelSyncManager syncManager) { + return Flow.row() + .coverChildren() + .child(createParentSlotWidget(syncManager, 0)) + .child(createParentSlotWidget(syncManager, 1)); + } + + // getOrCreateSyncHandler(...), NOT syncValue(...): this widget is only ever built inside + // createModeContent(), which only ever runs inside the DynamicSyncHandler's widgetProvider (see + // createModeContentWidget()) - registering a sync handler any other way there throws. + @SuppressWarnings("unchecked") + private IWidget createParentSlotWidget(PanelSyncManager syncManager, int index) { + GenericSyncValue parentSync = syncManager.getOrCreateSyncHandler( + "beeyondsParentSlot" + index, + 0, + GenericSyncValue.class, + () -> new GenericSyncValue<>( + ItemStack.class, + () -> multiblock.getParentDisplayStack(index), + stack -> {}, + ByteBufAdapters.ITEM_STACK, + ICopy.immutable())); + return new ItemDisplayWidget().item(parentSync); + } + // endregion + + protected IWidget createStatusWidget(PanelSyncManager syncManager) { + StringSyncValue statusSync = new StringSyncValue(multiblock::getStatusText); + syncManager.syncValue("beeyondsStatus", statusSync); + + // One aggregated "Producing X x N in Ts" tooltip for the whole machine - like GTNH's Steam + // Space Elevator shows a single countdown, not a separate tooltip per held queen. + StringSyncValue productionTooltipSync = new StringSyncValue(multiblock::getProductionPreviewTooltip); + syncManager.syncValue("beeyondsProductionTooltip", productionTooltipSync); + + TextWidget statusWidget = new TextWidget<>(IKey.dynamic(statusSync::getStringValue)); + statusWidget.tooltipDynamic((RichTooltip tooltip) -> { + String text = productionTooltipSync.getStringValue(); + if (text == null || text.isEmpty()) return; + for (String line : text.split("\n")) { + tooltip.addLine(line); + } + }); + return statusWidget; + } + + // region queen grid + // Read-only queen slot grid, like GTNH's own Industrial Apiary / Mega Industrial Apiary controllers + // show every queen they're working instead of hiding them behind a settings sub-panel. Capped at + // QUEEN_GRID_ROWS x QUEEN_GRID_COLUMNS icons - past that (higher tiers double the slot count every + // named GT voltage tier, see OTEBeeyonds#queenSlotCount()) the extra queens still work fully, they + // just aren't individually pictured in this fixed-size grid. + private static final int QUEEN_GRID_COLUMNS = 8; + private static final int QUEEN_GRID_ROWS = 4; + + protected IWidget createQueenGrid(PanelSyncManager syncManager) { + return Flow.column() + .coverChildren() + .children(QUEEN_GRID_ROWS, row -> createQueenGridRow(syncManager, row)); + } + + private IWidget createQueenGridRow(PanelSyncManager syncManager, int row) { + return Flow.row() + .coverChildren() + .children(QUEEN_GRID_COLUMNS, col -> createQueenSlotWidget(syncManager, row * QUEEN_GRID_COLUMNS + col)); + } + + // getOrCreateSyncHandler(...), NOT syncValue(...) - see the comment on createParentSlotWidget(). + @SuppressWarnings("unchecked") + private IWidget createQueenSlotWidget(PanelSyncManager syncManager, int index) { + // GenericSyncValue.forItem(...) throws unless the getter is guaranteed non-null - most queen + // cells are empty most of the time (null stack), so the type must be passed explicitly, which + // is the overload that actually tolerates a null getter value. + GenericSyncValue queenSync = syncManager.getOrCreateSyncHandler( + "beeyondsQueenSlot" + index, + 0, + GenericSyncValue.class, + () -> new GenericSyncValue<>( + ItemStack.class, + () -> multiblock.getQueenDisplayStack(index), + stack -> {}, + ByteBufAdapters.ITEM_STACK, + ICopy.immutable())); + // Per-queen tooltip deliberately not shown here - production preview is a single aggregated + // tooltip on the main status line instead, see createStatusWidget(). + return new ItemDisplayWidget().item(queenSync); + } + // endregion + + protected IWidget createModeButton() { + OTHBeeyondsMode[] modes = OTHBeeyondsMode.values(); + CycleButtonWidget modeButton = new CycleButtonWidget().marginBottom(2) + .value( + multiblock.getModeParameter() + .getSyncHandler()) + .stateCount(modes.length); + for (int i = 0; i < modes.length; i++) { + modeButton.stateOverlay(i, GTGuiTextures.OVERLAY_BUTTON_MODE[i]); + modeButton.addTooltip( + i, + IKey.lang( + modes[i] == OTHBeeyondsMode.PRODUCTION ? "otht.bee.gui.mode.production" + : "otht.bee.gui.mode.breed")); + } + return modeButton; + } +} diff --git a/src/main/java/com/newmaa/othtech/recipe/RecipesMain.java b/src/main/java/com/newmaa/othtech/recipe/RecipesMain.java index 3f6defc..fc6d2ea 100644 --- a/src/main/java/com/newmaa/othtech/recipe/RecipesMain.java +++ b/src/main/java/com/newmaa/othtech/recipe/RecipesMain.java @@ -48,6 +48,7 @@ import bartworks.common.loaders.ItemRegistry; import goodgenerator.loader.Loaders; +import gregtech.api.GregTechAPI; import gregtech.api.enums.GTValues; import gregtech.api.enums.ItemList; import gregtech.api.enums.Materials; @@ -1429,6 +1430,21 @@ public void loadRecipes() { new Object[] { "ABC", "DEF", "GHI", 'A', getGM(31041, 1), 'B', getGM(101, 1), 'C', getGM(31078, 1), 'D', getGM(31080, 1), 'E', getGM(31085, 1), 'F', getGM(31082, 1), 'G', getGM(31083, 1), 'H', getGM(31084, 1), 'I', getGM(23540, 1) }); + // Beeyonds Home controller + RecipeBuilder.builder() + .itemInputs( + GTOreDictUnificator.get(OrePrefixes.circuit, Materials.MV, 6), + new ItemStack(GregTechAPI.sBlockReinforced, 4, 2), + ItemList.Electric_Motor_MV.get(4), + ItemList.Robot_Arm_MV.get(2), + GTOreDictUnificator.get(OrePrefixes.plateDouble, Materials.StainlessSteel, 8), + GTOreDictUnificator.get(OrePrefixes.frameGt, Materials.Aluminium, 1), + new ItemStack(Blocks.glass, 16)) + .fluidInputs(Materials.SolderingAlloy.getMolten(288)) + .itemOutputs(OTHItemList.Beeyonds.get(1)) + .duration(40 * 20) + .eut(RECIPE_MV) + .addTo(assemblerRecipes); } public static final RecipeMap OTEquantumComputerFakeRecipes = RecipeMapBuilder diff --git a/src/main/resources/assets/123technology/lang/en_US.lang b/src/main/resources/assets/123technology/lang/en_US.lang index e2ee0da..025b543 100644 --- a/src/main/resources/assets/123technology/lang/en_US.lang +++ b/src/main/resources/assets/123technology/lang/en_US.lang @@ -668,8 +668,42 @@ ote.cm.s9in1.2=There are no two suns in the sky; 9-in-1 is the only sun in our h ote.tm.bee.0=§aBeedeng's Primary Creation - Beeyonds Home (Large Industrial Apiary) ote.tm.bee.1=An HV Large Apiary. #Really only for bees! ote.tm.bee.2=Seriously, why not use Industrial Apiaries :) +ote.tm.bee.3=Production mode: up to (4 x energy tier) queens work in parallel, Alveary-style. Insert queens via any Input Bus. +ote.tm.bee.4=Breed mode: insert 2 parent bees (princess/queen/drone). Configure target species and traits in the parameter panel. 20s cycle, pristine offspring, 300% drone yield (up to 64). ote.cm.bee.0=1 - Energy Hatch, Dynamo Hatch or Laser Hatch, Input/Output Bus/Hatch: Replace Plastic Concrete Block. +otht.quest.beeyonds.name=【Tier 3 HV】Beeyonds Home +otht.quest.beeyonds.desc=§a【Tier 3 HV】Beeyonds Home§r\nAn industrial-scale multiblock apiary for the Beeyonds bee mod.\nStructure: built from GT basic machine casing, hardened stone, glass, grass, and obsidian — buildable as early as the HV tier.\nHouses and automates management of large numbers of bees, boosting breeding and yield efficiency.\nIntegrates the bee system into an industrial production line — a key facility for mass-producing honey, wax, and other special products.\n§bTwo modes (switch in the parameter panel): §aProduction§b runs up to 4x-energy-tier queens in parallel like an Alveary; §aBreed§b takes 2 parent bees, lets you pick the target species and every trait, and after a 20s cycle outputs a pristine queen or up to 64 drones at 300% yield. + +otht.bee.param.mode=Operation Mode +otht.bee.param.eject=Eject Held Bees +otht.bee.gui.mode.production=Mode: Production +otht.bee.gui.mode.breed=Mode: Breed +otht.bee.gui.eject=Eject Held Bees +otht.bee.gui.status.production=Production: %d / %d queens active +otht.bee.gui.status.breed.waiting=Breed: waiting for parents +otht.bee.gui.status.breed.progress=Breed: %d%% complete +otht.bee.gui.status.breed.noMutation=Breed: no mutation for this pair - pick a target or eject +otht.bee.gui.effect.none=Effect: None (mutation default) +otht.bee.gui.flower.none=Flower: None (mutation default) +otht.bee.gui.species.none=Target Species: (insert both parents) +otht.bee.gui.queen.tooltip.producing=Producing %s x%d in %.1fs +otht.bee.gui.queen.tooltip.noProducts=(no products) +otht.bee.param.outputKind=Breed Output +otht.bee.param.species=Target Species +otht.bee.param.speed=Trait: Speed +otht.bee.param.fertility=Trait: Fertility +otht.bee.param.lifespan=Trait: Lifespan +otht.bee.param.flowering=Trait: Flowering +otht.bee.param.territory=Trait: Territory +otht.bee.param.tempTolerance=Trait: Temp. Tolerance +otht.bee.param.humidTolerance=Trait: Humidity Tolerance +otht.bee.param.nocturnal=Trait: Nocturnal +otht.bee.param.tolerantFlyer=Trait: Tolerant Flyer +otht.bee.param.caveDwelling=Trait: Cave Dwelling +otht.bee.param.effect=Trait: Effect +otht.bee.param.flowerProvider=Trait: Flower Provider + #MegaThermalCentrifuge ote.tn.mtc=Ural Mountains Thermal Separation Plant diff --git a/src/main/resources/assets/123technology/lang/zh_CN.lang b/src/main/resources/assets/123technology/lang/zh_CN.lang index 904d178..2bc6a2d 100644 --- a/src/main/resources/assets/123technology/lang/zh_CN.lang +++ b/src/main/resources/assets/123technology/lang/zh_CN.lang @@ -669,8 +669,42 @@ ote.cm.s9in1.2=天无二日, 九合一便是我们心中唯一的太阳 ote.tm.bee.0=§aBee登们的初级造物 - Beeyonds之家(大型工业蜂箱) ote.tm.bee.1=一个HV的大型蜂箱. #真的只能养蜂! ote.tm.bee.2=说真的, 为什么不用工业蜂箱呢:) +ote.tm.bee.3=生产模式: 最多(4 x 电压等级)只女王蜂并行工作, 类似蜂箱楼. 通过任意输入总线放入女王蜂. +ote.tm.bee.4=育种模式: 放入2只亲代蜜蜂(公主/女王/雄蜂). 在参数面板中设置目标物种和性状. 20秒一周期, 产出纯种后代, 雄蜂产量300%(最多64只). ote.cm.bee.0=1 - 能源仓, 动力仓或者激光仓, 输入输出总线/仓: 替换塑料混凝土方块 +otht.quest.beeyonds.name=【三级 HV】Beeyonds 之家 +otht.quest.beeyonds.desc=§a【Tier 3 HV】Beeyonds 之家§r\n为 Beeyonds 蜜蜂模组提供的工业级多方块蜂房。\n结构:GT基础机械外壳、钢化石、玻璃、草地、黑曜石组成,HV阶段即可建造。\n可容纳并自动化管理大量蜜蜂,提升蜜蜂育种和产出效率。\n将蜜蜂系统融入工业生产线,是蜂蜜/蜡/特殊产物量产的关键设施。\n§b两种模式(在参数面板切换):§a生产模式§b如同蜂箱楼般并行工作最多4倍能源等级数量的女王蜂;§a育种模式§b放入2只亲代蜜蜂,可自由选择目标物种与每一项性状,20秒周期结束后产出一只纯种女王蜂,或最多64只产量300%的雄蜂。 + +otht.bee.param.mode=运行模式 +otht.bee.param.eject=排出已容纳的蜂 +otht.bee.gui.mode.production=模式: 生产 +otht.bee.gui.mode.breed=模式: 育种 +otht.bee.gui.eject=排出已容纳的蜂 +otht.bee.gui.status.production=生产中: %d / %d 只女王蜂工作中 +otht.bee.gui.status.breed.waiting=育种: 等待亲代蜜蜂 +otht.bee.gui.status.breed.progress=育种: 完成度 %d%% +otht.bee.gui.status.breed.noMutation=育种: 这对亲本没有可用突变 - 请选择目标物种或手动弹出 +otht.bee.gui.effect.none=效果: 无(突变默认) +otht.bee.gui.flower.none=传粉花源: 无(突变默认) +otht.bee.gui.species.none=目标物种: (需放入两只亲本) +otht.bee.gui.queen.tooltip.producing=%3$.1f 秒后产出 %1$s x%2$d +otht.bee.gui.queen.tooltip.noProducts=(无产出) +otht.bee.param.outputKind=育种产出 +otht.bee.param.species=目标物种 +otht.bee.param.speed=性状: 速度 +otht.bee.param.fertility=性状: 繁殖力 +otht.bee.param.lifespan=性状: 寿命 +otht.bee.param.flowering=性状: 传粉频率 +otht.bee.param.territory=性状: 活动范围 +otht.bee.param.tempTolerance=性状: 温度耐受 +otht.bee.param.humidTolerance=性状: 湿度耐受 +otht.bee.param.nocturnal=性状: 夜行性 +otht.bee.param.tolerantFlyer=性状: 耐雨飞行 +otht.bee.param.caveDwelling=性状: 洞穴栖息 +otht.bee.param.effect=性状: 效果 +otht.bee.param.flowerProvider=性状: 传粉花源 + #MegaThermalCentrifuge ote.tn.mtc=乌拉尔山脉热分离厂 ote.tm.mtc.0=§l苏维埃的终极造物 - 巨型热力离心机 diff --git a/src/main/resources/assets/123technology/quest/DefaultQuests/QuestLines/123Technology-123TechQuestLine==/QuestLine.json b/src/main/resources/assets/123technology/quest/DefaultQuests/QuestLines/123Technology-123TechQuestLine==/QuestLine.json index 38ab87f..b1b62b0 100644 --- a/src/main/resources/assets/123technology/quest/DefaultQuests/QuestLines/123Technology-123TechQuestLine==/QuestLine.json +++ b/src/main/resources/assets/123technology/quest/DefaultQuests/QuestLines/123Technology-123TechQuestLine==/QuestLine.json @@ -3,7 +3,7 @@ "betterquesting:10": { "bg_image:8": "", "bg_size:3": 256, - "desc:8": "123Technology Mod 机器任务线\n按制作阶段分类介绍本模组所有多方块机器", + "desc:8": "123Technology Mod Machine Quest Line\nIntroduces all the multiblock machines in this mod, organized by crafting stage", "icon:10": { "Count:3": 1, "Damage:2": 0, diff --git a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/ActiveTransform-7092635789920456480.json b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/ActiveTransform-7092635789920456480.json index d148dca..8327287 100644 --- a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/ActiveTransform-7092635789920456480.json +++ b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/ActiveTransform-7092635789920456480.json @@ -2,7 +2,7 @@ "properties:10": { "betterquesting:10": { "autoClaim:1": 0, - "desc:8": "§a【Tier 8 UV】迷你有源变压器§r\n小型化的有源变压器,可在各电压等级之间高效升降压。\n相比标准变压器体积更小、部署更灵活,适合对空间有要求的场景。\n从UV阶段开始能体现出最佳的能量转换优势。", + "desc:8": "§a【Tier 8 UV】Mini Active Transformer§r\nA miniaturized active transformer that efficiently steps voltage up or down between tiers.\nSmaller and more flexible to place than a standard transformer, ideal for space-constrained builds.\nIts energy conversion advantage really shines starting from the UV tier.", "globalShare:1": 0, "icon:10": { "Count:3": 1, @@ -13,7 +13,7 @@ "isMain:1": 0, "isSilent:1": 0, "lockedProgress:1": 0, - "name:8": "【Tier 8 UV】迷你有源变压器", + "name:8": "【Tier 8 UV】Mini Active Transformer", "questLogic:8": "AND", "repeatTime:3": -1, "repeat_relative:1": 1, diff --git a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/BBPlasmaForge-2376869744404875678.json b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/BBPlasmaForge-2376869744404875678.json index ee94c96..537877b 100644 --- a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/BBPlasmaForge-2376869744404875678.json +++ b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/BBPlasmaForge-2376869744404875678.json @@ -2,7 +2,7 @@ "properties:10": { "betterquesting:10": { "autoClaim:1": 0, - "desc:8": "§a【Tier 10 UEV】超维度憋憋锻炉§r\n憋憋(BB)主题的超维度等离子锻炉——顶级金属加工机器。\n能在超高温等离子环境中进行极端材料处理。\n可处理常规方法无法完成的特殊合金配方,是 UEV 终极金属加工机器。", + "desc:8": "§a【Tier 10 UEV】Hyperdimensional BB Forge§r\nA BB-themed hyperdimensional plasma forge — the ultimate metal-processing machine.\nCapable of extreme material processing in a superheated plasma environment.\nHandles special alloy recipes that conventional methods can't complete — the definitive UEV metalworking machine.", "globalShare:1": 0, "icon:10": { "Count:3": 1, @@ -13,7 +13,7 @@ "isMain:1": 0, "isSilent:1": 0, "lockedProgress:1": 0, - "name:8": "【Tier 10 UEV】超维度憋憋锻炉", + "name:8": "【Tier 10 UEV】Hyperdimensional BB Forge", "questLogic:8": "AND", "repeatTime:3": -1, "repeat_relative:1": 1, diff --git a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/Beeyonds--3725891889802621959.json b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/Beeyonds--3725891889802621959.json index 9f38c6e..513f483 100644 --- a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/Beeyonds--3725891889802621959.json +++ b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/Beeyonds--3725891889802621959.json @@ -2,7 +2,7 @@ "properties:10": { "betterquesting:10": { "autoClaim:1": 0, - "desc:8": "§a【Tier 3 HV】Beeyonds 之家§r\n为 Beeyonds 蜜蜂模组提供的工业级多方块蜂房。\n结构:GT基础机械外壳、钢化石、玻璃、草地、黑曜石组成,HV阶段即可建造。\n可容纳并自动化管理大量蜜蜂,提升蜜蜂育种和产出效率。\n将蜜蜂系统融入工业生产线,是蜂蜜/蜡/特殊产物量产的关键设施。\n§c该机器还未完成", + "desc:8": "otht.quest.beeyonds.desc", "globalShare:1": 0, "icon:10": { "Count:3": 1, @@ -13,7 +13,7 @@ "isMain:1": 0, "isSilent:1": 0, "lockedProgress:1": 0, - "name:8": "【Tier 3 HV】Beeyonds 之家", + "name:8": "otht.quest.beeyonds.name", "questLogic:8": "AND", "repeatTime:3": -1, "repeat_relative:1": 1, diff --git a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/BlastFurnace--2865328750978580233.json b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/BlastFurnace--2865328750978580233.json index 542aa08..cfde962 100644 --- a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/BlastFurnace--2865328750978580233.json +++ b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/BlastFurnace--2865328750978580233.json @@ -2,7 +2,7 @@ "properties:10": { "betterquesting:10": { "autoClaim:1": 0, - "desc:8": "§a【Tier 13 UXV】IMBA 超模高炉§r\n不耗电!int 并行!任何配方 10 tick 完成!\n内置一颗中子星!炉温无上限!\n来自 EOHBUF 作者的眷顾——成本低到怀疑人生!\n只要有它,资源匮乏、效率低下、能量不足统统不存在。\n传说中,神明在喝醉后设计了一台机器,醒来后却忘了删掉它...", + "desc:8": "§a【Tier 13 UXV】IMBA Overpowered Blast Furnace§r\nNo power consumption! Infinite parallel! Any recipe done in 10 ticks!\nComes with a built-in neutron star! No upper limit on furnace temperature!\nBlessed by the author of EOHBUF — the cost is so low it'll make you question reality!\nWith this, resource scarcity, low efficiency, and power shortages simply cease to exist.\nLegend says a god designed this machine while drunk, then woke up and forgot to delete it...", "globalShare:1": 0, "icon:10": { "Count:3": 1, @@ -13,7 +13,7 @@ "isMain:1": 0, "isSilent:1": 0, "lockedProgress:1": 0, - "name:8": "【Tier 13 UXV】IMBA 超模高炉", + "name:8": "【Tier 13 UXV】IMBA Overpowered Blast Furnace", "questLogic:8": "AND", "repeatTime:3": -1, "repeat_relative:1": 1, diff --git a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/ChemReactor-2224328208122200209.json b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/ChemReactor-2224328208122200209.json index 6b51532..0cf684d 100644 --- a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/ChemReactor-2224328208122200209.json +++ b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/ChemReactor-2224328208122200209.json @@ -2,7 +2,7 @@ "properties:10": { "betterquesting:10": { "autoClaim:1": 0, - "desc:8": "§a【Tier 6 LuV】铑钯蜜汁化工厂§r\n老登的终极造物——铑钯反应釜(化学反应釜 & 化工厂)。\n以 LuV 级铑(Rh)/钯(Pd)催化剂为核心的神秘化工集成机器。\n化工厂模式:耗时=NEI耗时*(1-线圈等级*0.15),最低0.1;并行16。\n大型化学反应釜模式:耗时=NEI耗时*(1-线圈等级*0.1);无限并行,无损超频。\n玻璃等级决定化工厂等级(化工厂等级 = 玻璃信道等级 - 3)。", + "desc:8": "§a【Tier 6 LuV】Rhodium-Palladium Sweet Chem Plant§r\nThe ultimate creation of the old-timers — the Rhodium-Palladium Reactor (Chemical Reactor & Chemical Plant in one).\nA mysterious integrated chemical machine built around LuV-tier rhodium (Rh)/palladium (Pd) catalysts.\nChemical Plant mode: time = NEI time * (1 - coil tier * 0.15), minimum 0.1; 16x parallel.\nLarge Chemical Reactor mode: time = NEI time * (1 - coil tier * 0.1); unlimited parallel, lossless overclocking.\nGlass tier determines the Chemical Plant tier (Chemical Plant tier = glass pipe tier - 3).", "globalShare:1": 0, "icon:10": { "Count:3": 1, @@ -13,7 +13,7 @@ "isMain:1": 0, "isSilent:1": 0, "lockedProgress:1": 0, - "name:8": "【Tier 6 LuV】铑钯蜜汁化工厂", + "name:8": "【Tier 6 LuV】Rhodium-Palladium Sweet Chem Plant", "questLogic:8": "AND", "repeatTime:3": -1, "repeat_relative:1": 1, diff --git a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/Computer-487024529263250003.json b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/Computer-487024529263250003.json index 5852a89..7fba7ee 100644 --- a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/Computer-487024529263250003.json +++ b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/Computer-487024529263250003.json @@ -2,7 +2,7 @@ "properties:10": { "betterquesting:10": { "autoClaim:1": 0, - "desc:8": "§a【Tier 8 UV】异星量子超级计算机§r\n来自异星文明的量子级超级计算机,生成算力(Computing Power)。\n冷却液需求公式:(算力/50) × 超频^1.2 × 超压^0.6 L/tick\n需持续消耗大量超级冷却液和电力才能运行;冷却液不足时机器将停止工作。", + "desc:8": "§a【Tier 8 UV】Alien Quantum Supercomputer§r\nA quantum-tier supercomputer from an alien civilization, generating Computing Power.\nCoolant demand formula: (Computing Power/50) x Overclock^1.2 x Overvoltage^0.6 L/tick\nRequires a steady supply of large amounts of super coolant and power to run; the machine stops working if coolant runs out.", "globalShare:1": 0, "icon:10": { "Count:3": 1, @@ -13,7 +13,7 @@ "isMain:1": 0, "isSilent:1": 0, "lockedProgress:1": 0, - "name:8": "【Tier 8 UV】异星量子超级计算机", + "name:8": "【Tier 8 UV】Alien Quantum Supercomputer", "questLogic:8": "AND", "repeatTime:3": -1, "repeat_relative:1": 1, diff --git a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/DiracTide-2351942955227433758.json b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/DiracTide-2351942955227433758.json index 395b76b..723a586 100644 --- a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/DiracTide-2351942955227433758.json +++ b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/DiracTide-2351942955227433758.json @@ -2,7 +2,7 @@ "properties:10": { "betterquesting:10": { "autoClaim:1": 0, - "desc:8": "§a【Tier 13 UXV】狄拉克潮汐(Mega QFT)§r\n对虚粒子的操控在量子之海中掀起了滔天巨浪...\n以物理学家狄拉克命名的量子场论处理机(Mega QFT 123T UXV 强化版)。\n提供基础原料,可让复杂化学/物理过程一步到位完成。\n时间膨胀场发生器每提升一级减少 10% 配方时间。\n稳定力场发生器等级 >=6 时产量翻倍;狄拉克逆变模式有意想不到的惊喜。", + "desc:8": "§a【Tier 13 UXV】Dirac Tide (Mega QFT)§r\nManipulating virtual particles has stirred up towering waves in the quantum sea...\nA quantum field theory processor named after physicist Dirac (Mega QFT, 123T UXV-enhanced edition).\nSupply the base materials and let complex chemical/physical processes complete in a single step.\nEach tier of the Time Dilation Field Generator reduces recipe time by 10%.\nAt Stability Field Generator tier >= 6, output doubles; the Dirac Inversion mode holds an unexpected surprise.", "globalShare:1": 0, "icon:10": { "Count:3": 1, @@ -13,7 +13,7 @@ "isMain:1": 0, "isSilent:1": 0, "lockedProgress:1": 0, - "name:8": "【Tier 13 UXV】狄拉克潮汐(Mega QFT)", + "name:8": "【Tier 13 UXV】Dirac Tide (Mega QFT)", "questLogic:8": "AND", "repeatTime:3": -1, "repeat_relative:1": 1, diff --git a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/EnderIO--7210356133696356332.json b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/EnderIO--7210356133696356332.json index 871fcd4..6e934d2 100644 --- a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/EnderIO--7210356133696356332.json +++ b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/EnderIO--7210356133696356332.json @@ -2,7 +2,7 @@ "properties:10": { "betterquesting:10": { "autoClaim:1": 0, - "desc:8": "§a【Tier 3 HV】末影接口综合体§r\n中登的终极造物——末影接口综合体。\n以先进 EU 系机器代替 RF 系机器,处理 Ender IO 相关配方。\n支持:头颅装配机、灵魂绑定机等 EIO 核心配方。\n执行无损超频,默认并行 64。\n螺丝刀右键可切换刷怪笼绑定模式(固定耗电 114 EU/t,100 ticks)。", + "desc:8": "§a【Tier 3 HV】Ender IO Interface Complex§r\nThe ultimate creation of the mid-tier veterans — the Ender IO Interface Complex.\nReplaces RF-based machines with advanced EU-based ones to process Ender IO-related recipes.\nSupports EIO core recipes such as the Skull Assembler and Soul Binder.\nRuns lossless overclocking, with a default parallel of 64.\nRight-click with a screwdriver to toggle Spawner Binding mode (fixed power draw of 114 EU/t, 100 ticks).", "globalShare:1": 0, "icon:10": { "Count:3": 1, @@ -13,7 +13,7 @@ "isMain:1": 0, "isSilent:1": 0, "lockedProgress:1": 0, - "name:8": "【Tier 3 HV】末影接口综合体", + "name:8": "【Tier 3 HV】Ender IO Interface Complex", "questLogic:8": "AND", "repeatTime:3": -1, "repeat_relative:1": 1, diff --git a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/EpicCokeOven--2222461257957685751.json b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/EpicCokeOven--2222461257957685751.json index 8eb8999..0039629 100644 --- a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/EpicCokeOven--2222461257957685751.json +++ b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/EpicCokeOven--2222461257957685751.json @@ -2,7 +2,7 @@ "properties:10": { "betterquesting:10": { "autoClaim:1": 0, - "desc:8": "§a【Tier 2 MV】史诗焦炭终结者 T123§r\n小登的终极造物,中登的期盼之物——史诗焦炭炉 T123。\n真正的终极机器,没有高贵的造价,更没有乱七八糟的机制。\n转换比例:钻石(粉)->焦炭 1:1024,煤炭(粉)->焦炭 1:16,土->焦炭 1:1\n耗时: 1800*20 ticks - (煤炭产出/5) ticks,耗电:0 EU/t\n注意:污染过高时请放置消声仓以防止跳电。", + "desc:8": "§a【Tier 2 MV】Epic Coke Terminator T123§r\nThe ultimate creation of new players, and the object of mid-tier players' longing — the Epic Coke Oven T123.\nA truly ultimate machine, with no lofty build cost and none of that messy mechanics.\nConversion ratios: diamond (dust) -> coke 1:1024, coal (dust) -> coke 1:16, dirt -> coke 1:1\nTime: 1800*20 ticks - (coal output/5) ticks, power draw: 0 EU/t\nNote: if pollution gets too high, place a muffler hatch to prevent tripping the power.", "globalShare:1": 0, "icon:10": { "Count:3": 1, @@ -13,7 +13,7 @@ "isMain:1": 0, "isSilent:1": 0, "lockedProgress:1": 0, - "name:8": "【Tier 2 MV】史诗焦炭终结者 T123", + "name:8": "【Tier 2 MV】Epic Coke Terminator T123", "questLogic:8": "AND", "repeatTime:3": -1, "repeat_relative:1": 1, diff --git a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/FishFactory--1156493011789461005.json b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/FishFactory--1156493011789461005.json index 15876f2..5ce5462 100644 --- a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/FishFactory--1156493011789461005.json +++ b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/FishFactory--1156493011789461005.json @@ -2,7 +2,7 @@ "properties:10": { "betterquesting:10": { "autoClaim:1": 0, - "desc:8": "§a【Tier 4 EV】异星渔场§r\n老登的终极造物——异星渔场。\nJumping 恒等式(主=6)解决了维度方块的不确定性,建立微型维度渔场。\n捕获物:鱼、海象、韭菜盒子、鲸鱼座藻类、立方体、苔石墙等。\n主机需放入维度方块;玻璃等级决定能源仓等级;执行无损超频。\n随机产出——可恶的抽卡机制(憋憋)。\n§c该机器还未完成", + "desc:8": "§a【Tier 4 EV】Alien Fish Farm§r\nThe ultimate creation of the old-timers — the Alien Fish Farm.\nThe Jumping identity (main = 6) resolves the uncertainty of dimension blocks, establishing a miniature dimensional fish farm.\nCatches include: fish, walrus, chive pockets, Cetus algae, cubes, mossy stone walls, and more.\nThe controller requires a dimension block; glass tier determines the energy hatch tier; runs lossless overclocking.\nRandom output — the dreaded gacha mechanic strikes again.\n§cThis machine is not yet complete", "globalShare:1": 0, "icon:10": { "Count:3": 1, @@ -13,7 +13,7 @@ "isMain:1": 0, "isSilent:1": 0, "lockedProgress:1": 0, - "name:8": "【Tier 4 EV】异星渔场", + "name:8": "【Tier 4 EV】Alien Fish Farm", "questLogic:8": "AND", "repeatTime:3": -1, "repeat_relative:1": 1, diff --git a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/FoodGen-7097361839427765517.json b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/FoodGen-7097361839427765517.json index 705f851..20f35fe 100644 --- a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/FoodGen-7097361839427765517.json +++ b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/FoodGen-7097361839427765517.json @@ -2,7 +2,7 @@ "properties:10": { "betterquesting:10": { "autoClaim:1": 0, - "desc:8": "§a【Tier 3 HV】食物发电机§r\n以食物为燃料的特殊多方块发电机。\n发电公式:食物饥饿值^2 × 线圈等级 × 机械方块等级 × 2 EU/t\n最高发电:1A MAX/t;支持 64 并行,每次最多烧毁一组相同食物。\n注意:污染过高时请放置消声仓防止跳电。\n赞美富婆 Safari_xiu!没有他就没有现在的 123T!", + "desc:8": "§a【Tier 3 HV】Food Generator§r\nA special multiblock generator that runs on food as fuel.\nPower formula: food hunger value^2 x coil tier x machine casing tier x 2 EU/t\nMax power output: 1A MAX/t; supports 64x parallel, burning up to one stack of identical food at a time.\nNote: if pollution gets too high, place a muffler hatch to prevent tripping the power.\nAll praise to the generous Safari_xiu! Without them there would be no 123T today!", "globalShare:1": 0, "icon:10": { "Count:3": 1, @@ -13,7 +13,7 @@ "isMain:1": 0, "isSilent:1": 0, "lockedProgress:1": 0, - "name:8": "【Tier 3 HV】食物发电机", + "name:8": "【Tier 3 HV】Food Generator", "questLogic:8": "AND", "repeatTime:3": -1, "repeat_relative:1": 1, diff --git a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/GraveDragon--1204911824894343714.json b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/GraveDragon--1204911824894343714.json index 34a2260..492caa8 100644 --- a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/GraveDragon--1204911824894343714.json +++ b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/GraveDragon--1204911824894343714.json @@ -2,7 +2,7 @@ "properties:10": { "betterquesting:10": { "autoClaim:1": 0, - "desc:8": "§a【Tier 10 UEV】巨龙之墓(黑龙驭者)§r\n老登的终极造物——黑龙驭者(新式龙研聚合装置)。\n并行 = 机械方块等级 * 128,最高等级解锁 int 并行。\n耗时倍率 = 1 - 0.05 * 线圈等级;三级机械方块解锁无损超频。\n机械方块等级决定可执行的配方等级,更换结构时请重放主机检测。", + "desc:8": "§a【Tier 10 UEV】Dragon's Grave (Black Dragon Rider)§r\nThe ultimate creation of the old-timers — the Black Dragon Rider (a new-style Dragon Research aggregation device).\nParallel = machine casing tier * 128, with the highest tier unlocking infinite parallel.\nTime multiplier = 1 - 0.05 * coil tier; tier-3 machine casings unlock lossless overclocking.\nMachine casing tier determines which recipe tiers can be run — re-scan the controller after changing the structure.", "globalShare:1": 0, "icon:10": { "Count:3": 1, @@ -13,7 +13,7 @@ "isMain:1": 0, "isSilent:1": 0, "lockedProgress:1": 0, - "name:8": "【Tier 10 UEV】巨龙之墓(黑龙驭者)", + "name:8": "【Tier 10 UEV】Dragon's Grave (Black Dragon Rider)", "questLogic:8": "AND", "repeatTime:3": -1, "repeat_relative:1": 1, diff --git a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/HeatExchanger-3066769644673976920.json b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/HeatExchanger-3066769644673976920.json index cbd2c23..e89e1ab 100644 --- a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/HeatExchanger-3066769644673976920.json +++ b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/HeatExchanger-3066769644673976920.json @@ -2,7 +2,7 @@ "properties:10": { "betterquesting:10": { "autoClaim:1": 0, - "desc:8": "§a【Tier 7 ZPM】真空冷冻热交换机§r\n真正的热能饕餮——真空冷冻热交换机。\n回收高炉热锭冶炼产生的废热,将其转化为超临界蒸汽。\n耗电:123 EU/t;推荐与巨型高炉或标准 EBF 配套使用。", + "desc:8": "§a【Tier 7 ZPM】Vacuum Cryogenic Heat Exchanger§r\nA true glutton for heat energy — the Vacuum Cryogenic Heat Exchanger.\nRecovers waste heat from hot-ingot smelting in the blast furnace and converts it into supercritical steam.\nPower draw: 123 EU/t; recommended for pairing with a Mega Blast Furnace or a standard EBF.", "globalShare:1": 0, "icon:10": { "Count:3": 1, @@ -13,7 +13,7 @@ "isMain:1": 0, "isSilent:1": 0, "lockedProgress:1": 0, - "name:8": "【Tier 7 ZPM】真空冷冻热交换机", + "name:8": "【Tier 7 ZPM】Vacuum Cryogenic Heat Exchanger", "questLogic:8": "AND", "repeatTime:3": -1, "repeat_relative:1": 1, diff --git a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/IsaFactory--2618974253404830901.json b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/IsaFactory--2618974253404830901.json index f4999d4..0fa5ad0 100644 --- a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/IsaFactory--2618974253404830901.json +++ b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/IsaFactory--2618974253404830901.json @@ -2,7 +2,7 @@ "properties:10": { "betterquesting:10": { "autoClaim:1": 0, - "desc:8": "§a【Tier 8 UV】艾萨处理集成工厂 OTH§r\n神奇的艾萨系列,将垃圾和化工产品融为一体。\n执行无损超频;耗时倍率=1/线圈等级;还拥有意想不到的惊喜!", + "desc:8": "§a【Tier 8 UV】Isa Integrated Processing Factory OTH§r\nThe amazing Isa series, blending trash processing and chemical products into one.\nRuns lossless overclocking; time multiplier = 1/coil tier; also holds an unexpected surprise!", "globalShare:1": 0, "icon:10": { "Count:3": 1, @@ -13,7 +13,7 @@ "isMain:1": 0, "isSilent:1": 0, "lockedProgress:1": 0, - "name:8": "【Tier 8 UV】艾萨处理集成工厂 OTH", + "name:8": "【Tier 8 UV】Isa Integrated Processing Factory OTH", "questLogic:8": "AND", "repeatTime:3": -1, "repeat_relative:1": 1, diff --git a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/IsaForge-944708431315027684.json b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/IsaForge-944708431315027684.json index faa0fad..2293121 100644 --- a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/IsaForge-944708431315027684.json +++ b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/IsaForge-944708431315027684.json @@ -2,7 +2,7 @@ "properties:10": { "betterquesting:10": { "autoClaim:1": 0, - "desc:8": "§a【Tier 13 UXV】神之艾萨锻炉§r\n艾萨领主认证——神级速度与并行。\n结构同样需要戴森球组件(sBlockCasingsDyson),为 UXV 阶段方可建造。\n耗时 = NEI耗时 * { 1 / {电压等级^[(玻璃等级/2)+1]}^(1/3) }\n主机放入星阵可解锁太空组装模式。\n是大规模矿石研磨处理的终极解决方案之一。", + "desc:8": "§a【Tier 13 UXV】Godly Isa Forge§r\nCertified by the Lord of Isa — god-tier speed and parallel.\nThe structure likewise requires Dyson Sphere components (sBlockCasingsDyson), and can only be built at the UXV tier.\nTime = NEI time * { 1 / {voltage tier^[(glass tier/2)+1]}^(1/3) }\nPlace a star matrix in the controller to unlock Space Assembly mode.\nOne of the ultimate solutions for large-scale ore grinding.", "globalShare:1": 0, "icon:10": { "Count:3": 1, @@ -13,7 +13,7 @@ "isMain:1": 0, "isSilent:1": 0, "lockedProgress:1": 0, - "name:8": "【Tier 13 UXV】神之艾萨锻炉", + "name:8": "【Tier 13 UXV】Godly Isa Forge", "questLogic:8": "AND", "repeatTime:3": -1, "repeat_relative:1": 1, diff --git a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/LargeBin--8940520277995012008.json b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/LargeBin--8940520277995012008.json index d9a0376..8fb3f2e 100644 --- a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/LargeBin--8940520277995012008.json +++ b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/LargeBin--8940520277995012008.json @@ -2,7 +2,7 @@ "properties:10": { "betterquesting:10": { "autoClaim:1": 0, - "desc:8": "§a【Tier 1 LV】盖世神功垃圾桶§r\n盖世神功垃圾桶——大容量多方块储存/销毁设施。\n可吞噬大量物品和液体,适用于大规模副产品处理场景。\n容量远超普通桶或箱,是废料管理的理想选择。", + "desc:8": "§a【Tier 1 LV】World-Conquering Trash Bin§r\nThe World-Conquering Trash Bin — a high-capacity multiblock storage/disposal facility.\nCan swallow huge amounts of items and fluids, ideal for large-scale byproduct disposal.\nIts capacity far exceeds a regular barrel or chest, making it the ideal choice for waste management.", "globalShare:1": 0, "icon:10": { "Count:3": 1, @@ -13,7 +13,7 @@ "isMain:1": 0, "isSilent:1": 0, "lockedProgress:1": 0, - "name:8": "【Tier 1 LV】盖世神功垃圾桶", + "name:8": "【Tier 1 LV】World-Conquering Trash Bin", "questLogic:8": "AND", "repeatTime:3": -1, "repeat_relative:1": 1, diff --git a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/LargeCircuitAss--8560569534669306506.json b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/LargeCircuitAss--8560569534669306506.json index 0c68842..cf1e171 100644 --- a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/LargeCircuitAss--8560569534669306506.json +++ b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/LargeCircuitAss--8560569534669306506.json @@ -2,7 +2,7 @@ "properties:10": { "betterquesting:10": { "autoClaim:1": 0, - "desc:8": "§a【Tier 5 IV】大型电路组装机§r\n专为批量制造电路板设计的大型多方块组装机。\n覆盖从 LV 到 UHV 全电压等级的电路组装配方。\n支持并行加工,大幅节省中后期游戏电路制造时间。\n线圈等级越高,生产速度越快、并行数越多。\n电路需求贯穿整个游戏进程,此机器是不可或缺的核心设施。", + "desc:8": "§a【Tier 5 IV】Large Circuit Assembler§r\nA large multiblock assembler designed specifically for mass-producing circuit boards.\nCovers circuit assembly recipes across every voltage tier from LV to UHV.\nSupports parallel processing, saving huge amounts of circuit crafting time in the mid-to-late game.\nHigher coil tiers mean faster production speed and higher parallel counts.\nCircuit demand runs through the entire game progression, making this machine an indispensable core facility.", "globalShare:1": 0, "icon:10": { "Count:3": 1, @@ -13,7 +13,7 @@ "isMain:1": 0, "isSilent:1": 0, "lockedProgress:1": 0, - "name:8": "【Tier 5 IV】大型电路组装机", + "name:8": "【Tier 5 IV】Large Circuit Assembler", "questLogic:8": "AND", "repeatTime:3": -1, "repeat_relative:1": 1, diff --git a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/MegaBlastFurnace-3771014811916846052.json b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/MegaBlastFurnace-3771014811916846052.json index 53a9809..9c8cc0f 100644 --- a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/MegaBlastFurnace-3771014811916846052.json +++ b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/MegaBlastFurnace-3771014811916846052.json @@ -2,7 +2,7 @@ "properties:10": { "betterquesting:10": { "autoClaim:1": 0, - "desc:8": "§a【Tier 6 LuV】巨型炽焱高炉§r\nGTPP 巨型高炉的 123T 强化版本,专注大规模冶炼。\n特殊模式:不消耗烈焰之炽焱,但输入仓必须有 >=123*1000L 烈焰之炽焱才可运行。\n参数:EU 消耗 90%,速度 +120%,并行 2048。\n请注意玻璃等级要求;污染过高时请放置消声仓。", + "desc:8": "§a【Tier 6 LuV】Mega Blazing Blast Furnace§r\nThe 123T-enhanced version of the GTPP Mega Blast Furnace, focused on large-scale smelting.\nSpecial mode: doesn't consume Blaze's Blazing Flame, but the input hatch must hold >=123*1000L of Blaze's Blazing Flame for it to run.\nParameters: EU consumption 90%, speed +120%, parallel 2048.\nMind the glass tier requirement; place a muffler hatch if pollution gets too high.", "globalShare:1": 0, "icon:10": { "Count:3": 1, @@ -13,7 +13,7 @@ "isMain:1": 0, "isSilent:1": 0, "lockedProgress:1": 0, - "name:8": "【Tier 6 LuV】巨型炽焱高炉", + "name:8": "【Tier 6 LuV】Mega Blazing Blast Furnace", "questLogic:8": "AND", "repeatTime:3": -1, "repeat_relative:1": 1, diff --git a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/MegaCircuitLine--3684854234097562609.json b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/MegaCircuitLine--3684854234097562609.json index ff12308..54025d6 100644 --- a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/MegaCircuitLine--3684854234097562609.json +++ b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/MegaCircuitLine--3684854234097562609.json @@ -2,7 +2,7 @@ "properties:10": { "betterquesting:10": { "autoClaim:1": 0, - "desc:8": "§a【Tier 8 UV】进阶高能电路装配线§r\n超大规模电路装配线,专为 UV+ 阶段高等级电路批量生产设计。\n线圈等级决定加工速度与并行上限。", + "desc:8": "§a【Tier 8 UV】Advanced High-Energy Circuit Line§r\nAn ultra-large-scale circuit assembly line, designed specifically for mass-producing high-tier circuits at the UV+ stage.\nCoil tier determines processing speed and the parallel cap.", "globalShare:1": 0, "icon:10": { "Count:3": 1, @@ -13,7 +13,7 @@ "isMain:1": 0, "isSilent:1": 0, "lockedProgress:1": 0, - "name:8": "【Tier 8 UV】进阶高能电路装配线", + "name:8": "【Tier 8 UV】Advanced High-Energy Circuit Line", "questLogic:8": "AND", "repeatTime:3": -1, "repeat_relative:1": 1, diff --git a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/MegaFactory--4046027631891429286.json b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/MegaFactory--4046027631891429286.json index d704aff..e00c888 100644 --- a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/MegaFactory--4046027631891429286.json +++ b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/MegaFactory--4046027631891429286.json @@ -2,7 +2,7 @@ "properties:10": { "betterquesting:10": { "autoClaim:1": 0, - "desc:8": "§a【Tier 6 LuV】终极压缩巨型加工厂§r\n123T 核心旗舰机器——终极压缩巨型加工厂。\n集成十五种工业加工模式(螺丝刀切换类,编程电路20/21/22切换内部模式):\n Metal 类: 压缩机、车床、电力磁化机\n Fluid 类: 发酵槽、流体提取机、提取机\n Misc 类: 激光蚀刻机、高压釜、流体固化机\n Isa 类: 艾萨研磨机、工业浮选机、真空干燥炉\n Col 类: 分子重组仪、酿造室、流体加热机\n并行公式:电压等级提高一级并行-2,最低9,默认256。", + "desc:8": "§a【Tier 6 LuV】Ultimate Compressed Mega Factory§r\n123T's core flagship machine — the Ultimate Compressed Mega Factory.\nIntegrates fifteen industrial processing modes (switch category with a screwdriver, switch internal mode with Programmed Circuit 20/21/22):\n Metal category: Compressor, Lathe, Electric Polarizer\n Fluid category: Fermenter, Fluid Extractor, Extractor\n Misc category: Laser Engraver, Autoclave, Fluid Solidifier\n Isa category: Isa Mill, Industrial Froth Flotation, Vacuum Drying Furnace\n Col category: Molecular Reassembler, Brewery, Fluid Heater\nParallel formula: parallel -2 for each voltage tier increase, minimum 9, default 256.", "globalShare:1": 0, "icon:10": { "Count:3": 1, @@ -13,7 +13,7 @@ "isMain:1": 0, "isSilent:1": 0, "lockedProgress:1": 0, - "name:8": "【Tier 6 LuV】终极压缩巨型加工厂", + "name:8": "【Tier 6 LuV】Ultimate Compressed Mega Factory", "questLogic:8": "AND", "repeatTime:3": -1, "repeat_relative:1": 1, diff --git a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/MegaFreezer-8443499639976053383.json b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/MegaFreezer-8443499639976053383.json index e102892..6481b44 100644 --- a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/MegaFreezer-8443499639976053383.json +++ b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/MegaFreezer-8443499639976053383.json @@ -2,7 +2,7 @@ "properties:10": { "betterquesting:10": { "autoClaim:1": 0, - "desc:8": "§a【Tier 6 LuV】巨型凛冰冷冻机§r\nGTPP 巨型冷冻机的 123T 强化版本。\n特殊模式:不消耗极寒之凛冰,但输入仓必须有 >=123*1000L 凛冰才可运行。\n参数:EU 消耗 100%,速度 +100%,并行 1024。\n大规模液态金属与冷冻材料生产不可或缺的机器。", + "desc:8": "§a【Tier 6 LuV】Mega Glacial Freezer§r\nThe 123T-enhanced version of the GTPP Mega Freezer.\nSpecial mode: doesn't consume Glacial Deep Freeze, but the input hatch must hold >=123*1000L of Glacial Deep Freeze for it to run.\nParameters: EU consumption 100%, speed +100%, parallel 1024.\nAn indispensable machine for large-scale liquid metal and frozen-material production.", "globalShare:1": 0, "icon:10": { "Count:3": 1, @@ -13,7 +13,7 @@ "isMain:1": 0, "isSilent:1": 0, "lockedProgress:1": 0, - "name:8": "【Tier 6 LuV】巨型凛冰冷冻机", + "name:8": "【Tier 6 LuV】Mega Glacial Freezer", "questLogic:8": "AND", "repeatTime:3": -1, "repeat_relative:1": 1, diff --git a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/NQFuelEngine--5655652267192334289.json b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/NQFuelEngine--5655652267192334289.json index a826a86..7942521 100644 --- a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/NQFuelEngine--5655652267192334289.json +++ b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/NQFuelEngine--5655652267192334289.json @@ -2,7 +2,7 @@ "properties:10": { "betterquesting:10": { "autoClaim:1": 0, - "desc:8": "§a【Tier 7 ZPM】通用硅岩燃料引擎§r\n以硅岩(Naquadah)燃料驱动的通用型大型多方块发电机。\n支持多种硅岩燃料变体,发电量达到 UV+ 级别。\n与压缩硅岩燃料精炼厂配合使用,构建完整的硅岩能源体系。", + "desc:8": "§a【Tier 7 ZPM】Universal Naquadah Fuel Engine§r\nA general-purpose large multiblock generator powered by Naquadah fuel.\nSupports multiple Naquadah fuel variants, with power output reaching UV+ levels.\nPair it with the Compressed Naquadah Fuel Refinery to build a complete Naquadah energy system.", "globalShare:1": 0, "icon:10": { "Count:3": 1, @@ -13,7 +13,7 @@ "isMain:1": 0, "isSilent:1": 0, "lockedProgress:1": 0, - "name:8": "【Tier 7 ZPM】通用硅岩燃料引擎", + "name:8": "【Tier 7 ZPM】Universal Naquadah Fuel Engine", "questLogic:8": "AND", "repeatTime:3": -1, "repeat_relative:1": 1, diff --git a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/NQFuelRefinery--1250477325629895402.json b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/NQFuelRefinery--1250477325629895402.json index c42320f..4acaf6a 100644 --- a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/NQFuelRefinery--1250477325629895402.json +++ b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/NQFuelRefinery--1250477325629895402.json @@ -2,7 +2,7 @@ "properties:10": { "betterquesting:10": { "autoClaim:1": 0, - "desc:8": "§a【Tier 9 UHV】压缩硅岩燃料精炼厂§r\n专门生产高纯度硅岩燃料的大型精炼厂。\n将原始硅岩材料经多步骤精炼,转化为各级硅岩燃料液体。\n与通用硅岩燃料引擎搭配,构成完整的硅岩能源生产链条。", + "desc:8": "§a【Tier 9 UHV】Compressed Naquadah Fuel Refinery§r\nA large refinery dedicated to producing high-purity Naquadah fuel.\nRefines raw Naquadah material through multiple steps, converting it into fuel liquids of every tier.\nPair it with the Universal Naquadah Fuel Engine to form a complete Naquadah energy production chain.", "globalShare:1": 0, "icon:10": { "Count:3": 1, @@ -13,7 +13,7 @@ "isMain:1": 0, "isSilent:1": 0, "lockedProgress:1": 0, - "name:8": "【Tier 9 UHV】压缩硅岩燃料精炼厂", + "name:8": "【Tier 9 UHV】Compressed Naquadah Fuel Refinery", "questLogic:8": "AND", "repeatTime:3": -1, "repeat_relative:1": 1, diff --git a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/RocketAssembler-2203467160277829399.json b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/RocketAssembler-2203467160277829399.json index 369475d..67b9185 100644 --- a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/RocketAssembler-2203467160277829399.json +++ b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/RocketAssembler-2203467160277829399.json @@ -2,7 +2,7 @@ "properties:10": { "betterquesting:10": { "autoClaim:1": 0, - "desc:8": "§a【Tier 4 EV】集成式火箭装配车间§r\n专为火箭(含义双关)设计的大型一体化装配车间。\n结构:IC2合金块、GT机械外壳(T3)、GT标准框架等,EV阶段可建造。\n集成火箭零件制造与最终组装流程,简化太空探索前期准备。\n支持各型火箭及矿石无人机的批量生产,是进入 EV 星际探索阶段的起点。\n§c该机器还未完成", + "desc:8": "§a【Tier 4 EV】Integrated Rocket Assembly Workshop§r\nA large integrated assembly workshop designed specifically for rockets (pun intended).\nStructure: IC2 alloy block, GT machine casing (T3), GT standard frame, and more — buildable at the EV tier.\nIntegrates rocket part manufacturing and final assembly, simplifying early space-exploration preparation.\nSupports batch production of various rockets and ore drones — the starting point for entering the EV interstellar exploration stage.\n§cThis machine is not yet complete", "globalShare:1": 0, "icon:10": { "Count:3": 1, @@ -13,7 +13,7 @@ "isMain:1": 0, "isSilent:1": 0, "lockedProgress:1": 0, - "name:8": "【Tier 4 EV】集成式火箭装配车间", + "name:8": "【Tier 4 EV】Integrated Rocket Assembly Workshop", "questLogic:8": "AND", "repeatTime:3": -1, "repeat_relative:1": 1, diff --git a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/Sinopec--1316793397220718578.json b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/Sinopec--1316793397220718578.json index 3f8a7c6..59477b7 100644 --- a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/Sinopec--1316793397220718578.json +++ b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/Sinopec--1316793397220718578.json @@ -2,7 +2,7 @@ "properties:10": { "betterquesting:10": { "autoClaim:1": 0, - "desc:8": "§a【Tier 5 IV】中国石化集成工厂§r\n黑金的最终流处...一步到位。\n以中国石化为主题的大型化工集成机器,整合蒸馏/裂解/提炼等流程。\n线圈等级 <10 时耗时倍率 = 1 - 线圈等级*0.1;>=10 时固定为 0.1。\n主机放入铱锇钐合金粉可解锁无损超频及并行限制(默认并行 64)。", + "desc:8": "§a【Tier 5 IV】Sinopec Integrated Factory§r\nThe final destination of black gold... done in a single step.\nA large Sinopec-themed integrated chemical machine, combining distillation, cracking, refining, and more into one process.\nAt coil tier <10, time multiplier = 1 - coil tier*0.1; at >=10, it's fixed at 0.1.\nPlace Iridium-Osmium-Samarium alloy dust in the controller to unlock lossless overclocking and lift the parallel cap (default parallel 64).", "globalShare:1": 0, "icon:10": { "Count:3": 1, @@ -13,7 +13,7 @@ "isMain:1": 0, "isSilent:1": 0, "lockedProgress:1": 0, - "name:8": "【Tier 5 IV】中国石化集成工厂", + "name:8": "【Tier 5 IV】Sinopec Integrated Factory", "questLogic:8": "AND", "repeatTime:3": -1, "repeat_relative:1": 1, diff --git a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/SoulPrison--3428309261842625794.json b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/SoulPrison--3428309261842625794.json index dcab9ad..f3702cd 100644 --- a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/SoulPrison--3428309261842625794.json +++ b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/SoulPrison--3428309261842625794.json @@ -2,7 +2,7 @@ "properties:10": { "betterquesting:10": { "autoClaim:1": 0, - "desc:8": "§a【Tier 10 UEV】噬魂监狱(Mega EEC)§r\n巨型极端能量管道(EEC)的 123T 强化版本。\n(czqwq明确确认:UIV光学电路在UEV阶段就能做了)\n「噬魂」之名源于其恐怖的能量消耗——请确保有充足的能量供应!", + "desc:8": "§a【Tier 10 UEV】Soul-Devouring Prison (Mega EEC)§r\nThe 123T-enhanced version of the Mega Extreme Energy Conduit (EEC).\n(czqwq has explicitly confirmed: UIV optical circuits can already be made at the UEV tier)\nThe name \"Soul-Devouring\" comes from its terrifying energy consumption — make sure you have an ample power supply!", "globalShare:1": 0, "icon:10": { "Count:3": 1, @@ -13,7 +13,7 @@ "isMain:1": 0, "isSilent:1": 0, "lockedProgress:1": 0, - "name:8": "【Tier 10 UEV】噬魂监狱(Mega EEC)", + "name:8": "【Tier 10 UEV】Soul-Devouring Prison (Mega EEC)", "questLogic:8": "AND", "repeatTime:3": -1, "repeat_relative:1": 1, diff --git a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/SteamNineInOne-998481993715303752.json b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/SteamNineInOne-998481993715303752.json index f3388d5..52265d9 100644 --- a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/SteamNineInOne-998481993715303752.json +++ b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/SteamNineInOne-998481993715303752.json @@ -2,7 +2,7 @@ "properties:10": { "betterquesting:10": { "autoClaim:1": 0, - "desc:8": "§a【Tier 0.5 蒸汽】小型蒸汽加工厂§r\n最早可制作的123T多方块机器,在蒸汽时代(Tier 0.5)即可建造。\n集成蒸汽熔炉、蒸汽压缩机、蒸汽锤击等多种蒸汽配方。\n以蒸汽(Steam)驱动,无需任何电力输入,耗电约 4 EU/t 等效。\n是蒸汽时代向低压电时代过渡的核心多方块生产设施。", + "desc:8": "§a【Tier 0.5 Steam】Small Steam Processing Plant§r\nThe earliest craftable 123T multiblock machine, buildable as early as the Steam Age (Tier 0.5).\nIntegrates multiple steam recipes such as steam furnace, steam compressor, and steam hammering.\nPowered by steam, requiring no electrical input at all — power draw is roughly equivalent to 4 EU/t.\nThe core multiblock production facility bridging the Steam Age to the Low Voltage electrical age.", "globalShare:1": 0, "icon:10": { "Count:3": 1, @@ -13,7 +13,7 @@ "isMain:1": 0, "isSilent:1": 0, "lockedProgress:1": 0, - "name:8": "【Tier 0.5 蒸汽】小型蒸汽加工厂", + "name:8": "【Tier 0.5 Steam】Small Steam Processing Plant", "questLogic:8": "AND", "repeatTime:3": -1, "repeat_relative:1": 1, diff --git a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/SunFactory--8646128165033130074.json b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/SunFactory--8646128165033130074.json index 13b42fa..4613f09 100644 --- a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/SunFactory--8646128165033130074.json +++ b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/SunFactory--8646128165033130074.json @@ -2,7 +2,7 @@ "properties:10": { "betterquesting:10": { "autoClaim:1": 0, - "desc:8": "§a【Tier 12 UMV】红日之将军恩情配件厂§r\n主!体!南!下!\n从某半岛神秘北方国家引进的尖端高科技巨构。\n消耗恩情运行:1t/个;配方产出倍率可在配置文件中调整(默认 1.5,四舍五入)。\n主机放入一团蜂群可获得加速(加速=log2(蜂群数量),向下取整)。", + "desc:8": "§a【Tier 12 UMV】Red Sun General's Grace Component Factory§r\nJuche! Marching! Southward!\nA cutting-edge high-tech megastructure imported from a certain mysterious northern country on a certain peninsula.\nRuns by consuming Grace: 1t each; the recipe output multiplier can be adjusted in the config file (default 1.5, rounded).\nPlace a swarm of bees in the controller for a speed boost (boost = log2(swarm count), rounded down).", "globalShare:1": 0, "icon:10": { "Count:3": 1, @@ -13,7 +13,7 @@ "isMain:1": 0, "isSilent:1": 0, "lockedProgress:1": 0, - "name:8": "【Tier 12 UMV】红日之将军恩情配件厂", + "name:8": "【Tier 12 UMV】Red Sun General's Grace Component Factory", "questLogic:8": "AND", "repeatTime:3": -1, "repeat_relative:1": 1, diff --git a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/TangshanSteel--717211484021964706.json b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/TangshanSteel--717211484021964706.json index 34a7fd3..d6b195d 100644 --- a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/TangshanSteel--717211484021964706.json +++ b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/TangshanSteel--717211484021964706.json @@ -2,7 +2,7 @@ "properties:10": { "betterquesting:10": { "autoClaim:1": 0, - "desc:8": "§a【Tier 6 LuV】唐山炼钢厂§r\n无数黑烟从烟囱冒出,工业化的必由之路...\n以工业重镇唐山命名的大型炼钢集成机器。\n一步到位地生产各种钢铁相关合金:GTPP / GT5U,以及部分 BartWorks 合金。\n集成高炉与电弧炉功能,大幅减少独立多方块建造数量。\n是 LuV 阶段大规模金属冶炼的核心设施。", + "desc:8": "§a【Tier 6 LuV】Tangshan Steel Mill§r\nCountless plumes of black smoke pour from the chimneys — the inevitable path of industrialization...\nA large integrated steelmaking machine named after the heavy-industry city of Tangshan.\nProduces all manner of steel-related alloys in one step: GTPP / GT5U, plus some BartWorks alloys.\nIntegrates blast furnace and electric arc furnace functions, greatly cutting down on the number of separate multiblocks you need to build.\nThe core facility for large-scale metal smelting at the LuV tier.", "globalShare:1": 0, "icon:10": { "Count:3": 1, @@ -13,7 +13,7 @@ "isMain:1": 0, "isSilent:1": 0, "lockedProgress:1": 0, - "name:8": "【Tier 6 LuV】唐山炼钢厂", + "name:8": "【Tier 6 LuV】Tangshan Steel Mill", "questLogic:8": "AND", "repeatTime:3": -1, "repeat_relative:1": 1, diff --git a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/Welcome-7722944585155103069.json b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/Welcome-7722944585155103069.json index b104b5e..2321021 100644 --- a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/Welcome-7722944585155103069.json +++ b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/Welcome-7722944585155103069.json @@ -2,7 +2,7 @@ "properties:10": { "betterquesting:10": { "autoClaim:1": 0, - "desc:8": "§a【欢迎】123Technology 机器指引§r\n欢迎来到 123Technology 模组!\n\n本任务线按照配方等级(何时能制作该机器)分类介绍所有多方块机器:\n Tier 0.5 -- 蒸汽时代(Steam)\n Tier 1 -- LV 低压 (32 EU/t)\n Tier 2 -- MV 中压 (128 EU/t)\n Tier 3 -- HV 高压 (512 EU/t)\n Tier 4 -- EV 极高压 (2048 EU/t)\n Tier 5 -- IV 绝缘压 (8192 EU/t)\n Tier 6 -- LuV 低超压 (32768 EU/t)\n Tier 7 -- ZPM 零点模 (131072 EU/t)\n Tier 8 -- UV 终极压 (524288 EU/t)\n Tier 9 -- UHV 超终极压 (2097152 EU/t)\n Tier 10 -- UEV 超绝缘压 (8388608 EU/t)\n Tier 11 -- UIV 超绝缘压 II (33554432 EU/t)\n Tier 12 -- UMV 超绝缘压 III (134217728 EU/t)\n Tier 13 -- UXV 超绝缘压 IV (536870912 EU/t)\n\n任务要求:完成复选框即可,无需持有物品。\n祝游戏愉快!憋憋~", + "desc:8": "§a【Welcome】123Technology Machine Guide§r\nWelcome to the 123Technology mod!\n\nThis quest line introduces all the multiblock machines, organized by recipe tier (when you can craft each machine):\n Tier 0.5 -- Steam Age\n Tier 1 -- LV Low Voltage (32 EU/t)\n Tier 2 -- MV Medium Voltage (128 EU/t)\n Tier 3 -- HV High Voltage (512 EU/t)\n Tier 4 -- EV Extreme Voltage (2048 EU/t)\n Tier 5 -- IV Insulated Voltage (8192 EU/t)\n Tier 6 -- LuV Low Ultra Voltage (32768 EU/t)\n Tier 7 -- ZPM Zero Point Module (131072 EU/t)\n Tier 8 -- UV Ultimate Voltage (524288 EU/t)\n Tier 9 -- UHV Ultra High Voltage (2097152 EU/t)\n Tier 10 -- UEV Ultra Excessive Voltage (8388608 EU/t)\n Tier 11 -- UIV Ultra Immense Voltage II (33554432 EU/t)\n Tier 12 -- UMV Ultra Mega Voltage III (134217728 EU/t)\n Tier 13 -- UXV Ultra eXtreme Voltage IV (536870912 EU/t)\n\nQuest requirement: just tick the checkbox, no need to hold any items.\nHave fun playing! BB~", "globalShare:1": 0, "icon:10": { "Count:3": 1, @@ -13,7 +13,7 @@ "isMain:1": 1, "isSilent:1": 0, "lockedProgress:1": 0, - "name:8": "【欢迎】123Technology 机器指引", + "name:8": "【Welcome】123Technology Machine Guide", "questLogic:8": "AND", "repeatTime:3": -1, "repeat_relative:1": 1, diff --git a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/WoodFusion-171878863013233495.json b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/WoodFusion-171878863013233495.json index 0803cb6..6c12e4d 100644 --- a/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/WoodFusion-171878863013233495.json +++ b/src/main/resources/assets/123technology/quest/DefaultQuests/Quests/123Technology-123TechQuestLine==/WoodFusion-171878863013233495.json @@ -2,7 +2,7 @@ "properties:10": { "betterquesting:10": { "autoClaim:1": 0, - "desc:8": "§a【Tier 7 ZPM】压缩原木聚变反应堆 Mk 0§r\n以原木为玩笑名的功能完整聚变反应堆(Mk 0 级别)。\n可生产大量等离子体,是高级材料合成的能源基础。\n作为聚变系列的起点(Mk 0),为更高等级的聚变研究奠定基础。\nZPM 阶段核聚变技术的入门机器。", + "desc:8": "§a【Tier 7 ZPM】Compressed Log Fusion Reactor Mk 0§r\nA fully functional fusion reactor (Mk 0 tier) with \"log\" as a joke name.\nCan produce large amounts of plasma, forming the energy foundation for advanced material synthesis.\nAs the starting point of the fusion series (Mk 0), it lays the groundwork for higher-tier fusion research.\nThe entry-level machine for nuclear fusion technology at the ZPM tier.", "globalShare:1": 0, "icon:10": { "Count:3": 1, @@ -13,7 +13,7 @@ "isMain:1": 0, "isSilent:1": 0, "lockedProgress:1": 0, - "name:8": "【Tier 7 ZPM】压缩原木聚变反应堆 Mk 0", + "name:8": "【Tier 7 ZPM】Compressed Log Fusion Reactor Mk 0", "questLogic:8": "AND", "repeatTime:3": -1, "repeat_relative:1": 1,