diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/items/MemoryCardItemMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/items/MemoryCardItemMixin.java index 7add032..f341f62 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/items/MemoryCardItemMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/items/MemoryCardItemMixin.java @@ -14,6 +14,7 @@ import net.minecraft.nbt.ListTag; import net.minecraft.nbt.Tag; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.entity.BlockEntity; import org.spongepowered.asm.mixin.Mixin; @@ -23,6 +24,8 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import java.util.ArrayList; +import java.util.IdentityHashMap; +import java.util.List; @Mixin(value = MemoryCardItem.class, remap = false) public class MemoryCardItemMixin { @@ -46,7 +49,6 @@ public class MemoryCardItemMixin { if (upgradeStack.getItem().equals(ModItems.ENTITY_SPEED_CARD.get())) { CompoundTag stackTag = new CompoundTag(); - stackTag.putInt("Slot", i); upgradeStack.save(stackTag); entitySpeedCards.add(stackTag); } else { @@ -78,6 +80,8 @@ public class MemoryCardItemMixin { CompoundTag desiredUpgradesTag = input.getCompound("upgrades"); InternalInventory upgrades = upgradeableObject.getUpgrades(); + var desiredUpgrades = new IdentityHashMap(); + var desiredEntitySpeedCards = new ArrayList(); // 收集背包和网络工具作为升级卡来源 var upgradeSources = new ArrayList(); @@ -87,77 +91,21 @@ public class MemoryCardItemMixin { upgradeSources.add(networkTool.getInventory()); } - // 清空所有槽位中的 EntitySpeedCardItem - for (int i = 0; i < upgrades.size(); i++) { - ItemStack stack = upgrades.getStackInSlot(i); - if (!stack.isEmpty() && stack.getItem().equals(ModItems.ENTITY_SPEED_CARD.get())) { - ItemStack removed = upgrades.extractItem(i, stack.getCount(), false); - for (var source : upgradeSources) { - if (!removed.isEmpty()) { - removed = source.addItems(removed); - } - } - if (!removed.isEmpty()) { - player.drop(removed, false); - } - } - } - - // 恢复 EntitySpeedCardItem if (desiredUpgradesTag.contains("entity_speed_cards", Tag.TAG_LIST)) { ListTag entitySpeedCards = desiredUpgradesTag.getList("entity_speed_cards", Tag.TAG_COMPOUND); for (int i = 0; i < entitySpeedCards.size(); i++) { - CompoundTag stackTag = entitySpeedCards.getCompound(i); - ItemStack desiredStack = ItemStack.of(stackTag); - int slot = stackTag.contains("Slot") ? stackTag.getInt("Slot") : i; - - if (player.getAbilities().instabuild) { - // 创造模式:直接生成 - if (slot >= 0 && slot < upgrades.size()) { - upgrades.setItemDirect(slot, desiredStack); - } else { - upgrades.addItems(desiredStack); - } - } else { - // 非创造模式:从背包或网络工具提取 - int missingAmount = desiredStack.getCount(); - ItemStack extracted = ItemStack.EMPTY; - - for (var source : upgradeSources) { - ItemStack potential = new ItemStack(desiredStack.getItem(), missingAmount); - if (desiredStack.hasTag()) { - potential.setTag(desiredStack.getTag().copy()); - } - ItemStack cards = source.removeItems(missingAmount, potential, null); - if (!cards.isEmpty()) { - ItemStack overflow = upgrades.addItems(cards); - if (!overflow.isEmpty()) { - player.getInventory().placeItemBackInInventory(overflow); - } - missingAmount -= cards.getCount(); - extracted = cards; - } - if (missingAmount <= 0) break; - } - - if (missingAmount > 0 && !player.level().isClientSide()) { - player.displayClientMessage( - PlayerMessages.MissingUpgrades.text(desiredStack.getItem().getDescription(), missingAmount), - true - ); - } else if (!extracted.isEmpty()) { - if (slot >= 0 && slot < upgrades.size()) { - upgrades.setItemDirect(slot, extracted); - } else { - upgrades.addItems(extracted); - } - } + ItemStack desiredStack = ItemStack.of(entitySpeedCards.getCompound(i)); + if (!desiredStack.isEmpty()) { + desiredEntitySpeedCards.add(desiredStack); } } } - // 恢复其他升级卡(按 AE2 原逻辑) for (String key : desiredUpgradesTag.getAllKeys()) { + if ("entity_speed_cards".equals(key)) { + continue; + } + ResourceLocation id; try { id = new ResourceLocation(key); @@ -166,58 +114,249 @@ public class MemoryCardItemMixin { } var item = BuiltInRegistries.ITEM.getOptional(id).orElse(null); - if (item == null || item.equals(ModItems.ENTITY_SPEED_CARD.get())) continue; + if (item == null || item.equals(ModItems.ENTITY_SPEED_CARD.get())) { + continue; + } int desiredCount = desiredUpgradesTag.getInt(key); if (desiredCount > 0) { - if (player.getAbilities().instabuild) { - // 创造模式:直接生成 - ItemStack stack = new ItemStack(item, desiredCount); - upgrades.addItems(stack); - } else { - // 非创造模式:从背包或网络工具提取 - int missingAmount = desiredCount; - ItemStack potential = new ItemStack(item, missingAmount); - ItemStack overflow = upgrades.addItems(potential, true); - if (!overflow.isEmpty()) { - missingAmount -= overflow.getCount(); - } + desiredUpgrades.put(item, desiredCount); + } + } - for (var source : upgradeSources) { - ItemStack cards = source.removeItems(missingAmount, potential, null); - if (!cards.isEmpty()) { - overflow = upgrades.addItems(cards); - if (!overflow.isEmpty()) { - player.getInventory().placeItemBackInInventory(overflow); - } - missingAmount -= cards.getCount(); - } - if (missingAmount <= 0) break; - } + if (player.getAbilities().instabuild) { + for (int i = 0; i < upgrades.size(); i++) { + upgrades.setItemDirect(i, ItemStack.EMPTY); + } + for (var desiredStack : desiredEntitySpeedCards) { + upgrades.addItems(desiredStack.copy()); + } + for (var entry : desiredUpgrades.entrySet()) { + upgrades.addItems(new ItemStack(entry.getKey(), entry.getValue())); + } + markUpgradesChanged(upgradeableObject); + cir.setReturnValue(true); + return; + } - if (missingAmount > 0 && !player.level().isClientSide()) { - player.displayClientMessage( - PlayerMessages.MissingUpgrades.text(item.getDescription(), missingAmount), - true - ); + // 先精确移除多余的实体加速卡(按完整 NBT 匹配) + var desiredEntityStacks = new ArrayList(); + var desiredEntityCounts = new ArrayList(); + for (var desiredStack : desiredEntitySpeedCards) { + mergeExactCount(desiredEntityStacks, desiredEntityCounts, desiredStack, desiredStack.getCount()); + } + + var currentEntityStacks = new ArrayList(); + var currentEntityCounts = new ArrayList(); + collectInstalledEntitySpeedCards(upgrades, currentEntityStacks, currentEntityCounts); + for (int i = 0; i < upgrades.size(); i++) { + ItemStack stack = upgrades.getStackInSlot(i); + if (stack.isEmpty() || !stack.getItem().equals(ModItems.ENTITY_SPEED_CARD.get())) { + continue; + } + + int installed = getExactCount(currentEntityStacks, currentEntityCounts, stack); + int desired = getExactCount(desiredEntityStacks, desiredEntityCounts, stack); + int excess = installed - desired; + if (excess <= 0) { + continue; + } + + ItemStack removed = upgrades.extractItem(i, Math.min(excess, stack.getCount()), false); + if (removed.isEmpty()) { + continue; + } + + decrementExactCount(currentEntityStacks, currentEntityCounts, stack, removed.getCount()); + for (var source : upgradeSources) { + if (!removed.isEmpty()) { + removed = source.addItems(removed); + } + } + if (!removed.isEmpty()) { + player.drop(removed, false); + } + } + + // 按 AE2 原逻辑移除多余的其他升级卡 + for (int i = 0; i < upgrades.size(); i++) { + var current = upgrades.getStackInSlot(i); + if (current.isEmpty() || current.getItem().equals(ModItems.ENTITY_SPEED_CARD.get())) { + continue; + } + + var desiredCount = desiredUpgrades.getOrDefault(current.getItem(), 0); + var totalInstalled = upgradeableObject.getInstalledUpgrades(current.getItem()); + var toRemove = totalInstalled - desiredCount; + if (toRemove > 0) { + var removed = upgrades.extractItem(i, toRemove, false); + + for (var upgradeSource : upgradeSources) { + if (!removed.isEmpty()) { + removed = upgradeSource.addItems(removed); } } + if (!removed.isEmpty()) { + player.drop(removed, false); + } } } - // 标记保存并通知升级变化 - if (upgradeableObject instanceof EntitySpeedTickerPart speedTickerPart) { - BlockEntity be = speedTickerPart.getBlockEntity(); - if (be != null) { - be.setChanged(); + // 精确补回缺失的实体加速卡(按完整 NBT 匹配) + var afterRemovalEntityStacks = new ArrayList(); + var afterRemovalEntityCounts = new ArrayList(); + collectInstalledEntitySpeedCards(upgrades, afterRemovalEntityStacks, afterRemovalEntityCounts); + for (int i = 0; i < desiredEntityStacks.size(); i++) { + var desiredStack = desiredEntityStacks.get(i); + int desiredCount = desiredEntityCounts.get(i); + int missingAmount = desiredCount - getExactCount(afterRemovalEntityStacks, afterRemovalEntityCounts, desiredStack); + if (missingAmount <= 0) { + continue; + } + + var potential = desiredStack.copy(); + potential.setCount(missingAmount); + var overflow = upgrades.addItems(potential, true); + if (!overflow.isEmpty()) { + missingAmount -= overflow.getCount(); + } + if (missingAmount <= 0) { + continue; + } + + for (var source : upgradeSources) { + var preciseRequest = desiredStack.copy(); + preciseRequest.setCount(missingAmount); + var cards = source.removeItems(missingAmount, preciseRequest, null); + if (!cards.isEmpty()) { + overflow = upgrades.addItems(cards); + if (!overflow.isEmpty()) { + player.getInventory().placeItemBackInInventory(overflow); + } + missingAmount -= cards.getCount(); + } + if (missingAmount <= 0) { + break; + } + } + + if (missingAmount > 0 && !player.level().isClientSide()) { + player.displayClientMessage( + PlayerMessages.MissingUpgrades.text(desiredStack.getHoverName(), missingAmount), + true + ); } - speedTickerPart.upgradesChanged(); } + // 恢复其他升级卡(按 AE2 原逻辑) + for (var entry : desiredUpgrades.entrySet()) { + int missingAmount = entry.getValue() - upgradeableObject.getInstalledUpgrades(entry.getKey()); + if (missingAmount > 0) { + ItemStack potential = new ItemStack(entry.getKey(), missingAmount); + ItemStack overflow = upgrades.addItems(potential, true); + if (!overflow.isEmpty()) { + missingAmount -= overflow.getCount(); + } + + for (var source : upgradeSources) { + ItemStack cards = source.removeItems(missingAmount, potential, null); + if (!cards.isEmpty()) { + overflow = upgrades.addItems(cards); + if (!overflow.isEmpty()) { + player.getInventory().placeItemBackInInventory(overflow); + } + missingAmount -= cards.getCount(); + } + if (missingAmount <= 0) break; + } + + if (missingAmount > 0 && !player.level().isClientSide()) { + player.displayClientMessage( + PlayerMessages.MissingUpgrades.text(entry.getKey().getDescription(), missingAmount), + true + ); + } + } + } + + markUpgradesChanged(upgradeableObject); cir.setReturnValue(true); } catch (Exception e) { e.printStackTrace(); cir.setReturnValue(false); } } -} \ No newline at end of file + + private static void markUpgradesChanged(IUpgradeableObject upgradeableObject) { + if (upgradeableObject instanceof EntitySpeedTickerPart speedTickerPart) { + BlockEntity be = speedTickerPart.getBlockEntity(); + if (be != null) { + be.setChanged(); + } + speedTickerPart.upgradesChanged(); + } + } + + private static void collectInstalledEntitySpeedCards(InternalInventory upgrades, List stacks, + List counts) { + for (int i = 0; i < upgrades.size(); i++) { + var stack = upgrades.getStackInSlot(i); + if (!stack.isEmpty() && stack.getItem().equals(ModItems.ENTITY_SPEED_CARD.get())) { + mergeExactCount(stacks, counts, stack, stack.getCount()); + } + } + } + + private static void mergeExactCount(List stacks, List counts, ItemStack stack, int amount) { + if (stack.isEmpty() || amount <= 0) { + return; + } + + for (int i = 0; i < stacks.size(); i++) { + if (sameExactStack(stacks.get(i), stack)) { + counts.set(i, counts.get(i) + amount); + return; + } + } + stacks.add(copySingle(stack)); + counts.add(amount); + } + + private static int getExactCount(List stacks, List counts, ItemStack stack) { + for (int i = 0; i < stacks.size(); i++) { + if (sameExactStack(stacks.get(i), stack)) { + return counts.get(i); + } + } + return 0; + } + + private static void decrementExactCount(List stacks, List counts, ItemStack stack, int amount) { + if (amount <= 0) { + return; + } + + for (int i = 0; i < stacks.size(); i++) { + if (sameExactStack(stacks.get(i), stack)) { + int remaining = counts.get(i) - amount; + if (remaining <= 0) { + stacks.remove(i); + counts.remove(i); + } else { + counts.set(i, remaining); + } + return; + } + } + } + + private static boolean sameExactStack(ItemStack a, ItemStack b) { + return ItemStack.isSameItemSameTags(a, b); + } + + private static ItemStack copySingle(ItemStack stack) { + var copy = stack.copy(); + copy.setCount(1); + return copy; + } +}