diff --git a/src/main/java/com/extendedae_plus/ExtendedAEPlus.java b/src/main/java/com/extendedae_plus/ExtendedAEPlus.java index a7fa4fd..416740e 100644 --- a/src/main/java/com/extendedae_plus/ExtendedAEPlus.java +++ b/src/main/java/com/extendedae_plus/ExtendedAEPlus.java @@ -1,11 +1,14 @@ package com.extendedae_plus; +import appeng.init.client.InitScreens; import appeng.menu.locator.MenuLocators; import com.extendedae_plus.client.ClientProxy; import com.extendedae_plus.config.ModConfigs; import com.extendedae_plus.init.*; +import com.extendedae_plus.menu.EntitySpeedTickerMenu; import com.extendedae_plus.menu.locator.CuriosItemLocator; import com.extendedae_plus.network.ModNetwork; +import com.extendedae_plus.screen.EntitySpeedTickerScreen; import net.minecraft.resources.ResourceLocation; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.client.event.ModelEvent; @@ -65,6 +68,8 @@ public class ExtendedAEPlus { private void commonSetup(final FMLCommonSetupEvent event) { // 注册本模组网络通道与数据包 event.enqueueWork(() -> { + // 注册升级卡 + new UpgradeCards(event); ModNetwork.register(); // 注册自定义 Curios 宿主定位器,便于将菜单宿主信息在服务端与客户端间同步 MenuLocators.register(CuriosItemLocator.class, CuriosItemLocator::writeToPacket, CuriosItemLocator::readFromPacket); @@ -93,6 +98,11 @@ public class ExtendedAEPlus { // 直接在此处执行客户端一次性注册(UI/屏幕/渲染器绑定) // 注册客户端配置界面 ClientProxy.registerConfigScreen(); + + InitScreens.register(ModMenuTypes.ENTITY_TICKER_MENU.get(), + EntitySpeedTickerScreen::new, + "/screens/entity_speed_ticker.json"); + // 菜单 -> 屏幕 绑定 ClientProxy.registerMenuScreens(); } @@ -101,6 +111,8 @@ public class ExtendedAEPlus { public static void onRegisterGeometryLoaders(final ModelEvent.RegisterGeometryLoaders evt) { try { ClientProxy.initBuiltInModels(); + // 注册 AE2 部件模型(例如 entity_ticker_part_item),仿照 CrazyAddons 的做法 + ModItems.registerPartModels(); } catch (Exception ignored) {} } } diff --git a/src/main/java/com/extendedae_plus/ae/parts/EntitySpeedTickerPart.java b/src/main/java/com/extendedae_plus/ae/parts/EntitySpeedTickerPart.java new file mode 100644 index 0000000..75a4bf8 --- /dev/null +++ b/src/main/java/com/extendedae_plus/ae/parts/EntitySpeedTickerPart.java @@ -0,0 +1,251 @@ +package com.extendedae_plus.ae.parts; + +import appeng.api.config.Actionable; +import appeng.api.config.PowerMultiplier; +import appeng.api.networking.GridFlags; +import appeng.api.networking.IGridNode; +import appeng.api.networking.ticking.IGridTickable; +import appeng.api.networking.ticking.TickRateModulation; +import appeng.api.networking.ticking.TickingRequest; +import appeng.api.parts.IPartCollisionHelper; +import appeng.api.parts.IPartItem; +import appeng.api.parts.IPartModel; +import appeng.api.upgrades.IUpgradeableObject; +import appeng.core.definitions.AEItems; +import appeng.items.parts.PartModels; +import appeng.menu.MenuOpener; +import appeng.menu.locator.MenuLocators; +import appeng.parts.automation.UpgradeablePart; +import appeng.parts.p2p.P2PModels; +import com.extendedae_plus.ExtendedAEPlus; +import com.extendedae_plus.init.ModMenuTypes; +import com.extendedae_plus.menu.EntitySpeedTickerMenu; +import com.extendedae_plus.util.PowerUtils; +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.MenuProvider; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityTicker; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.phys.Vec3; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +/** + * EntitySpeedTickerPart 是一个可升级的 AE2 部件

+ * 该部件可以加速目标方块实体的 tick 速率,消耗 AE 网络能量,并支持加速卡升级

+ * 功能受Crazy AE2 Addons启发 + */ +public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTickable, MenuProvider, IUpgradeableObject { + // 当前打开的菜单实例(如果有) + public EntitySpeedTickerMenu menu; + + // P2P 模型,用于渲染部件的外观 + private static final P2PModels MODELS = new P2PModels( + new ResourceLocation(ExtendedAEPlus.MODID, "part/entity_speed_ticker_part")); + + /** + * 获取该部件的所有模型(用于渲染) + * @return 模型列表 + */ + @PartModels + public static List getModels() { + return MODELS.getModels(); + } + + /** + * 构造函数,初始化部件并设置网络节点属性 + * @param partItem 部件物品 + */ + public EntitySpeedTickerPart(IPartItem partItem) { + super(partItem); + // 设置网络节点属性:需要通道、空闲功耗为1,并注册为 IGridTickable 服务 + this.getMainNode() + .setFlags(GridFlags.REQUIRE_CHANNEL) + .setIdlePowerUsage(1) + .addService(IGridTickable.class, this); + } + + /** + * 获取当前状态下的静态模型(用于渲染) + * @return 当前状态的模型 + */ + public IPartModel getStaticModels() { + return MODELS.getModel(this.isPowered(), this.isActive()); + } + + /** + * 当玩家激活部件(右键)时调用,打开自定义菜单 + * @param player 玩家 + * @param hand 手 + * @param pos 点击位置 + * @return 总是返回 true,表示激活成功 + */ + @Override + public boolean onPartActivate(Player player, InteractionHand hand, Vec3 pos) { + // 仅在服务端打开菜单 + if (!player.getCommandSenderWorld().isClientSide()) { + MenuOpener.open(ModMenuTypes.ENTITY_TICKER_MENU.get(), player, MenuLocators.forPart(this)); + } + return true; + } + + /** + * 定义部件的碰撞箱(用于物理碰撞和渲染) + * @param bch 碰撞辅助器 + */ + @Override + public void getBoxes(IPartCollisionHelper bch) { + bch.addBox(5, 5, 12, 11, 11, 13); + bch.addBox(3, 3, 13, 13, 13, 14); + bch.addBox(2, 2, 14, 14, 14, 16); + } + + /** + * 获取定时请求,决定本部件多久 tick 一次 + * @param iGridNode 网络节点 + * @return TickingRequest 对象 + */ + @Override + public TickingRequest getTickingRequest(IGridNode iGridNode) { + // 每 1 tick 执行一次 + return new TickingRequest(1, 1, false, true); + } + + /** + * 当升级卡数量发生变化时调用,通知菜单更新 + */ + @Override + public void upgradesChanged() { + if (this.menu != null) { + // 使用 AE2 风格:当升级发生变化时让菜单广播变化(槽/数据会被同步),客户端会基于槽内容重新计算并刷新界面 + this.menu.broadcastChanges(); + } + } + + /** + * 网络定时回调,每次 tick 时调用 + * @param iGridNode 网络节点 + * @param ticksSinceLastCall 距离上次调用经过的 tick 数 + * @return TickRateModulation.IDLE 表示继续保持当前 tick 速率 + */ + @Override + public TickRateModulation tickingRequest(IGridNode iGridNode, int ticksSinceLastCall) { + // 获取目标方块实体(本部件朝向的方块) + BlockEntity target = getLevel().getBlockEntity(getBlockEntity().getBlockPos().relative(getSide())); + // 仅在目标存在且部件处于激活状态时执行加速 + if (target != null && isActive()) { + ticker(target); + } + return TickRateModulation.IDLE; + } + + /** + * 以指定速度对目标方块实体进行 tick 操作 + * @param blockEntity 需要被 tick 的方块实体 + * @param 方块实体类型 + */ + private void ticker(@NotNull T blockEntity) { + if (this.getGridNode() == null + || this.getMainNode() == null + || this.getMainNode().getGrid() == null) { + return; + } + +// String id = Objects.requireNonNull(ForgeRegistries.BLOCKS.getKey(blockEntity.getBlockState().getBlock())).toString(); + + // 获取方块实体的位置 + BlockPos pos = blockEntity.getBlockPos(); + if (blockEntity.getLevel() == null) return; + + // 获取该方块实体的 Ticker + @SuppressWarnings("unchecked") + BlockEntityTicker blockEntityTicker = this.getLevel() + .getBlockState(pos) + .getTicker(this.getLevel(), (BlockEntityType) blockEntity.getType()); + if (blockEntityTicker == null) return; + + int speedCardCount = getUpgrades().getInstalledUpgrades(AEItems.SPEED_CARD); + int energyCardCount = getUpgrades().getInstalledUpgrades(AEItems.ENERGY_CARD); + + // 计算本次 tick 所需能量 + // - 基础消耗为 512 + // - 加速卡的数量对能耗有分段增长: + // 0: 无增长 + // 1: 翻倍 + // 2-6: 指数增长(较快) + // 7-8: 幂级数增长(极高) + // 使用工具类统一计算增长因子与原始功耗,并从网络中抽取对应能量 + double requiredPower = PowerUtils.getFinalPower(speedCardCount, energyCardCount); + + // 先模拟提取以检查网络中是否有足够能量,再真正抽取 + double simulated = getMainNode().getGrid().getEnergyService() + .extractAEPower(requiredPower, Actionable.SIMULATE, PowerMultiplier.CONFIG); + if (simulated < requiredPower) return; + + double extractedPower = getMainNode().getGrid().getEnergyService() + .extractAEPower(requiredPower, Actionable.MODULATE, PowerMultiplier.CONFIG); + if (extractedPower < requiredPower) return; + + // 计算加速倍数:基于 2 的次方,并把 8 张映射到最大 1024x(2^10) + int speed = PowerUtils.getSpeedMultiplier(speedCardCount); + + // 执行 tick 操作 + for (int i = 0; i < speed - 1; i++) { + blockEntityTicker.tick( + blockEntity.getLevel(), + blockEntity.getBlockPos(), + blockEntity.getBlockState(), + blockEntity + ); + } + } + + /** + * 判断部件是否有自定义名称 + * @return 是否有自定义名称 + */ + @Override + public boolean hasCustomName() { + return super.hasCustomName(); + } + + /** + * 获取部件的显示名称 + * @return 显示名称 + */ + @Override + public @NotNull Component getDisplayName() { + return super.getDisplayName(); + } + + /** + * 创建自定义菜单(GUI) + * @param containerId 容器ID + * @param playerInventory 玩家背包 + * @param player 玩家 + * @return 菜单实例 + */ + @Override + public @Nullable AbstractContainerMenu createMenu(int containerId, + @NotNull Inventory playerInventory, + @NotNull Player player) { + return new EntitySpeedTickerMenu(containerId, playerInventory, this); + } + + /** + * 获取可用的升级卡槽数量 + * @return 升级卡槽数量 + */ + @Override + protected int getUpgradeSlots() { + return 8; + } +} \ No newline at end of file diff --git a/src/main/java/com/extendedae_plus/config/ModConfigs.java b/src/main/java/com/extendedae_plus/config/ModConfigs.java index 5f8e545..a746b53 100644 --- a/src/main/java/com/extendedae_plus/config/ModConfigs.java +++ b/src/main/java/com/extendedae_plus/config/ModConfigs.java @@ -12,6 +12,8 @@ public final class ModConfigs { public static final ForgeConfigSpec.BooleanValue PATTERN_TERMINAL_SHOW_SLOTS_DEFAULT; public static final ForgeConfigSpec.IntValue SMART_SCALING_MAX_MULTIPLIER; public static final ForgeConfigSpec.IntValue SMART_SCALING_MIN_BENEFIT_FACTOR; + public static final ForgeConfigSpec.IntValue EntityTickerCost; + static { ForgeConfigSpec.Builder builder = new ForgeConfigSpec.Builder(); @@ -77,6 +79,12 @@ public final class ModConfigs { "影响进入界面时SlotsRow的默认可见性,仅影响客户端显示" ) .define("patternTerminalShowSlotsDefault", true); + + + EntityTickerCost = builder + .comment("实体加速器的能量消耗基础值") + .defineInRange("EntityTickerCost", 512, 0, Integer.MAX_VALUE); + builder.pop(); COMMON_SPEC = builder.build(); } diff --git a/src/main/java/com/extendedae_plus/init/ModCreativeTabs.java b/src/main/java/com/extendedae_plus/init/ModCreativeTabs.java index e48a376..d44b721 100644 --- a/src/main/java/com/extendedae_plus/init/ModCreativeTabs.java +++ b/src/main/java/com/extendedae_plus/init/ModCreativeTabs.java @@ -26,6 +26,7 @@ public final class ModCreativeTabs { output.accept(ModItems.ACCELERATOR_64x.get()); output.accept(ModItems.ACCELERATOR_256x.get()); output.accept(ModItems.ACCELERATOR_1024x.get()); + output.accept(ModItems.ENTITY_TICKER_PART_ITEM.get()); }) .build()); } diff --git a/src/main/java/com/extendedae_plus/init/ModItems.java b/src/main/java/com/extendedae_plus/init/ModItems.java index d2d290f..d3ddcb2 100644 --- a/src/main/java/com/extendedae_plus/init/ModItems.java +++ b/src/main/java/com/extendedae_plus/init/ModItems.java @@ -1,6 +1,10 @@ package com.extendedae_plus.init; +import appeng.api.parts.IPart; +import appeng.api.parts.PartModels; +import appeng.items.parts.PartModelsHelper; import com.extendedae_plus.ExtendedAEPlus; +import com.extendedae_plus.items.EntitySpeedTickerPartItem; import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.Item; import net.minecraftforge.registries.DeferredRegister; @@ -47,4 +51,20 @@ public final class ModItems { "1024x_crafting_accelerator", () -> new BlockItem(ModBlocks.ACCELERATOR_1024x.get(), new Item.Properties()) ); + + public static final RegistryObject ENTITY_TICKER_PART_ITEM = + ITEMS.register("entity_speed_ticker", + () -> new EntitySpeedTickerPartItem(new Item.Properties())); + + /** + * 为 PartItem 注册 AE2 部件模型。 + * 在客户端进行模型/几何体注册时调用。 + */ + public static void registerPartModels() { + PartModels.registerModels( + PartModelsHelper.createModels( + ENTITY_TICKER_PART_ITEM.get().getPartClass().asSubclass(IPart.class) + ) + ); + } } diff --git a/src/main/java/com/extendedae_plus/init/ModMenuTypes.java b/src/main/java/com/extendedae_plus/init/ModMenuTypes.java index 47a0936..238ad40 100644 --- a/src/main/java/com/extendedae_plus/init/ModMenuTypes.java +++ b/src/main/java/com/extendedae_plus/init/ModMenuTypes.java @@ -1,6 +1,9 @@ package com.extendedae_plus.init; +import appeng.menu.implementations.MenuTypeBuilder; import com.extendedae_plus.ExtendedAEPlus; +import com.extendedae_plus.ae.parts.EntitySpeedTickerPart; +import com.extendedae_plus.menu.EntitySpeedTickerMenu; import com.extendedae_plus.menu.NetworkPatternControllerMenu; import net.minecraft.world.inventory.MenuType; import net.minecraftforge.common.extensions.IForgeMenuType; @@ -9,12 +12,19 @@ import net.minecraftforge.registries.ForgeRegistries; import net.minecraftforge.registries.RegistryObject; public final class ModMenuTypes { - private ModMenuTypes() {} - public static final DeferredRegister> MENUS = DeferredRegister.create(ForgeRegistries.MENU_TYPES, ExtendedAEPlus.MODID); + private ModMenuTypes() { + } + public static final RegistryObject> NETWORK_PATTERN_CONTROLLER = MENUS.register("network_pattern_controller", () -> IForgeMenuType.create(NetworkPatternControllerMenu::new)); + + public static final RegistryObject> ENTITY_TICKER_MENU = + MENUS.register("entity_speed_ticker", + () -> MenuTypeBuilder + .create(EntitySpeedTickerMenu::new, EntitySpeedTickerPart.class) + .build("entity_speed_ticker")); } diff --git a/src/main/java/com/extendedae_plus/init/UpgradeCards.java b/src/main/java/com/extendedae_plus/init/UpgradeCards.java new file mode 100644 index 0000000..8c7dd5d --- /dev/null +++ b/src/main/java/com/extendedae_plus/init/UpgradeCards.java @@ -0,0 +1,15 @@ +package com.extendedae_plus.init; + +import appeng.api.upgrades.Upgrades; +import appeng.core.definitions.AEItems; +import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; + + +public class UpgradeCards { + public UpgradeCards(final FMLCommonSetupEvent event) { + event.enqueueWork(() -> { + Upgrades.add(AEItems.SPEED_CARD, ModItems.ENTITY_TICKER_PART_ITEM.get(), 8, "group.entity_ticker.name"); + Upgrades.add(AEItems.ENERGY_CARD, ModItems.ENTITY_TICKER_PART_ITEM.get(), 8, "group.entity_ticker.name"); + }); + } +} \ No newline at end of file diff --git a/src/main/java/com/extendedae_plus/items/EntitySpeedTickerPartItem.java b/src/main/java/com/extendedae_plus/items/EntitySpeedTickerPartItem.java new file mode 100644 index 0000000..011cebf --- /dev/null +++ b/src/main/java/com/extendedae_plus/items/EntitySpeedTickerPartItem.java @@ -0,0 +1,11 @@ +package com.extendedae_plus.items; + +import appeng.items.parts.PartItem; +import com.extendedae_plus.ae.parts.EntitySpeedTickerPart; + + +public class EntitySpeedTickerPartItem extends PartItem { + public EntitySpeedTickerPartItem(Properties properties) { + super(properties, EntitySpeedTickerPart.class, EntitySpeedTickerPart::new); + } +} \ No newline at end of file diff --git a/src/main/java/com/extendedae_plus/menu/EntitySpeedTickerMenu.java b/src/main/java/com/extendedae_plus/menu/EntitySpeedTickerMenu.java new file mode 100644 index 0000000..7b93fb9 --- /dev/null +++ b/src/main/java/com/extendedae_plus/menu/EntitySpeedTickerMenu.java @@ -0,0 +1,68 @@ +package com.extendedae_plus.menu; + +import appeng.core.definitions.AEItems; +import appeng.menu.implementations.UpgradeableMenu; +import appeng.menu.slot.OptionalFakeSlot; +import com.extendedae_plus.ae.parts.EntitySpeedTickerPart; +import com.extendedae_plus.init.ModMenuTypes; +import com.extendedae_plus.screen.EntitySpeedTickerScreen; +import net.minecraft.client.Minecraft; +import net.minecraft.world.entity.player.Inventory; + +// 实体加速器菜单,负责与客户端界面同步数据 +public class EntitySpeedTickerMenu extends UpgradeableMenu { + // 已安装的速度卡数量 + public int speedCardCount; + // 已安装的能量卡数量 + public int energyCardCount; + + // 构造方法,初始化菜单并与部件绑定 + public EntitySpeedTickerMenu(int id, Inventory ip, EntitySpeedTickerPart host) { + super(ModMenuTypes.ENTITY_TICKER_MENU.get(), id, ip, host); + // 让部件持有当前菜单实例,便于通信 + getHost().menu = this; + } + + // 当服务器数据同步到客户端时调用 + @Override + public void onServerDataSync() { + super.onServerDataSync(); + // 重新统计速度卡和能量卡数量 + this.speedCardCount = this.getUpgrades().getInstalledUpgrades(AEItems.SPEED_CARD); + this.energyCardCount = this.getUpgrades().getInstalledUpgrades(AEItems.ENERGY_CARD); + // 如果在客户端,刷新界面 + if (isClientSide()) { + if (Minecraft.getInstance().screen instanceof EntitySpeedTickerScreen screen) { + screen.refreshGui(); + } + } + } + + // 当任意槽位发生变化时调用 + @Override + public void onSlotChange(net.minecraft.world.inventory.Slot slot) { + super.onSlotChange(slot); + // 客户端重新统计卡数量并刷新界面 + if (isClientSide()) { + this.speedCardCount = this.getUpgrades().getInstalledUpgrades(AEItems.SPEED_CARD); + this.energyCardCount = this.getUpgrades().getInstalledUpgrades(AEItems.ENERGY_CARD); + if (Minecraft.getInstance().screen instanceof EntitySpeedTickerScreen screen) { + screen.refreshGui(); + } + } + } + + @Override + public void broadcastChanges(){ + // 遍历所有槽位,清理未启用但有物品显示的 OptionalFakeSlot + for (Object o : this.slots) { + if (o instanceof OptionalFakeSlot fs) { + if (!fs.isSlotEnabled() && !fs.getDisplayStack().isEmpty()) { + fs.clearStack(); + } + } + } + // 调用标准的同步方法,通知监听者数据已更新 + this.standardDetectAndSendChanges(); + } +} \ No newline at end of file diff --git a/src/main/java/com/extendedae_plus/screen/EntitySpeedTickerScreen.java b/src/main/java/com/extendedae_plus/screen/EntitySpeedTickerScreen.java new file mode 100644 index 0000000..8929071 --- /dev/null +++ b/src/main/java/com/extendedae_plus/screen/EntitySpeedTickerScreen.java @@ -0,0 +1,39 @@ +package com.extendedae_plus.screen; + +import appeng.client.gui.implementations.UpgradeableScreen; +import appeng.client.gui.style.ScreenStyle; +import com.extendedae_plus.menu.EntitySpeedTickerMenu; +import com.extendedae_plus.util.PowerUtils; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.player.Inventory; + +public class EntitySpeedTickerScreen extends UpgradeableScreen { + + public EntitySpeedTickerScreen( + EntitySpeedTickerMenu menu, Inventory playerInventory, Component title, ScreenStyle style) { + super((C) menu, playerInventory, title, style); + } + + @Override + protected void updateBeforeRender(){ + super.updateBeforeRender(); + textData(); + } + + public void refreshGui(){ + textData(); + } + + private void textData() { + int speedCardCount = getMenu().speedCardCount; + int energyCardCount = getMenu().energyCardCount; + + double finalPower = PowerUtils.getFinalPower(speedCardCount, energyCardCount); + int speed = PowerUtils.getSpeedMultiplier(speedCardCount); + double remainingRatio = PowerUtils.getRemainingRatio(energyCardCount); + + setTextContent("speed", Component.translatable("screen.extendedae_plus.entity_speed_ticker.speed", speed)); + setTextContent("energy", Component.translatable("screen.extendedae_plus.entity_speed_ticker.energy", PowerUtils.formatPower(finalPower))); + setTextContent("power_ratio", Component.translatable("screen.extendedae_plus.entity_speed_ticker.power_ratio", PowerUtils.formatPercentage(remainingRatio))); + } +} \ No newline at end of file diff --git a/src/main/java/com/extendedae_plus/util/PowerUtils.java b/src/main/java/com/extendedae_plus/util/PowerUtils.java new file mode 100644 index 0000000..ef42443 --- /dev/null +++ b/src/main/java/com/extendedae_plus/util/PowerUtils.java @@ -0,0 +1,130 @@ +package com.extendedae_plus.util; + +import com.extendedae_plus.config.ModConfigs; + +/** + * 用于计算实体加速器的能耗与加速倍率的工具类 + */ +public final class PowerUtils { + private PowerUtils() {} + + /** + * 计算加速卡数量对应的加速倍率(返回 2 的幂次方) + * 0 张卡 = 1x,8 张卡 = 1024x + */ + public static int getSpeedMultiplier(int speedCardCount) { + if (speedCardCount <= 0) return 1; + // 线性映射 0~8 -> 0~10,最大 2^10=1024 + int exponent = (int) Math.round((10.0 / 8.0) * speedCardCount); + exponent = Math.min(exponent, 10); + return (int) Math.pow(2, exponent); + } + + /** + * 计算加速卡数量对应的能耗增长因子 + * 0: 1x,1: 2x,2-6: 2^(2*count),7-8: 2^(3*count) + */ + public static double getGrowthFactor(int speedCardCount) { + if (speedCardCount <= 0) return 1.0; + if (speedCardCount == 1) return 2.0; + if (speedCardCount <= 6) return Math.pow(2.0, 2.0 * speedCardCount); + return Math.pow(2.0, 3.0 * speedCardCount); + } + + /** + * 计算加速卡数量对应的原始能耗(未减免) + * @param speedCardCount 加速卡数量 + * @return 原始能耗 + */ + public static double getRawPower(int speedCardCount) { + double base = ModConfigs.EntityTickerCost.get(); + return base * getGrowthFactor(speedCardCount); + } + + /** + * 计算能源卡数量对应的能耗减免百分比 + * 0: 0%,1: 10%,8: 50%,2-7: 0.5*(1-0.7^n) + */ + public static double getReductionPercent(int energyCardCount) { + if (energyCardCount <= 0) return 0.0; + if (energyCardCount == 1) return 0.1; + if (energyCardCount >= 8) return 0.5; + return 0.5 * (1.0 - Math.pow(0.7, energyCardCount)); + } + + /** + * 计算最终能耗(取整,最小为 1) + * @param speedCardCount 加速卡数量 + * @param energyCardCount 能源卡数量 + * @return 最终能耗(int) + */ + public static int getFinalPowerDraw(int speedCardCount, int energyCardCount) { + double raw = getRawPower(speedCardCount); + double reduction = getReductionPercent(energyCardCount); + // 与原始实现兼容:raw * (1 - reduction) 后按 0.5 的单位转换(与 AE/FE 换算保持一致) + double adjusted = raw * (1.0 - reduction) / 2.0; + return (int) Math.max(1, Math.round(adjusted)); + } + + /** + * 计算最终能耗(浮点数) + * @param speedCardCount 加速卡数量 + * @param energyCardCount 能源卡数量 + * @return 最终能耗(double) + */ + public static double getFinalPower(int speedCardCount, int energyCardCount) { + double raw = getRawPower(speedCardCount); + double reduction = getReductionPercent(energyCardCount); + // 返回与实际抽取值一致的浮点能耗(包含与原实现一致的 /2 单位转换) + return raw * (1.0 - reduction) / 2.0; + } + + /** + * 返回能源卡减免后剩余的功耗比率(例如 1 张能源卡 -> 0.9) + * @param energyCardCount 能源卡数量 + * @return 剩余功耗比率 + */ + public static double getRemainingRatio(int energyCardCount) { + return 1.0 - getReductionPercent(energyCardCount); + } + + /** + * 将能耗数字按单位缩写(K/M/G/T/P/E)格式化为更短的字符串 + * 例如 1500 -> "1.50K", 2000000 -> "2.00M" + */ + public static String formatPower(double value) { + double abs = Math.abs(value); + if (abs >= 1e18) return formatWithSuffix(value, 1e18, "E"); + if (abs >= 1e15) return formatWithSuffix(value, 1e15, "P"); + if (abs >= 1e12) return formatWithSuffix(value, 1e12, "T"); + if (abs >= 1e9) return formatWithSuffix(value, 1e9, "G"); + if (abs >= 1e6) return formatWithSuffix(value, 1e6, "M"); + if (abs >= 1e3) return formatWithSuffix(value, 1e3, "K"); + // 小于 1000 直接返回整数形式 + if (Math.floor(value) == value) { + return String.format("%d", (long) value); + } + return String.format("%.2f", value); + } + + private static String formatWithSuffix(double value, double unit, String suffix) { + double v = value / unit; + // 如果 v 是整数则不显示小数 + if (Math.abs(v - Math.round(v)) < 1e-9) { + return String.format("%d%s", Math.round(v), suffix); + } + return String.format("%.2f%s", v, suffix); + } + + /** + * 将剩余功耗比率格式化为百分比字符串(例如 0.9 -> "90%") + */ + public static String formatPercentage(double ratio) { + double pct = ratio * 100.0; + // 如果为整数则无小数 + if (Math.abs(pct - Math.round(pct)) < 1e-9) { + return String.format("%d%%", Math.round(pct)); + } + return String.format("%.2f%%", pct); + } +} diff --git a/src/main/resources/assets/ae2/screens/entity_speed_ticker.json b/src/main/resources/assets/ae2/screens/entity_speed_ticker.json new file mode 100644 index 0000000..acde6c9 --- /dev/null +++ b/src/main/resources/assets/ae2/screens/entity_speed_ticker.json @@ -0,0 +1,63 @@ +{ + "$schema": "schema.json", + "includes": [ + "common/common.json", + "common/player_inventory.json" + ], + "background": { + "texture": "guis/blank_background.png", + "srcRect": [ + 0, + 0, + 176, + 205 + ] + }, + "text": { + "dialog_title": { + "text": { + "translate": "item.extendedae_plus.entity_speed_ticker" + }, + "position": { + "left": 8, + "top": 6 + } + }, + "speed": { + "position": { + "top": 45, + "left": 88 + }, + "align": "CENTER" + }, + "energy": { + "position": { + "top": 60, + "left": 88 + }, + "align": "CENTER" + }, + "power_ratio": { + "position": { + "top": 75, + "left": 88 + }, + "align": "CENTER" + } + }, + "widgets": { + "toolbox": { + "right": -2, + "bottom": 50, + "width": 68, + "height": 68 + } + }, + "slots": { + "TOOLBOX": { + "bottom": 42, + "right": -10, + "grid": "BREAK_AFTER_3COLS" + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/ae2/textures/guis/blank_background.png b/src/main/resources/assets/ae2/textures/guis/blank_background.png new file mode 100644 index 0000000..daf4474 Binary files /dev/null and b/src/main/resources/assets/ae2/textures/guis/blank_background.png differ diff --git a/src/main/resources/assets/extendedae_plus/lang/en_us.json b/src/main/resources/assets/extendedae_plus/lang/en_us.json index 3de5a8c..93331f1 100644 --- a/src/main/resources/assets/extendedae_plus/lang/en_us.json +++ b/src/main/resources/assets/extendedae_plus/lang/en_us.json @@ -1,4 +1,9 @@ { + "item.extendedae_plus.entity_speed_ticker": "Entity Speed Ticker", + "screen.extendedae_plus.entity_speed_ticker.energy": "Energy Usage: %s FE/t", + "screen.extendedae_plus.entity_speed_ticker.power_ratio": "Power ratio: %s", + "screen.extendedae_plus.entity_speed_ticker.speed": "Current speed multiplier: %d", + "gui.expatternprovider.toggle_slots": "Toggle Slots", "gui.expatternprovider.hide_slots": "Hide Slots", "gui.expatternprovider.show_slots": "Show Slots", @@ -27,6 +32,7 @@ "extendedae_plus.tooltip.locked": "Locked: %s" , "screen.extendedae_plus.title": "ExtendedAE Plus Config", + "config.extendedae_plus.pageMultiplier": "Pattern Provider Page Multiplier", "config.extendedae_plus.pageMultiplier_with_range": "Pattern Provider Page Multiplier (1-64)", "config.extendedae_plus.wirelessMaxRange": "Wireless Max Range", diff --git a/src/main/resources/assets/extendedae_plus/lang/zh_cn.json b/src/main/resources/assets/extendedae_plus/lang/zh_cn.json index 4e6ad18..09eadc2 100644 --- a/src/main/resources/assets/extendedae_plus/lang/zh_cn.json +++ b/src/main/resources/assets/extendedae_plus/lang/zh_cn.json @@ -29,6 +29,11 @@ "item.extendedae_plus.256x_crafting_accelerator": "256x并行处理单元", "item.extendedae_plus.1024x_crafting_accelerator": "1024x并行处理单元", + "item.extendedae_plus.entity_speed_ticker": "实体加速器", + "screen.extendedae_plus.entity_speed_ticker.energy": "能耗: %s FE/t", + "screen.extendedae_plus.entity_speed_ticker.power_ratio": "功耗比例: %s", + "screen.extendedae_plus.entity_speed_ticker.speed": "当前加速倍率: %d", + "config.jade.plugin_extendedae_plus.wireless_transceiver_info": "无线收发器信息", "config.jade.plugin_extendedae_plus.wt_frequency": "显示频率", "config.jade.plugin_extendedae_plus.wt_master_mode": "显示主/从模式", diff --git a/src/main/resources/assets/extendedae_plus/models/item/entity_speed_ticker.json b/src/main/resources/assets/extendedae_plus/models/item/entity_speed_ticker.json new file mode 100644 index 0000000..62f3cea --- /dev/null +++ b/src/main/resources/assets/extendedae_plus/models/item/entity_speed_ticker.json @@ -0,0 +1,6 @@ +{ + "parent": "ae2:item/p2p_tunnel_base", + "textures": { + "type": "extendedae_plus:part/entity_speed_ticker" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/extendedae_plus/models/part/entity_speed_ticker_part.json b/src/main/resources/assets/extendedae_plus/models/part/entity_speed_ticker_part.json new file mode 100644 index 0000000..3e091c5 --- /dev/null +++ b/src/main/resources/assets/extendedae_plus/models/part/entity_speed_ticker_part.json @@ -0,0 +1,6 @@ +{ + "parent": "ae2:part/p2p/p2p_tunnel_base", + "textures": { + "type": "extendedae_plus:part/entity_speed_ticker" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/extendedae_plus/textures/part/entity_speed_ticker.png b/src/main/resources/assets/extendedae_plus/textures/part/entity_speed_ticker.png new file mode 100644 index 0000000..488c715 Binary files /dev/null and b/src/main/resources/assets/extendedae_plus/textures/part/entity_speed_ticker.png differ