新增虚拟合成卡(暂无材质&&配方)

This commit is contained in:
GaLi 2025-11-24 10:35:59 +08:00
parent d06f30cfe5
commit 0b5fb458c0
10 changed files with 202 additions and 24 deletions

View File

@ -9,6 +9,7 @@ import com.extendedae_plus.items.EntitySpeedTickerPartItem;
import com.extendedae_plus.items.InfinityBigIntegerCellItem;
import com.extendedae_plus.items.materials.ChannelCardItem;
import com.extendedae_plus.items.materials.EntitySpeedCardItem;
import com.extendedae_plus.items.materials.VirtualCraftingCardItem;
import com.extendedae_plus.util.ModCheckUtils;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.Item;
@ -84,6 +85,12 @@ public final class ModItems {
() -> new ChannelCardItem(new Item.Properties())
);
// 虚拟合成卡
public static final RegistryObject<VirtualCraftingCardItem> VIRTUAL_CRAFTING_CARD = ITEMS.register(
"virtual_crafting_card",
() -> new VirtualCraftingCardItem(new Item.Properties())
);
public static final RegistryObject<BasicCoreItem> BASIC_CORE = ITEMS.register(
"basic_core",
() -> new BasicCoreItem(new Item.Properties())

View File

@ -25,14 +25,18 @@ public final class UpgradeCards {
Upgrades.add(ModItems.CHANNEL_CARD.get(), AEBlocks.INTERFACE, 1, interfaceGroup);
Upgrades.add(ModItems.CHANNEL_CARD.get(), AEParts.INTERFACE, 1, interfaceGroup);
// 新增样板供应器方块与部件支持频道卡每台最多 1
// 新增样板供应器方块与部件支持频道卡虚拟合成卡每台最多 1
String patternProviderGroup = "group.pattern_provider.name";
Upgrades.add(ModItems.CHANNEL_CARD.get(), AEBlocks.PATTERN_PROVIDER, 1, patternProviderGroup);
Upgrades.add(ModItems.CHANNEL_CARD.get(), AEParts.PATTERN_PROVIDER, 1, patternProviderGroup);
Upgrades.add(ModItems.VIRTUAL_CRAFTING_CARD.get(), AEBlocks.PATTERN_PROVIDER, 1, patternProviderGroup);
Upgrades.add(ModItems.VIRTUAL_CRAFTING_CARD.get(), AEParts.PATTERN_PROVIDER, 1, patternProviderGroup);
// ExtendedAE 的扩展样板供应器方块与部件
Upgrades.add(ModItems.CHANNEL_CARD.get(),EX_PATTERN_PROVIDER, 1, patternProviderGroup);
Upgrades.add(ModItems.CHANNEL_CARD.get(),EX_PATTERN_PROVIDER_PART, 1, patternProviderGroup);
Upgrades.add(ModItems.VIRTUAL_CRAFTING_CARD.get(),EX_PATTERN_PROVIDER, 1, patternProviderGroup);
Upgrades.add(ModItems.VIRTUAL_CRAFTING_CARD.get(),EX_PATTERN_PROVIDER_PART, 1, patternProviderGroup);
//EAE 的扩展接口与超大接口方块与部件支持频道卡
Upgrades.add(ModItems.CHANNEL_CARD.get(), EX_INTERFACE, 1, interfaceGroup);

View File

@ -0,0 +1,31 @@
package com.extendedae_plus.items.materials;
import appeng.items.materials.UpgradeCardItem;
import net.minecraft.network.chat.Component;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.TooltipFlag;
import net.minecraft.world.level.Level;
import org.jetbrains.annotations.Nullable;
import java.util.List;
/**
* 虚拟合成卡安装在样板供应器中以触发自动完成逻辑
*/
public class VirtualCraftingCardItem extends UpgradeCardItem {
public VirtualCraftingCardItem(Item.Properties properties) {
super(properties);
}
@Override
public void appendHoverText(ItemStack stack,
@Nullable Level level,
List<Component> lines,
TooltipFlag flag) {
super.appendHoverText(stack, level, lines, flag);
lines.add(Component.translatable("item.extendedae_plus.virtual_crafting_card.tooltip_main"));
lines.add(Component.translatable("item.extendedae_plus.virtual_crafting_card.tooltip_detail"));
}
}

View File

@ -0,0 +1,13 @@
package com.extendedae_plus.mixin.ae2.accessor;
import appeng.crafting.execution.CraftingCpuLogic;
import appeng.crafting.execution.ExecutingCraftingJob;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(value = CraftingCpuLogic.class, remap = false)
public interface CraftingCpuLogicAccessor {
@Accessor("job")
ExecutingCraftingJob extendedae_plus$getJob();
}

View File

@ -0,0 +1,15 @@
package com.extendedae_plus.mixin.ae2.accessor;
import appeng.api.crafting.IPatternDetails;
import appeng.crafting.execution.ExecutingCraftingJob;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import java.util.Map;
@Mixin(value = ExecutingCraftingJob.class, remap = false)
public interface ExecutingCraftingJobAccessor {
@Accessor("tasks")
Map<IPatternDetails, ExecutingCraftingJobTaskProgressAccessor> extendedae_plus$getTasks();
}

View File

@ -0,0 +1,11 @@
package com.extendedae_plus.mixin.ae2.accessor;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(targets = "appeng.crafting.execution.ExecutingCraftingJob$TaskProgress", remap = false)
public interface ExecutingCraftingJobTaskProgressAccessor {
@Accessor("value")
long extendedae_plus$getValue();
}

View File

@ -1,18 +1,25 @@
package com.extendedae_plus.mixin.ae2.compat;
import appeng.api.crafting.IPatternDetails;
import appeng.api.networking.IGrid;
import appeng.api.networking.IManagedGridNode;
import appeng.api.networking.crafting.ICraftingCPU;
import appeng.api.networking.security.IActionSource;
import appeng.api.stacks.KeyCounter;
import appeng.api.upgrades.IUpgradeInventory;
import appeng.api.upgrades.IUpgradeableObject;
import appeng.api.upgrades.UpgradeInventories;
import appeng.helpers.patternprovider.PatternProviderLogic;
import appeng.helpers.patternprovider.PatternProviderLogicHost;
import appeng.me.cluster.implementations.CraftingCPUCluster;
import com.extendedae_plus.ae.wireless.WirelessSlaveLink;
import com.extendedae_plus.ae.wireless.endpoint.GenericNodeEndpointImpl;
import com.extendedae_plus.api.bridge.IInterfaceWirelessLinkBridge;
import com.extendedae_plus.compat.UpgradeSlotCompat;
import com.extendedae_plus.init.ModItems;
import com.extendedae_plus.items.materials.ChannelCardItem;
import com.extendedae_plus.mixin.ae2.accessor.CraftingCpuLogicAccessor;
import com.extendedae_plus.mixin.ae2.accessor.ExecutingCraftingJobAccessor;
import com.extendedae_plus.util.Logger;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.item.ItemStack;
@ -23,7 +30,9 @@ import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.lang.reflect.Field;
import java.util.List;
/**
@ -64,13 +73,21 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj
@Shadow
private IActionSource actionSource;
@Unique
private boolean eap$compatVirtualCraftingEnabled = false;
@Unique
private static Field eap$compatAppfluxUpgradesField;
@Shadow
public abstract IGrid getGrid();
@Unique
private void eap$compatOnUpgradesChanged() {
try {
this.host.saveChanges();
// 频道卡功能独立于升级槽功能总是处理
eap$compatSyncVirtualCraftingState();
if (UpgradeSlotCompat.shouldEnableChannelCard()) {
// 升级变更重置并尝试初始化频道卡
eap$compatLastChannel = -1;
eap$compatHasInitialized = false;
eap$compatInitializeChannelLink();
@ -79,11 +96,63 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj
Logger.EAP$LOGGER.error("兼容性升级变更处理失败", e);
}
}
@Unique
private void eap$compatSyncVirtualCraftingState() {
boolean hasCard = false;
var inventory = eap$compatGetEffectiveUpgradeInventory();
if (inventory != null) {
for (ItemStack stack : inventory) {
if (!stack.isEmpty() && stack.getItem() == ModItems.VIRTUAL_CRAFTING_CARD.get()) {
hasCard = true;
break;
}
}
}
eap$compatVirtualCraftingEnabled = hasCard;
}
@Unique
private void eap$compatTryVirtualCompletion(IPatternDetails patternDetails) {
if (!eap$compatVirtualCraftingEnabled) {
return;
}
var grid = getGrid();
if (grid == null) {
return;
}
var craftingService = grid.getCraftingService();
if (craftingService == null) {
return;
}
for (ICraftingCPU cpu : craftingService.getCpus()) {
if (!cpu.isBusy()) {
continue;
}
if (cpu instanceof CraftingCPUCluster cluster) {
if (cluster.craftingLogic instanceof CraftingCpuLogicAccessor logicAccessor) {
var job = logicAccessor.extendedae_plus$getJob();
if (job instanceof ExecutingCraftingJobAccessor accessor) {
var tasks = accessor.extendedae_plus$getTasks();
var progress = tasks.get(patternDetails);
if (progress != null && progress.extendedae_plus$getValue() <= 1) {
cluster.cancelJob();
break;
}
}
}
}
}
}
// 监听appflux的升级变化 - 通过注入到appflux的af_$onUpgradesChanged方法
@Inject(method = "af_$onUpgradesChanged", at = @At("TAIL"), remap = false, require = 0)
private void eap$onAppfluxUpgradesChanged(CallbackInfo ci) {
try {
eap$compatSyncVirtualCraftingState();
if (UpgradeSlotCompat.shouldEnableChannelCard()) {
// 升级变更重置并尝试初始化频道卡
eap$compatLastChannel = -1;
@ -136,12 +205,12 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj
try {
if (UpgradeSlotCompat.shouldEnableUpgradeSlots() || UpgradeSlotCompat.shouldEnableChannelCard()) {
this.eap$compatUpgrades.readFromNBT(tag, "compat_upgrades");
// NBT 加载后重置并尝试初始化频道卡
if (UpgradeSlotCompat.shouldEnableChannelCard()) {
eap$compatLastChannel = -1;
eap$compatHasInitialized = false;
eap$compatInitializeChannelLink();
}
eap$compatSyncVirtualCraftingState();
}
} catch (Exception e) {
Logger.EAP$LOGGER.error("兼容性升级加载失败", e);
@ -168,6 +237,7 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj
try {
if (UpgradeSlotCompat.shouldEnableUpgradeSlots() || UpgradeSlotCompat.shouldEnableChannelCard()) {
this.eap$compatUpgrades.clear();
eap$compatVirtualCraftingEnabled = false;
}
} catch (Exception e) {
Logger.EAP$LOGGER.error("兼容性升级清理失败", e);
@ -177,15 +247,17 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj
@Override
public IUpgradeInventory getUpgrades() {
if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
// 不装appflux时返回我们自己的升级槽
return this.eap$compatUpgrades != null ? this.eap$compatUpgrades : UpgradeInventories.empty();
} else {
// 装了appflux时这个方法不应该被调用因为appflux的Mixin会覆盖它
// 但是为了安全起见返回空的升级槽
return UpgradeInventories.empty();
return eap$compatGetEffectiveUpgradeInventory();
}
}
@Inject(method = "pushPattern", at = @At("HEAD"))
private void eap$compatOnPushPattern(IPatternDetails patternDetails, KeyCounter[] inputHolder, CallbackInfoReturnable<Boolean> cir) {
eap$compatTryVirtualCompletion(patternDetails);
}
@Override
public void eap$updateWirelessLink() {
if (!UpgradeSlotCompat.shouldEnableChannelCard()) {
@ -202,7 +274,7 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj
}
@Unique
public void eap$compatInitializeChannelLink() {
private void eap$compatInitializeChannelLink() {
if (!UpgradeSlotCompat.shouldEnableChannelCard()) {
return;
}
@ -233,21 +305,7 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj
boolean found = false;
// 获取升级槽 - 如果装了appflux则从appflux获取否则从我们自己的获取
IUpgradeInventory upgrades = null;
if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
// 不装appflux时使用我们自己的升级槽
upgrades = this.eap$compatUpgrades;
} else if (UpgradeSlotCompat.shouldEnableChannelCard()) {
// 装了appflux时尝试从PatternProviderLogic获取升级槽
try {
if (this instanceof IUpgradeableObject) {
IUpgradeableObject upgradeableThis = (IUpgradeableObject) this;
upgrades = upgradeableThis.getUpgrades();
}
} catch (Exception e) {
Logger.EAP$LOGGER.error("获取appflux升级槽失败", e);
}
}
IUpgradeInventory upgrades = eap$compatGetEffectiveUpgradeInventory();
if (upgrades != null) {
for (ItemStack stack : upgrades) {
@ -310,6 +368,36 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj
}
}
@Unique
private IUpgradeInventory eap$compatGetEffectiveUpgradeInventory() {
if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
return this.eap$compatUpgrades;
}
if (!UpgradeSlotCompat.shouldEnableChannelCard()) {
return null;
}
if (this.eap$compatUpgrades != null && this.eap$compatUpgrades != UpgradeInventories.empty()) {
return this.eap$compatUpgrades;
}
try {
if (eap$compatAppfluxUpgradesField == null) {
eap$compatAppfluxUpgradesField = PatternProviderLogic.class.getDeclaredField("af_$upgrades");
eap$compatAppfluxUpgradesField.setAccessible(true);
}
Object value = eap$compatAppfluxUpgradesField.get(this);
if (value instanceof IUpgradeInventory inventory) {
return inventory;
}
} catch (Exception e) {
Logger.EAP$LOGGER.error("获取appflux升级槽失败", e);
}
return UpgradeInventories.empty();
}
@Override
public void eap$setClientWirelessState(boolean connected) {
if (UpgradeSlotCompat.shouldEnableChannelCard()) {

View File

@ -152,6 +152,9 @@
"item.extendedae_plus.channel_card.owner.player": "Owner: Player %s",
"item.extendedae_plus.channel_card.owner.bound": "Bound to: %s",
"item.extendedae_plus.channel_card.owner.cleared": "Owner binding cleared",
"item.extendedae_plus.virtual_crafting_card": "Virtual Crafting Card",
"item.extendedae_plus.virtual_crafting_card.tooltip_main": "Auto-completes crafting jobs once the current pattern is about to finish",
"item.extendedae_plus.virtual_crafting_card.tooltip_detail": "Install inside a Pattern Provider upgrade slot",
"extendedae_plus.screen.frequency_input.title": "Set Frequency",
"extendedae_plus.screen.frequency_input.current": "Current Frequency: %s",

View File

@ -152,6 +152,9 @@
"item.extendedae_plus.channel_card.owner.player": "所有者:玩家 %s",
"item.extendedae_plus.channel_card.owner.bound": "已绑定至:%s",
"item.extendedae_plus.channel_card.owner.cleared": "已清除所有者绑定",
"item.extendedae_plus.virtual_crafting_card": "虚拟合成卡",
"item.extendedae_plus.virtual_crafting_card.tooltip_main": "当前样板即将完成时尝试终止剩余合成",
"item.extendedae_plus.virtual_crafting_card.tooltip_detail": "需安装在样板供应器的升级槽内",
"extendedae_plus.screen.frequency_input.title": "设置频率",
"extendedae_plus.screen.frequency_input.current": "当前频率:%s",

View File

@ -50,6 +50,9 @@
"ae2.accessor.MEStorageMenuAccessor",
"ae2.accessor.PatternEncodingTermMenuAccessor",
"ae2.accessor.PatternProviderLogicAccessor",
"ae2.accessor.CraftingCpuLogicAccessor",
"ae2.accessor.ExecutingCraftingJobAccessor",
"ae2.accessor.ExecutingCraftingJobTaskProgressAccessor",
"ae2.accessor.PatternProviderMenuAccessor",
"ae2.autopattern.CraftingCalculationMixin",
"ae2.autopattern.CraftingServiceGetProvidersMixin",