Skip to content

Commit

Permalink
feat: autocrafting transfer support
Browse files Browse the repository at this point in the history
  • Loading branch information
raoulvdberge committed Oct 6, 2024
1 parent e56c21c commit 534ef87
Show file tree
Hide file tree
Showing 22 changed files with 278 additions and 42 deletions.
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 - EMI 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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ 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.

## [0.4.1] - 2024-08-11

### Fixed
Expand Down
8 changes: 6 additions & 2 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
refinedarchitectVersion=0.17.1
refinedstorageVersion=2.0.0-milestone.4.7
refinedarchitectVersion=0.19.1
refinedstorageVersion=0.0.0
emiVersion=1.1.11+1.21
# 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
1 change: 1 addition & 0 deletions refinedstorage-emi-integration-common/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ plugins {
}

repositories {
mavenLocal()
maven {
url = uri("https://maven.pkg.github.com/refinedmods/refinedstorage2")
credentials {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.refinedmods.refinedstorage.emi.common;

import com.refinedmods.refinedstorage.common.grid.AutocraftableResourceHint;

import dev.emi.emi.api.recipe.handler.EmiRecipeHandler;
import net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipComponent;
import net.minecraft.network.chat.Component;
import net.minecraft.world.inventory.AbstractContainerMenu;

abstract class AbstractEmiRecipeHandler<T extends AbstractContainerMenu> implements EmiRecipeHandler<T> {
protected static final int AUTOCRAFTABLE_COLOR = AutocraftableResourceHint.AUTOCRAFTABLE.getColor();

protected static ClientTooltipComponent createAutocraftableHint(final Component component) {
return ClientTooltipComponent.create(component.copy().withColor(AUTOCRAFTABLE_COLOR).getVisualOrderText());
}
}
Original file line number Diff line number Diff line change
@@ -1,28 +1,45 @@
package com.refinedmods.refinedstorage.emi.common;

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.api.resource.list.MutableResourceListImpl;
import com.refinedmods.refinedstorage.api.resource.list.ResourceList;
import com.refinedmods.refinedstorage.common.api.RefinedStorageApi;
import com.refinedmods.refinedstorage.common.grid.CraftingGridContainerMenu;
import com.refinedmods.refinedstorage.common.support.resource.ItemResource;
import com.refinedmods.refinedstorage.common.support.tooltip.HelpClientTooltipComponent;

import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

import com.mojang.blaze3d.systems.RenderSystem;
import dev.emi.emi.EmiPort;
import dev.emi.emi.api.recipe.EmiCraftingRecipe;
import dev.emi.emi.api.recipe.EmiPlayerInventory;
import dev.emi.emi.api.recipe.EmiRecipe;
import dev.emi.emi.api.recipe.handler.EmiCraftContext;
import dev.emi.emi.api.recipe.handler.EmiRecipeHandler;
import dev.emi.emi.api.stack.EmiIngredient;
import dev.emi.emi.api.stack.EmiStack;
import dev.emi.emi.api.widget.Bounds;
import dev.emi.emi.api.widget.SlotWidget;
import dev.emi.emi.api.widget.Widget;
import dev.emi.emi.runtime.EmiDrawContext;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
import net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipComponent;
import net.minecraft.network.chat.Component;

import static com.refinedmods.refinedstorage.emi.common.Common.MOD_ID;
import static java.util.Comparator.comparingLong;

class CraftingGridEmiRecipeHandler extends AbstractEmiRecipeHandler<CraftingGridContainerMenu> {
private static final Component CTRL_CLICK_TO_AUTOCRAFT = Component.translatable(
"gui.%s.transfer.ctrl_click_to_autocraft".formatted(MOD_ID)
);

class CraftingGridEmiRecipeHandler implements EmiRecipeHandler<CraftingGridContainerMenu> {
@Override
public EmiPlayerInventory getInventory(final AbstractContainerScreen<CraftingGridContainerMenu> screen) {
final ResourceList available = screen.getMenu().getAvailableListForRecipeTransfer();
Expand All @@ -42,33 +59,30 @@ public boolean supportsRecipe(final EmiRecipe recipe) {

@Override
public boolean canCraft(final EmiRecipe recipe, final EmiCraftContext<CraftingGridContainerMenu> context) {
final ResourceList available = context.getScreenHandler().getAvailableListForRecipeTransfer();
return !hasMissingItems(recipe.getInputs(), available);
}

private boolean hasMissingItems(final List<EmiIngredient> inputs, final ResourceList available) {
for (final EmiIngredient input : inputs) {
if (input.isEmpty()) {
continue;
}
if (!isAvailable(available, input)) {
return true;
}
}
return false;
return true;
}

private boolean isAvailable(final ResourceList available, final EmiIngredient input) {
private static TransferInput toTransferInput(final GridView view,
final MutableResourceList available,
final EmiIngredient input) {
final List<ItemResource> possibilities = getItems(input);
for (final ItemResource possibility : possibilities) {
if (available.remove(possibility, 1).isPresent()) {
return true;
return new TransferInput(TransferInputType.AVAILABLE, null);
}
}
return false;
final List<ItemResource> autocraftingPossibilities = possibilities
.stream()
.filter(view::isAutocraftable)
.sorted(comparingLong(view::getAmount))
.toList();
if (!autocraftingPossibilities.isEmpty()) {
return new TransferInput(TransferInputType.AUTOCRAFTABLE, autocraftingPossibilities.getFirst());
}
return new TransferInput(TransferInputType.MISSING, null);
}

private List<ItemResource> getItems(final EmiIngredient ingredient) {
private static List<ItemResource> getItems(final EmiIngredient ingredient) {
return ingredient.getEmiStacks()
.stream()
.map(EmiStack::getItemStack)
Expand All @@ -79,9 +93,21 @@ private List<ItemResource> getItems(final EmiIngredient ingredient) {

@Override
public boolean craft(final EmiRecipe recipe, final EmiCraftContext<CraftingGridContainerMenu> context) {
final MutableResourceList available = context.getScreenHandler().getAvailableListForRecipeTransfer();
final List<TransferInput> transferInputs = recipe.getInputs()
.stream()
.filter(input -> !input.isEmpty())
.map(input -> toTransferInput(context.getScreenHandler().getView(), available, input))
.toList();
final TransferType transferType = getTransferType(transferInputs);
if (transferType.canOpenAutocraftingPreview() && Screen.hasControlDown()) {
final List<ResourceAmount> craftingRequests = createCraftingRequests(transferInputs);
RefinedStorageApi.INSTANCE.openCraftingPreview(craftingRequests, context.getScreen());
return false;
}
final List<List<ItemResource>> inputs = recipe.getInputs()
.stream()
.map(this::getItems)
.map(CraftingGridEmiRecipeHandler::getItems)
.toList();
context.getScreenHandler().transferRecipe(inputs);
return true;
Expand All @@ -94,16 +120,80 @@ public void render(final EmiRecipe recipe,
final GuiGraphics draw) {
final EmiDrawContext context = EmiDrawContext.wrap(draw);
RenderSystem.enableDepthTest();
final ResourceList available = craftContext.getScreenHandler().getAvailableListForRecipeTransfer();
final MutableResourceList available = craftContext.getScreenHandler().getAvailableListForRecipeTransfer();
final GridView view = craftContext.getScreenHandler().getView();
for (final Widget widget : widgets) {
if (!(widget instanceof SlotWidget slotWidget)) {
continue;
}
final EmiIngredient stack = slotWidget.getStack();
final Bounds bounds = slotWidget.getBounds();
if (slotWidget.getRecipe() == null && !stack.isEmpty() && !isAvailable(available, stack)) {
context.fill(bounds.x(), bounds.y(), bounds.width(), bounds.height(), 0x44FF0000);
if (slotWidget.getRecipe() == null && !stack.isEmpty()) {
final TransferInput transferInput = toTransferInput(view, available, stack);
if (transferInput.type() == TransferInputType.MISSING) {
context.fill(bounds.x(), bounds.y(), bounds.width(), bounds.height(), 0x44FF0000);
} else if (transferInput.type() == TransferInputType.AUTOCRAFTABLE) {
context.fill(bounds.x(), bounds.y(), bounds.width(), bounds.height(), AUTOCRAFTABLE_COLOR);
}
}
}
}

@Override
public List<ClientTooltipComponent> getTooltip(final EmiRecipe recipe,
final EmiCraftContext<CraftingGridContainerMenu> context) {
final MutableResourceList available = context.getScreenHandler().getAvailableListForRecipeTransfer();
final List<TransferInput> transferInputs = recipe.getInputs()
.stream()
.filter(input -> !input.isEmpty())
.map(input -> toTransferInput(context.getScreenHandler().getView(), available, input))
.toList();
final TransferType transferType = getTransferType(transferInputs);
return calculateTooltip(transferType);
}

private List<ClientTooltipComponent> calculateTooltip(final TransferType type) {
return switch (type) {
case MISSING -> List.of(ClientTooltipComponent.create(EmiPort.ordered(NOT_ENOUGH_INGREDIENTS)));
case MISSING_BUT_ALL_AUTOCRAFTABLE -> List.of(
createAutocraftableHint(
Component.translatable("gui.%s.transfer.missing_but_all_autocraftable".formatted(MOD_ID))
),
HelpClientTooltipComponent.createAlwaysDisplayed(CTRL_CLICK_TO_AUTOCRAFT)
);
case MISSING_BUT_SOME_AUTOCRAFTABLE -> List.of(
createAutocraftableHint(
Component.translatable("gui.%s.transfer.missing_but_some_autocraftable".formatted(MOD_ID))
),
HelpClientTooltipComponent.createAlwaysDisplayed(CTRL_CLICK_TO_AUTOCRAFT)
);
default -> Collections.emptyList();
};
}

private TransferType getTransferType(final List<TransferInput> transferInputs) {
if (transferInputs.stream().allMatch(input -> input.type() == TransferInputType.AVAILABLE)) {
return TransferType.AVAILABLE;
}
final boolean hasMissing = transferInputs.stream().anyMatch(input -> input.type() == TransferInputType.MISSING);
final boolean hasAutocraftable = transferInputs.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;
}

private static List<ResourceAmount> createCraftingRequests(final List<TransferInput> transferInputs) {
final MutableResourceList requests = MutableResourceListImpl.orderPreserving();
for (final TransferInput transferInput : transferInputs) {
if (transferInput.type() == TransferInputType.AUTOCRAFTABLE
&& transferInput.autocraftableResource() != null) {
requests.add(transferInput.autocraftableResource(), 1);
}
}
return requests.copyState().stream().toList();
}
}
Loading

0 comments on commit 534ef87

Please sign in to comment.