feat: 移植1.20的实体加速器
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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<Component> 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<Component> lines, TooltipFlag advancedTooltips) {
|
||||
super.appendHoverText(stack, context, lines, advancedTooltips);
|
||||
lines.addAll(this.getTooltipLines(stack));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -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<EntitySpeedTickerPart> {
|
||||
public EntitySpeedTickerPartItem(Properties properties) {
|
||||
super(properties, EntitySpeedTickerPart.class, EntitySpeedTickerPart::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendHoverText(ItemStack stack, TooltipContext context, List<Component> 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 网络能量,网络能量不足时无法加速"));
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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<EntitySpeedTickerPart> {
|
||||
@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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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 部件<p>
|
||||
* 该部件可以加速目标方块实体的 tick 速率,消耗 AE 网络能量,并支持加速卡升级<p>
|
||||
* 功能受<a href="https://github.com/GilbertzRivi/crazyae2addons">Crazy AE2 Addons</a>启发
|
||||
*/
|
||||
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 <T> 方块实体类型
|
||||
*/
|
||||
private <T extends BlockEntity> 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<Pattern> compiledBlacklist = ConfigParsingUtils.getCachedBlacklist(ModConfigs.ENTITY_TICKER_BLACK_LIST.get());
|
||||
for (Pattern p : compiledBlacklist) {
|
||||
if (p.matcher(blockId).matches()) return;
|
||||
}
|
||||
|
||||
// 获取该方块实体的 Ticker
|
||||
@SuppressWarnings("unchecked")
|
||||
BlockEntityTicker<T> blockEntityTicker = this.getLevel()
|
||||
.getBlockState(pos)
|
||||
.getTicker(this.getLevel(), (BlockEntityType<T>) 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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<C extends EntitySpeedTickerMenu> extends UpgradeableScreen<C> {
|
||||
private boolean eap$entitySpeedTickerEnabled = false;
|
||||
private SettingToggleButton<YesNo> 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<YesNo> 的外观(原版图标),但自定义悬停描述为“智能阻挡”
|
||||
// 不做本地切换,点击仅发送自定义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<Component> 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)));
|
||||
}
|
||||
}
|
||||
|
|
@ -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<EntitySpeedTickerMenu>::new, "/screens/entity_speed_ticker.json");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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<List<? extends String>> ENTITY_TICKER_BLACK_LIST;
|
||||
public static final ModConfigSpec.ConfigValue<List<? extends String>> 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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Item> INFINITY_BIGINTEGER_CELL_ITEM = ITEMS.register(
|
||||
// "infinity_biginteger_cell", InfinityBigIntegerCellItem::new
|
||||
// );
|
||||
public static final DeferredItem<EntitySpeedTickerPartItem> 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<EntitySpeedCardItem> 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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<?>, MenuType<NetworkPatternControllerMenu>> 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<?>, MenuType<EntitySpeedTickerMenu>> ENTITY_TICKER_MENU =
|
||||
MENUS.register("entity_speed_ticker",
|
||||
() -> MenuTypeBuilder
|
||||
.create(EntitySpeedTickerMenu::new, EntitySpeedTickerPart.class)
|
||||
.build("entity_speed_ticker"));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
17
src/main/java/com/extendedae_plus/init/UpgradeCards.java
Normal file
|
|
@ -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");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -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<ItemStack>() {
|
||||
@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));
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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<ToggleEntityTickerC2SPacket> TYPE = new CustomPacketPayload.Type<>(
|
||||
ResourceLocation.fromNamespaceAndPath(ExtendedAEPlus.MODID, "toggle_entity_ticker"));
|
||||
|
||||
public static final ToggleEntityTickerC2SPacket INSTANCE = new ToggleEntityTickerC2SPacket();
|
||||
|
||||
public static final StreamCodec<FriendlyByteBuf, ToggleEntityTickerC2SPacket> STREAM_CODEC =
|
||||
StreamCodec.unit(INSTANCE);
|
||||
|
||||
private ToggleEntityTickerC2SPacket() {}
|
||||
|
||||
@Override
|
||||
public CustomPacketPayload.Type<? extends CustomPacketPayload> 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();
|
||||
});
|
||||
}
|
||||
}
|
||||
172
src/main/java/com/extendedae_plus/util/ConfigParsingUtils.java
Normal file
|
|
@ -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<Pattern> compilePatterns(List<? extends String> raw) {
|
||||
List<Pattern> 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<MultiplierEntry> parseMultiplierList(List<? extends String> raw) {
|
||||
List<MultiplierEntry> 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<Pattern> cachedBlacklist = null;
|
||||
private static volatile List<MultiplierEntry> cachedMultiplierEntries = null;
|
||||
private static final Object CACHE_LOCK = new Object();
|
||||
|
||||
/**
|
||||
* 获取已解析并缓存的黑名单(线程安全、懒加载)。
|
||||
*/
|
||||
public static List<Pattern> getCachedBlacklist(List<? extends String> source) {
|
||||
if (cachedBlacklist == null) {
|
||||
synchronized (CACHE_LOCK) {
|
||||
if (cachedBlacklist == null) {
|
||||
cachedBlacklist = compilePatterns(source);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
synchronized (CACHE_LOCK) {
|
||||
List<Pattern> newCachedBlackList = compilePatterns(source);
|
||||
if (!cachedBlacklist.equals(newCachedBlackList)) {
|
||||
cachedBlacklist = newCachedBlackList;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Collections.unmodifiableList(cachedBlacklist);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取已解析并缓存的倍率列表(线程安全、懒加载)。
|
||||
*/
|
||||
public static List<MultiplierEntry> getCachedMultiplierEntries(List<? extends String> source) {
|
||||
if (cachedMultiplierEntries == null) {
|
||||
synchronized (CACHE_LOCK) {
|
||||
if (cachedMultiplierEntries == null) {
|
||||
cachedMultiplierEntries = parseMultiplierList(source);
|
||||
}
|
||||
}
|
||||
}else {
|
||||
synchronized (CACHE_LOCK) {
|
||||
List<MultiplierEntry> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
162
src/main/java/com/extendedae_plus/util/PowerUtils.java
Normal file
|
|
@ -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<Integer> 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<Integer> 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<net.minecraft.world.item.ItemStack> stacks, int maxCards) {
|
||||
java.util.List<Integer> 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
src/main/resources/assets/ae2/textures/guis/blank_background.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
|
|
@ -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": "样板供应器状态控制器",
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "item/generated",
|
||||
"textures": { "layer0": "extendedae_plus:item/entity_speed_card_x16" }
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "item/generated",
|
||||
"textures": { "layer0": "extendedae_plus:item/entity_speed_card_x2" }
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "item/generated",
|
||||
"textures": { "layer0": "extendedae_plus:item/entity_speed_card_x4" }
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "item/generated",
|
||||
"textures": { "layer0": "extendedae_plus:item/entity_speed_card_x8" }
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"parent": "ae2:part/interface_has_channel"
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"parent": "ae2:part/interface_off"
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"parent": "ae2:part/interface_on"
|
||||
}
|
||||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"animation": {
|
||||
"interpolate": true,
|
||||
"frames": [
|
||||
{
|
||||
"index": 0,
|
||||
"time": 32
|
||||
},
|
||||
{
|
||||
"index": 1,
|
||||
"time": 16
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"animation": {
|
||||
"interpolate": true,
|
||||
"frames": [
|
||||
{
|
||||
"index": 0,
|
||||
"time": 32
|
||||
},
|
||||
{
|
||||
"index": 1,
|
||||
"time": 16
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"animation": {
|
||||
"interpolate": true,
|
||||
"frames": [
|
||||
{
|
||||
"index": 0,
|
||||
"time": 32
|
||||
},
|
||||
{
|
||||
"index": 1,
|
||||
"time": 16
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"animation": {
|
||||
"interpolate": true,
|
||||
"frames": [
|
||||
{
|
||||
"index": 0,
|
||||
"time": 32
|
||||
},
|
||||
{
|
||||
"index": 1,
|
||||
"time": 16
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||