Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
package net.liukrast.schematicdisplay.clipboard;

import com.simibubi.create.AllBlocks;
import com.simibubi.create.content.equipment.clipboard.ClipboardEntry;
import dev.emi.emi.api.EmiApi;
import dev.emi.emi.api.stack.Comparison;
import dev.emi.emi.api.stack.EmiStack;
import dev.emi.emi.runtime.EmiFavorite;
import dev.emi.emi.runtime.EmiFavorites;
import dev.emi.emi.bom.BoM;
import net.liukrast.schematicdisplay.EMICreateSchematics;
import net.minecraft.client.Minecraft;
import net.minecraft.core.component.DataComponents;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.component.CustomData;

Expand All @@ -14,37 +20,93 @@

public final class ClipboardScreenUtils {
public static final String REAL_COUNT_KEY = "CreateEMISchematicsData";
public static final String VIRTUAL_RECIPE_KEY = "CreateEMISchematicsVirtualRecipe";
private static long emi_create_schematics$recipeCounter = 0;

private ClipboardScreenUtils() {}

public static void load(List<List<ClipboardEntry>> pages) {
List<ClipboardEntry> allEntries = new ArrayList<>();
for(var page : pages)
allEntries.addAll(page);
allEntries.sort(Comparator.comparingInt((ClipboardEntry entry) -> entry.itemAmount).reversed());

var copiedList = new ArrayList<>(EmiFavorites.favorites);

copiedList.stream()
.filter(fav -> {
if(!(fav.getStack() instanceof EmiStack stack)) return false;
var data = stack.getItemStack().getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY);

if(data.isEmpty()) return false;
return data.contains(REAL_COUNT_KEY);
})
.forEach(EmiFavorites::removeFavorite);

for(var entry : allEntries) {
ItemStack stack1 = entry.icon.copy();
stack1.update(DataComponents.CUSTOM_DATA, CustomData.EMPTY, existingData ->
existingData.update(tag -> tag.putLong(REAL_COUNT_KEY, entry.itemAmount))
);
EmiStack emiStack = EmiStack.of(stack1);
emiStack.setAmount(1);
List<EntryAmount> remainingEntries = collectRemainingEntries(pages);
if (remainingEntries.isEmpty()) {
notifyEmptyTree();
return;
}

List<EmiStack> inputs = new ArrayList<>(remainingEntries.size());
for (EntryAmount entry : remainingEntries) {
inputs.add(EmiStack.of(entry.stack.copy()).setAmount(entry.amount));
}

ClipboardTreeRecipe recipe = new ClipboardTreeRecipe(
inputs,
createVirtualOutput(),
nextVirtualRecipeId()
);

BoM.setGoal(recipe);
BoM.craftingMode = true;

Minecraft minecraft = Minecraft.getInstance();
minecraft.setScreen(null);
EmiApi.viewRecipeTree();
}

private static List<EntryAmount> collectRemainingEntries(List<List<ClipboardEntry>> pages) {
List<EntryAmount> mergedEntries = new ArrayList<>();
for (List<ClipboardEntry> page : pages) {
for (ClipboardEntry entry : page) {
if (entry.checked || entry.icon.isEmpty() || entry.itemAmount <= 0) {
continue;
}
mergeEntry(mergedEntries, entry.icon, entry.itemAmount);
}
}
mergedEntries.sort(Comparator.comparingLong((EntryAmount entry) -> entry.amount).reversed());
return mergedEntries;
}

private static void mergeEntry(List<EntryAmount> mergedEntries, ItemStack stack, long amount) {
for (EntryAmount entry : mergedEntries) {
if (ItemStack.isSameItemSameComponents(entry.stack, stack)) {
entry.amount += amount;
return;
}
}
mergedEntries.add(new EntryAmount(stack.copy(), amount));
}

private static EmiStack createVirtualOutput() {
ItemStack stack = AllBlocks.CLIPBOARD.asStack();
stack.set(DataComponents.CUSTOM_NAME, Component.translatable("gui." + EMICreateSchematics.MOD_ID + ".clipboard.tree_output"));
stack.update(DataComponents.CUSTOM_DATA, CustomData.EMPTY,
existingData -> existingData.update(tag -> tag.putBoolean(VIRTUAL_RECIPE_KEY, true)));
return EmiStack.of(stack).comparison(Comparison.compareComponents());
}

private static ResourceLocation nextVirtualRecipeId() {
return ResourceLocation.fromNamespaceAndPath(
EMICreateSchematics.MOD_ID,
"/clipboard/" + emi_create_schematics$recipeCounter++
);
}

private static void notifyEmptyTree() {
Minecraft minecraft = Minecraft.getInstance();
if (minecraft.player != null) {
minecraft.player.displayClientMessage(
Component.translatable("gui." + EMICreateSchematics.MOD_ID + ".clipboard.tree_empty"),
true
);
}
}

private static final class EntryAmount {
private final ItemStack stack;
private long amount;

if(!entry.checked) EmiFavorites.addFavorite(emiStack);
private EntryAmount(ItemStack stack, long amount) {
this.stack = stack;
this.amount = amount;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package net.liukrast.schematicdisplay.clipboard;

import dev.emi.emi.api.recipe.BasicEmiRecipe;
import dev.emi.emi.api.recipe.EmiRecipe;
import dev.emi.emi.api.recipe.VanillaEmiRecipeCategories;
import dev.emi.emi.api.render.EmiTexture;
import dev.emi.emi.api.stack.EmiStack;
import dev.emi.emi.api.widget.WidgetHolder;
import net.liukrast.schematicdisplay.EMICreateSchematics;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;

import java.util.List;

public final class ClipboardTreeRecipe extends BasicEmiRecipe {
private static final int DISPLAYED_INPUTS = 9;
private final Component overflowLabel;
private boolean completed;

public ClipboardTreeRecipe(List<EmiStack> inputs, EmiStack output, ResourceLocation id) {
super(VanillaEmiRecipeCategories.CRAFTING, id, 118, 72);
this.inputs.addAll(inputs);
this.outputs.add(output);

int hiddenInputs = Math.max(0, inputs.size() - DISPLAYED_INPUTS);
overflowLabel = hiddenInputs > 0
? Component.translatable("gui." + EMICreateSchematics.MOD_ID + ".clipboard.tree_more", hiddenInputs)
: Component.empty();
}

public static boolean isVirtual(EmiRecipe recipe) {
return recipe instanceof ClipboardTreeRecipe;
}

public static ClipboardTreeRecipe getVirtual(EmiRecipe recipe) {
return recipe instanceof ClipboardTreeRecipe virtual ? virtual : null;
}

public boolean isCompleted() {
return completed;
}

public void markCompleted() {
completed = true;
}

@Override
public boolean hideCraftable() {
return true;
}

@Override
public void addWidgets(WidgetHolder widgets) {
widgets.addText(
Component.translatable("gui." + EMICreateSchematics.MOD_ID + ".clipboard.tree_recipe"),
0,
0,
0x404040,
false
);
widgets.addTexture(EmiTexture.EMPTY_ARROW, 60, 22);

for (int i = 0; i < DISPLAYED_INPUTS; i++) {
if (i < inputs.size()) {
widgets.addSlot(inputs.get(i), i % 3 * 18, 12 + i / 3 * 18);
} else {
widgets.addSlot(i % 3 * 18, 12 + i / 3 * 18);
}
}

widgets.addSlot(outputs.get(0), 92, 18).large(true).recipeContext(this);
if (!overflowLabel.getString().isEmpty()) {
widgets.addText(overflowLabel, 0, 62, 0x787878, false);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package net.liukrast.schematicdisplay.mixin;

import dev.emi.emi.bom.BoM;
import dev.emi.emi.bom.MaterialNode;
import dev.emi.emi.bom.ProgressState;
import dev.emi.emi.screen.BoMScreen;
import net.liukrast.schematicdisplay.clipboard.ClipboardTreeRecipe;
import net.minecraft.client.Minecraft;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

@Mixin(BoMScreen.class)
public abstract class BoMScreenMixin {
@Inject(method = "recalculateTree", at = @At("TAIL"), remap = false)
private void emi_create_schematics$markVirtualRootProgress(CallbackInfo ci) {
if (BoM.tree == null) {
return;
}

ClipboardTreeRecipe virtualRecipe = ClipboardTreeRecipe.getVirtual(BoM.tree.goal.recipe);
if (virtualRecipe == null || BoM.tree.goal.children == null) {
return;
}

if (!virtualRecipe.isCompleted() && Minecraft.getInstance().player != null
&& dev.emi.emi.api.recipe.EmiPlayerInventory.of(Minecraft.getInstance().player).canCraft(virtualRecipe)) {
virtualRecipe.markCompleted();
BoM.craftingMode = false;
}

if (virtualRecipe.isCompleted()) {
emi_create_schematics$completeNode(BoM.tree.goal);
return;
}

boolean anyProgress = false;
boolean allComplete = !BoM.tree.goal.children.isEmpty();

for (MaterialNode child : BoM.tree.goal.children) {
if (child.progress != ProgressState.UNSTARTED) {
anyProgress = true;
}
if (child.progress != ProgressState.COMPLETED) {
allComplete = false;
}
}

if (allComplete) {
BoM.tree.goal.progress = ProgressState.COMPLETED;
} else if (anyProgress) {
BoM.tree.goal.progress = ProgressState.PARTIAL;
} else {
BoM.tree.goal.progress = ProgressState.UNSTARTED;
}
}

private void emi_create_schematics$completeNode(MaterialNode node) {
node.progress = ProgressState.COMPLETED;
if (node.children == null) {
return;
}
for (MaterialNode child : node.children) {
emi_create_schematics$completeNode(child);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ protected ClipboardScreenMixin(final Component ignored) {
private void init(CallbackInfo ci) {
final int x = guiLeft;
final int y = guiTop - 8;
final IconButton customButton = new IconButton(x + 234, y + 197, AllIcons.I_WHITELIST).withCallback(() -> ClipboardScreenUtils.load(pages));
final IconButton customButton = new IconButton(x + 234, y + 197, AllIcons.I_SCHEMATIC).withCallback(() -> ClipboardScreenUtils.load(pages));
customButton.setToolTip(emi_create_schematics$TOOLTIP);
this.addRenderableWidget(customButton);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package net.liukrast.schematicdisplay.mixin;

import dev.emi.emi.api.recipe.EmiPlayerInventory;
import dev.emi.emi.bom.BoM;
import dev.emi.emi.runtime.EmiFavorites;
import net.liukrast.schematicdisplay.clipboard.ClipboardTreeRecipe;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

@Mixin(EmiFavorites.class)
public abstract class EmiFavoritesMixin {
@Inject(method = "updateSynthetic", at = @At("TAIL"), remap = false)
private static void emi_create_schematics$hideVirtualRootRecipe(EmiPlayerInventory inv, CallbackInfo ci) {
if (BoM.tree == null) {
return;
}

ClipboardTreeRecipe virtualRecipe = ClipboardTreeRecipe.getVirtual(BoM.tree.goal.recipe);
if (virtualRecipe == null) {
return;
}

if (!virtualRecipe.isCompleted() && inv.canCraft(virtualRecipe)) {
virtualRecipe.markCompleted();
BoM.craftingMode = false;
}

if (virtualRecipe.isCompleted()) {
EmiFavorites.syntheticFavorites.clear();
return;
}

EmiFavorites.syntheticFavorites.removeIf(favorite -> favorite.getRecipe() == virtualRecipe);
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
{
"gui.emi_create_schematics.clipboard.favourite": "Transfer all items to EMI Favourites"
}
"gui.emi_create_schematics.clipboard.favourite": "Open the EMI recipe tree from this clipboard",
"gui.emi_create_schematics.clipboard.tree_empty": "No unchecked item requirements were found on this clipboard",
"gui.emi_create_schematics.clipboard.tree_output": "Schematic Materials",
"gui.emi_create_schematics.clipboard.tree_recipe": "Clipboard Checklist",
"gui.emi_create_schematics.clipboard.tree_more": "+%s more"
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
{
"gui.emi_create_schematics.clipboard.favourite": "Trasferisci tutti gli item nei preferiti di EMI"
}
"gui.emi_create_schematics.clipboard.favourite": "Apri l'albero ricette di EMI da questa clipboard",
"gui.emi_create_schematics.clipboard.tree_empty": "Nessun requisito oggetto non spuntato trovato su questa clipboard",
"gui.emi_create_schematics.clipboard.tree_output": "Materiali dello schema",
"gui.emi_create_schematics.clipboard.tree_recipe": "Checklist della clipboard",
"gui.emi_create_schematics.clipboard.tree_more": "+%s altri"
}
4 changes: 3 additions & 1 deletion src/main/resources/emi_create_schematics.mixins.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
"ItemEmiStackMixin"
],
"client": [
"ClipboardScreenMixin"
"BoMScreenMixin",
"ClipboardScreenMixin",
"EmiFavoritesMixin"
],
"injectors": {
"defaultRequire": 1
Expand Down