From 150e8df35eccbeccaddcd208597ce6e5acefe1ad Mon Sep 17 00:00:00 2001 From: raoulvdberge Date: Sun, 6 Oct 2024 22:59:06 +0200 Subject: [PATCH] feat: autocrafting transfer support --- .github/workflows/build.yml | 2 +- .github/workflows/draft-release.yml | 2 +- .../issue-for-unsupported-version.yml | 2 +- .github/workflows/publish-release.yml | 2 +- .github/workflows/resolved-issue-locking.yml | 2 +- .github/workflows/validate-branch-name.yml | 2 +- .github/workflows/validate-changelog.yml | 2 +- .../workflows/validate-commit-messages.yml | 2 +- CHANGELOG.md | 9 ++ gradle.properties | 8 +- gradle/wrapper/gradle-wrapper.properties | 2 +- .../lang/en_us.json | 8 +- .../build.gradle.kts | 7 +- .../rei/fabric/AbstractTransferHandler.java | 34 +++++ .../fabric/CraftingGridTransferHandler.java | 130 ++++++++++++------ .../fabric/PatternGridTransferHandler.java | 110 +++++++++++---- .../rei/fabric/TransferInput.java | 10 ++ .../rei/fabric/TransferInputType.java | 7 + .../rei/fabric/TransferInputs.java | 52 +++++++ .../rei/fabric/TransferType.java | 13 ++ .../src/main/resources/fabric.mod.json | 2 +- .../build.gradle.kts | 7 +- .../rei/forge/AbstractTransferHandler.java | 34 +++++ .../forge/CraftingGridTransferHandler.java | 130 ++++++++++++------ .../rei/forge/MissingIngredients.java | 30 ---- .../rei/forge/PatternGridTransferHandler.java | 110 +++++++++++---- .../rei/forge/TransferInput.java | 10 ++ .../rei/forge/TransferInputType.java | 7 + .../rei/forge/TransferInputs.java | 52 +++++++ .../rei/forge/TransferType.java | 13 ++ .../META-INF/neoforge.mods.toml | 2 +- 31 files changed, 615 insertions(+), 188 deletions(-) create mode 100644 refinedstorage-rei-integration-fabric/src/main/java/com/refinedmods/refinedstorage/rei/fabric/AbstractTransferHandler.java create mode 100644 refinedstorage-rei-integration-fabric/src/main/java/com/refinedmods/refinedstorage/rei/fabric/TransferInput.java create mode 100644 refinedstorage-rei-integration-fabric/src/main/java/com/refinedmods/refinedstorage/rei/fabric/TransferInputType.java create mode 100644 refinedstorage-rei-integration-fabric/src/main/java/com/refinedmods/refinedstorage/rei/fabric/TransferInputs.java create mode 100644 refinedstorage-rei-integration-fabric/src/main/java/com/refinedmods/refinedstorage/rei/fabric/TransferType.java create mode 100644 refinedstorage-rei-integration-neoforge/src/main/java/com/refinedmods/refinedstorage/rei/forge/AbstractTransferHandler.java delete mode 100644 refinedstorage-rei-integration-neoforge/src/main/java/com/refinedmods/refinedstorage/rei/forge/MissingIngredients.java create mode 100644 refinedstorage-rei-integration-neoforge/src/main/java/com/refinedmods/refinedstorage/rei/forge/TransferInput.java create mode 100644 refinedstorage-rei-integration-neoforge/src/main/java/com/refinedmods/refinedstorage/rei/forge/TransferInputType.java create mode 100644 refinedstorage-rei-integration-neoforge/src/main/java/com/refinedmods/refinedstorage/rei/forge/TransferInputs.java create mode 100644 refinedstorage-rei-integration-neoforge/src/main/java/com/refinedmods/refinedstorage/rei/forge/TransferType.java rename refinedstorage-rei-integration-neoforge/src/main/{resources => templates}/META-INF/neoforge.mods.toml (94%) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3ba2c44..856e83b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,7 +8,7 @@ on: types: [ opened, synchronize, reopened ] jobs: build: - uses: refinedmods/refinedarchitect/.github/workflows/build.yml@v0.17.1 + uses: refinedmods/refinedarchitect/.github/workflows/build.yml@v0.19.1 with: mutation-testing: false secrets: inherit diff --git a/.github/workflows/draft-release.yml b/.github/workflows/draft-release.yml index e21ed03..c6d98d5 100644 --- a/.github/workflows/draft-release.yml +++ b/.github/workflows/draft-release.yml @@ -17,7 +17,7 @@ on: type: string jobs: draft: - uses: refinedmods/refinedarchitect/.github/workflows/draft-release.yml@v0.17.1 + uses: refinedmods/refinedarchitect/.github/workflows/draft-release.yml@v0.19.1 with: release-type: ${{ inputs.release-type }} version-number-override: ${{ inputs.version-number-override }} diff --git a/.github/workflows/issue-for-unsupported-version.yml b/.github/workflows/issue-for-unsupported-version.yml index f8ce865..002c2ac 100644 --- a/.github/workflows/issue-for-unsupported-version.yml +++ b/.github/workflows/issue-for-unsupported-version.yml @@ -4,4 +4,4 @@ on: types: [ labeled, unlabeled, reopened ] jobs: unsupported-labeler: - uses: refinedmods/refinedarchitect/.github/workflows/issue-for-unsupported-version.yml@v0.17.1 \ No newline at end of file + uses: refinedmods/refinedarchitect/.github/workflows/issue-for-unsupported-version.yml@v0.19.1 \ No newline at end of file diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index 80916d6..894c052 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -7,7 +7,7 @@ on: - closed jobs: publish-release: - uses: refinedmods/refinedarchitect/.github/workflows/publish-release.yml@v0.17.1 + uses: refinedmods/refinedarchitect/.github/workflows/publish-release.yml@v0.19.1 secrets: inherit with: project-name: 'Refined Storage - REI Integration' diff --git a/.github/workflows/resolved-issue-locking.yml b/.github/workflows/resolved-issue-locking.yml index 67112b4..f8dbbdf 100644 --- a/.github/workflows/resolved-issue-locking.yml +++ b/.github/workflows/resolved-issue-locking.yml @@ -4,4 +4,4 @@ on: - cron: '0 0 * * *' jobs: lock: - uses: refinedmods/refinedarchitect/.github/workflows/resolved-issue-locking.yml@v0.17.1 \ No newline at end of file + uses: refinedmods/refinedarchitect/.github/workflows/resolved-issue-locking.yml@v0.19.1 \ No newline at end of file diff --git a/.github/workflows/validate-branch-name.yml b/.github/workflows/validate-branch-name.yml index ce5e06e..c1733fb 100644 --- a/.github/workflows/validate-branch-name.yml +++ b/.github/workflows/validate-branch-name.yml @@ -2,4 +2,4 @@ name: Validate branch name on: [ pull_request ] jobs: validate-branch-name: - uses: refinedmods/refinedarchitect/.github/workflows/validate-branch-name.yml@v0.17.1 \ No newline at end of file + uses: refinedmods/refinedarchitect/.github/workflows/validate-branch-name.yml@v0.19.1 \ No newline at end of file diff --git a/.github/workflows/validate-changelog.yml b/.github/workflows/validate-changelog.yml index db04af1..f5fb9ae 100644 --- a/.github/workflows/validate-changelog.yml +++ b/.github/workflows/validate-changelog.yml @@ -4,4 +4,4 @@ on: types: [ opened, synchronize, reopened, ready_for_review, labeled, unlabeled ] jobs: validate-changelog: - uses: refinedmods/refinedarchitect/.github/workflows/validate-changelog.yml@v0.17.1 \ No newline at end of file + uses: refinedmods/refinedarchitect/.github/workflows/validate-changelog.yml@v0.19.1 \ No newline at end of file diff --git a/.github/workflows/validate-commit-messages.yml b/.github/workflows/validate-commit-messages.yml index 043dba2..620b9e8 100644 --- a/.github/workflows/validate-commit-messages.yml +++ b/.github/workflows/validate-commit-messages.yml @@ -2,4 +2,4 @@ name: Validate commit messages on: [ pull_request ] jobs: validate-commit-messages: - uses: refinedmods/refinedarchitect/.github/workflows/validate-commit-messages.yml@v0.17.1 \ No newline at end of file + uses: refinedmods/refinedarchitect/.github/workflows/validate-commit-messages.yml@v0.19.1 \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index b482f84..d72faaa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,15 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Added + +- The recipe transfer support in the Pattern Grid and Crafting Grid now indicates whether all or some resources are autocraftable. +- You can now start autocrafting for missing autocraftable items from the Crafting Grid via the recipe transfer. + +### Fixed + +- Support for Refined Storage v2.0.0-milestone.4.8. + ## [0.3.1] - 2024-08-11 ### Fixed diff --git a/gradle.properties b/gradle.properties index 9593239..04c28be 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ -refinedarchitectVersion=0.17.1 -refinedstorageVersion=2.0.0-milestone.4.7 +refinedarchitectVersion=0.19.1 +refinedstorageVersion=2.0.0-milestone.4.8 # https://linkie.shedaniel.dev/dependencies?loader=neoforge&version=1.21 # https://linkie.shedaniel.dev/dependencies?loader=fabric&version=1.21 architecturyVersion=13.0.3 @@ -7,3 +7,7 @@ clothConfigVersion=15.0.127 reiVersion=16.0.729 # Gradle org.gradle.jvmargs=-Xmx1G +org.gradle.configureondemand=true +org.gradle.caching=true +org.gradle.configuration-cache=true +org.gradle.configuration-cache.problems=warn diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a441313..9355b41 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/refinedstorage-rei-integration-common/src/main/resources/assets/refinedstorage_rei_integration/lang/en_us.json b/refinedstorage-rei-integration-common/src/main/resources/assets/refinedstorage_rei_integration/lang/en_us.json index caa5280..3c309dc 100644 --- a/refinedstorage-rei-integration-common/src/main/resources/assets/refinedstorage_rei_integration/lang/en_us.json +++ b/refinedstorage-rei-integration-common/src/main/resources/assets/refinedstorage_rei_integration/lang/en_us.json @@ -3,5 +3,11 @@ "gui.refinedstorage_rei_integration.grid.synchronizer.help": "Sync the search box text to the REI filter.", "gui.refinedstorage_rei_integration.grid.synchronizer.two_way": "REI two-way", "gui.refinedstorage_rei_integration.grid.synchronizer.two_way.help": "Sync the search box text to the REI filter, and the REI filter to the search box text.", - "block.refinedstorage_rei_integration.controller.fully_charged": "Fully charged Controllers" + "block.refinedstorage_rei_integration.controller.fully_charged": "Fully charged Controllers", + "gui.refinedstorage_rei_integration.transfer.missing": "Missing items", + "gui.refinedstorage_rei_integration.transfer.missing_but_all_autocraftable": "Missing items, all are autocraftable", + "gui.refinedstorage_rei_integration.transfer.missing_but_some_autocraftable": "Missing items, some are autocraftable", + "gui.refinedstorage_rei_integration.transfer.ctrl_click_to_autocraft": "CTRL + click to autocraft", + "gui.refinedstorage_rei_integration.transfer.all_autocraftable": "All are autocraftable", + "gui.refinedstorage_rei_integration.transfer.some_autocraftable": "Some are autocraftable" } \ No newline at end of file diff --git a/refinedstorage-rei-integration-fabric/build.gradle.kts b/refinedstorage-rei-integration-fabric/build.gradle.kts index f7210c6..750b14d 100644 --- a/refinedstorage-rei-integration-fabric/build.gradle.kts +++ b/refinedstorage-rei-integration-fabric/build.gradle.kts @@ -23,7 +23,6 @@ repositories { refinedarchitect { modId = "refinedstorage_rei_integration" fabric() - compileWithProject(project(":refinedstorage-rei-integration-common")) publishing { maven = true } @@ -38,7 +37,13 @@ val architecturyVersion: String by project val clothConfigVersion: String by project val reiVersion: String by project +val commonJava by configurations.existing +val commonResources by configurations.existing + dependencies { + compileOnly(project(":refinedstorage-rei-integration-common")) + commonJava(project(path = ":refinedstorage-rei-integration-common", configuration = "commonJava")) + commonResources(project(path = ":refinedstorage-rei-integration-common", configuration = "commonResources")) modApi("com.refinedmods.refinedstorage:refinedstorage-fabric:${refinedstorageVersion}") modApi("dev.architectury:architectury-fabric:${architecturyVersion}") modApi("me.shedaniel.cloth:cloth-config-fabric:${clothConfigVersion}") diff --git a/refinedstorage-rei-integration-fabric/src/main/java/com/refinedmods/refinedstorage/rei/fabric/AbstractTransferHandler.java b/refinedstorage-rei-integration-fabric/src/main/java/com/refinedmods/refinedstorage/rei/fabric/AbstractTransferHandler.java new file mode 100644 index 0000000..9d72e11 --- /dev/null +++ b/refinedstorage-rei-integration-fabric/src/main/java/com/refinedmods/refinedstorage/rei/fabric/AbstractTransferHandler.java @@ -0,0 +1,34 @@ +package com.refinedmods.refinedstorage.rei.fabric; + +import com.refinedmods.refinedstorage.common.grid.AutocraftableResourceHint; + +import com.mojang.blaze3d.vertex.PoseStack; +import me.shedaniel.math.Rectangle; +import me.shedaniel.rei.api.client.gui.widgets.Slot; +import me.shedaniel.rei.api.client.registry.transfer.TransferHandler; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; + +abstract class AbstractTransferHandler implements TransferHandler { + protected static final int AUTOCRAFTABLE_COLOR = AutocraftableResourceHint.AUTOCRAFTABLE.getColor(); + + protected static MutableComponent createAutocraftableHint(final Component component) { + return component.copy().withColor(AUTOCRAFTABLE_COLOR); + } + + protected static void renderSlotHighlight(final GuiGraphics graphics, final Slot slot, final int color) { + final PoseStack poseStack = graphics.pose(); + poseStack.pushPose(); + poseStack.translate(0, 0, 50); + final Rectangle innerBounds = slot.getInnerBounds(); + graphics.fill( + innerBounds.x, + innerBounds.y, + innerBounds.getMaxX(), + innerBounds.getMaxY(), + color + ); + poseStack.popPose(); + } +} diff --git a/refinedstorage-rei-integration-fabric/src/main/java/com/refinedmods/refinedstorage/rei/fabric/CraftingGridTransferHandler.java b/refinedstorage-rei-integration-fabric/src/main/java/com/refinedmods/refinedstorage/rei/fabric/CraftingGridTransferHandler.java index 06701d2..82a20c5 100644 --- a/refinedstorage-rei-integration-fabric/src/main/java/com/refinedmods/refinedstorage/rei/fabric/CraftingGridTransferHandler.java +++ b/refinedstorage-rei-integration-fabric/src/main/java/com/refinedmods/refinedstorage/rei/fabric/CraftingGridTransferHandler.java @@ -1,6 +1,9 @@ package com.refinedmods.refinedstorage.rei.fabric; -import com.refinedmods.refinedstorage.api.resource.list.ResourceList; +import com.refinedmods.refinedstorage.api.grid.view.GridView; +import com.refinedmods.refinedstorage.api.resource.ResourceAmount; +import com.refinedmods.refinedstorage.api.resource.list.MutableResourceList; +import com.refinedmods.refinedstorage.common.api.RefinedStorageApi; import com.refinedmods.refinedstorage.common.grid.CraftingGridContainerMenu; import com.refinedmods.refinedstorage.common.support.resource.ItemResource; @@ -8,11 +11,8 @@ import java.util.List; import java.util.stream.Collectors; -import com.mojang.blaze3d.vertex.PoseStack; -import me.shedaniel.math.Rectangle; import me.shedaniel.rei.api.client.gui.widgets.Slot; import me.shedaniel.rei.api.client.gui.widgets.Widget; -import me.shedaniel.rei.api.client.registry.transfer.TransferHandler; import me.shedaniel.rei.api.client.registry.transfer.TransferHandlerRenderer; import me.shedaniel.rei.api.common.entry.EntryIngredient; import me.shedaniel.rei.api.common.entry.EntryStack; @@ -20,11 +20,28 @@ import me.shedaniel.rei.api.common.util.CollectionUtils; import me.shedaniel.rei.plugin.common.BuiltinPlugin; import me.shedaniel.rei.plugin.common.displays.crafting.DefaultCraftingDisplay; -import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.ChatFormatting; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.Style; import net.minecraft.world.item.ItemStack; -class CraftingGridTransferHandler implements TransferHandler { +import static com.refinedmods.refinedstorage.rei.common.Common.MOD_ID; +import static java.util.Comparator.comparingLong; + +class CraftingGridTransferHandler extends AbstractTransferHandler { private static final Color MISSING_COLOR = new Color(1.0f, 0.0f, 0.0f, 0.4f); + private static final Component MISSING = Component.translatable("gui.%s.transfer.missing".formatted(MOD_ID)) + .withStyle(ChatFormatting.RED); + private static final Component CTRL_CLICK_TO_AUTOCRAFT = Component + .translatable("gui.%s.transfer.ctrl_click_to_autocraft".formatted(MOD_ID)) + .withStyle(Style.EMPTY.withItalic(true).withColor(ChatFormatting.WHITE)); + private static final Component MISSING_BUT_ALL_AUTOCRAFTABLE = createAutocraftableHint( + Component.translatable("gui.%s.transfer.missing_but_all_autocraftable".formatted(MOD_ID)) + ).append("\n").append(CTRL_CLICK_TO_AUTOCRAFT); + private static final Component MISSING_BUT_AUTOCRAFTABLE = createAutocraftableHint( + Component.translatable("gui.%s.transfer.missing_but_some_autocraftable".formatted(MOD_ID)) + ).append("\n").append(CTRL_CLICK_TO_AUTOCRAFT); @Override public Result handle(final Context context) { @@ -34,51 +51,87 @@ public Result handle(final Context context) { return Result.createNotApplicable(); } final List ingredients = defaultCraftingDisplay.getOrganisedInputEntries(3, 3); + final MutableResourceList available = containerMenu.getAvailableListForRecipeTransfer(); + final GridView view = containerMenu.getView(); + final TransferInputs transferInputs = getTransferInputs(view, ingredients, available); + final TransferType type = transferInputs.getType(); if (context.isActuallyCrafting()) { - doTransfer(ingredients, containerMenu); + if (type.canOpenAutocraftingPreview() && Screen.hasControlDown()) { + final List craftingRequests = transferInputs.createCraftingRequests(); + RefinedStorageApi.INSTANCE.openAutocraftingPreview(craftingRequests, context.getContainerScreen()); + return Result.createSuccessful().blocksFurtherHandling(false); + } else { + doTransfer(ingredients, containerMenu); + } return Result.createSuccessful().blocksFurtherHandling(); } - final ResourceList available = containerMenu.getAvailableListForRecipeTransfer(); - final MissingIngredients missingIngredients = findMissingIngredients(ingredients, available); - if (missingIngredients.isEmpty()) { + if (type == TransferType.AVAILABLE) { return Result.createSuccessful().blocksFurtherHandling(); } return Result.createSuccessful() - .color(MISSING_COLOR.getRGB()) - .tooltipMissing(missingIngredients.getIngredients()) - .renderer(createMissingItemsRenderer(missingIngredients)) + .color(getColor(type)) + .tooltip(getTooltip(type)) + .renderer(createTransferInputsRenderer(transferInputs)) .blocksFurtherHandling(); } + private static int getColor(final TransferType type) { + return switch (type) { + case MISSING -> MISSING_COLOR.getRGB(); + case MISSING_BUT_ALL_AUTOCRAFTABLE, MISSING_BUT_SOME_AUTOCRAFTABLE -> AUTOCRAFTABLE_COLOR; + default -> 0; + }; + } + + private static Component getTooltip(final TransferType type) { + return switch (type) { + case MISSING -> MISSING; + case MISSING_BUT_ALL_AUTOCRAFTABLE -> MISSING_BUT_ALL_AUTOCRAFTABLE; + case MISSING_BUT_SOME_AUTOCRAFTABLE -> MISSING_BUT_AUTOCRAFTABLE; + default -> Component.empty(); + }; + } + private void doTransfer(final List ingredients, final CraftingGridContainerMenu containerMenu) { final List> inputs = getInputs(ingredients); containerMenu.transferRecipe(inputs); } - private MissingIngredients findMissingIngredients(final List ingredients, - final ResourceList available) { - final MissingIngredients missingIngredients = new MissingIngredients(); + private TransferInputs getTransferInputs(final GridView view, + final List ingredients, + final MutableResourceList available) { + final TransferInputs transferInputs = new TransferInputs(); for (int i = 0; i < ingredients.size(); ++i) { final EntryIngredient ingredient = ingredients.get(i); if (ingredient.isEmpty()) { continue; } - if (!isAvailable(available, ingredient)) { - missingIngredients.addIngredient(ingredient, i); - } + final TransferInput transferInput = toTransferInput(view, available, ingredient); + transferInputs.addInput(i, transferInput); } - return missingIngredients; + return transferInputs; } - private boolean isAvailable(final ResourceList available, final EntryIngredient ingredient) { + private TransferInput toTransferInput(final GridView view, + final MutableResourceList available, + final EntryIngredient ingredient) { final List possibilities = convertIngredientToItemStacks(ingredient); for (final ItemStack possibility : possibilities) { final ItemResource possibilityResource = ItemResource.ofItemStack(possibility); if (available.remove(possibilityResource, 1).isPresent()) { - return true; + return new TransferInput(ingredient, TransferInputType.AVAILABLE, null); } } - return false; + final List autocraftingPossibilities = possibilities + .stream() + .map(ItemResource::ofItemStack) + .filter(view::isAutocraftable) + .sorted(comparingLong(view::getAmount)) + .toList(); + if (!autocraftingPossibilities.isEmpty()) { + return new TransferInput(ingredient, TransferInputType.AUTOCRAFTABLE, autocraftingPossibilities.getFirst()); + } + return new TransferInput(ingredient, TransferInputType.MISSING, null); } private List> getInputs(final List ingredients) { @@ -96,31 +149,22 @@ private List convertIngredientToItemStacks(final EntryIngredient ingr ); } - private TransferHandlerRenderer createMissingItemsRenderer(final MissingIngredients missingIngredients) { + private TransferHandlerRenderer createTransferInputsRenderer(final TransferInputs transferInputs) { return (graphics, mouseX, mouseY, delta, widgets, bounds, display) -> { int index = 0; for (final Widget widget : widgets) { - if (widget instanceof Slot slot - && slot.getNoticeMark() == Slot.INPUT - && missingIngredients.isMissing(index++)) { - renderMissingItemOverlay(graphics, slot); + if (widget instanceof Slot slot && slot.getNoticeMark() == Slot.INPUT) { + final TransferInput input = transferInputs.getInput(index++); + if (input == null) { + continue; + } + if (input.type() == TransferInputType.MISSING) { + renderSlotHighlight(graphics, slot, MISSING_COLOR.getRGB()); + } else if (input.type() == TransferInputType.AUTOCRAFTABLE) { + renderSlotHighlight(graphics, slot, AUTOCRAFTABLE_COLOR); + } } } }; } - - private void renderMissingItemOverlay(final GuiGraphics graphics, final Slot slot) { - final PoseStack poseStack = graphics.pose(); - poseStack.pushPose(); - poseStack.translate(0, 0, 400); - final Rectangle innerBounds = slot.getInnerBounds(); - graphics.fill( - innerBounds.x, - innerBounds.y, - innerBounds.getMaxX(), - innerBounds.getMaxY(), - MISSING_COLOR.getRGB() - ); - poseStack.popPose(); - } } diff --git a/refinedstorage-rei-integration-fabric/src/main/java/com/refinedmods/refinedstorage/rei/fabric/PatternGridTransferHandler.java b/refinedstorage-rei-integration-fabric/src/main/java/com/refinedmods/refinedstorage/rei/fabric/PatternGridTransferHandler.java index 49b4d7e..f6a5d3f 100644 --- a/refinedstorage-rei-integration-fabric/src/main/java/com/refinedmods/refinedstorage/rei/fabric/PatternGridTransferHandler.java +++ b/refinedstorage-rei-integration-fabric/src/main/java/com/refinedmods/refinedstorage/rei/fabric/PatternGridTransferHandler.java @@ -1,5 +1,6 @@ package com.refinedmods.refinedstorage.rei.fabric; +import com.refinedmods.refinedstorage.api.grid.view.GridView; import com.refinedmods.refinedstorage.api.resource.ResourceAmount; import com.refinedmods.refinedstorage.common.api.RefinedStorageApi; import com.refinedmods.refinedstorage.common.autocrafting.PatternGridContainerMenu; @@ -7,56 +8,85 @@ import java.util.List; import java.util.stream.Collectors; -import javax.annotation.Nullable; -import me.shedaniel.rei.api.client.registry.transfer.TransferHandler; +import me.shedaniel.rei.api.client.gui.widgets.Slot; +import me.shedaniel.rei.api.client.gui.widgets.Widget; +import me.shedaniel.rei.api.client.registry.transfer.TransferHandlerRenderer; import me.shedaniel.rei.api.common.entry.EntryIngredient; import me.shedaniel.rei.api.common.entry.EntryStack; import me.shedaniel.rei.api.common.entry.type.VanillaEntryTypes; import me.shedaniel.rei.api.common.util.CollectionUtils; import me.shedaniel.rei.plugin.common.BuiltinPlugin; import me.shedaniel.rei.plugin.common.displays.crafting.DefaultCraftingDisplay; +import net.minecraft.network.chat.Component; import net.minecraft.world.item.ItemStack; -class PatternGridTransferHandler implements TransferHandler { +import static com.refinedmods.refinedstorage.rei.common.Common.MOD_ID; + +class PatternGridTransferHandler extends AbstractTransferHandler { + private static final Component ALL_AUTOCRAFTABLE = createAutocraftableHint( + Component.translatable("gui.%s.transfer.all_autocraftable".formatted(MOD_ID)) + ); + private static final Component SOME_AUTOCRAFTABLE = createAutocraftableHint( + Component.translatable("gui.%s.transfer.some_autocraftable".formatted(MOD_ID)) + ); + @Override public Result handle(final Context context) { if (!(context.getMenu() instanceof PatternGridContainerMenu containerMenu)) { return Result.createNotApplicable(); } - final Result result = transferRegularRecipe(context, containerMenu); - if (result != null) { - return result; + final GridView view = containerMenu.getView(); + final List inputs = context.getDisplay() + .getInputEntries() + .stream() + .filter(ingredient -> !ingredient.isEmpty()) + .toList(); + final List autocraftable = getAutocraftableIngredients(view, inputs); + if (!transferRegularRecipe(context, containerMenu)) { + transferProcessingRecipe(context, containerMenu); + } + final Result result = Result.createSuccessful(); + if (!autocraftable.isEmpty()) { + final boolean areAllAutocraftable = inputs.size() == autocraftable.size(); + if (areAllAutocraftable) { + result.tooltip(ALL_AUTOCRAFTABLE); + } else { + result.tooltip(SOME_AUTOCRAFTABLE); + } + result.color(AUTOCRAFTABLE_COLOR); + result.renderer(createAutocraftableRenderer(autocraftable)); } - return transferProcessingRecipe(context, containerMenu); + return result.blocksFurtherHandling(); } - @Nullable - private Result transferRegularRecipe(final Context context, final PatternGridContainerMenu containerMenu) { + private boolean transferRegularRecipe(final Context context, final PatternGridContainerMenu containerMenu) { if (context.getDisplay().getCategoryIdentifier().equals(BuiltinPlugin.CRAFTING) && context.getDisplay() instanceof DefaultCraftingDisplay defaultCraftingDisplay) { - return transferCraftingRecipe(context, containerMenu, defaultCraftingDisplay); + transferCraftingRecipe(context, containerMenu, defaultCraftingDisplay); + return true; } else if (context.getDisplay().getCategoryIdentifier().equals(BuiltinPlugin.STONE_CUTTING)) { - return transferStonecutterRecipe(context, containerMenu); + transferStonecutterRecipe(context, containerMenu); + return true; } else if (context.getDisplay().getCategoryIdentifier().equals(BuiltinPlugin.SMITHING)) { - return transferSmithingTableRecipe(context, containerMenu); + transferSmithingTableRecipe(context, containerMenu); + return true; } - return null; + return false; } - private Result transferCraftingRecipe(final Context context, - final PatternGridContainerMenu containerMenu, - final DefaultCraftingDisplay display) { + private void transferCraftingRecipe(final Context context, + final PatternGridContainerMenu containerMenu, + final DefaultCraftingDisplay display) { final List> inputs = getItems( display.getOrganisedInputEntries(3, 3) ); if (context.isActuallyCrafting()) { containerMenu.transferCraftingRecipe(inputs); } - return Result.createSuccessful().blocksFurtherHandling(); } - private Result transferStonecutterRecipe(final Context context, final PatternGridContainerMenu menu) { + private void transferStonecutterRecipe(final Context context, final PatternGridContainerMenu menu) { final List> inputs = getItems(context.getDisplay().getInputEntries()); final List> outputs = getItems(context.getDisplay().getOutputEntries()); final boolean valid = !inputs.isEmpty() @@ -66,24 +96,21 @@ private Result transferStonecutterRecipe(final Context context, final PatternGri if (context.isActuallyCrafting() && valid) { menu.transferStonecutterRecipe(inputs.getFirst().getFirst(), outputs.getFirst().getFirst()); } - return Result.createSuccessful().blocksFurtherHandling(); } - private Result transferSmithingTableRecipe(final Context context, final PatternGridContainerMenu menu) { + private void transferSmithingTableRecipe(final Context context, final PatternGridContainerMenu menu) { final List> inputs = getItems(context.getDisplay().getInputEntries()); if (context.isActuallyCrafting() && inputs.size() == 3) { menu.transferSmithingTableRecipe(inputs.get(0), inputs.get(1), inputs.get(2)); } - return Result.createSuccessful().blocksFurtherHandling(); } - private Result transferProcessingRecipe(final Context context, final PatternGridContainerMenu menu) { + private void transferProcessingRecipe(final Context context, final PatternGridContainerMenu menu) { final List> inputs = getResources(context.getDisplay().getInputEntries()); final List> outputs = getResources(context.getDisplay().getOutputEntries()); if (context.isActuallyCrafting()) { menu.transferProcessingRecipe(inputs, outputs); } - return Result.createSuccessful().blocksFurtherHandling(); } private List> getItems(final List ingredients) { @@ -103,11 +130,38 @@ private List convertIngredientToItemStacks(final EntryIngredient ingr private List> getResources(final List ingredients) { return ingredients.stream() - .map(ingredient -> ingredient.stream() - .flatMap( - item -> RefinedStorageApi.INSTANCE.getIngredientConverter().convertToResourceAmount(item.getValue()) - .stream()) - .collect(Collectors.toList())) + .map(this::getResources) .toList(); } + + private List getResources(final EntryIngredient ingredient) { + return ingredient.stream() + .flatMap( + item -> RefinedStorageApi.INSTANCE.getIngredientConverter().convertToResourceAmount(item.getValue()) + .stream()) + .collect(Collectors.toList()); + } + + private List getAutocraftableIngredients(final GridView view, + final List ingredients) { + return ingredients.stream().filter(ingredient -> getResources(ingredient) + .stream() + .map(ResourceAmount::resource) + .anyMatch(view::isAutocraftable)) + .toList(); + } + + private TransferHandlerRenderer createAutocraftableRenderer(final List autocraftableIngredients) { + return (graphics, mouseX, mouseY, delta, widgets, bounds, display) -> { + for (final Widget widget : widgets) { + if (widget instanceof Slot slot && slot.getNoticeMark() == Slot.INPUT) { + final boolean autocraftable = autocraftableIngredients.stream() + .anyMatch(ingredient -> ingredient.stream().anyMatch(ing -> slot.getEntries().contains(ing))); + if (autocraftable) { + renderSlotHighlight(graphics, slot, AUTOCRAFTABLE_COLOR); + } + } + } + }; + } } diff --git a/refinedstorage-rei-integration-fabric/src/main/java/com/refinedmods/refinedstorage/rei/fabric/TransferInput.java b/refinedstorage-rei-integration-fabric/src/main/java/com/refinedmods/refinedstorage/rei/fabric/TransferInput.java new file mode 100644 index 0000000..e8cd2dc --- /dev/null +++ b/refinedstorage-rei-integration-fabric/src/main/java/com/refinedmods/refinedstorage/rei/fabric/TransferInput.java @@ -0,0 +1,10 @@ +package com.refinedmods.refinedstorage.rei.fabric; + +import com.refinedmods.refinedstorage.common.support.resource.ItemResource; + +import javax.annotation.Nullable; + +import me.shedaniel.rei.api.common.entry.EntryIngredient; + +record TransferInput(EntryIngredient ingredient, TransferInputType type, @Nullable ItemResource autocraftableResource) { +} diff --git a/refinedstorage-rei-integration-fabric/src/main/java/com/refinedmods/refinedstorage/rei/fabric/TransferInputType.java b/refinedstorage-rei-integration-fabric/src/main/java/com/refinedmods/refinedstorage/rei/fabric/TransferInputType.java new file mode 100644 index 0000000..5597d7b --- /dev/null +++ b/refinedstorage-rei-integration-fabric/src/main/java/com/refinedmods/refinedstorage/rei/fabric/TransferInputType.java @@ -0,0 +1,7 @@ +package com.refinedmods.refinedstorage.rei.fabric; + +enum TransferInputType { + AVAILABLE, + MISSING, + AUTOCRAFTABLE +} diff --git a/refinedstorage-rei-integration-fabric/src/main/java/com/refinedmods/refinedstorage/rei/fabric/TransferInputs.java b/refinedstorage-rei-integration-fabric/src/main/java/com/refinedmods/refinedstorage/rei/fabric/TransferInputs.java new file mode 100644 index 0000000..bbe32a1 --- /dev/null +++ b/refinedstorage-rei-integration-fabric/src/main/java/com/refinedmods/refinedstorage/rei/fabric/TransferInputs.java @@ -0,0 +1,52 @@ +package com.refinedmods.refinedstorage.rei.fabric; + +import com.refinedmods.refinedstorage.api.resource.ResourceAmount; +import com.refinedmods.refinedstorage.api.resource.list.MutableResourceList; +import com.refinedmods.refinedstorage.api.resource.list.MutableResourceListImpl; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.annotation.Nullable; + +class TransferInputs { + private final List inputs = new ArrayList<>(); + private final Map inputsBySlotIndex = new HashMap<>(); + + void addInput(final int index, final TransferInput input) { + inputs.add(input); + inputsBySlotIndex.put(index, input); + } + + List createCraftingRequests() { + final MutableResourceList requests = MutableResourceListImpl.orderPreserving(); + for (final TransferInput transferInput : inputs) { + if (transferInput.type() == TransferInputType.AUTOCRAFTABLE + && transferInput.autocraftableResource() != null) { + requests.add(transferInput.autocraftableResource(), 1); + } + } + return requests.copyState().stream().toList(); + } + + @Nullable + TransferInput getInput(final int slotIndex) { + return inputsBySlotIndex.get(slotIndex); + } + + TransferType getType() { + if (inputs.stream().allMatch(input -> input.type() == TransferInputType.AVAILABLE)) { + return TransferType.AVAILABLE; + } + final boolean hasMissing = inputs.stream().anyMatch(input -> input.type() == TransferInputType.MISSING); + final boolean hasAutocraftable = inputs.stream() + .anyMatch(input -> input.type() == TransferInputType.AUTOCRAFTABLE); + if (hasMissing && hasAutocraftable) { + return TransferType.MISSING_BUT_SOME_AUTOCRAFTABLE; + } else if (hasAutocraftable) { + return TransferType.MISSING_BUT_ALL_AUTOCRAFTABLE; + } + return TransferType.MISSING; + } +} diff --git a/refinedstorage-rei-integration-fabric/src/main/java/com/refinedmods/refinedstorage/rei/fabric/TransferType.java b/refinedstorage-rei-integration-fabric/src/main/java/com/refinedmods/refinedstorage/rei/fabric/TransferType.java new file mode 100644 index 0000000..bc3b377 --- /dev/null +++ b/refinedstorage-rei-integration-fabric/src/main/java/com/refinedmods/refinedstorage/rei/fabric/TransferType.java @@ -0,0 +1,13 @@ +package com.refinedmods.refinedstorage.rei.fabric; + +enum TransferType { + AVAILABLE, + MISSING, + MISSING_BUT_ALL_AUTOCRAFTABLE, + MISSING_BUT_SOME_AUTOCRAFTABLE; + + boolean canOpenAutocraftingPreview() { + return this == TransferType.MISSING_BUT_ALL_AUTOCRAFTABLE + || this == TransferType.MISSING_BUT_SOME_AUTOCRAFTABLE; + } +} diff --git a/refinedstorage-rei-integration-fabric/src/main/resources/fabric.mod.json b/refinedstorage-rei-integration-fabric/src/main/resources/fabric.mod.json index a8c67f2..05016aa 100644 --- a/refinedstorage-rei-integration-fabric/src/main/resources/fabric.mod.json +++ b/refinedstorage-rei-integration-fabric/src/main/resources/fabric.mod.json @@ -33,7 +33,7 @@ "fabricloader": ">=0.14.6", "fabric-api": "*", "minecraft": "~1.21", - "refinedstorage": ">=2.0.0-milestone.4.7", + "refinedstorage": ">=2.0.0-milestone.4.8", "roughlyenoughitems": ">=16", "java": ">=17" } diff --git a/refinedstorage-rei-integration-neoforge/build.gradle.kts b/refinedstorage-rei-integration-neoforge/build.gradle.kts index 166a557..f50a6e2 100644 --- a/refinedstorage-rei-integration-neoforge/build.gradle.kts +++ b/refinedstorage-rei-integration-neoforge/build.gradle.kts @@ -23,7 +23,6 @@ repositories { refinedarchitect { modId = "refinedstorage_rei_integration" neoForge() - compileWithProject(project(":refinedstorage-rei-integration-common")) publishing { maven = true } @@ -38,7 +37,13 @@ val architecturyVersion: String by project val clothConfigVersion: String by project val reiVersion: String by project +val commonJava by configurations.existing +val commonResources by configurations.existing + dependencies { + compileOnly(project(":refinedstorage-rei-integration-common")) + commonJava(project(path = ":refinedstorage-rei-integration-common", configuration = "commonJava")) + commonResources(project(path = ":refinedstorage-rei-integration-common", configuration = "commonResources")) api("com.refinedmods.refinedstorage:refinedstorage-neoforge:${refinedstorageVersion}") api("dev.architectury:architectury-neoforge:${architecturyVersion}") api("me.shedaniel.cloth:cloth-config-neoforge:${clothConfigVersion}") diff --git a/refinedstorage-rei-integration-neoforge/src/main/java/com/refinedmods/refinedstorage/rei/forge/AbstractTransferHandler.java b/refinedstorage-rei-integration-neoforge/src/main/java/com/refinedmods/refinedstorage/rei/forge/AbstractTransferHandler.java new file mode 100644 index 0000000..ad5946c --- /dev/null +++ b/refinedstorage-rei-integration-neoforge/src/main/java/com/refinedmods/refinedstorage/rei/forge/AbstractTransferHandler.java @@ -0,0 +1,34 @@ +package com.refinedmods.refinedstorage.rei.forge; + +import com.refinedmods.refinedstorage.common.grid.AutocraftableResourceHint; + +import com.mojang.blaze3d.vertex.PoseStack; +import me.shedaniel.math.Rectangle; +import me.shedaniel.rei.api.client.gui.widgets.Slot; +import me.shedaniel.rei.api.client.registry.transfer.TransferHandler; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; + +abstract class AbstractTransferHandler implements TransferHandler { + protected static final int AUTOCRAFTABLE_COLOR = AutocraftableResourceHint.AUTOCRAFTABLE.getColor(); + + protected static MutableComponent createAutocraftableHint(final Component component) { + return component.copy().withColor(AUTOCRAFTABLE_COLOR); + } + + protected static void renderSlotHighlight(final GuiGraphics graphics, final Slot slot, final int color) { + final PoseStack poseStack = graphics.pose(); + poseStack.pushPose(); + poseStack.translate(0, 0, 50); + final Rectangle innerBounds = slot.getInnerBounds(); + graphics.fill( + innerBounds.x, + innerBounds.y, + innerBounds.getMaxX(), + innerBounds.getMaxY(), + color + ); + poseStack.popPose(); + } +} diff --git a/refinedstorage-rei-integration-neoforge/src/main/java/com/refinedmods/refinedstorage/rei/forge/CraftingGridTransferHandler.java b/refinedstorage-rei-integration-neoforge/src/main/java/com/refinedmods/refinedstorage/rei/forge/CraftingGridTransferHandler.java index f37db46..e08df50 100644 --- a/refinedstorage-rei-integration-neoforge/src/main/java/com/refinedmods/refinedstorage/rei/forge/CraftingGridTransferHandler.java +++ b/refinedstorage-rei-integration-neoforge/src/main/java/com/refinedmods/refinedstorage/rei/forge/CraftingGridTransferHandler.java @@ -1,6 +1,9 @@ package com.refinedmods.refinedstorage.rei.forge; -import com.refinedmods.refinedstorage.api.resource.list.ResourceList; +import com.refinedmods.refinedstorage.api.grid.view.GridView; +import com.refinedmods.refinedstorage.api.resource.ResourceAmount; +import com.refinedmods.refinedstorage.api.resource.list.MutableResourceList; +import com.refinedmods.refinedstorage.common.api.RefinedStorageApi; import com.refinedmods.refinedstorage.common.grid.CraftingGridContainerMenu; import com.refinedmods.refinedstorage.common.support.resource.ItemResource; @@ -8,11 +11,8 @@ import java.util.List; import java.util.stream.Collectors; -import com.mojang.blaze3d.vertex.PoseStack; -import me.shedaniel.math.Rectangle; import me.shedaniel.rei.api.client.gui.widgets.Slot; import me.shedaniel.rei.api.client.gui.widgets.Widget; -import me.shedaniel.rei.api.client.registry.transfer.TransferHandler; import me.shedaniel.rei.api.client.registry.transfer.TransferHandlerRenderer; import me.shedaniel.rei.api.common.entry.EntryIngredient; import me.shedaniel.rei.api.common.entry.EntryStack; @@ -20,11 +20,28 @@ import me.shedaniel.rei.api.common.util.CollectionUtils; import me.shedaniel.rei.plugin.common.BuiltinPlugin; import me.shedaniel.rei.plugin.common.displays.crafting.DefaultCraftingDisplay; -import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.ChatFormatting; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.Style; import net.minecraft.world.item.ItemStack; -class CraftingGridTransferHandler implements TransferHandler { +import static com.refinedmods.refinedstorage.rei.common.Common.MOD_ID; +import static java.util.Comparator.comparingLong; + +class CraftingGridTransferHandler extends AbstractTransferHandler { private static final Color MISSING_COLOR = new Color(1.0f, 0.0f, 0.0f, 0.4f); + private static final Component MISSING = Component.translatable("gui.%s.transfer.missing".formatted(MOD_ID)) + .withStyle(ChatFormatting.RED); + private static final Component CTRL_CLICK_TO_AUTOCRAFT = Component + .translatable("gui.%s.transfer.ctrl_click_to_autocraft".formatted(MOD_ID)) + .withStyle(Style.EMPTY.withItalic(true).withColor(ChatFormatting.WHITE)); + private static final Component MISSING_BUT_ALL_AUTOCRAFTABLE = createAutocraftableHint( + Component.translatable("gui.%s.transfer.missing_but_all_autocraftable".formatted(MOD_ID)) + ).append("\n").append(CTRL_CLICK_TO_AUTOCRAFT); + private static final Component MISSING_BUT_AUTOCRAFTABLE = createAutocraftableHint( + Component.translatable("gui.%s.transfer.missing_but_some_autocraftable".formatted(MOD_ID)) + ).append("\n").append(CTRL_CLICK_TO_AUTOCRAFT); @Override public Result handle(final Context context) { @@ -34,51 +51,87 @@ public Result handle(final Context context) { return Result.createNotApplicable(); } final List ingredients = defaultCraftingDisplay.getOrganisedInputEntries(3, 3); + final MutableResourceList available = containerMenu.getAvailableListForRecipeTransfer(); + final GridView view = containerMenu.getView(); + final TransferInputs transferInputs = getTransferInputs(view, ingredients, available); + final TransferType type = transferInputs.getType(); if (context.isActuallyCrafting()) { - doTransfer(ingredients, containerMenu); + if (type.canOpenAutocraftingPreview() && Screen.hasControlDown()) { + final List craftingRequests = transferInputs.createCraftingRequests(); + RefinedStorageApi.INSTANCE.openAutocraftingPreview(craftingRequests, context.getContainerScreen()); + return Result.createSuccessful().blocksFurtherHandling(false); + } else { + doTransfer(ingredients, containerMenu); + } return Result.createSuccessful().blocksFurtherHandling(); } - final ResourceList available = containerMenu.getAvailableListForRecipeTransfer(); - final MissingIngredients missingIngredients = findMissingIngredients(ingredients, available); - if (missingIngredients.isEmpty()) { + if (type == TransferType.AVAILABLE) { return Result.createSuccessful().blocksFurtherHandling(); } return Result.createSuccessful() - .color(MISSING_COLOR.getRGB()) - .tooltipMissing(missingIngredients.getIngredients()) - .renderer(createMissingItemsRenderer(missingIngredients)) + .color(getColor(type)) + .tooltip(getTooltip(type)) + .renderer(createTransferInputsRenderer(transferInputs)) .blocksFurtherHandling(); } + private static int getColor(final TransferType type) { + return switch (type) { + case MISSING -> MISSING_COLOR.getRGB(); + case MISSING_BUT_ALL_AUTOCRAFTABLE, MISSING_BUT_SOME_AUTOCRAFTABLE -> AUTOCRAFTABLE_COLOR; + default -> 0; + }; + } + + private static Component getTooltip(final TransferType type) { + return switch (type) { + case MISSING -> MISSING; + case MISSING_BUT_ALL_AUTOCRAFTABLE -> MISSING_BUT_ALL_AUTOCRAFTABLE; + case MISSING_BUT_SOME_AUTOCRAFTABLE -> MISSING_BUT_AUTOCRAFTABLE; + default -> Component.empty(); + }; + } + private void doTransfer(final List ingredients, final CraftingGridContainerMenu containerMenu) { final List> inputs = getInputs(ingredients); containerMenu.transferRecipe(inputs); } - private MissingIngredients findMissingIngredients(final List ingredients, - final ResourceList available) { - final MissingIngredients missingIngredients = new MissingIngredients(); + private TransferInputs getTransferInputs(final GridView view, + final List ingredients, + final MutableResourceList available) { + final TransferInputs transferInputs = new TransferInputs(); for (int i = 0; i < ingredients.size(); ++i) { final EntryIngredient ingredient = ingredients.get(i); if (ingredient.isEmpty()) { continue; } - if (!isAvailable(available, ingredient)) { - missingIngredients.addIngredient(ingredient, i); - } + final TransferInput transferInput = toTransferInput(view, available, ingredient); + transferInputs.addInput(i, transferInput); } - return missingIngredients; + return transferInputs; } - private boolean isAvailable(final ResourceList available, final EntryIngredient ingredient) { + private TransferInput toTransferInput(final GridView view, + final MutableResourceList available, + final EntryIngredient ingredient) { final List possibilities = convertIngredientToItemStacks(ingredient); for (final ItemStack possibility : possibilities) { final ItemResource possibilityResource = ItemResource.ofItemStack(possibility); if (available.remove(possibilityResource, 1).isPresent()) { - return true; + return new TransferInput(ingredient, TransferInputType.AVAILABLE, null); } } - return false; + final List autocraftingPossibilities = possibilities + .stream() + .map(ItemResource::ofItemStack) + .filter(view::isAutocraftable) + .sorted(comparingLong(view::getAmount)) + .toList(); + if (!autocraftingPossibilities.isEmpty()) { + return new TransferInput(ingredient, TransferInputType.AUTOCRAFTABLE, autocraftingPossibilities.getFirst()); + } + return new TransferInput(ingredient, TransferInputType.MISSING, null); } private List> getInputs(final List ingredients) { @@ -96,31 +149,22 @@ private List convertIngredientToItemStacks(final EntryIngredient ingr ); } - private TransferHandlerRenderer createMissingItemsRenderer(final MissingIngredients missingIngredients) { + private TransferHandlerRenderer createTransferInputsRenderer(final TransferInputs transferInputs) { return (graphics, mouseX, mouseY, delta, widgets, bounds, display) -> { int index = 0; for (final Widget widget : widgets) { - if (widget instanceof Slot slot - && slot.getNoticeMark() == Slot.INPUT - && missingIngredients.isMissing(index++)) { - renderMissingItemOverlay(graphics, slot); + if (widget instanceof Slot slot && slot.getNoticeMark() == Slot.INPUT) { + final TransferInput input = transferInputs.getInput(index++); + if (input == null) { + continue; + } + if (input.type() == TransferInputType.MISSING) { + renderSlotHighlight(graphics, slot, MISSING_COLOR.getRGB()); + } else if (input.type() == TransferInputType.AUTOCRAFTABLE) { + renderSlotHighlight(graphics, slot, AUTOCRAFTABLE_COLOR); + } } } }; } - - private void renderMissingItemOverlay(final GuiGraphics graphics, final Slot slot) { - final PoseStack poseStack = graphics.pose(); - poseStack.pushPose(); - poseStack.translate(0, 0, 400); - final Rectangle innerBounds = slot.getInnerBounds(); - graphics.fill( - innerBounds.x, - innerBounds.y, - innerBounds.getMaxX(), - innerBounds.getMaxY(), - MISSING_COLOR.getRGB() - ); - poseStack.popPose(); - } } diff --git a/refinedstorage-rei-integration-neoforge/src/main/java/com/refinedmods/refinedstorage/rei/forge/MissingIngredients.java b/refinedstorage-rei-integration-neoforge/src/main/java/com/refinedmods/refinedstorage/rei/forge/MissingIngredients.java deleted file mode 100644 index ca235e2..0000000 --- a/refinedstorage-rei-integration-neoforge/src/main/java/com/refinedmods/refinedstorage/rei/forge/MissingIngredients.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.refinedmods.refinedstorage.rei.forge; - -import java.util.ArrayList; -import java.util.List; - -import it.unimi.dsi.fastutil.ints.IntOpenHashSet; -import it.unimi.dsi.fastutil.ints.IntSet; -import me.shedaniel.rei.api.common.entry.EntryIngredient; - -class MissingIngredients { - private final List ingredients = new ArrayList<>(); - private final IntSet indices = new IntOpenHashSet(); - - boolean isEmpty() { - return indices.isEmpty(); - } - - void addIngredient(final EntryIngredient ingredient, final int slotIndex) { - ingredients.add(ingredient); - indices.add(slotIndex); - } - - List getIngredients() { - return ingredients; - } - - boolean isMissing(final int slotIndex) { - return indices.contains(slotIndex); - } -} diff --git a/refinedstorage-rei-integration-neoforge/src/main/java/com/refinedmods/refinedstorage/rei/forge/PatternGridTransferHandler.java b/refinedstorage-rei-integration-neoforge/src/main/java/com/refinedmods/refinedstorage/rei/forge/PatternGridTransferHandler.java index c111dc6..c919552 100644 --- a/refinedstorage-rei-integration-neoforge/src/main/java/com/refinedmods/refinedstorage/rei/forge/PatternGridTransferHandler.java +++ b/refinedstorage-rei-integration-neoforge/src/main/java/com/refinedmods/refinedstorage/rei/forge/PatternGridTransferHandler.java @@ -1,5 +1,6 @@ package com.refinedmods.refinedstorage.rei.forge; +import com.refinedmods.refinedstorage.api.grid.view.GridView; import com.refinedmods.refinedstorage.api.resource.ResourceAmount; import com.refinedmods.refinedstorage.common.api.RefinedStorageApi; import com.refinedmods.refinedstorage.common.autocrafting.PatternGridContainerMenu; @@ -7,56 +8,85 @@ import java.util.List; import java.util.stream.Collectors; -import javax.annotation.Nullable; -import me.shedaniel.rei.api.client.registry.transfer.TransferHandler; +import me.shedaniel.rei.api.client.gui.widgets.Slot; +import me.shedaniel.rei.api.client.gui.widgets.Widget; +import me.shedaniel.rei.api.client.registry.transfer.TransferHandlerRenderer; import me.shedaniel.rei.api.common.entry.EntryIngredient; import me.shedaniel.rei.api.common.entry.EntryStack; import me.shedaniel.rei.api.common.entry.type.VanillaEntryTypes; import me.shedaniel.rei.api.common.util.CollectionUtils; import me.shedaniel.rei.plugin.common.BuiltinPlugin; import me.shedaniel.rei.plugin.common.displays.crafting.DefaultCraftingDisplay; +import net.minecraft.network.chat.Component; import net.minecraft.world.item.ItemStack; -class PatternGridTransferHandler implements TransferHandler { +import static com.refinedmods.refinedstorage.rei.common.Common.MOD_ID; + +class PatternGridTransferHandler extends AbstractTransferHandler { + private static final Component ALL_AUTOCRAFTABLE = createAutocraftableHint( + Component.translatable("gui.%s.transfer.all_autocraftable".formatted(MOD_ID)) + ); + private static final Component SOME_AUTOCRAFTABLE = createAutocraftableHint( + Component.translatable("gui.%s.transfer.some_autocraftable".formatted(MOD_ID)) + ); + @Override public Result handle(final Context context) { if (!(context.getMenu() instanceof PatternGridContainerMenu containerMenu)) { return Result.createNotApplicable(); } - final Result result = transferRegularRecipe(context, containerMenu); - if (result != null) { - return result; + final GridView view = containerMenu.getView(); + final List inputs = context.getDisplay() + .getInputEntries() + .stream() + .filter(ingredient -> !ingredient.isEmpty()) + .toList(); + final List autocraftable = getAutocraftableIngredients(view, inputs); + if (!transferRegularRecipe(context, containerMenu)) { + transferProcessingRecipe(context, containerMenu); + } + final Result result = Result.createSuccessful(); + if (!autocraftable.isEmpty()) { + final boolean areAllAutocraftable = inputs.size() == autocraftable.size(); + if (areAllAutocraftable) { + result.tooltip(ALL_AUTOCRAFTABLE); + } else { + result.tooltip(SOME_AUTOCRAFTABLE); + } + result.color(AUTOCRAFTABLE_COLOR); + result.renderer(createAutocraftableRenderer(autocraftable)); } - return transferProcessingRecipe(context, containerMenu); + return result.blocksFurtherHandling(); } - @Nullable - private Result transferRegularRecipe(final Context context, final PatternGridContainerMenu containerMenu) { + private boolean transferRegularRecipe(final Context context, final PatternGridContainerMenu containerMenu) { if (context.getDisplay().getCategoryIdentifier().equals(BuiltinPlugin.CRAFTING) && context.getDisplay() instanceof DefaultCraftingDisplay defaultCraftingDisplay) { - return transferCraftingRecipe(context, containerMenu, defaultCraftingDisplay); + transferCraftingRecipe(context, containerMenu, defaultCraftingDisplay); + return true; } else if (context.getDisplay().getCategoryIdentifier().equals(BuiltinPlugin.STONE_CUTTING)) { - return transferStonecutterRecipe(context, containerMenu); + transferStonecutterRecipe(context, containerMenu); + return true; } else if (context.getDisplay().getCategoryIdentifier().equals(BuiltinPlugin.SMITHING)) { - return transferSmithingTableRecipe(context, containerMenu); + transferSmithingTableRecipe(context, containerMenu); + return true; } - return null; + return false; } - private Result transferCraftingRecipe(final Context context, - final PatternGridContainerMenu containerMenu, - final DefaultCraftingDisplay display) { + private void transferCraftingRecipe(final Context context, + final PatternGridContainerMenu containerMenu, + final DefaultCraftingDisplay display) { final List> inputs = getItems( display.getOrganisedInputEntries(3, 3) ); if (context.isActuallyCrafting()) { containerMenu.transferCraftingRecipe(inputs); } - return Result.createSuccessful().blocksFurtherHandling(); } - private Result transferStonecutterRecipe(final Context context, final PatternGridContainerMenu menu) { + private void transferStonecutterRecipe(final Context context, final PatternGridContainerMenu menu) { final List> inputs = getItems(context.getDisplay().getInputEntries()); final List> outputs = getItems(context.getDisplay().getOutputEntries()); final boolean valid = !inputs.isEmpty() @@ -66,24 +96,21 @@ private Result transferStonecutterRecipe(final Context context, final PatternGri if (context.isActuallyCrafting() && valid) { menu.transferStonecutterRecipe(inputs.getFirst().getFirst(), outputs.getFirst().getFirst()); } - return Result.createSuccessful().blocksFurtherHandling(); } - private Result transferSmithingTableRecipe(final Context context, final PatternGridContainerMenu menu) { + private void transferSmithingTableRecipe(final Context context, final PatternGridContainerMenu menu) { final List> inputs = getItems(context.getDisplay().getInputEntries()); if (context.isActuallyCrafting() && inputs.size() == 3) { menu.transferSmithingTableRecipe(inputs.get(0), inputs.get(1), inputs.get(2)); } - return Result.createSuccessful().blocksFurtherHandling(); } - private Result transferProcessingRecipe(final Context context, final PatternGridContainerMenu menu) { + private void transferProcessingRecipe(final Context context, final PatternGridContainerMenu menu) { final List> inputs = getResources(context.getDisplay().getInputEntries()); final List> outputs = getResources(context.getDisplay().getOutputEntries()); if (context.isActuallyCrafting()) { menu.transferProcessingRecipe(inputs, outputs); } - return Result.createSuccessful().blocksFurtherHandling(); } private List> getItems(final List ingredients) { @@ -103,11 +130,38 @@ private List convertIngredientToItemStacks(final EntryIngredient ingr private List> getResources(final List ingredients) { return ingredients.stream() - .map(ingredient -> ingredient.stream() - .flatMap( - item -> RefinedStorageApi.INSTANCE.getIngredientConverter().convertToResourceAmount(item.getValue()) - .stream()) - .collect(Collectors.toList())) + .map(this::getResources) .toList(); } + + private List getResources(final EntryIngredient ingredient) { + return ingredient.stream() + .flatMap( + item -> RefinedStorageApi.INSTANCE.getIngredientConverter().convertToResourceAmount(item.getValue()) + .stream()) + .collect(Collectors.toList()); + } + + private List getAutocraftableIngredients(final GridView view, + final List ingredients) { + return ingredients.stream().filter(ingredient -> getResources(ingredient) + .stream() + .map(ResourceAmount::resource) + .anyMatch(view::isAutocraftable)) + .toList(); + } + + private TransferHandlerRenderer createAutocraftableRenderer(final List autocraftableIngredients) { + return (graphics, mouseX, mouseY, delta, widgets, bounds, display) -> { + for (final Widget widget : widgets) { + if (widget instanceof Slot slot && slot.getNoticeMark() == Slot.INPUT) { + final boolean autocraftable = autocraftableIngredients.stream() + .anyMatch(ingredient -> ingredient.stream().anyMatch(ing -> slot.getEntries().contains(ing))); + if (autocraftable) { + renderSlotHighlight(graphics, slot, AUTOCRAFTABLE_COLOR); + } + } + } + }; + } } diff --git a/refinedstorage-rei-integration-neoforge/src/main/java/com/refinedmods/refinedstorage/rei/forge/TransferInput.java b/refinedstorage-rei-integration-neoforge/src/main/java/com/refinedmods/refinedstorage/rei/forge/TransferInput.java new file mode 100644 index 0000000..ea96c41 --- /dev/null +++ b/refinedstorage-rei-integration-neoforge/src/main/java/com/refinedmods/refinedstorage/rei/forge/TransferInput.java @@ -0,0 +1,10 @@ +package com.refinedmods.refinedstorage.rei.forge; + +import com.refinedmods.refinedstorage.common.support.resource.ItemResource; + +import javax.annotation.Nullable; + +import me.shedaniel.rei.api.common.entry.EntryIngredient; + +record TransferInput(EntryIngredient ingredient, TransferInputType type, @Nullable ItemResource autocraftableResource) { +} diff --git a/refinedstorage-rei-integration-neoforge/src/main/java/com/refinedmods/refinedstorage/rei/forge/TransferInputType.java b/refinedstorage-rei-integration-neoforge/src/main/java/com/refinedmods/refinedstorage/rei/forge/TransferInputType.java new file mode 100644 index 0000000..69145fc --- /dev/null +++ b/refinedstorage-rei-integration-neoforge/src/main/java/com/refinedmods/refinedstorage/rei/forge/TransferInputType.java @@ -0,0 +1,7 @@ +package com.refinedmods.refinedstorage.rei.forge; + +enum TransferInputType { + AVAILABLE, + MISSING, + AUTOCRAFTABLE +} diff --git a/refinedstorage-rei-integration-neoforge/src/main/java/com/refinedmods/refinedstorage/rei/forge/TransferInputs.java b/refinedstorage-rei-integration-neoforge/src/main/java/com/refinedmods/refinedstorage/rei/forge/TransferInputs.java new file mode 100644 index 0000000..3826a20 --- /dev/null +++ b/refinedstorage-rei-integration-neoforge/src/main/java/com/refinedmods/refinedstorage/rei/forge/TransferInputs.java @@ -0,0 +1,52 @@ +package com.refinedmods.refinedstorage.rei.forge; + +import com.refinedmods.refinedstorage.api.resource.ResourceAmount; +import com.refinedmods.refinedstorage.api.resource.list.MutableResourceList; +import com.refinedmods.refinedstorage.api.resource.list.MutableResourceListImpl; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.annotation.Nullable; + +class TransferInputs { + private final List inputs = new ArrayList<>(); + private final Map inputsBySlotIndex = new HashMap<>(); + + void addInput(final int index, final TransferInput input) { + inputs.add(input); + inputsBySlotIndex.put(index, input); + } + + List createCraftingRequests() { + final MutableResourceList requests = MutableResourceListImpl.orderPreserving(); + for (final TransferInput transferInput : inputs) { + if (transferInput.type() == TransferInputType.AUTOCRAFTABLE + && transferInput.autocraftableResource() != null) { + requests.add(transferInput.autocraftableResource(), 1); + } + } + return requests.copyState().stream().toList(); + } + + @Nullable + TransferInput getInput(final int slotIndex) { + return inputsBySlotIndex.get(slotIndex); + } + + TransferType getType() { + if (inputs.stream().allMatch(input -> input.type() == TransferInputType.AVAILABLE)) { + return TransferType.AVAILABLE; + } + final boolean hasMissing = inputs.stream().anyMatch(input -> input.type() == TransferInputType.MISSING); + final boolean hasAutocraftable = inputs.stream() + .anyMatch(input -> input.type() == TransferInputType.AUTOCRAFTABLE); + if (hasMissing && hasAutocraftable) { + return TransferType.MISSING_BUT_SOME_AUTOCRAFTABLE; + } else if (hasAutocraftable) { + return TransferType.MISSING_BUT_ALL_AUTOCRAFTABLE; + } + return TransferType.MISSING; + } +} diff --git a/refinedstorage-rei-integration-neoforge/src/main/java/com/refinedmods/refinedstorage/rei/forge/TransferType.java b/refinedstorage-rei-integration-neoforge/src/main/java/com/refinedmods/refinedstorage/rei/forge/TransferType.java new file mode 100644 index 0000000..2b3e53b --- /dev/null +++ b/refinedstorage-rei-integration-neoforge/src/main/java/com/refinedmods/refinedstorage/rei/forge/TransferType.java @@ -0,0 +1,13 @@ +package com.refinedmods.refinedstorage.rei.forge; + +enum TransferType { + AVAILABLE, + MISSING, + MISSING_BUT_ALL_AUTOCRAFTABLE, + MISSING_BUT_SOME_AUTOCRAFTABLE; + + boolean canOpenAutocraftingPreview() { + return this == TransferType.MISSING_BUT_ALL_AUTOCRAFTABLE + || this == TransferType.MISSING_BUT_SOME_AUTOCRAFTABLE; + } +} diff --git a/refinedstorage-rei-integration-neoforge/src/main/resources/META-INF/neoforge.mods.toml b/refinedstorage-rei-integration-neoforge/src/main/templates/META-INF/neoforge.mods.toml similarity index 94% rename from refinedstorage-rei-integration-neoforge/src/main/resources/META-INF/neoforge.mods.toml rename to refinedstorage-rei-integration-neoforge/src/main/templates/META-INF/neoforge.mods.toml index 73de28d..4369be2 100644 --- a/refinedstorage-rei-integration-neoforge/src/main/resources/META-INF/neoforge.mods.toml +++ b/refinedstorage-rei-integration-neoforge/src/main/templates/META-INF/neoforge.mods.toml @@ -14,7 +14,7 @@ REI integration for Refined Storage. [[dependencies.refinedstorage_rei_integration]] modId = "refinedstorage" type = "required" -versionRange = "2.0.0-milestone.4.7" +versionRange = "2.0.0-milestone.4.8" side = "BOTH" [[dependencies.refinedstorage_rei_integration]] modId = "roughlyenoughitems"