diff --git a/build.gradle b/build.gradle index 8bbce35..7394979 100644 --- a/build.gradle +++ b/build.gradle @@ -159,7 +159,10 @@ dependencies { runtimeOnly "de.mari_023:ae2wtlib:19.2.1" runtimeOnly "curse.maven:jade-324717:5427817" runtimeOnly "curse.maven:mega-cells-622112:6005043" - // runtimeOnly "mekanism:Mekanism:1.21.1-10.7.0.55" + + runtimeOnly "curse.maven:mekanism-268560:6895130" + runtimeOnly "curse.maven:applied-mekanistics-574300:5978711" + runtimeOnly "curse.maven:ex-pattern-provider-892005:6863556" //aea diff --git a/src/main/java/com/extendedae_plus/ExtendedAEPlus.java b/src/main/java/com/extendedae_plus/ExtendedAEPlus.java index 6670527..3f6640a 100644 --- a/src/main/java/com/extendedae_plus/ExtendedAEPlus.java +++ b/src/main/java/com/extendedae_plus/ExtendedAEPlus.java @@ -1,10 +1,12 @@ package com.extendedae_plus; +import appeng.api.parts.IPart; +import appeng.api.parts.PartModels; import appeng.block.AEBaseEntityBlock; import appeng.blockentity.crafting.CraftingBlockEntity; +import appeng.items.parts.PartModelsHelper; import com.extendedae_plus.config.ModConfigs; import com.extendedae_plus.init.*; -import com.extendedae_plus.network.ModNetwork; import com.mojang.logging.LogUtils; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.resources.ResourceLocation; @@ -69,6 +71,16 @@ public class ExtendedAEPlus { // 绑定 AE2 的 CraftingBlockEntity 到本模组的自定义加速器方块,避免 AEBaseEntityBlock.blockEntityType 为空 event.enqueueWork(() -> { try { + // 注册升级卡 + new UpgradeCards(event); + + // 为 PartItem 注册 AE2 部件模型 + PartModels.registerModels( + PartModelsHelper.createModels( + ModItems.ENTITY_TICKER_PART_ITEM.get().getPartClass().asSubclass(IPart.class) + ) + ); + // 注册自定义 AE2 MenuLocator(用于 Curios 槽位打开菜单) try { appeng.menu.locator.MenuLocators.register( diff --git a/src/main/java/com/extendedae_plus/ae/definitions/upgrades/EntitySpeedCardItem.java b/src/main/java/com/extendedae_plus/ae/definitions/upgrades/EntitySpeedCardItem.java new file mode 100644 index 0000000..fe253ae --- /dev/null +++ b/src/main/java/com/extendedae_plus/ae/definitions/upgrades/EntitySpeedCardItem.java @@ -0,0 +1,80 @@ +package com.extendedae_plus.ae.definitions.upgrades; + +import appeng.items.materials.UpgradeCardItem; +import com.extendedae_plus.init.ModItems; +import net.minecraft.core.component.DataComponents; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.item.component.CustomData; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.api.distmarker.OnlyIn; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +/** + * 单一的实体加速卡 Item,通过 ItemStack 的 NBT 存储 exponent(0/1/2/3)来区分等级 + */ +public class EntitySpeedCardItem extends UpgradeCardItem { + public static final String NBT_MULTIPLIER = "EAS:mult"; + + public EntitySpeedCardItem(Properties props) { + super(props); + } + + public static ItemStack withMultiplier(int multiplier) { + ItemStack stack = new ItemStack(ModItems.ENTITY_SPEED_CARD.get()); + CompoundTag tag = new CompoundTag(); + tag.putInt(NBT_MULTIPLIER, multiplier); + stack.set(DataComponents.CUSTOM_DATA, CustomData.of(tag)); + return stack; + } + + public static int readMultiplier(ItemStack stack) { + if (stack == null || stack.isEmpty()) return 1; + CustomData customData = stack.get(DataComponents.CUSTOM_DATA); + if (customData == null || !customData.copyTag().contains(NBT_MULTIPLIER)) return 1; + return customData.copyTag().getInt(NBT_MULTIPLIER); + } + + @Override + public @NotNull Component getName(@NotNull ItemStack stack) { + int mult = readMultiplier(stack); + String key; + switch (mult) { + case 2 -> key = "item." + com.extendedae_plus.ExtendedAEPlus.MODID + ".entity_speed_card.x2"; + case 4 -> key = "item." + com.extendedae_plus.ExtendedAEPlus.MODID + ".entity_speed_card.x4"; + case 8 -> key = "item." + com.extendedae_plus.ExtendedAEPlus.MODID + ".entity_speed_card.x8"; + case 16 -> key = "item." + com.extendedae_plus.ExtendedAEPlus.MODID + ".entity_speed_card.x16"; + default -> key = "item." + com.extendedae_plus.ExtendedAEPlus.MODID + ".entity_speed_card.x1"; + } + return Component.translatable(key); + } + + + public List getTooltipLines(ItemStack stack) { + int mult = readMultiplier(stack); + long cap = 1L; + switch (mult) { + case 16 -> cap = 1024L; + case 8 -> cap = 256L; + case 4 -> cap = 64L; + case 2 -> cap = 8L; + } + MutableComponent line1 = Component.translatable("tooltip." + com.extendedae_plus.ExtendedAEPlus.MODID + ".entity_speed_card.multiplier", "x" + mult); + MutableComponent line2 = Component.translatable("tooltip." + com.extendedae_plus.ExtendedAEPlus.MODID + ".entity_speed_card.max", cap); + return List.of(line1, line2); + } + + @OnlyIn(Dist.CLIENT) + @Override + public void appendHoverText(ItemStack stack, TooltipContext context, List lines, TooltipFlag advancedTooltips) { + super.appendHoverText(stack, context, lines, advancedTooltips); + lines.addAll(this.getTooltipLines(stack)); + } +} + + diff --git a/src/main/java/com/extendedae_plus/ae/items/EntitySpeedTickerPartItem.java b/src/main/java/com/extendedae_plus/ae/items/EntitySpeedTickerPartItem.java new file mode 100644 index 0000000..ccee587 --- /dev/null +++ b/src/main/java/com/extendedae_plus/ae/items/EntitySpeedTickerPartItem.java @@ -0,0 +1,25 @@ +package com.extendedae_plus.ae.items; + +import appeng.items.parts.PartItem; +import com.extendedae_plus.ae.parts.EntitySpeedTickerPart; +import net.minecraft.network.chat.Component; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; + +import java.util.List; + + +public class EntitySpeedTickerPartItem extends PartItem { + public EntitySpeedTickerPartItem(Properties properties) { + super(properties, EntitySpeedTickerPart.class, EntitySpeedTickerPart::new); + } + + @Override + public void appendHoverText(ItemStack stack, TooltipContext context, List tooltip, TooltipFlag tooltipFlag) { + super.appendHoverText(stack, context, tooltip, tooltipFlag); + tooltip.add(Component.translatable("item.extendedae_plus.entity_speed_ticker.tip.requirement", "需要放入实体加速卡以启用加速")); + tooltip.add(Component.translatable("item.extendedae_plus.entity_speed_ticker.tip.max", "最高可达 1024x 加速")); + tooltip.add(Component.translatable("item.extendedae_plus.entity_speed_ticker.tip.energy", "加速将消耗 AE 网络能量,网络能量不足时无法加速")); + + } +} \ No newline at end of file diff --git a/src/main/java/com/extendedae_plus/ae/menu/EntitySpeedTickerMenu.java b/src/main/java/com/extendedae_plus/ae/menu/EntitySpeedTickerMenu.java new file mode 100644 index 0000000..e72e8ce --- /dev/null +++ b/src/main/java/com/extendedae_plus/ae/menu/EntitySpeedTickerMenu.java @@ -0,0 +1,151 @@ +package com.extendedae_plus.ae.menu; + +import appeng.core.definitions.AEItems; +import appeng.menu.guisync.GuiSync; +import appeng.menu.implementations.UpgradeableMenu; +import appeng.menu.slot.OptionalFakeSlot; +import com.extendedae_plus.ae.parts.EntitySpeedTickerPart; +import com.extendedae_plus.ae.screen.EntitySpeedTickerScreen; +import com.extendedae_plus.config.ModConfigs; +import com.extendedae_plus.init.ModItems; +import com.extendedae_plus.init.ModMenuTypes; +import com.extendedae_plus.util.ConfigParsingUtils; +import com.extendedae_plus.util.PowerUtils; +import it.unimi.dsi.fastutil.shorts.ShortSet; +import net.minecraft.client.Minecraft; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; + +// 实体加速器菜单,负责与客户端界面同步数据 +public class EntitySpeedTickerMenu extends UpgradeableMenu { + @GuiSync(716) + public boolean accelerateEnabled = true; + // 已安装的实体加速卡数量(用于能耗计算) + @GuiSync(717) + public int entitySpeedCardCount; + // 已安装的能量卡数量 + @GuiSync(718) + public int energyCardCount; + // 当前生效的配置倍率(从配置中读取并同步) + // 当前计算出的生效速度(product of multipliers),同步给客户端用于显示 + @GuiSync(719) + public int effectiveSpeed = 1; + @GuiSync(720) + public double multiplier = 1.0; + @GuiSync(721) + public boolean targetBlacklisted = false; + + // 构造方法,初始化菜单并与部件绑定 + public EntitySpeedTickerMenu(int id, Inventory ip, EntitySpeedTickerPart host) { + super(ModMenuTypes.ENTITY_TICKER_MENU.get(), id, ip, host); + // 让部件持有当前菜单实例,便于通信 + getHost().menu = this; + // 初始同步部件上的开关状态到菜单(服务器端构造时保证一致) + try { + this.accelerateEnabled = getHost().getAccelerateEnabled(); + } catch (Exception ignored) { + } + } + + public boolean getAccelerateEnabled() { + return this.accelerateEnabled; + } + + public void setAccelerateEnabled(boolean enabled) { + this.accelerateEnabled = enabled; + } + + @Override + public void onServerDataSync(ShortSet updatedFields) { + super.onServerDataSync(updatedFields); + // 重新统计实体加速卡和能量卡数量 + this.entitySpeedCardCount = this.getUpgrades().getInstalledUpgrades(ModItems.ENTITY_SPEED_CARD.get()); + this.energyCardCount = this.getUpgrades().getInstalledUpgrades(AEItems.ENERGY_CARD); + + // 计算当前面向方块的倍率(服务器端),并同步给客户端 + double mult = 1.0; + try { + BlockEntity target = getHost().getLevel().getBlockEntity( + getHost().getBlockEntity().getBlockPos().relative(getHost().getSide()) + ); + if (target != null) { + String blockId = BuiltInRegistries.BLOCK.getKey(target.getBlockState().getBlock()).toString(); + for (ConfigParsingUtils.MultiplierEntry me : + ConfigParsingUtils.getCachedMultiplierEntries(ModConfigs.ENTITY_TICKER_MULTIPLIERS.get())) { + if (me.pattern.matcher(blockId).matches()) { + mult = Math.max(mult, me.multiplier); + } + } + } + } catch (Exception ignored) {} + this.multiplier = mult; + + // 检查目标是否在黑名单中,如果是则标记并将生效速度设为 0(服务器端计算) + boolean blacklisted = false; + try { + BlockEntity target = getHost().getLevel().getBlockEntity( + getHost().getBlockEntity().getBlockPos().relative(getHost().getSide()) + ); + if (target != null) { + Block block = target.getBlockState().getBlock(); + String blockId = BuiltInRegistries.BLOCK.getKey(block).toString(); // 直接拿到 "minecraft:stone" + + for (java.util.regex.Pattern p : ConfigParsingUtils.getCachedBlacklist( + ModConfigs.ENTITY_TICKER_BLACK_LIST.get())) { + if (p.matcher(blockId).matches()) { + blacklisted = true; + break; + } + } + } + } catch (Exception ignored) { + } + this.targetBlacklisted = blacklisted; + + // 计算生效速度:如果被黑名单则为 0,否则进行正常计算(使用工具类从菜单直接计算 product with cap,最多 8 张) + if (this.targetBlacklisted) { + this.effectiveSpeed = 0; + } else { + this.effectiveSpeed = (int) PowerUtils.computeProductWithCapFromMenu(this, 8); + } + + // 如果在客户端,刷新界面 + 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.entitySpeedCardCount = this.getUpgrades().getInstalledUpgrades(ModItems.ENTITY_SPEED_CARD.get()); + this.energyCardCount = this.getUpgrades().getInstalledUpgrades(AEItems.ENERGY_CARD); + // 立即在客户端计算生效速度以便界面即时反馈(使用与服务端相同的工具方法,最多 8 张卡) + this.effectiveSpeed = (int) PowerUtils.computeProductWithCapFromMenu(this, 8); + 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/ae/parts/EntitySpeedTickerPart.java b/src/main/java/com/extendedae_plus/ae/parts/EntitySpeedTickerPart.java new file mode 100644 index 0000000..0f6655f --- /dev/null +++ b/src/main/java/com/extendedae_plus/ae/parts/EntitySpeedTickerPart.java @@ -0,0 +1,309 @@ +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.PartModel; +import appeng.parts.automation.UpgradeablePart; +import com.extendedae_plus.ExtendedAEPlus; +import com.extendedae_plus.ae.menu.EntitySpeedTickerMenu; +import com.extendedae_plus.config.ModConfigs; +import com.extendedae_plus.init.ModItems; +import com.extendedae_plus.init.ModMenuTypes; +import com.extendedae_plus.util.ConfigParsingUtils; +import com.extendedae_plus.util.PowerUtils; +import net.minecraft.core.BlockPos; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +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; +import java.util.regex.Pattern; + +/** + * EntitySpeedTickerPart 是一个可升级的 AE2 部件

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

+ * 功能受Crazy AE2 Addons启发 + */ +public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTickable, MenuProvider, IUpgradeableObject { + public static final ResourceLocation MODEL_BASE = ResourceLocation.fromNamespaceAndPath( + ExtendedAEPlus.MODID, "part/entity_speed_ticker_part"); + @PartModels + public static final PartModel MODELS_OFF; + @PartModels + public static final PartModel MODELS_ON; + @PartModels + public static final PartModel MODELS_HAS_CHANNEL; + + static { + MODELS_OFF = new PartModel(MODEL_BASE, ResourceLocation.fromNamespaceAndPath(ExtendedAEPlus.MODID, "part/entity_speed_ticker_off")); + MODELS_ON = new PartModel(MODEL_BASE, ResourceLocation.fromNamespaceAndPath(ExtendedAEPlus.MODID, "part/entity_speed_ticker_on")); + MODELS_HAS_CHANNEL = new PartModel(MODEL_BASE, ResourceLocation.fromNamespaceAndPath(ExtendedAEPlus.MODID, "part/entity_speed_ticker_has_channel")); + } + + // 当前打开的菜单实例(如果有) + public EntitySpeedTickerMenu menu; + // 控制是否启用加速(默认启用) + private boolean accelerateEnabled = true; + + /** + * 构造函数,初始化部件并设置网络节点属性 + * + * @param partItem 部件物品 + */ + public EntitySpeedTickerPart(IPartItem partItem) { + super(partItem); + // 设置网络节点属性:需要通道、空闲功耗为1,并注册为 IGridTickable 服务 + this.getMainNode() + .setFlags(GridFlags.REQUIRE_CHANNEL) + .setIdlePowerUsage(1) + .addService(IGridTickable.class, this); + } + + public boolean getAccelerateEnabled() { + return this.accelerateEnabled; + } + + public void setAccelerateEnabled(boolean accelerateEnabled) { + this.accelerateEnabled = accelerateEnabled; + } + + /** + * 获取当前状态下的静态模型(用于渲染) + * + * @return 当前状态的模型 + */ + public IPartModel getStaticModels() { + if (this.isActive() && this.isPowered()) { + return MODELS_HAS_CHANNEL; + } else if (this.isPowered()) { + return MODELS_ON; + } else { + return MODELS_OFF; + } + } + + + + /** + * 当玩家激活部件(右键)时调用,打开自定义菜单 + * + * @param player 玩家 + * @param pos 点击位置 + * @return 总是返回 true,表示激活成功 + */ + @Override + public final boolean onUseWithoutItem(Player player, Vec3 pos) { + if (!isClientSide()) { + MenuOpener.open(ModMenuTypes.ENTITY_TICKER_MENU.get(), player, MenuLocators.forPart(this)); + } + return true; + } + + /** + * 定义部件的碰撞箱(用于物理碰撞和渲染) + * + * @param bch 碰撞辅助器 + */ + @Override + public void getBoxes(IPartCollisionHelper bch) { + bch.addBox(2, 2, 14, 14, 14, 16); + bch.addBox(5, 5, 12, 11, 11, 14); + } + + /** + * 获取定时请求,决定本部件多久 tick 一次 + * + * @param iGridNode 网络节点 + * @return TickingRequest 对象 + */ + @Override + public TickingRequest getTickingRequest(IGridNode iGridNode) { + // 每 1 tick 执行一次 + return new TickingRequest(1, 1, false); + } + + /** + * 当升级卡数量发生变化时调用,通知菜单更新 + */ + @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) { + // 如果部件的加速开关被关闭,则不进行加速(提前返回) + if (!this.getAccelerateEnabled()) { + return TickRateModulation.IDLE; + } + + // 获取目标方块实体(本部件朝向的方块) + 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; + } + + // 获取方块实体的位置 + BlockPos pos = blockEntity.getBlockPos(); + if (blockEntity.getLevel() == null) return; + + + // 检查黑名单(支持通配符/正则) + String blockId = BuiltInRegistries.BLOCK.getKey(blockEntity.getBlockState().getBlock()).toString(); + + // 使用工具类的缓存接口(工具类内部负责懒加载/线程安全) + List compiledBlacklist = ConfigParsingUtils.getCachedBlacklist(ModConfigs.ENTITY_TICKER_BLACK_LIST.get()); + for (Pattern p : compiledBlacklist) { + if (p.matcher(blockId).matches()) return; + } + + // 获取该方块实体的 Ticker + @SuppressWarnings("unchecked") + BlockEntityTicker blockEntityTicker = this.getLevel() + .getBlockState(pos) + .getTicker(this.getLevel(), (BlockEntityType) blockEntity.getType()); + if (blockEntityTicker == null) return; + + // 使用集中定义的 CardDef 列表,支持以后添加等级或改倍率而无需修改此逻辑 + int energyCardCount = getUpgrades().getInstalledUpgrades(AEItems.ENERGY_CARD); + + // 使用已注册的单一 Item 计算已安装卡数量(总计,用于能耗计算) + int entitySpeedCardCount = getUpgrades().getInstalledUpgrades(ModItems.ENTITY_SPEED_CARD.get()); + + // 使用工具方法从槽位直接计算乘积并应用 cap(最多 8 张卡) + long product = PowerUtils.computeProductWithCapFromStacks(this.getUpgrades(), 8); + + // 如果没有任何实体加速卡,则不进行加速且不消耗额外能量(只保留部件的被动功耗) + if (entitySpeedCardCount <= 0) return; + + // 计算本次 tick 所需能量:使用工具类根据 product 计算最终能耗 + double requiredPower = PowerUtils.computeFinalPowerForProduct(product, energyCardCount); + + int speed = (int) product; + + double multiplier = 1.0; + for (ConfigParsingUtils.MultiplierEntry me : ConfigParsingUtils.getCachedMultiplierEntries(ModConfigs.ENTITY_TICKER_MULTIPLIERS.get())) { + if (me.pattern.matcher(blockId).matches()) { + multiplier = Math.max(multiplier, me.multiplier); + } + } + + requiredPower *= multiplier; + + // 先模拟提取以检查网络中是否有足够能量,再真正抽取 + 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) + // 已由 product 计算得到 speed;上面已在没有卡时提前返回 + + // 执行 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/ae/screen/EntitySpeedTickerScreen.java b/src/main/java/com/extendedae_plus/ae/screen/EntitySpeedTickerScreen.java new file mode 100644 index 0000000..32421e2 --- /dev/null +++ b/src/main/java/com/extendedae_plus/ae/screen/EntitySpeedTickerScreen.java @@ -0,0 +1,136 @@ +package com.extendedae_plus.ae.screen; + +import appeng.api.config.Settings; +import appeng.api.config.YesNo; +import appeng.client.gui.Icon; +import appeng.client.gui.implementations.UpgradeableScreen; +import appeng.client.gui.style.ScreenStyle; +import appeng.client.gui.widgets.CommonButtons; +import appeng.client.gui.widgets.SettingToggleButton; +import appeng.util.Platform; +import com.extendedae_plus.ae.menu.EntitySpeedTickerMenu; +import com.extendedae_plus.network.ToggleEntityTickerC2SPacket; +import com.extendedae_plus.util.PowerUtils; +import net.minecraft.client.Minecraft; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.player.Inventory; + +import java.util.List; + +public class EntitySpeedTickerScreen extends UpgradeableScreen { + private boolean eap$entitySpeedTickerEnabled = false; + private SettingToggleButton eap$entitySpeedTickerToggle; + + public EntitySpeedTickerScreen( + EntitySpeedTickerMenu menu, Inventory playerInventory, Component title, ScreenStyle style) { + super((C) menu, playerInventory, title, style); + this.addToLeftToolbar(CommonButtons.togglePowerUnit()); + + try{ + this.eap$entitySpeedTickerEnabled = menu.getAccelerateEnabled(); + }catch (Exception ignored){} + + // 使用 SettingToggleButton 的外观(原版图标),但自定义悬停描述为“智能阻挡” + // 不做本地切换,点击仅发送自定义C2S,显示由@GuiSync回传 + eap$entitySpeedTickerToggle = new SettingToggleButton<>( + Settings.BLOCKING_MODE, + this.eap$entitySpeedTickerEnabled ? YesNo.YES : YesNo.NO, + (btn, backwards) -> { + // 不做本地切换,点击仅发送自定义C2S,显示由@GuiSync回传 + var conn = Minecraft.getInstance().getConnection(); + if (conn != null) conn.send(ToggleEntityTickerC2SPacket.INSTANCE); + } + ) { + @Override + public List getTooltipMessage() { + // 如果目标在黑名单中,直接显示已禁用的提示 + try { + if (menu != null && menu.targetBlacklisted) { + var title = Component.literal("实体加速"); + var stateLine = Component.literal("已禁用(目标在黑名单)"); + return List.of(title, stateLine); + } + } catch (Exception ignored) {} + + boolean enabled = eap$entitySpeedTickerEnabled; + var title = Component.literal("实体加速"); + var stateLine = enabled + ? Component.literal("已启用: 将加速目标方块实体的tick") + : Component.literal("已关闭: 不会对目标方块实体进行加速"); + return List.of(title, stateLine); + } + + @Override + protected Icon getIcon() { + try { + if (menu != null && menu.targetBlacklisted) { + // 黑名单时显示禁用图标 + return Icon.INVALID; + } + } catch (Exception ignored) {} + + // 根据当前值显示不同图标(可按需替换 Icon 常量) + if (this.getCurrentValue() == YesNo.YES) { + return Icon.VALID; + } else { + return Icon.INVALID; + } + } + }; + // 初始化后立刻对齐当前@GuiSync状态,避免首帧显示不一致 + eap$entitySpeedTickerToggle.set(this.eap$entitySpeedTickerEnabled ? YesNo.YES : YesNo.NO); + + this.addToLeftToolbar(eap$entitySpeedTickerToggle); + } + + @Override + protected void updateBeforeRender() { + super.updateBeforeRender(); + + if (this.eap$entitySpeedTickerToggle != null) { + boolean desired = this.eap$entitySpeedTickerEnabled; + if (this.menu != null) { + desired = this.menu.getAccelerateEnabled(); + } + + this.eap$entitySpeedTickerEnabled = desired; + // 如果目标在黑名单中,禁用切换并强制显示为关闭 + if (this.menu != null && this.menu.targetBlacklisted) { + this.eap$entitySpeedTickerToggle.set(YesNo.NO); + this.eap$entitySpeedTickerToggle.active = false; + } else { + this.eap$entitySpeedTickerToggle.set(desired ? YesNo.YES : YesNo.NO); + this.eap$entitySpeedTickerToggle.active = true; + } + } + textData(); + } + + public void refreshGui() { + textData(); + } + + private void textData() { + // 如果目标被黑名单禁止,则显示禁用状态并把数值显示为 0 + if (getMenu().targetBlacklisted) { + setTextContent("enable", Component.translatable("screen.extendedae_plus.entity_speed_ticker.enable")); + setTextContent("speed", Component.translatable("screen.extendedae_plus.entity_speed_ticker.speed", 0)); + setTextContent("energy", Component.translatable("screen.extendedae_plus.entity_speed_ticker.energy", Platform.formatPower(0.0, false))); + setTextContent("power_ratio", Component.translatable("screen.extendedae_plus.entity_speed_ticker.power_ratio", PowerUtils.formatPercentage(0.0))); + setTextContent("multiplier", Component.translatable("screen.extendedae_plus.entity_speed_ticker.multiplier", String.format("%.2fx", 0.0))); + return; + } + + int energyCardCount = getMenu().energyCardCount; + double multiplier = getMenu().multiplier; + int effectiveSpeed = getMenu().effectiveSpeed; + + double finalPower = PowerUtils.computeFinalPowerForProduct(effectiveSpeed, energyCardCount); + double remainingRatio = PowerUtils.getRemainingRatio(energyCardCount); + + setTextContent("speed", Component.translatable("screen.extendedae_plus.entity_speed_ticker.speed", effectiveSpeed)); + setTextContent("energy", Component.translatable("screen.extendedae_plus.entity_speed_ticker.energy", Platform.formatPower(finalPower, false))); + setTextContent("power_ratio", Component.translatable("screen.extendedae_plus.entity_speed_ticker.power_ratio", PowerUtils.formatPercentage(remainingRatio))); + setTextContent("multiplier", Component.translatable("screen.extendedae_plus.entity_speed_ticker.multiplier", String.format("%.2fx", multiplier))); + } +} \ No newline at end of file diff --git a/src/main/java/com/extendedae_plus/client/ClientProxy.java b/src/main/java/com/extendedae_plus/client/ClientProxy.java index 5a7d020..9c373ee 100644 --- a/src/main/java/com/extendedae_plus/client/ClientProxy.java +++ b/src/main/java/com/extendedae_plus/client/ClientProxy.java @@ -1,18 +1,21 @@ package com.extendedae_plus.client; import appeng.client.render.crafting.CraftingCubeModel; +import appeng.init.client.InitScreens; import com.extendedae_plus.ExtendedAEPlus; +import com.extendedae_plus.ae.definitions.upgrades.EntitySpeedCardItem; +import com.extendedae_plus.ae.menu.EntitySpeedTickerMenu; +import com.extendedae_plus.ae.screen.EntitySpeedTickerScreen; import com.extendedae_plus.client.render.crafting.EPlusCraftingCubeModelProvider; -import com.extendedae_plus.client.screen.GlobalProviderModesScreen; -import com.extendedae_plus.init.ModMenuTypes; import com.extendedae_plus.content.crafting.EPlusCraftingUnitType; import com.extendedae_plus.hooks.BuiltInModelHooks; -import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent; -import net.minecraft.client.gui.screens.MenuScreens; -import net.neoforged.neoforge.client.event.RegisterMenuScreensEvent; -import net.neoforged.fml.common.EventBusSubscriber; +import com.extendedae_plus.init.ModItems; +import com.extendedae_plus.init.ModMenuTypes; +import net.minecraft.client.renderer.item.ItemProperties; import net.neoforged.api.distmarker.Dist; import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.common.EventBusSubscriber; +import net.neoforged.neoforge.client.event.RegisterMenuScreensEvent; /** * 客户端模型注册,将 formed 模型注册为内置模型。 @@ -26,6 +29,10 @@ public final class ClientProxy { public static void init() { if (REGISTERED) return; REGISTERED = true; + // 注册 Item property,用于根据 ItemStack 的 NBT exponent 切换模型 + ItemProperties.register(ModItems.ENTITY_SPEED_CARD.get(), ExtendedAEPlus.id("mult"), + (stack, world, entity, seed) -> (float) EntitySpeedCardItem.readMultiplier(stack)); + // 注册四种形成态模型为内置模型 BuiltInModelHooks.addBuiltInModel( ExtendedAEPlus.id("block/crafting/4x_accelerator_formed_v2"), @@ -46,16 +53,7 @@ public final class ClientProxy { BuiltInModelHooks.addBuiltInModel( ExtendedAEPlus.id("block/crafting/1024x_accelerator_formed_v2"), new CraftingCubeModel(new EPlusCraftingCubeModelProvider(EPlusCraftingUnitType.ACCELERATOR_1024x))); - } - /** - * 客户端设置阶段:延迟执行需要访问注册对象的客户端注册。 - */ - public static void onClientSetup(final FMLClientSetupEvent event) { - event.enqueueWork(() -> { - // 确保在首次资源加载前完成内置模型注册(REGISTERED 保护避免重复) - init(); - }); } @SubscribeEvent @@ -75,5 +73,10 @@ public final class ClientProxy { } } ); + + /** + * 注册由 AE2 InitScreens 所需的屏幕资源映射(用于内置 JSON 屏幕注册) + */ + InitScreens.register(event, ModMenuTypes.ENTITY_TICKER_MENU.get(), EntitySpeedTickerScreen::new, "/screens/entity_speed_ticker.json"); } } diff --git a/src/main/java/com/extendedae_plus/client/ui/ProviderSelectScreen.java b/src/main/java/com/extendedae_plus/client/ui/ProviderSelectScreen.java index 11426b6..f0704e0 100644 --- a/src/main/java/com/extendedae_plus/client/ui/ProviderSelectScreen.java +++ b/src/main/java/com/extendedae_plus/client/ui/ProviderSelectScreen.java @@ -1,6 +1,5 @@ package com.extendedae_plus.client.ui; -import com.extendedae_plus.network.ModNetwork; import com.extendedae_plus.network.UploadEncodedPatternToProviderC2SPacket; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.components.Button; diff --git a/src/main/java/com/extendedae_plus/config/ModConfigs.java b/src/main/java/com/extendedae_plus/config/ModConfigs.java index d74f914..5abe719 100644 --- a/src/main/java/com/extendedae_plus/config/ModConfigs.java +++ b/src/main/java/com/extendedae_plus/config/ModConfigs.java @@ -2,6 +2,8 @@ package com.extendedae_plus.config; import net.neoforged.neoforge.common.ModConfigSpec; +import java.util.List; + public final class ModConfigs { public static final ModConfigSpec COMMON_SPEC; public static final ModConfigSpec.IntValue PAGE_MULTIPLIER; @@ -12,7 +14,9 @@ public final class ModConfigs { public static final ModConfigSpec.BooleanValue PATTERN_TERMINAL_SHOW_SLOTS_DEFAULT; public static final ModConfigSpec.IntValue SMART_SCALING_MAX_MULTIPLIER; public static final ModConfigSpec.IntValue CRAFTING_PAUSE_THRESHOLD; - + public static final ModConfigSpec.IntValue ENTITY_TICKER_COST; + public static final ModConfigSpec.ConfigValue> ENTITY_TICKER_BLACK_LIST; + public static final ModConfigSpec.ConfigValue> ENTITY_TICKER_MULTIPLIERS; static { ModConfigSpec.Builder builder = new ModConfigSpec.Builder(); @@ -22,7 +26,8 @@ public final class ModConfigs { .comment( "扩展样板供应器总槽位容量的倍率", "基础为36,每页仍显示36格,倍率会增加总页数/总容量", - "建议范围 1-16") + "建议范围 1-16" + ) .defineInRange("pageMultiplier", 1, 1, 64); // 是否显示样板编码玩家(通用) @@ -59,7 +64,8 @@ public final class ModConfigs { "智能倍增时是否对样板供应器轮询分配", "仅多个供应器有相同样板时生效,开启后请求会均分到所有可用供应器,关闭则全部分配给单一供应器", "注意:所有相关供应器需开启智能倍增,否则可能失效", - "默认: true") + "默认: true" + ) .define("providerRoundRobinEnable", true); // 智能倍增的最大倍数(以单次样板产出为单位)。 @@ -67,7 +73,8 @@ public final class ModConfigs { SMART_SCALING_MAX_MULTIPLIER = builder .comment( "智能倍增的最大倍数(0 表示不限制)", - "此倍数是针对单次样板产出的放大倍数上限,用于限制一次推送中按倍增缩放的规模") + "此倍数是针对单次样板产出的放大倍数上限,用于限制一次推送中按倍增缩放的规模" + ) .defineInRange("smartScalingMaxMultiplier", 0, 0, 1048576); builder.pop(); // pop smart @@ -79,19 +86,55 @@ public final class ModConfigs { WIRELESS_MAX_RANGE = builder .comment( "无线收发器最大连接距离(单位:方块)", - "从端与主端的直线距离需小于等于该值才会建立连接。") + "从端与主端的直线距离需小于等于该值才会建立连接。" + ) .defineInRange("wirelessMaxRange", 256.0D, 1.0D, 4096.0D); // 是否允许跨维度连接(忽略维度差异进行频道传输)。 WIRELESS_CROSS_DIM_ENABLE = builder .comment( "是否允许无线收发器跨维度建立连接", - "开启后,从端可连接到不同维度的主端(忽略距离限制)") + "开启后,从端可连接到不同维度的主端(忽略距离限制)" + ) .define("wirelessCrossDimEnable", true); builder.pop(); // pop wireless -// builder.pop(); // pop extendedae_plus + builder.push("entitySpeedTicker"); + + ENTITY_TICKER_COST = builder + .comment( + "实体加速器能量消耗基础值" + ) + .defineInRange("entityTickerCost", 512, 0 , Integer.MAX_VALUE); + + + ENTITY_TICKER_BLACK_LIST = builder + .comment( + "实体加速器黑名单:匹配的方块将不会被加速。支持通配符/正则(例如:minecraft:*)", + "格式:全名或通配符/正则字符串,例如 'minecraft:chest'、'minecraft:*'、'modid:.*_fluid'" + ) + .defineListAllowEmpty( + List.of("entityTickerBlackList"), // 路径 + List::of, // 默认值 + () -> "", // 新元素默认值(空字符串,供配置编辑器使用) + obj -> obj instanceof String // 验证每个元素是字符串 + ); + + ENTITY_TICKER_MULTIPLIERS = builder + .comment( + "额外消耗倍率配置:为某些方块设置额外能量倍率,格式 'modid:blockid multiplier',例如 'minecraft:chest 2x'", + "支持通配符/正则匹配(例如 'minecraft:* 2x' 会对整个命名空间生效)。" + ) + .defineListAllowEmpty( + List.of("entityTickerMultipliers"), // 路径 + List::of, // 默认值 + () -> "", // 新元素默认值(空字符串,供配置编辑器使用) + obj -> obj instanceof String // 验证每个元素是字符串 + ); + + 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 f07a83b..f6c2a3c 100644 --- a/src/main/java/com/extendedae_plus/init/ModCreativeTabs.java +++ b/src/main/java/com/extendedae_plus/init/ModCreativeTabs.java @@ -26,8 +26,12 @@ 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.INFINITY_BIGINTEGER_CELL_ITEM.get()); + output.accept(ModItems.ENTITY_TICKER_PART_ITEM.get()); + // 放入四个预设的 stacks(x2,x4,x8,x16),使用 ModItems 工厂创建 + output.accept(ModItems.createEntitySpeedCardStack(2)); + output.accept(ModItems.createEntitySpeedCardStack(4)); + output.accept(ModItems.createEntitySpeedCardStack(8)); + output.accept(ModItems.createEntitySpeedCardStack(16)); }) .build()); } diff --git a/src/main/java/com/extendedae_plus/init/ModItems.java b/src/main/java/com/extendedae_plus/init/ModItems.java index 71a6220..1f2dcc6 100644 --- a/src/main/java/com/extendedae_plus/init/ModItems.java +++ b/src/main/java/com/extendedae_plus/init/ModItems.java @@ -1,8 +1,14 @@ 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.ae.definitions.upgrades.EntitySpeedCardItem; +import com.extendedae_plus.ae.items.EntitySpeedTickerPartItem; import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; import net.neoforged.neoforge.registries.DeferredItem; import net.neoforged.neoforge.registries.DeferredRegister; @@ -47,8 +53,34 @@ public final class ModItems { () -> new BlockItem(ModBlocks.ACCELERATOR_1024x.get(), new Item.Properties()) ); -// public static final DeferredItem INFINITY_BIGINTEGER_CELL_ITEM = ITEMS.register( -// "infinity_biginteger_cell", InfinityBigIntegerCellItem::new -// ); + public static final DeferredItem ENTITY_TICKER_PART_ITEM = ITEMS.register( + "entity_speed_ticker", + () -> new EntitySpeedTickerPartItem(new Item.Properties()) + ); + // AE Upgrade Cards: 实体加速卡(四个等级:x2,x4,x8,x16) + // 单一实体加速卡 Item(不同等级由 ItemStack.nbt 存储) + public static final DeferredItem ENTITY_SPEED_CARD = ITEMS.register( + "entity_speed_card", + () -> new EntitySpeedCardItem(new Item.Properties()) + ); + + /** + * 为 PartItem 注册 AE2 部件模型。 + * 在客户端进行模型/几何体注册时调用。 + */ + public static void registerPartModels() { + PartModels.registerModels( + PartModelsHelper.createModels( + ENTITY_TICKER_PART_ITEM.get().getPartClass().asSubclass(IPart.class) + ) + ); + } + + /** + * 工厂:创建带 multiplier 的实体加速卡 ItemStack(2/4/8/16) + */ + public static ItemStack createEntitySpeedCardStack(int multiplier) { + return EntitySpeedCardItem.withMultiplier(multiplier); + } } diff --git a/src/main/java/com/extendedae_plus/init/ModMenuTypes.java b/src/main/java/com/extendedae_plus/init/ModMenuTypes.java index 960ece9..c42986b 100644 --- a/src/main/java/com/extendedae_plus/init/ModMenuTypes.java +++ b/src/main/java/com/extendedae_plus/init/ModMenuTypes.java @@ -1,12 +1,15 @@ package com.extendedae_plus.init; +import appeng.menu.implementations.MenuTypeBuilder; import com.extendedae_plus.ExtendedAEPlus; +import com.extendedae_plus.ae.menu.EntitySpeedTickerMenu; +import com.extendedae_plus.ae.parts.EntitySpeedTickerPart; import com.extendedae_plus.menu.NetworkPatternControllerMenu; -import net.minecraft.world.inventory.MenuType; import net.minecraft.core.registries.Registries; +import net.minecraft.world.inventory.MenuType; +import net.neoforged.neoforge.common.extensions.IMenuTypeExtension; import net.neoforged.neoforge.registries.DeferredHolder; import net.neoforged.neoforge.registries.DeferredRegister; -import net.neoforged.neoforge.common.extensions.IMenuTypeExtension; public final class ModMenuTypes { private ModMenuTypes() {} @@ -16,5 +19,11 @@ public final class ModMenuTypes { public static final DeferredHolder, MenuType> NETWORK_PATTERN_CONTROLLER = MENUS.register("network_pattern_controller", - () -> IMenuTypeExtension.create((id, inv, buf) -> new NetworkPatternControllerMenu(id, inv, buf))); + () -> IMenuTypeExtension.create(NetworkPatternControllerMenu::new)); + + public static final DeferredHolder, MenuType> 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/network/ModNetwork.java b/src/main/java/com/extendedae_plus/init/ModNetwork.java similarity index 94% rename from src/main/java/com/extendedae_plus/network/ModNetwork.java rename to src/main/java/com/extendedae_plus/init/ModNetwork.java index 6a57257..4d618f5 100644 --- a/src/main/java/com/extendedae_plus/network/ModNetwork.java +++ b/src/main/java/com/extendedae_plus/init/ModNetwork.java @@ -1,12 +1,14 @@ -package com.extendedae_plus.network; +package com.extendedae_plus.init; import com.extendedae_plus.ExtendedAEPlus; +import com.extendedae_plus.network.*; import net.neoforged.neoforge.network.event.RegisterPayloadHandlersEvent; public class ModNetwork { // 在 Mod 构造中通过 modEventBus.addListener(ModNetwork::registerPayloadHandlers) 注册 public static void registerPayloadHandlers(final RegisterPayloadHandlersEvent event) { var registrar = event.registrar(ExtendedAEPlus.MODID); + registrar.playToServer(ToggleEntityTickerC2SPacket.TYPE, ToggleEntityTickerC2SPacket.STREAM_CODEC, ToggleEntityTickerC2SPacket::handle); registrar.playToServer(ToggleAdvancedBlockingC2SPacket.TYPE, ToggleAdvancedBlockingC2SPacket.STREAM_CODEC, ToggleAdvancedBlockingC2SPacket::handle); registrar.playToServer(ToggleSmartDoublingC2SPacket.TYPE, ToggleSmartDoublingC2SPacket.STREAM_CODEC, ToggleSmartDoublingC2SPacket::handle); registrar.playToServer(ScalePatternsC2SPacket.TYPE, ScalePatternsC2SPacket.STREAM_CODEC, ScalePatternsC2SPacket::handle); 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..9a8de67 --- /dev/null +++ b/src/main/java/com/extendedae_plus/init/UpgradeCards.java @@ -0,0 +1,17 @@ +package com.extendedae_plus.init; + +import appeng.api.upgrades.Upgrades; +import appeng.core.definitions.AEItems; +import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent; + + +public class UpgradeCards { + public UpgradeCards(final FMLCommonSetupEvent event) { + event.enqueueWork(() -> { + // 现有:把 Entity Ticker 的部件注册为处理 SPEED/ENERGY 卡的宿主 + Upgrades.add(AEItems.ENERGY_CARD, ModItems.ENTITY_TICKER_PART_ITEM.get(), 8, "group.entity_ticker.name"); + // 使用单一的 UpgradeCard Item 作为注册键,总共允许安装 4 张(不同等级由 ItemStack NBT 区分) + Upgrades.add(ModItems.ENTITY_SPEED_CARD.get(), ModItems.ENTITY_TICKER_PART_ITEM.get(), 4, "group.entity_ticker.name"); + }); + } +} \ No newline at end of file diff --git a/src/main/java/com/extendedae_plus/integration/jei/ExtendedAEJeiPlugin.java b/src/main/java/com/extendedae_plus/integration/jei/ExtendedAEJeiPlugin.java index 8c249a2..eecb9dd 100644 --- a/src/main/java/com/extendedae_plus/integration/jei/ExtendedAEJeiPlugin.java +++ b/src/main/java/com/extendedae_plus/integration/jei/ExtendedAEJeiPlugin.java @@ -1,10 +1,17 @@ package com.extendedae_plus.integration.jei; import com.extendedae_plus.ExtendedAEPlus; +import com.extendedae_plus.ae.definitions.upgrades.EntitySpeedCardItem; import mezz.jei.api.IModPlugin; import mezz.jei.api.JeiPlugin; +import mezz.jei.api.constants.VanillaTypes; +import mezz.jei.api.ingredients.subtypes.ISubtypeInterpreter; +import mezz.jei.api.ingredients.subtypes.UidContext; +import mezz.jei.api.registration.ISubtypeRegistration; import mezz.jei.api.runtime.IJeiRuntime; import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.NotNull; @JeiPlugin public class ExtendedAEJeiPlugin implements IModPlugin { @@ -19,4 +26,28 @@ public class ExtendedAEJeiPlugin implements IModPlugin { public void onRuntimeAvailable(IJeiRuntime jeiRuntime) { JeiRuntimeProxy.setRuntime(jeiRuntime); } + + + @Override + public void registerItemSubtypes(ISubtypeRegistration registration) { + // Register NBT-based subtype interpreter so JEI treats different multipliers as distinct items + registration.registerSubtypeInterpreter( + VanillaTypes.ITEM_STACK, + com.extendedae_plus.init.ModItems.ENTITY_SPEED_CARD.get(), + new ISubtypeInterpreter() { + @Override + public @NotNull Object getSubtypeData(@NotNull ItemStack ingredient, @NotNull UidContext context) { + // 返回你想让 JEI 区分子类型的数据,这里用 multiplier + return EntitySpeedCardItem.readMultiplier(ingredient); + } + + @Override + public @NotNull String getLegacyStringSubtypeInfo(@NotNull ItemStack ingredient, @NotNull UidContext context) { + // 返回同样的值给旧接口兼容 + return String.valueOf(EntitySpeedCardItem.readMultiplier(ingredient)); + } + } + ); + + } } diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/AEBaseScreenMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/AEBaseScreenMixin.java index bf5f561..b423345 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/AEBaseScreenMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/AEBaseScreenMixin.java @@ -17,7 +17,6 @@ import com.extendedae_plus.api.ExPatternPageAccessor; import com.extendedae_plus.content.ClientPatternHighlightStore; import com.extendedae_plus.network.CraftingMonitorJumpC2SPacket; import com.extendedae_plus.network.CraftingMonitorOpenProviderC2SPacket; -import com.extendedae_plus.network.ModNetwork; import com.extendedae_plus.util.GuiUtil; import com.glodblock.github.extendedae.client.gui.GuiExPatternProvider; import com.mojang.logging.LogUtils; diff --git a/src/main/java/com/extendedae_plus/network/ToggleEntityTickerC2SPacket.java b/src/main/java/com/extendedae_plus/network/ToggleEntityTickerC2SPacket.java new file mode 100644 index 0000000..0407f95 --- /dev/null +++ b/src/main/java/com/extendedae_plus/network/ToggleEntityTickerC2SPacket.java @@ -0,0 +1,50 @@ +package com.extendedae_plus.network; + +import com.extendedae_plus.ExtendedAEPlus; +import com.extendedae_plus.ae.menu.EntitySpeedTickerMenu; +import com.extendedae_plus.ae.parts.EntitySpeedTickerPart; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerPlayer; +import net.neoforged.neoforge.network.handling.IPayloadContext; + +/** + * C2S: Toggle the accelerateEnabled flag on the EntitySpeedTickerPart bound to the open menu. + */ +public class ToggleEntityTickerC2SPacket implements CustomPacketPayload { + public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>( + ResourceLocation.fromNamespaceAndPath(ExtendedAEPlus.MODID, "toggle_entity_ticker")); + + public static final ToggleEntityTickerC2SPacket INSTANCE = new ToggleEntityTickerC2SPacket(); + + public static final StreamCodec STREAM_CODEC = + StreamCodec.unit(INSTANCE); + + private ToggleEntityTickerC2SPacket() {} + + @Override + public CustomPacketPayload.Type type() { + return TYPE; + } + + public static void handle(final ToggleEntityTickerC2SPacket msg, final IPayloadContext ctx) { + ctx.enqueueWork(() -> { + if (!(ctx.player() instanceof ServerPlayer player)) return; + + if (!(player.containerMenu instanceof EntitySpeedTickerMenu menu)) return; + + EntitySpeedTickerPart part = menu.getHost(); + if (part == null) return; + + // 切换部件上的状态,并把新状态同步到菜单字段,随后广播以通知客户端 + boolean current = part.getAccelerateEnabled(); + boolean next = !current; + part.setAccelerateEnabled(next); + // 确保菜单上的字段也被更新,这样 @GuiSync 会把状态发回客户端 + menu.setAccelerateEnabled(next); + menu.broadcastChanges(); + }); + } +} \ No newline at end of file diff --git a/src/main/java/com/extendedae_plus/util/ConfigParsingUtils.java b/src/main/java/com/extendedae_plus/util/ConfigParsingUtils.java new file mode 100644 index 0000000..c1f2395 --- /dev/null +++ b/src/main/java/com/extendedae_plus/util/ConfigParsingUtils.java @@ -0,0 +1,172 @@ +package com.extendedae_plus.util; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; +/** + * 配置解析工具类:用于解析黑名单与倍率配置的字符串 + */ +public final class ConfigParsingUtils { + private ConfigParsingUtils() {} + + public static final class MultiplierEntry { + public final Pattern pattern; + public final double multiplier; + + public MultiplierEntry(Pattern pattern, double multiplier) { + this.pattern = pattern; + this.multiplier = multiplier; + } + } + + /** + * 编译用户提供的匹配串。支持简单的 glob 语法('*' 和 '?')以及完整的正则表达式。 + * 规则: + * - 如果字符串包含 ".*" 或其他正则元字符,优先尝试按正则编译(若失败则回退到 glob 转换)。 + * - 否则若包含 '*' 或 '?',将按 glob 语法转换为正则。 + * - 否则按字面量匹配处理。 + */ + public static Pattern compilePattern(String raw) { + if (raw == null) throw new IllegalArgumentException("pattern is null"); + raw = raw.trim(); + if (raw.isEmpty()) throw new IllegalArgumentException("pattern is empty"); + + // If it looks like regex (contains '.*' or regex metachar), try regex first + if (raw.contains(".*") || raw.matches(".*[\\[\\(\\+\\{\\\\].*")) { + try { + return Pattern.compile("^" + raw + "$"); + } catch (PatternSyntaxException ignored) { + // fallback to glob below + } + } + + // If contains glob chars, convert to regex + if (raw.contains("*") || raw.contains("?")) { + StringBuilder sb = new StringBuilder(); + sb.append('^'); + for (char c : raw.toCharArray()) { + switch (c) { + case '*': sb.append(".*"); break; + case '?': sb.append('.'); break; + default: + // escape regex special chars + if (".\\+[]{}()^$|".indexOf(c) >= 0) { + sb.append('\\'); + } + sb.append(c); + } + } + sb.append('$'); + return Pattern.compile(sb.toString()); + } + + // Otherwise treat as a literal (match exact) + return Pattern.compile("^" + Pattern.quote(raw) + "$"); + } + + /** + * Parse multiplier entries like 'modid:block 2x' into MultiplierEntry objects. + * Accepts values with optional trailing 'x' (case-insensitive). + */ + public static MultiplierEntry parseMultiplierEntry(String entry) { + if (entry == null) return null; + String[] parts = entry.trim().split("\\s+"); + if (parts.length < 2) return null; + String key = parts[0]; + String val = parts[1].toLowerCase(); + if (val.endsWith("x")) val = val.substring(0, val.length() - 1); + double m; + try { + m = Double.parseDouble(val); + } catch (NumberFormatException ex) { + return null; + } + try { + Pattern p = compilePattern(key); + return new MultiplierEntry(p, m); + } catch (IllegalArgumentException e) { + return null; + } + } + + public static List compilePatterns(List raw) { + List out = new ArrayList<>(); + if (raw == null) return out; + for (String s : raw) { + if (s == null || s.isBlank()) continue; + try { out.add(compilePattern(s)); } catch (IllegalArgumentException ignored) {} + } + return out; + } + + public static List parseMultiplierList(List raw) { + List out = new ArrayList<>(); + if (raw == null) return out; + for (String s : raw) { + MultiplierEntry me = parseMultiplierEntry(s); + if (me != null) out.add(me); + } + return out; + } + + // ------------------ 全局缓存与接口 ------------------ + private static volatile List cachedBlacklist = null; + private static volatile List cachedMultiplierEntries = null; + private static final Object CACHE_LOCK = new Object(); + + /** + * 获取已解析并缓存的黑名单(线程安全、懒加载)。 + */ + public static List getCachedBlacklist(List source) { + if (cachedBlacklist == null) { + synchronized (CACHE_LOCK) { + if (cachedBlacklist == null) { + cachedBlacklist = compilePatterns(source); + } + } + } else { + synchronized (CACHE_LOCK) { + List newCachedBlackList = compilePatterns(source); + if (!cachedBlacklist.equals(newCachedBlackList)) { + cachedBlacklist = newCachedBlackList; + } + } + } + return Collections.unmodifiableList(cachedBlacklist); + } + + /** + * 获取已解析并缓存的倍率列表(线程安全、懒加载)。 + */ + public static List getCachedMultiplierEntries(List source) { + if (cachedMultiplierEntries == null) { + synchronized (CACHE_LOCK) { + if (cachedMultiplierEntries == null) { + cachedMultiplierEntries = parseMultiplierList(source); + } + } + }else { + synchronized (CACHE_LOCK) { + List newCachedMultiplierEntries = parseMultiplierList(source); + if (!cachedMultiplierEntries.equals(newCachedMultiplierEntries)) { + cachedMultiplierEntries = newCachedMultiplierEntries; + } + } + } + return Collections.unmodifiableList(cachedMultiplierEntries); + } + + /** + * 清空缓存,下一次获取时将重新从提供的源解析(或调用方可以重新调用 getter)。 + */ + public static void reload() { + synchronized (CACHE_LOCK) { + cachedBlacklist = null; + cachedMultiplierEntries = null; + } + } +} + + 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..4da3f2c --- /dev/null +++ b/src/main/java/com/extendedae_plus/util/PowerUtils.java @@ -0,0 +1,162 @@ +package com.extendedae_plus.util; + +import com.extendedae_plus.ae.definitions.upgrades.EntitySpeedCardItem; +import com.extendedae_plus.config.ModConfigs; + + +/** + * 用于计算实体加速器的能耗与加速倍率的工具类 + */ +public final class PowerUtils { + private PowerUtils() {} + // ---- 重构后的 API ---- + /** + * 将 card multipliers(按插槽序)计算乘积并应用 cap 规则(见 capForHighestMultiplier) + * @param multipliers iterable of per-card multipliers + * @param maxCards 最多计入的卡数 + * @return 被 cap 约束后的乘积 + */ + public static long computeProductWithCap(Iterable multipliers, int maxCards) { + long product = 1L; + int considered = 0; + int highest = 1; + for (Integer m : multipliers) { + if (m == null) continue; + if (considered >= maxCards) break; + int mult = m.intValue(); + if (mult <= 0) mult = 1; + product *= mult; + highest = Math.max(highest, mult); + considered++; + } + long cap = capForHighestMultiplier(highest); + return Math.min(product, cap); + } + + /** + * 根据最高单卡 multiplier 返回 cap 值 + */ + public static long capForHighestMultiplier(int highestMultiplier) { + if (highestMultiplier >= 16) return 1024L; + if (highestMultiplier >= 8) return 256L; + if (highestMultiplier >= 4) return 64L; + if (highestMultiplier >= 2) return 8L; + return 1L; + } + + /** + * 从菜单对象读取前 maxCards 个加速卡的 multiplier 并计算 product with cap + */ + public static long computeProductWithCapFromMenu(appeng.menu.implementations.UpgradeableMenu menu, int maxCards) { + java.util.List list = new java.util.ArrayList<>(); + int considered = 0; + for (var stack : menu.getUpgrades()) { + if (considered >= maxCards) break; + if (stack != null && !stack.isEmpty() && stack.getItem() instanceof EntitySpeedCardItem) { + int multVal = EntitySpeedCardItem.readMultiplier(stack); + int count = Math.min(stack.getCount(), maxCards - considered); + for (int i = 0; i < count; i++) { + list.add(multVal); + considered++; + if (considered >= maxCards) break; + } + } + } + return computeProductWithCap(list, maxCards); + } + + /** + * 从一组 ItemStack(升级槽)直接计算 product with cap(最多 maxCards) + */ + public static long computeProductWithCapFromStacks(Iterable stacks, int maxCards) { + java.util.List list = new java.util.ArrayList<>(); + int considered = 0; + for (var stack : stacks) { + if (considered >= maxCards) break; + if (stack != null && !stack.isEmpty() && stack.getItem() instanceof EntitySpeedCardItem) { + int multVal = EntitySpeedCardItem.readMultiplier(stack); + int count = Math.min(stack.getCount(), maxCards - considered); + for (int i = 0; i < count; i++) { + list.add(multVal); + considered++; + if (considered >= maxCards) break; + } + } + } + return computeProductWithCap(list, maxCards); + } + + /** + * 计算最终消耗:把 product 转换为等效卡数(log2)并调用 getFinalPower + */ + public static double computeFinalPowerForProduct(long product, int energyCardCount) { + if (product <= 1L) return 0.0; + double base = ModConfigs.ENTITY_TICKER_COST.getAsInt(); + + // 计算以2为底的对数(用于分档与公式) + double log2 = Math.log(product) / Math.log(2.0); + + // 分档:product==2 为一档;4..256 为中档;512..1024 为高档 + double raw; + if (product == 2L) { + // 轻量档:线性小幅增长 + raw = base * 4; + } else if (product <= 256L) { + // 中档:增长放缓(使用 1.5 * log2) + raw = base * Math.pow(2.0, 1.5 * log2) * 2; + } else { + // 高档:增长较快(使用 2.5 * log2) + raw = base * Math.pow(2.0, 2.5 * log2); + } + + double reduction = getReductionPercent(energyCardCount); + return raw * (1.0 - reduction) / 8.0; + } + + /* ----------------- legacy helpers (restored) ----------------- */ + 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); + } + + public static double getRawPower(int speedCardCount) { + double base = ModConfigs.ENTITY_TICKER_COST.getAsInt(); + return base * getGrowthFactor(speedCardCount); + } + + 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)); + } + + public static double getFinalPower(int speedCardCount, int energyCardCount) { + double raw = getRawPower(speedCardCount); + double reduction = getReductionPercent(energyCardCount); + return raw * (1.0 - reduction) / 4.0; + } + + /** + * 返回能源卡减免后剩余的功耗比率(例如 1 张能源卡 -> 0.9) + * @param energyCardCount 能源卡数量 + * @return 剩余功耗比率 + */ + public static double getRemainingRatio(int energyCardCount) { + return 1.0 - getReductionPercent(energyCardCount); + } + + /** + * 将剩余功耗比率格式化为百分比字符串(例如 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..e30da1a --- /dev/null +++ b/src/main/resources/assets/ae2/screens/entity_speed_ticker.json @@ -0,0 +1,78 @@ +{ + "$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 + } + }, + "enable": { + "position": { + "top": 20, + "left": 88 + }, + "align": "CENTER" + }, + "speed": { + "position": { + "top": 35, + "left": 88 + }, + "align": "CENTER" + }, + "energy": { + "position": { + "top": 50, + "left": 88 + }, + "align": "CENTER" + }, + "power_ratio": { + "position": { + "top": 65, + "left": 88 + }, + "align": "CENTER" + } + , + "multiplier": { + "position": { + "top": 80, + "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/zh_cn.json b/src/main/resources/assets/extendedae_plus/lang/zh_cn.json index 2fa9a07..1699f08 100644 --- a/src/main/resources/assets/extendedae_plus/lang/zh_cn.json +++ b/src/main/resources/assets/extendedae_plus/lang/zh_cn.json @@ -28,6 +28,25 @@ "item.extendedae_plus.64x_crafting_accelerator": "64x并行处理单元", "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.enable": "§c§l机器已被禁用", + "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", + "screen.extendedae_plus.entity_speed_ticker.multiplier": "额外消耗倍率: %s", + + "item.extendedae_plus.entity_speed_ticker.tip.requirement": "需要放入实体加速卡以启用加速", + "item.extendedae_plus.entity_speed_ticker.tip.max": "最高可达 1024x 加速", + "item.extendedae_plus.entity_speed_ticker.tip.energy": "加速将消耗 AE 网络能量,网络能量不足时无法加速", + + "item.extendedae_plus.entity_speed_card": "实体加速卡", + "item.extendedae_plus.entity_speed_card.x2": "实体加速卡 (x2)", + "item.extendedae_plus.entity_speed_card.x4": "实体加速卡 (x4)", + "item.extendedae_plus.entity_speed_card.x8": "实体加速卡 (x8)", + "item.extendedae_plus.entity_speed_card.x16": "实体加速卡 (x16)", + "tooltip.extendedae_plus.entity_speed_card.multiplier": "乘数: %s", + "tooltip.extendedae_plus.entity_speed_card.max": "最大生效: %s 倍", "item.extendedae_plus.infinity_biginteger_cell": "§4吞§c噬§6万§e籁§a的§b寂§d静", "tooltip.extendedae_plus.infinity_biginteger_cell.summon1": "§6九重献祭, 终得虚空回响§r——觐见§8虚空之主Iava§r, 赐汝此物", "tooltip.extendedae_plus.infinity_biginteger_cell.summon2": "§b——§4方§d寸§c之§e间§a, §6自§b有§5千§9寰", @@ -62,6 +81,12 @@ "extendedae_plus.configuration.wirelessMaxRange_with_range": "无线最大距离 (1-4096)", "extendedae_plus.configuration.wirelessCrossDimEnable": "无线收发器允许跨维度连接", + "extendedae_plus.configuration.entitySpeedTicker": "实体加速器", + "extendedae_plus.configuration.entityTickerCost": "实体加速器能量消耗基础值", + "extendedae_plus.configuration.entityTickerBlackList": "实体加速器黑名单", + "extendedae_plus.configuration.entityTickerMultipliers": "实体加速器额外消耗倍率", + "extendedae_pluscraftingPauseThreshold": "AE合成计算暂停检查阈值", + "extendedae_plus.configuration.state_on": "开", "extendedae_plus.configuration.state_off": "关", "block.extendedae_plus.network_pattern_controller": "样板供应器状态控制器", diff --git a/src/main/resources/assets/extendedae_plus/models/item/entity_speed_card.json b/src/main/resources/assets/extendedae_plus/models/item/entity_speed_card.json new file mode 100644 index 0000000..b33694f --- /dev/null +++ b/src/main/resources/assets/extendedae_plus/models/item/entity_speed_card.json @@ -0,0 +1,14 @@ +{ + "parent": "item/generated", + "overrides": [ + { "predicate": { "extendedae_plus:mult": 2.0 }, "model": "extendedae_plus:item/entity_speed_card_x2" }, + { "predicate": { "extendedae_plus:mult": 4.0 }, "model": "extendedae_plus:item/entity_speed_card_x4" }, + { "predicate": { "extendedae_plus:mult": 8.0 }, "model": "extendedae_plus:item/entity_speed_card_x8" }, + { "predicate": { "extendedae_plus:mult": 16.0 }, "model": "extendedae_plus:item/entity_speed_card_x16" } + ], + "textures": { + "layer0": "extendedae_plus:item/entity_speed_card_x2" + } +} + + diff --git a/src/main/resources/assets/extendedae_plus/models/item/entity_speed_card_x16.json b/src/main/resources/assets/extendedae_plus/models/item/entity_speed_card_x16.json new file mode 100644 index 0000000..b1cd950 --- /dev/null +++ b/src/main/resources/assets/extendedae_plus/models/item/entity_speed_card_x16.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { "layer0": "extendedae_plus:item/entity_speed_card_x16" } +} + + diff --git a/src/main/resources/assets/extendedae_plus/models/item/entity_speed_card_x2.json b/src/main/resources/assets/extendedae_plus/models/item/entity_speed_card_x2.json new file mode 100644 index 0000000..1107187 --- /dev/null +++ b/src/main/resources/assets/extendedae_plus/models/item/entity_speed_card_x2.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { "layer0": "extendedae_plus:item/entity_speed_card_x2" } +} + + diff --git a/src/main/resources/assets/extendedae_plus/models/item/entity_speed_card_x4.json b/src/main/resources/assets/extendedae_plus/models/item/entity_speed_card_x4.json new file mode 100644 index 0000000..dc0dfcf --- /dev/null +++ b/src/main/resources/assets/extendedae_plus/models/item/entity_speed_card_x4.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { "layer0": "extendedae_plus:item/entity_speed_card_x4" } +} + + diff --git a/src/main/resources/assets/extendedae_plus/models/item/entity_speed_card_x8.json b/src/main/resources/assets/extendedae_plus/models/item/entity_speed_card_x8.json new file mode 100644 index 0000000..0b75b61 --- /dev/null +++ b/src/main/resources/assets/extendedae_plus/models/item/entity_speed_card_x8.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { "layer0": "extendedae_plus:item/entity_speed_card_x8" } +} + + 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..51789c2 --- /dev/null +++ b/src/main/resources/assets/extendedae_plus/models/item/entity_speed_ticker.json @@ -0,0 +1,7 @@ +{ + "parent": "ae2:item/cable_interface", + "textures": { + "front": "extendedae_plus:part/entity_speed_ticker_font", + "back": "extendedae_plus:part/entity_speed_ticker_back" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/extendedae_plus/models/part/entity_speed_ticker_has_channel.json b/src/main/resources/assets/extendedae_plus/models/part/entity_speed_ticker_has_channel.json new file mode 100644 index 0000000..22f76cb --- /dev/null +++ b/src/main/resources/assets/extendedae_plus/models/part/entity_speed_ticker_has_channel.json @@ -0,0 +1,3 @@ +{ + "parent": "ae2:part/interface_has_channel" +} diff --git a/src/main/resources/assets/extendedae_plus/models/part/entity_speed_ticker_off.json b/src/main/resources/assets/extendedae_plus/models/part/entity_speed_ticker_off.json new file mode 100644 index 0000000..a31e532 --- /dev/null +++ b/src/main/resources/assets/extendedae_plus/models/part/entity_speed_ticker_off.json @@ -0,0 +1,3 @@ +{ + "parent": "ae2:part/interface_off" +} diff --git a/src/main/resources/assets/extendedae_plus/models/part/entity_speed_ticker_on.json b/src/main/resources/assets/extendedae_plus/models/part/entity_speed_ticker_on.json new file mode 100644 index 0000000..9cfa323 --- /dev/null +++ b/src/main/resources/assets/extendedae_plus/models/part/entity_speed_ticker_on.json @@ -0,0 +1,3 @@ +{ + "parent": "ae2:part/interface_on" +} 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..df7a2d4 --- /dev/null +++ b/src/main/resources/assets/extendedae_plus/models/part/entity_speed_ticker_part.json @@ -0,0 +1,9 @@ +{ + "parent": "ae2:part/interface_base", + "textures": { + "front": "extendedae_plus:part/entity_speed_ticker_font", + "sides": "extendedae_plus:part/entity_speed_ticker_sides", + "back": "extendedae_plus:part/entity_speed_ticker_back", + "particle": "extendedae_plus:part/entity_speed_ticker_back" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x16.png b/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x16.png new file mode 100644 index 0000000..adfcc54 Binary files /dev/null and b/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x16.png differ diff --git a/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x16.png.mcmeta b/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x16.png.mcmeta new file mode 100644 index 0000000..c488c5a --- /dev/null +++ b/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x16.png.mcmeta @@ -0,0 +1,15 @@ +{ + "animation": { + "interpolate": true, + "frames": [ + { + "index": 0, + "time": 32 + }, + { + "index": 1, + "time": 16 + } + ] + } +} \ No newline at end of file diff --git a/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x2.png b/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x2.png new file mode 100644 index 0000000..043aa02 Binary files /dev/null and b/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x2.png differ diff --git a/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x2.png.mcmeta b/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x2.png.mcmeta new file mode 100644 index 0000000..c488c5a --- /dev/null +++ b/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x2.png.mcmeta @@ -0,0 +1,15 @@ +{ + "animation": { + "interpolate": true, + "frames": [ + { + "index": 0, + "time": 32 + }, + { + "index": 1, + "time": 16 + } + ] + } +} \ No newline at end of file diff --git a/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x4.png b/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x4.png new file mode 100644 index 0000000..ab8775f Binary files /dev/null and b/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x4.png differ diff --git a/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x4.png.mcmeta b/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x4.png.mcmeta new file mode 100644 index 0000000..c488c5a --- /dev/null +++ b/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x4.png.mcmeta @@ -0,0 +1,15 @@ +{ + "animation": { + "interpolate": true, + "frames": [ + { + "index": 0, + "time": 32 + }, + { + "index": 1, + "time": 16 + } + ] + } +} \ No newline at end of file diff --git a/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x8.png b/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x8.png new file mode 100644 index 0000000..dbfdae5 Binary files /dev/null and b/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x8.png differ diff --git a/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x8.png.mcmeta b/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x8.png.mcmeta new file mode 100644 index 0000000..c488c5a --- /dev/null +++ b/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x8.png.mcmeta @@ -0,0 +1,15 @@ +{ + "animation": { + "interpolate": true, + "frames": [ + { + "index": 0, + "time": 32 + }, + { + "index": 1, + "time": 16 + } + ] + } +} \ No newline at end of file diff --git a/src/main/resources/assets/extendedae_plus/textures/part/entity_speed_ticker_back.png b/src/main/resources/assets/extendedae_plus/textures/part/entity_speed_ticker_back.png new file mode 100644 index 0000000..d74b5fe Binary files /dev/null and b/src/main/resources/assets/extendedae_plus/textures/part/entity_speed_ticker_back.png differ diff --git a/src/main/resources/assets/extendedae_plus/textures/part/entity_speed_ticker_font.png b/src/main/resources/assets/extendedae_plus/textures/part/entity_speed_ticker_font.png new file mode 100644 index 0000000..9b32c45 Binary files /dev/null and b/src/main/resources/assets/extendedae_plus/textures/part/entity_speed_ticker_font.png differ diff --git a/src/main/resources/assets/extendedae_plus/textures/part/entity_speed_ticker_sides.png b/src/main/resources/assets/extendedae_plus/textures/part/entity_speed_ticker_sides.png new file mode 100644 index 0000000..5607b0e Binary files /dev/null and b/src/main/resources/assets/extendedae_plus/textures/part/entity_speed_ticker_sides.png differ diff --git a/src/main/resources/data/extendedae_plus/recipe/entity_speed_card_x16.json b/src/main/resources/data/extendedae_plus/recipe/entity_speed_card_x16.json new file mode 100644 index 0000000..821378b --- /dev/null +++ b/src/main/resources/data/extendedae_plus/recipe/entity_speed_card_x16.json @@ -0,0 +1,37 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "SAS", + "QXQ", + "SBS" + ], + "key": { + "S": { + "type": "forge:partial_nbt", + "item": "extendedae_plus:entity_speed_card", + "nbt": { + "EAS:mult": 8 + } + }, + "A": { + "item": "minecraft:nether_star" + }, + "B": { + "item": "minecraft:beacon" + }, + "Q": { + "item": "ae2:spatial_cell_component_128" + }, + "X": { + "item": "minecraft:dragon_egg" + } + }, + "result": { + "type": "forge:partial_nbt", + "item": "extendedae_plus:entity_speed_card", + "count": 1, + "nbt": { + "EAS:mult": 16 + } + } +} \ No newline at end of file diff --git a/src/main/resources/data/extendedae_plus/recipe/entity_speed_card_x2.json b/src/main/resources/data/extendedae_plus/recipe/entity_speed_card_x2.json new file mode 100644 index 0000000..6c4466a --- /dev/null +++ b/src/main/resources/data/extendedae_plus/recipe/entity_speed_card_x2.json @@ -0,0 +1,30 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "SBS", + "QXQ", + "SBS" + ], + "key": { + "S": { + "item": "ae2:speed_card" + }, + "B": { + "item": "extendedae_plus:64x_crafting_accelerator" + }, + "Q": { + "item": "ae2:spatial_cell_component_2" + }, + "X": { + "item": "ae2:cell_component_256k" + } + }, + "result": { + "type": "forge:partial_nbt", + "item": "extendedae_plus:entity_speed_card", + "count": 1, + "nbt": { + "EAS:mult": 2 + } + } +} \ No newline at end of file diff --git a/src/main/resources/data/extendedae_plus/recipe/entity_speed_card_x4.json b/src/main/resources/data/extendedae_plus/recipe/entity_speed_card_x4.json new file mode 100644 index 0000000..91defa0 --- /dev/null +++ b/src/main/resources/data/extendedae_plus/recipe/entity_speed_card_x4.json @@ -0,0 +1,34 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "SBS", + "QXQ", + "SBS" + ], + "key": { + "S": { + "type": "forge:partial_nbt", + "item": "extendedae_plus:entity_speed_card", + "nbt": { + "EAS:mult": 2 + } + }, + "B": { + "item": "extendedae_plus:256x_crafting_accelerator" + }, + "Q": { + "item": "ae2:spatial_cell_component_16" + }, + "X": { + "item": "ae2:dense_energy_cell" + } + }, + "result": { + "type": "forge:partial_nbt", + "item": "extendedae_plus:entity_speed_card", + "count": 1, + "nbt": { + "EAS:mult": 4 + } + } +} \ No newline at end of file diff --git a/src/main/resources/data/extendedae_plus/recipe/entity_speed_card_x8.json b/src/main/resources/data/extendedae_plus/recipe/entity_speed_card_x8.json new file mode 100644 index 0000000..73384f0 --- /dev/null +++ b/src/main/resources/data/extendedae_plus/recipe/entity_speed_card_x8.json @@ -0,0 +1,34 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "SBS", + "QXQ", + "SBS" + ], + "key": { + "S": { + "type": "forge:partial_nbt", + "item": "extendedae_plus:entity_speed_card", + "nbt": { + "EAS:mult": 4 + } + }, + "B": { + "item": "extendedae_plus:1024x_crafting_accelerator" + }, + "Q": { + "item": "ae2:spatial_cell_component_128" + }, + "X": { + "item": "minecraft:nether_star" + } + }, + "result": { + "type": "forge:partial_nbt", + "item": "extendedae_plus:entity_speed_card", + "count": 1, + "nbt": { + "EAS:mult": 8 + } + } +} \ No newline at end of file diff --git a/src/main/resources/data/extendedae_plus/recipe/entity_speed_ticker.json b/src/main/resources/data/extendedae_plus/recipe/entity_speed_ticker.json new file mode 100644 index 0000000..076d943 --- /dev/null +++ b/src/main/resources/data/extendedae_plus/recipe/entity_speed_ticker.json @@ -0,0 +1,33 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "SZS", + "QXQ", + "SIS" + ], + "key": { + "S": { + "type": "forge:partial_nbt", + "item": "extendedae_plus:entity_speed_card", + "nbt": { + "EAS:mult": 2 + } + }, + "Z": { + "item": "ae2:dense_energy_cell" + }, + "Q": { + "item": "ae2:singularity" + }, + "X": { + "item": "minecraft:nether_star" + }, + "I": { + "item": "expatternprovider:ex_io_port" + } + }, + "result": { + "item": "extendedae_plus:entity_speed_ticker", + "count": 1 + } +} \ No newline at end of file