From 91153ef92216df922c7a856e584c98c2852380f0 Mon Sep 17 00:00:00 2001 From: czqwq <87927909+czqwq@users.noreply.github.com> Date: Sun, 9 Aug 2026 06:19:08 +0800 Subject: [PATCH] fix BBPF structure identify & port GUI to MUI2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 塞了点私货进去 --- .github/ISSUE_TEMPLATE/00-BugReport.yml | 79 +++++++ .github/ISSUE_TEMPLATE/01-FeatureRequest.yml | 59 ++++++ .github/ISSUE_TEMPLATE/02-SuggestChange.yml | 59 ++++++ .github/ISSUE_TEMPLATE/config.yml | 1 + .../com/newmaa/othtech/OTHTechnology.java | 1 + .../newmaa/othtech/machine/MachineLoader.java | 5 + .../othtech/machine/OTEBBPlasmaForge.java | 198 +++++++++--------- .../machine/gui/OTEBBPlasmaForgeGui.java | 82 ++++++++ .../newmaa/othtech/utils/AuthorAnimation.java | 115 ++++++++++ .../assets/123technology/lang/en_US.lang | 9 + .../assets/123technology/lang/zh_CN.lang | 9 + 11 files changed, 523 insertions(+), 94 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/00-BugReport.yml create mode 100644 .github/ISSUE_TEMPLATE/01-FeatureRequest.yml create mode 100644 .github/ISSUE_TEMPLATE/02-SuggestChange.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 src/main/java/com/newmaa/othtech/machine/gui/OTEBBPlasmaForgeGui.java create mode 100644 src/main/java/com/newmaa/othtech/utils/AuthorAnimation.java diff --git a/.github/ISSUE_TEMPLATE/00-BugReport.yml b/.github/ISSUE_TEMPLATE/00-BugReport.yml new file mode 100644 index 0000000..f2bccc5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/00-BugReport.yml @@ -0,0 +1,79 @@ +name: Report Bug (报告 Bug) +description: "Report a crash, mod conflict, or any other type of bug. (报告崩溃、模组冲突或任何其他类型的 Bug。)" +labels: + - "bug" +assignees: CallmeSHaobe +body: + - type: input + id: pack_version + attributes: + label: Your Pack Version (你的整合包版本) + description: What version of the pack are you playing? If you're using an ancient version maybe first try updating the pack. We usually fix hundreds of issues per release, so there is a good chance your problem have been fixed already in the latest version. There are also usually Dev releases inbetween Official releases that may not show up on other platforms. (你玩的是哪个版本的整合包?如果版本很旧,建议先尝试更新整合包。我们通常每个版本修复若干个问题,所以你的问题很可能在最新版本中已经修复。此外,官方版本之间通常还有 Dev 版本,其他平台可能不会显示。) + placeholder: "Example: 2.1.1.0 (示例: 2.1.1.0)" + validations: + required: true + - type: input + id: mod_version + attributes: + label: Mod Version (模组版本) + description: What version of the mod are you using? Give an actual version number or jar name, not "latest". Speaking of which, if you are not on the latest jar, try updating first. (你正在使用哪个版本的模组?请给出实际的版本号或 jar 文件名,而不是"latest"。另外,如果你没有使用最新版 jar,请先尝试更新。) + placeholder: "example: 3.4 (示例: 3.4)" + validations: + required: true + - type: textarea + id: whatistheissue + attributes: + label: Describe the Issue (描述问题) + description: Give a description of the issue, steps to reproduce the issue, as well as what you think the expected result should be. (请描述问题内容、复现步骤,以及你认为的预期结果。) + placeholder: "Example: 1 Foo is crafted but no Bar is consumed. 1 Bar should be consumed. (示例: 合成了 1 个 Foo,但没有消耗 Bar。应该消耗 1 个 Bar。)" + validations: + required: true + - type: textarea + id: conflict + attributes: + label: Conflicting Mods (冲突模组) + description: If this is a mod conflict, list the mods you think are causing the issue. (如果是模组冲突,请列出你认为导致问题的模组。) + placeholder: "Example: Causes to break only if is installed (示例: 只有安装了 <模组名> 时,<功能> 才会出问题)" + validations: + required: false + - type: dropdown + id: java + attributes: + label: Java Version (Java 版本) + description: What Java version are you using? It's worth mentioning that if you play on Java9+ you should try update to latest minor release (e.g. prefer Java 17.0.6 over 17.0.2) of that version. (你使用的是哪个 Java 版本?值得一提的是,如果你在 Java9+ 上游玩,建议更新到该版本的最新小版本,例如优先选择 Java 17.0.6 而不是 17.0.2。) + options: + - Java 8 + - Java 9 + - Java 11 + - Java 17 + - Java 19 + - Java 20 + - Java 21 + - Java 22 + - Java 23 + - Java 24 + - Java 25 + - Java 26 + - Other versions in between (其他介于两者之间的版本) + validations: + required: true + - type: textarea + id: report + attributes: + label: Crash Report / Log (崩溃报告 / 日志) + description: "If no crash report is created, attach `logs/fml-client-latest.log` or `logs/fml-server-latest.log`, depending on if it's a dedicated server crash or a client crash. You can also type in additional info that you consider important. (如果没有生成崩溃报告,请根据是专用服务器崩溃还是客户端崩溃,附上 `logs/fml-client-latest.log` 或 `logs/fml-server-latest.log`。你也可以补充你认为重要的其他信息。)" + placeholder: "Example: pastebin/something OR submit the file to github by dragging it to this textbox. (示例: pastebin 链接,或将文件拖拽到输入框中上传到 GitHub。)" + validations: + required: true + - type: checkboxes + id: final + attributes: + label: Final Checklist (最终确认清单) + description: Please mark them so we can better handle this report. (请勾选以下选项,以便我们更好地处理这份报告。) + options: + - label: "I have searched this issue tracker and there is nothing similar already. Posting on a closed issue saying the crash still exists will prompt us to investigate and reopen it once we confirm your report. (我已经搜索过问题跟踪器,没有类似的已存在报告。如果在已关闭的问题下回复说崩溃仍然存在,我们会在确认你的报告后进行调查并重新打开它。)" + required: true + - label: "I can reproduce this crash consistently by following the exact steps I described above. (我可以按照上述步骤稳定复现这个崩溃。)" + required: false + - label: "I have asked other people and they confirm they also crash by following the exact steps I described above. (我已经询问过其他人,他们确认按上述步骤操作也会崩溃。)" + required: false diff --git a/.github/ISSUE_TEMPLATE/01-FeatureRequest.yml b/.github/ISSUE_TEMPLATE/01-FeatureRequest.yml new file mode 100644 index 0000000..2a513cb --- /dev/null +++ b/.github/ISSUE_TEMPLATE/01-FeatureRequest.yml @@ -0,0 +1,59 @@ +name: 'Request New Feature (请求新功能)' +description: Request a new feature. (请求一个新功能。) +assignees: CallmeSHaobe +labels: + - "Feature" +body: + - type: markdown + attributes: + value: 'Examples of this category include: add a new item, add a new function. (此类示例包括:添加新物品、添加新功能。) + + Please note that if you want to change an existing feature, e.g. think something is too OP, you should use the Suggest a Change template instead. (请注意,如果你想修改现有功能,例如认为某个东西太强,请改用 Suggest a Change 模板。)' + - type: input + id: version + attributes: + label: Your Pack Version (你的整合包版本) + description: What version of the pack are you playing? If you're using an ancient version maybe first try updating the pack. We add new features all the time, so your feature may already be in the latest version. There are also usually Dev releases inbetween Official releases that may not show up on other platforms. (你玩的是哪个版本的整合包?如果版本很旧,建议先尝试更新整合包。我们一直在添加新功能,所以你的功能请求可能已经存在于最新版本中。此外,官方版本之间通常还有 Dev 版本,其他平台可能不会显示。) + placeholder: "Example: 2.1.1.0 (示例: 2.1.1.0)" + validations: + required: true + - type: input + id: mod_version + attributes: + label: Mod Version (模组版本) + description: What version of the mod are you using? Give an actual version number or jar name, not "latest". Speaking of which, if you are not on the latest jar, try updating first. (你正在使用哪个版本的模组?请给出实际的版本号或 jar 文件名,而不是"latest"。另外,如果你没有使用最新版 jar,请先尝试更新。) + placeholder: "Example: 3.4 (示例: 3.4)" + validations: + required: true + - type: textarea + id: proposal + attributes: + label: Your Proposal (你的提议) + description: What do you propose? Please describe it briefly in one or two sentence. (你想提议什么?请用一两句话简要描述。) + placeholder: "Example: Add a new chain function. (示例: 添加一个新的连锁功能。)" + validations: + required: true + - type: textarea + id: goal + attributes: + label: Your Goal (你的目标) + description: Why is this feature needed? (为什么需要这个功能?) + placeholder: "Example: Old mode can't fill my heart. (示例: 旧的模式无法填满我的心。)" + validations: + required: true + - type: checkboxes + id: final + attributes: + label: Final Checklist (最终确认清单) + description: Please mark them so we know you are serious with your suggestion. (请勾选以下选项,以便我们知道你是认真提出建议的。) + options: + - label: "I have searched this issue tracker and there is nothing similar already. Posting on a closed issue saying *I like this feature please reconsider adding it* will prompt us to investigate and reopen it once we confirm your report. (我已经搜索过问题跟踪器,没有类似的已存在请求。如果在已关闭的问题下回复*我喜欢这个功能,请重新考虑添加它*,我们会在确认你的报告后进行调查并重新打开它。)" + required: true + - label: "I believe there is nothing similar in the pack already, or the existing solution isn't good enough. (我认为整合包中还没有类似的功能,或者现有的解决方案不够好。)" + required: true + - label: "I understand this change request may not attract enough attention and thus not be implemented. (我理解这个修改请求可能得不到足够的关注,因此可能不会被实现。)" + required: true + - label: "I understand this change request may be rejected due to other community members thinking it's inappropriate. (我理解这个修改请求可能会因为其他社区成员认为不合适而被拒绝。)" + required: true + - label: "I believe this feature would make the pack better. (我相信这个功能会让整合包变得更好。)" + required: true diff --git a/.github/ISSUE_TEMPLATE/02-SuggestChange.yml b/.github/ISSUE_TEMPLATE/02-SuggestChange.yml new file mode 100644 index 0000000..2ca3c24 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/02-SuggestChange.yml @@ -0,0 +1,59 @@ +name: 'Suggest a Change (建议修改)' +description: Suggest a Change to existing content. (对现有内容提出修改建议。) +assignees: CallmeSHaobe +labels: + - "Request-Change" +body: + - type: markdown + attributes: + value: 'Examples of this category include: changing an existing feature, changing a recipe, nerfing an item, buffing an item. (此类示例包括:修改现有功能、修改合成配方、削弱或加强某个物品。) + + Please note that if you want to add a new feature, use the Feature Request template instead. (请注意,如果你想添加新功能,请改用"请求新功能"模板。)' + - type: input + id: version + attributes: + label: Your Pack Version (你的整合包版本) + description: What version of the pack are you playing? If you're using an ancient version maybe first try updating the pack. We change things all the time, so your suggested change may already be applied in the latest version. There are also usually Dev releases inbetween Official releases that may not show up on other platforms. (你玩的是哪个版本的整合包?如果版本很旧,建议先尝试更新整合包。我们一直在修改内容,所以你的建议可能已经在最新版本中生效。此外,官方版本之间通常还有 Dev 版本,其他平台可能不会显示。) + placeholder: "Example: 2.1.1.0 (示例: 2.1.1.0)" + validations: + required: true + - type: input + id: mod_version + attributes: + label: Mod Version (模组版本) + description: What version of the mod are you using? Give an actual version number or jar name, not "latest". Speaking of which, if you are not on the latest jar, try updating first. (你正在使用哪个版本的模组?请给出实际的版本号或 jar 文件名,而不是"latest"。另外,如果你没有使用最新版 jar,请先尝试更新。) + placeholder: "Example: 3.4 (示例: 3.4)" + validations: + required: true + - type: textarea + id: change + attributes: + label: Your Suggestion (你的建议) + description: What do you want to change? Please describe it briefly in one or two sentences. (你想修改什么?请用一两句话简要描述。) + placeholder: "Example: Buff the chain mining speed. (示例: 提升连锁挖矿的速度。)" + validations: + required: true + - type: textarea + id: goal + attributes: + label: Your Goal (你的目标) + description: Why is this change needed? (为什么需要这个修改?) + placeholder: "Example: The current speed is too slow for large tunnels. (示例: 对于大型隧道来说当前速度太慢了。)" + validations: + required: true + - type: checkboxes + id: final + attributes: + label: Final Checklist (最终确认清单) + description: Please mark them so we know you are serious with your suggestion. (请勾选以下选项,以便我们知道你是认真提出建议的。) + options: + - label: "I have searched this issue tracker and there is nothing similar already. Posting on a closed issue saying *I like this change please reconsider it* will prompt us to investigate and reopen it once we confirm your report. (我已经搜索过问题跟踪器,没有类似的已存在请求。如果在已关闭的问题下回复*我喜欢这个修改,请重新考虑*,我们会在确认你的报告后进行调查并重新打开它。)" + required: true + - label: "I believe there is nothing similar in the pack already, or the existing solution isn't good enough. (我认为整合包中还没有类似的内容,或者现有的解决方案不够好。)" + required: true + - label: "I understand this change request may not attract enough attention and thus not be implemented. (我理解这个修改请求可能得不到足够的关注,因此可能不会被实现。)" + required: true + - label: "I understand this change request may be rejected due to other community members thinking it's inappropriate. (我理解这个修改请求可能会因为其他社区成员认为不合适而被拒绝。)" + required: true + - label: "I believe this change would make the pack better. (我相信这个修改会让整合包变得更好。)" + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..3ba13e0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1 @@ +blank_issues_enabled: false diff --git a/src/main/java/com/newmaa/othtech/OTHTechnology.java b/src/main/java/com/newmaa/othtech/OTHTechnology.java index d3dd464..f5881f7 100644 --- a/src/main/java/com/newmaa/othtech/OTHTechnology.java +++ b/src/main/java/com/newmaa/othtech/OTHTechnology.java @@ -105,6 +105,7 @@ public static void registerRenderers() {} public void postInit(FMLPostInitializationEvent event) { proxy.postInit(event); LOG.info("The Isa Mill is invincible!"); + LOG.info("NewMaa的恩情还不完!"); MachineLoader.loadMachinePostInit(); // RecipeLoader.loadRecipesPostInit(); // 添加自定义配方 diff --git a/src/main/java/com/newmaa/othtech/machine/MachineLoader.java b/src/main/java/com/newmaa/othtech/machine/MachineLoader.java index 5eb1141..cc96b79 100644 --- a/src/main/java/com/newmaa/othtech/machine/MachineLoader.java +++ b/src/main/java/com/newmaa/othtech/machine/MachineLoader.java @@ -2,9 +2,11 @@ import static net.minecraft.util.StatCollector.translateToLocal; +import com.gtnewhorizon.gtnhlib.util.AnimatedTooltipHandler; import com.newmaa.othtech.common.OTHItemList; import com.newmaa.othtech.machine.hatch.OTEHatchRack; import com.newmaa.othtech.machine.hatch.OTEWTFHatch; +import com.newmaa.othtech.utils.AuthorAnimation; import gregtech.api.enums.Materials; import gregtech.api.enums.TierEU; @@ -183,6 +185,9 @@ public static void loadMachines() { OTHItemList.Beeyonds.set(new OTEBeeyonds(IDs + 58, "IndustrialBeeHouse", translateToLocal("ote.tn.bee"))); OTHItemList.OTEBBPlasmaForge .set(new OTEBBPlasmaForge(IDs + 59, "OTEBBPlasmaForge", translateToLocal("ote.tn.bbpf"))); + // 注册实时动画作者行(gtnhlib AnimatedTooltipHandler 在渲染时逐帧求值,同 GT5U LoaderMetaTileEntities 的做法) + AnimatedTooltipHandler + .addItemTooltip(OTHItemList.OTEBBPlasmaForge.get(1), AuthorAnimation.getTooltipSupplier()); OTHItemList.OTEComputer.set(new OTEComputer(IDs + 60, "OTEComputer", translateToLocal("ote.computer.name"))); OTHItemList.OTEHatchRack .set(new OTEHatchRack(IDs + 61, "OTEHatchRack", translateToLocal("ote.calc.rank"), 11).getStackForm(1L)); diff --git a/src/main/java/com/newmaa/othtech/machine/OTEBBPlasmaForge.java b/src/main/java/com/newmaa/othtech/machine/OTEBBPlasmaForge.java index 98094f3..16004b8 100644 --- a/src/main/java/com/newmaa/othtech/machine/OTEBBPlasmaForge.java +++ b/src/main/java/com/newmaa/othtech/machine/OTEBBPlasmaForge.java @@ -1,7 +1,7 @@ package com.newmaa.othtech.machine; +import static com.gtnewhorizon.gtnhlib.util.numberformatting.NumberFormatUtil.formatNumber; import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlock; -import static com.gtnewhorizon.structurelib.structure.StructureUtility.withChannel; import static gregtech.api.GregTechAPI.*; import static gregtech.api.enums.GTValues.VN; import static gregtech.api.enums.HatchElement.*; @@ -9,6 +9,7 @@ import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_DTPF_ON; import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FUSION1_GLOW; import static gregtech.api.enums.Textures.BlockIcons.casingTexturePages; +import static gregtech.api.util.GTStructureUtility.activeCoils; import static gregtech.api.util.GTStructureUtility.buildHatchAdder; import static gregtech.api.util.GTStructureUtility.ofCoil; import static gregtech.api.util.GTUtility.validMTEList; @@ -41,12 +42,7 @@ import com.gtnewhorizon.structurelib.structure.IStructureDefinition; import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment; import com.gtnewhorizon.structurelib.structure.StructureDefinition; -import com.gtnewhorizons.modularui.api.drawable.IDrawable; -import com.gtnewhorizons.modularui.api.drawable.UITexture; -import com.gtnewhorizons.modularui.api.screen.ModularWindow; -import com.gtnewhorizons.modularui.api.screen.UIBuildContext; -import com.gtnewhorizons.modularui.common.widget.ButtonWidget; -import com.gtnewhorizons.modularui.common.widget.FakeSyncWidget; +import com.newmaa.othtech.machine.gui.OTEBBPlasmaForgeGui; import com.newmaa.othtech.machine.machineclass.OTHMultiMachineBase; import com.newmaa.othtech.machine.machineclass.OTHProcessingLogic; import com.newmaa.othtech.utils.Utils; @@ -55,9 +51,9 @@ import cpw.mods.fml.relauncher.SideOnly; import gregtech.api.enums.HeatingCoilLevel; import gregtech.api.enums.SoundResource; -import gregtech.api.gui.modularui.GTUITextures; import gregtech.api.interfaces.ITexture; import gregtech.api.interfaces.metatileentity.IMetaTileEntity; +import gregtech.api.interfaces.tileentity.IGregTechDeviceInformation; import gregtech.api.interfaces.tileentity.IGregTechTileEntity; import gregtech.api.logic.ProcessingLogic; import gregtech.api.metatileentity.implementations.MTEHatch; @@ -67,33 +63,43 @@ import gregtech.api.recipe.check.CheckRecipeResultRegistry; import gregtech.api.recipe.check.SimpleCheckRecipeResult; import gregtech.api.render.TextureFactory; +import gregtech.api.structure.error.ErrorType; import gregtech.api.structure.error.StructureError; +import gregtech.api.structure.error.StructureErrorRegistry; +import gregtech.api.structure.error.StructureErrors; import gregtech.api.util.GTModHandler; import gregtech.api.util.GTRecipe; import gregtech.api.util.GTRecipeConstants; import gregtech.api.util.GTUtility; import gregtech.api.util.MultiblockTooltipBuilder; import gregtech.api.util.OverclockCalculator; +import gregtech.common.gui.modularui.multiblock.base.MTEMultiBlockBaseGui; import gregtech.common.misc.GTStructureChannels; import mcp.mobius.waila.api.IWailaConfigHandler; import mcp.mobius.waila.api.IWailaDataAccessor; import tectech.thing.block.BlockQuantumGlass; -import tectech.thing.gui.TecTechUITextures; public class OTEBBPlasmaForge extends OTHMultiMachineBase implements ISurvivalConstructable { // 老大哥锻炉,老大哥的恩情还不完 protected void updatetier() { - ItemStack aGuiStack = this.getControllerSlot(); - if (aGuiStack != null) { - if (GTUtility.areStacksEqual(aGuiStack, GTModHandler.getModItem("gregtech", "gt.metaitem.03", 1, 32758))) { - this.MLevel = 2; - } + if (hasCalibrationMatrix()) { + this.MLevel = 2; } else { this.MLevel = 1; } } + /** + * 控制器槽位是否放入超维校准矩阵(meta 32758),MLevel 2 的判定依据。 + * 控制器槽是同步的库存槽,客户端永远最新,GUI 按钮以此作为门槛(同 DTPF 的做法)。 + */ + public boolean hasCalibrationMatrix() { + ItemStack aGuiStack = this.getControllerSlot(); + return aGuiStack != null + && GTUtility.areStacksEqual(aGuiStack, GTModHandler.getModItem("gregtech", "gt.metaitem.03", 1, 32758)); + } + @Override public int getMaxParallelRecipes() { if (MLevel == 2) { @@ -461,7 +467,8 @@ public IStructureDefinition getStructureDefinition() { .addElement('C', ofBlock(sBlockCasings1, 14)) .addElement( 'D', - withChannel("coil", ofCoil(OTEBBPlasmaForge::setCoilLevel, OTEBBPlasmaForge::getCoilLevel))) + GTStructureChannels.HEATING_COIL + .use(activeCoils(ofCoil(OTEBBPlasmaForge::setCoilLevel, OTEBBPlasmaForge::getCoilLevel)))) .addElement('E', ofBlock(sBlockCasingsTT, 7)) .addElement('F', ofBlock(sBlockCasingsTT, 8)) .addElement('G', ofBlock(BlockQuantumGlass.INSTANCE, 0)) @@ -486,24 +493,36 @@ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) { @Override protected MultiblockTooltipBuilder createTooltip() { MultiblockTooltipBuilder tt = new MultiblockTooltipBuilder(); + // ── 序章:中二宣言 ── tt.addMachineType(translateToLocal("ote.bbpf.0")) .addInfo(translateToLocal("ote.bbpf.1")) + .addInfo(translateToLocal("ote.bbpf.8")) + .addSeparator() + // ── 核心数值:并行 / 线圈效率 / 上限突破 ── .addInfo(translateToLocal("ote.bbpf.2")) + .addInfo(translateToLocal("ote.bbpf.9")) + .addInfo(translateToLocal("ote.bbpf.10")) .addInfo(translateToLocal("ote.bbpf.3")) - .addInfo((translateToLocal("ote.bbpf.4"))) - .addInfo((translateToLocal("ote.bbpf.6"))) - .addInfo((translateToLocal("ote.bbpf.7"))) .addSeparator() + // ── 强化:校准矩阵 → 完美超频 + 无线模式 ── + .addInfo(translateToLocal("ote.bbpf.4")) + .addInfo(translateToLocal("ote.bbpf.11")) + .addInfo(translateToLocal("ote.bbpf.6")) + .addSeparator() + .addInfo(translateToLocal("ote.bbpf.7")) + .addSeparator() + // ── 结构(按 SHIFT 查看)── .addController(translateToLocal("ote.bbpf.0")) .beginStructureBlock(47, 11, 47, false) + .addStructureInfo(translateToLocal("ote.bbpf.12")) + .addStructureInfo(translateToLocal("ote.bbpf.13")) + .addStructureInfo(translateToLocal("ote.bbpf.14")) .addInputBus("AnyInputBus", 1) .addOutputBus("AnyOutputBus", 1) .addInputHatch("AnyInputHatch", 1) .addOutputHatch("AnyOutputHatch", 1) .addEnergyHatch("AnyEnergyHatch", 1) .addSubChannelUsage(GTStructureChannels.HEATING_COIL) - .addSeparator() - .addInfo("§b§lAuthor:§r§kunknown§r§lczqwq§r") .toolTipFinisher(); return tt; } @@ -724,27 +743,42 @@ protected long getActualEnergyUsage() { @Override public void checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack, List errors) { - // Reset heating capacity. - updatetier(); + // Reset value mHeatingCapacity = 0; - - // Get heating capacity from coils in structure. setCoilLevel(HeatingCoilLevel.None); - mEnergyHatches.clear(); - mExoticEnergyHatches.clear(); - mInputHatches.clear(); - mInputBusses.clear(); - if (getCoilLevel() == HeatingCoilLevel.None) return; + + // 线圈等级由 checkPiece 扫描结构时通过 ofCoil 写入 mCoilLevel,必须先扫描再判断 if (!checkPiece(STRUCTURE_PIECE_MAIN, 23, 5, 20, errors)) return; - mHeatingCapacity = (int) getCoilLevel().getHeat(); + if (getCoilLevel() == HeatingCoilLevel.None) { + errors.add(StructureErrorRegistry.COIL_LEVEL_NOT_ENOUGH); + } + + // 无线模式使用无线网络供电,不需要能源仓 + if (!isWirelessMode) { + checkHasAnyEnergy(errors); + } + checkHasInputBus(errors); + checkHasOutputBus(errors); + // 无线模式下不允许能源仓 if (isWirelessMode && (!mEnergyHatches.isEmpty() || !mExoticEnergyHatches.isEmpty())) { + errors.add( + StructureErrors + .hatchCount(ErrorType.TOO_MANY, Energy, mEnergyHatches.size() + mExoticEnergyHatches.size(), 0)); return; } + if (errors.isEmpty()) { + mHeatingCapacity = (int) getCoilLevel().getHeat(); + } updatetier(); repairMachine(); } + @Override + public void getExtraInfoData(List info) { + info.add(IGregTechDeviceInformation.encode("GT5U.EBF.heat.s", formatNumber(this.mHeatingCapacity))); + } + @NotNull @Override public CheckRecipeResult checkProcessing() { @@ -759,10 +793,9 @@ public CheckRecipeResult checkProcessing() { mOutputItems = processingLogic.getOutputItems(); mOutputFluids = processingLogic.getOutputFluids(); - // 设置能量消耗(无线模式下已设为0,有线模式下正常设置) - if (!isWirelessMode) { - lEUt = -processingLogic.getCalculatedEut(); - } + // 设置能量消耗(无线模式下 calculatedEut 已在 onRecipeStart 中被置为 0,此处恒为 0; + // 若不更新,lEUt 会残留有线模式下的数值,导致机器无能源仓却空转耗电) + lEUt = -processingLogic.getCalculatedEut(); return result; } @@ -892,57 +925,35 @@ protected SoundResource getActivitySoundLoop() { } @Override - public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) { - builder.widget(new ButtonWidget().setOnClick((clickData, widget) -> { - // isWireless on! - // 亿万火种之怒,燃尽此身! - if (getMLevel() >= 2) { - if (this.mEnergyHatches.isEmpty() || this.mExoticEnergyHatches.isEmpty()) { - isWirelessMode = !isWirelessMode; - } else { - isWirelessMode = false; - } - } + protected @NotNull MTEMultiBlockBaseGui getGui() { + return new OTEBBPlasmaForgeGui(this); + } - }) - .setPlayClickSound(true) - .setBackground(() -> { - List ret = new ArrayList<>(); - ret.add(GTUITextures.BUTTON_STANDARD); - - // fix:只有在 MLevel >= 2 时才根据 isWirelessMode 显示不同图标 - if (getMLevel() >= 2) { - if (isWirelessMode) { - ret.add(TecTechUITextures.OVERLAY_BUTTON_POWER_PASS_ON); - } else { - ret.add(TecTechUITextures.OVERLAY_BUTTON_POWER_PASS_OFF); - } - } else { - // 如果 MLevel < 2,总是显示关闭状态的图标 - ret.add(TecTechUITextures.OVERLAY_BUTTON_POWER_PASS_OFF); - } - return ret.toArray(new IDrawable[0]); - }) - .addTooltip(translateToLocal("ote.bbpf.5")) - .setPos(174, 112) - .setSize(16, 16) - .attachSyncer( - new FakeSyncWidget.BooleanSyncer( - () -> isWirelessMode && getMLevel() >= 2, // 只同步当条件满足时的状态 - (val) -> { - // 只有当MLevel >= 2时才更新isWirelessMode - if (getMLevel() >= 2) { - isWirelessMode = val; - } - }), - builder)); - super.addUIWidgets(builder, buildContext); - } - - private int getMLevel() { + public int getMLevel() { return MLevel; } + public boolean isWirelessModeEnabled() { + return isWirelessMode; + } + + /** + * 无线模式开关。规则在服务端强制执行(GUI 的 C2S 同步值经此落字段): + * 只有 MLevel >= 2 且无能源仓才能开启;关闭永远允许。 + * 注意:NBT 加载(loadNBTData)直接写字段,不走此规则。 + */ + public void setWirelessModeEnabled(boolean value) { + if (value && (getMLevel() < 2 || !areEnergyHatchesEmpty())) { + isWirelessMode = false; + return; + } + isWirelessMode = value; + } + + public boolean areEnergyHatchesEmpty() { + return mEnergyHatches.isEmpty() && mExoticEnergyHatches.isEmpty(); + } + public void saveNBTData(NBTTagCompound aNBT) { aNBT.setBoolean("wireless", isWirelessMode); aNBT.setInteger("MLevel", MLevel); @@ -981,21 +992,20 @@ public boolean getDefaultHasMaintenanceChecks() { public final void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, float aX, float aY, float aZ, ItemStack aTool) { - if (getMLevel() >= 2) { - if (this.mEnergyHatches.isEmpty() && this.mExoticEnergyHatches.isEmpty()) { - isWirelessMode = !isWirelessMode; - if (isWirelessMode) { - GTUtility.sendChatToPlayer(aPlayer, StatCollector.translateToLocal("ote.bbpf.wireless.on")); - } else { - GTUtility.sendChatToPlayer(aPlayer, StatCollector.translateToLocal("ote.bbpf.wireless.off")); - } + if (getMLevel() < 2) { + GTUtility.sendChatToPlayer(aPlayer, StatCollector.translateToLocal("ote.bbpf.wireless.invalid")); + return; + } + if (areEnergyHatchesEmpty()) { + setWirelessModeEnabled(!isWirelessModeEnabled()); + if (isWirelessModeEnabled()) { + GTUtility.sendChatToPlayer(aPlayer, StatCollector.translateToLocal("ote.bbpf.wireless.on")); } else { - isWirelessMode = false; - GTUtility.sendChatToPlayer(aPlayer, StatCollector.translateToLocal("ote.bbpf.wireless.energyhatch")); - } - { - GTUtility.sendChatToPlayer(aPlayer, StatCollector.translateToLocal("ote.bbpf.wireless.invalid")); + GTUtility.sendChatToPlayer(aPlayer, StatCollector.translateToLocal("ote.bbpf.wireless.off")); } + } else { + setWirelessModeEnabled(false); + GTUtility.sendChatToPlayer(aPlayer, StatCollector.translateToLocal("ote.bbpf.wireless.energyhatch")); } } } diff --git a/src/main/java/com/newmaa/othtech/machine/gui/OTEBBPlasmaForgeGui.java b/src/main/java/com/newmaa/othtech/machine/gui/OTEBBPlasmaForgeGui.java new file mode 100644 index 0000000..d4829a6 --- /dev/null +++ b/src/main/java/com/newmaa/othtech/machine/gui/OTEBBPlasmaForgeGui.java @@ -0,0 +1,82 @@ +package com.newmaa.othtech.machine.gui; + +import com.cleanroommc.modularui.api.drawable.IKey; +import com.cleanroommc.modularui.api.widget.IWidget; +import com.cleanroommc.modularui.drawable.DynamicDrawable; +import com.cleanroommc.modularui.screen.ModularPanel; +import com.cleanroommc.modularui.value.sync.BooleanSyncValue; +import com.cleanroommc.modularui.value.sync.PanelSyncManager; +import com.cleanroommc.modularui.widgets.ButtonWidget; +import com.cleanroommc.modularui.widgets.layout.Flow; +import com.newmaa.othtech.machine.OTEBBPlasmaForge; + +import gregtech.api.modularui2.GTGuiTextures; +import gregtech.common.gui.modularui.multiblock.base.MTEMultiBlockBaseGui; + +/** + * MUI2 (ModularUI2) GUI for the {@link OTEBBPlasmaForge}, decoupled from the + * machine — same pattern as GT5U's {@code MTEPlasmaForgeGui}. + * + *

