Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: autocrafting transfer support #30

Merged
merged 1 commit into from
Oct 12, 2024
Merged
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
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 1 addition & 1 deletion .github/workflows/draft-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/issue-for-unsupported-version.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
uses: refinedmods/refinedarchitect/.github/workflows/issue-for-unsupported-version.yml@v0.19.1
2 changes: 1 addition & 1 deletion .github/workflows/publish-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/resolved-issue-locking.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ on:
- cron: '0 0 * * *'
jobs:
lock:
uses: refinedmods/refinedarchitect/.github/workflows/resolved-issue-locking.yml@v0.17.1
uses: refinedmods/refinedarchitect/.github/workflows/resolved-issue-locking.yml@v0.19.1
2 changes: 1 addition & 1 deletion .github/workflows/validate-branch-name.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
uses: refinedmods/refinedarchitect/.github/workflows/validate-branch-name.yml@v0.19.1
2 changes: 1 addition & 1 deletion .github/workflows/validate-changelog.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
uses: refinedmods/refinedarchitect/.github/workflows/validate-changelog.yml@v0.19.1
2 changes: 1 addition & 1 deletion .github/workflows/validate-commit-messages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
uses: refinedmods/refinedarchitect/.github/workflows/validate-commit-messages.yml@v0.19.1
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ Adds support for:
- [Crowdin](https://crowdin.com/project/refined-storage-rei-integration)
- [Discord](https://discordapp.com/invite/VYzsydb)
- [Twitter](https://twitter.com/refinedmods)
- [Mastodon](https://anvil.social/@refinedmods)

## Building

Expand Down
8 changes: 6 additions & 2 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
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
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
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
7 changes: 6 additions & 1 deletion refinedstorage-rei-integration-fabric/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ repositories {
refinedarchitect {
modId = "refinedstorage_rei_integration"
fabric()
compileWithProject(project(":refinedstorage-rei-integration-common"))
publishing {
maven = true
}
Expand All @@ -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}")
Expand Down
Original file line number Diff line number Diff line change
@@ -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();
}
}
Original file line number Diff line number Diff line change
@@ -1,30 +1,47 @@
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;

import java.awt.Color;
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;
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.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) {
Expand All @@ -34,51 +51,93 @@ public Result handle(final Context context) {
return Result.createNotApplicable();
}
final List<EntryIngredient> 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);
return Result.createSuccessful().blocksFurtherHandling();
return doActuallyCrafting(context, containerMenu, type, transferInputs, ingredients);
}
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 Result doActuallyCrafting(final Context context, final CraftingGridContainerMenu containerMenu,
final TransferType type, final TransferInputs transferInputs,
final List<EntryIngredient> ingredients) {
if (type.canOpenAutocraftingPreview() && Screen.hasControlDown()) {
final List<ResourceAmount> craftingRequests = transferInputs.createCraftingRequests();
RefinedStorageApi.INSTANCE.openAutocraftingPreview(craftingRequests, context.getContainerScreen());
return Result.createSuccessful().blocksFurtherHandling(false);
} else {
doTransfer(ingredients, containerMenu);
}
return Result.createSuccessful().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<EntryIngredient> ingredients, final CraftingGridContainerMenu containerMenu) {
final List<List<ItemResource>> inputs = getInputs(ingredients);
containerMenu.transferRecipe(inputs);
}

private MissingIngredients findMissingIngredients(final List<EntryIngredient> ingredients,
final ResourceList available) {
final MissingIngredients missingIngredients = new MissingIngredients();
private TransferInputs getTransferInputs(final GridView view,
final List<EntryIngredient> 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<ItemStack> 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<ItemResource> 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<List<ItemResource>> getInputs(final List<EntryIngredient> ingredients) {
Expand All @@ -96,31 +155,22 @@ private List<ItemStack> 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();
}
}
Loading
Loading