+ * Responsibilities: + *

    + *
  • Registers the {@code wireless} sync value (C2S) for the wireless-mode toggle.
  • + *
  • Adds the wireless-mode button to the right button column — at the same + * position as DTPF's convergence button ({@code MTEPlasmaForgeGui}), i.e. the + * top of the column, above the structure-update button. Icon on/off states and + * the click gating (controller slot) match DTPF exactly.
  • + *
+ * All machine logic (MLevel, hatches, NBT, rule enforcement) stays in the + * machine; this class is purely presentational. + */ +public class OTEBBPlasmaForgeGui extends MTEMultiBlockBaseGui { + + public OTEBBPlasmaForgeGui(OTEBBPlasmaForge multiblock) { + super(multiblock); + } + + @Override + protected void registerSyncValues(PanelSyncManager syncManager) { + super.registerSyncValues(syncManager); + syncManager.syncValue( + "wireless", + new BooleanSyncValue(multiblock::isWirelessModeEnabled, multiblock::setWirelessModeEnabled).allowC2S()); + } + + @Override + protected Flow createButtonColumn(ModularPanel panel, PanelSyncManager syncManager) { + // 按钮列子项自底向上排列,追加在最上面 = 与 DTPF 收敛按钮相同的位置 + return super.createButtonColumn(panel, syncManager).child(createWirelessModeButton(syncManager)); + } + + /** + * Wireless-mode toggle button, mirroring {@code MTEPlasmaForgeGui#createConvergenceButton}: + *
    + *
  • icon: {@code TT_SAFE_VOID_ON/OFF}, driven by the synced value (not a client + * machine field) so it always reflects the server state;
  • + *
  • click: gated on the controller slot holding the calibration matrix + * (a synced inventory slot, always current on the client — unlike the derived + * MLevel field), then toggles the synced value with {@code allowC2S()}.
  • + *
+ * The MLevel / energy-hatch rule is enforced server-side in + * {@link OTEBBPlasmaForge#setWirelessModeEnabled(boolean)}. + */ + protected IWidget createWirelessModeButton(PanelSyncManager syncManager) { + BooleanSyncValue wirelessSyncer = syncManager.findSyncHandler("wireless", BooleanSyncValue.class); + return new ButtonWidget<>().marginBottom(2) + .tooltip(t -> t.addLine(IKey.lang("ote.bbpf.5"))) + .overlay(new DynamicDrawable(() -> { + // 与 DTPF 一致:激活 → TT_SAFE_VOID_ON,关闭 → TT_SAFE_VOID_OFF + if (wirelessSyncer.getBoolValue()) { + return GTGuiTextures.TT_SAFE_VOID_ON; + } + return GTGuiTextures.TT_SAFE_VOID_OFF; + })) + .onMousePressed(mouseButton -> { + // 亿万火种之怒,燃尽此身! + if (!multiblock.hasCalibrationMatrix()) return false; + wirelessSyncer.setBoolValue(!wirelessSyncer.getBoolValue()); + return true; + }); + } +} diff --git a/src/main/java/com/newmaa/othtech/utils/AuthorAnimation.java b/src/main/java/com/newmaa/othtech/utils/AuthorAnimation.java new file mode 100644 index 0000000..46b36b8 --- /dev/null +++ b/src/main/java/com/newmaa/othtech/utils/AuthorAnimation.java @@ -0,0 +1,115 @@ +package com.newmaa.othtech.utils; + +import static com.gtnewhorizon.gtnhlib.util.AnimatedTooltipHandler.BOLD; +import static com.gtnewhorizon.gtnhlib.util.AnimatedTooltipHandler.RESET; +import static com.gtnewhorizon.gtnhlib.util.AnimatedTooltipHandler.UNDERLINE; +import static com.gtnewhorizon.gtnhlib.util.AnimatedTooltipHandler.chain; +import static com.gtnewhorizon.gtnhlib.util.AnimatedTooltipHandler.text; + +import java.util.function.Supplier; + +/** + * "Author credit" animation — rainbow bounce, adapted from the + * {@code drawRainbowBounceHeader} method in czqwq/EZMiner's {@code HudRenderer}. + * + *
+ * §b§lAuthor:§r §7c§7z§cq§7w§7q  →  §7c§7z§6q§7w§7q  →  ...
+ *               ↑ spotlight   ↑
+ * 
+ * + *

+ * One character of the author name holds the spotlight at a time; the spotlight + * character cycles through the rainbow hues and is rendered bold + underlined + * (a text-only "bounce"), while the remaining characters are dimmed. + * + *

+ * Live tooltip registration (GT5U pattern): GT5U registers animated author + * lines on machine controllers at registration time via gtnhlib's + * {@link com.gtnewhorizon.gtnhlib.util.AnimatedTooltipHandler} — see + * {@code gregtech.loaders.preload.LoaderMetaTileEntities} + {@code GTAuthors}. + * The handler hooks the client {@code ItemTooltipEvent} and re-evaluates the + * {@link Supplier} on every frame, so the line animates in real time: + * + *

+ * AnimatedTooltipHandler.addItemTooltip(stack, AuthorAnimation.getTooltipSupplier());
+ * 
+ * + *

+ * Module decoupling: this class keeps no mutable state — every frame is + * derived from {@link System#currentTimeMillis()} alone, so the supplier is safe + * to evaluate on either side (client/server) and reusable by any machine. + */ +public final class AuthorAnimation { + + // ── Rainbow animation constants ────────────────────────────────────────── + /** Minecraft § colour-codes that form the rainbow sequence (red → pink). */ + private static final String[] RAINBOW_CODES = { "§c", // red + "§6", // orange + "§e", // yellow + "§a", // green + "§b", // cyan + "§9", // blue + "§5", // purple + "§d", // pink + }; + /** Author name whose characters are individually animated. */ + private static final String AUTHOR = "czqwq"; + /** Static label rendered before the animated name. */ + private static final String PREFIX = "§b§lAuthor:§r "; + /** Colour of the non-spotlight characters. */ + private static final String IDLE_COLOR = "§7"; + /** Emphasis of the spotlight character (bold + underline = "bounced"). */ + private static final String BOUNCE_STYLE = BOLD + UNDERLINE; + /** Milliseconds each character holds the spotlight before advancing. */ + private static final long CHAR_PERIOD_MS = 400; + /** Milliseconds each rainbow hue is displayed while a character is spotlit. */ + private static final long COLOR_PERIOD_MS = 90; + + private AuthorAnimation() {} + + /** + * A live tooltip supplier — register it on the controller's ItemStack with + * {@code AnimatedTooltipHandler.addItemTooltip(stack, supplier)} at machine + * registration time. + * + * @return supplier producing a new frame per evaluation + */ + public static Supplier getTooltipSupplier() { + return chain(text(PREFIX), () -> buildFrame(System.currentTimeMillis())); + } + + /** + * @return an animated frame for the current time. + */ + public static String getFrame() { + return getTooltipSupplier().get(); + } + + /** + * Computes the animated frame at the given instant. + * + * @param nowMs wall-clock time in milliseconds + * @return a single line string with § colour codes + */ + public static String getFrame(long nowMs) { + return PREFIX + buildFrame(nowMs); + } + + private static String buildFrame(long nowMs) { + int activeIdx = (int) ((nowMs / CHAR_PERIOD_MS) % AUTHOR.length()); + int colorIdx = (int) ((nowMs / COLOR_PERIOD_MS) % RAINBOW_CODES.length); + StringBuilder sb = new StringBuilder(AUTHOR.length() * 4); + for (int i = 0; i < AUTHOR.length(); i++) { + if (i == activeIdx) { + sb.append(RAINBOW_CODES[colorIdx]) + .append(BOUNCE_STYLE) + .append(AUTHOR.charAt(i)) + .append(RESET); + } else { + sb.append(IDLE_COLOR) + .append(AUTHOR.charAt(i)); + } + } + return sb.toString(); + } +} diff --git a/src/main/resources/assets/123technology/lang/en_US.lang b/src/main/resources/assets/123technology/lang/en_US.lang index 7895176..e2ee0da 100644 --- a/src/main/resources/assets/123technology/lang/en_US.lang +++ b/src/main/resources/assets/123technology/lang/en_US.lang @@ -552,6 +552,15 @@ ote.bbpf.6=If wireless power is insufficient, input materials will directly §cd ote.bbpf.7=§cSupports Multi-Amp Hatch/TecTech Laser/Energy Hatch. ote.bbpf.wireless.off=This machine's power is sealed (Wireless Off). ote.bbpf.wireless.on=This machine unleashes its full power (Wireless On). +ote.bbpf.wireless.energyhatch=Please remove all energy hatches before enabling wireless mode. +ote.bbpf.wireless.invalid=The Hyperdimensional Calibration Matrix is required to use wireless mode. +ote.bbpf.8=§6"The fury of innumerable Coreflames, consume me!"§r§7 — tearing dimensions apart, burning causality itself +ote.bbpf.9=§7Each coil tier: energy §c-8%§7, duration §a-8%§7. A §fMAX (Eternal) coil§7 costs only §b1%§7 energy at §b100x§7 speed +ote.bbpf.10=§7At MLevel 2 the parallel count breaks the int ceiling: §f2,147,483,647§7, no longer bound by coil tier +ote.bbpf.11=§7Wireless mode: resonates with the Calibration Matrix, toggled via GUI/screwdriver; §cenergy hatches forbidden§7; parallel is capped by wireless energy, drained in one instant at recipe start +ote.bbpf.12=§7Structure: §f605§7 heating coils — the source of heat and efficiency +ote.bbpf.13=§7plus §f1,864§7 transcendent casings, §f153§7 bridge casings, §f250§7 injection casings (hatches) +ote.bbpf.14=§7§f54§7 quantum glass blocks float at the core, staring into the dimensional rift #computer ote.calc.rank=Computer Case diff --git a/src/main/resources/assets/123technology/lang/zh_CN.lang b/src/main/resources/assets/123technology/lang/zh_CN.lang index 5905370..904d178 100644 --- a/src/main/resources/assets/123technology/lang/zh_CN.lang +++ b/src/main/resources/assets/123technology/lang/zh_CN.lang @@ -553,6 +553,15 @@ ote.bbpf.6=如果无线电量不足,输入材料将会直接§c湮灭 ote.bbpf.7=§c支持多安仓/TecTech激光/能源仓 ote.bbpf.wireless.off=该机器的力量被封印(无线关闭) ote.bbpf.wireless.on=该机器解放了它全部的力量(无线开启) +ote.bbpf.wireless.energyhatch=请先移除所有能源仓再开启无线模式 +ote.bbpf.wireless.invalid=需要超维校准矩阵才能使用无线模式 +ote.bbpf.8=§6"亿万火种之怒,燃尽此身!"§r§7——撕裂维度,烧穿因果 +ote.bbpf.9=§7线圈每升一级:能耗§c-8%§7|耗时§a-8%§7;§fMAX(永恒)线圈§7:能耗仅剩§b1%§7,速度为凡人的§b百倍§7 +ote.bbpf.10=§7MLevel2时并行数突破至§f2,147,483,647§7(int上限),不再受线圈等级束缚 +ote.bbpf.11=§7无线模式:与校准矩阵共鸣后由GUI/螺丝刀开启;§c禁止携带能源仓§7;并行数以无线能量为限,开炉瞬间一次性扣除 +ote.bbpf.12=§7结构:§f605§7个加热线圈——热量与效率的源头 +ote.bbpf.13=§7另有§f1864§7个超维度机械方块、§f153§7个维度桥接方块、§f250§7个维度注入机械方块(仓位) +ote.bbpf.14=§7核心悬浮§f54§7个量子玻璃,凝视着维度裂缝 #computer ote.calc.rank=计算机箱