9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-26 02:19:23 +00:00

Merge branch 'Xiao-MoMi:dev' into dev

This commit is contained in:
jhqwqmc
2025-03-20 04:30:57 +08:00
committed by GitHub
19 changed files with 393 additions and 32 deletions

View File

@@ -26,9 +26,9 @@ resource-pack:
obfuscation:
enable: false
seed: 0 # 0 = random seed
fake-directory: true
escape-unicode: true
break-json: true
fake-directory: false
escape-unicode: false
break-json: false
resource-location:
enable: true
random-namespace:
@@ -184,12 +184,14 @@ gui:
title: "<white><shift:-11><image:internal:cooking_recipe><shift:-136><image:internal:smelting>"
smoking:
title: "<white><shift:-11><image:internal:cooking_recipe><shift:-136><image:internal:smoking>"
campfire:
campfire-cooking:
title: "<white><shift:-11><image:internal:cooking_recipe><shift:-136><image:internal:campfire>"
crafting:
title: "<white><shift:-11><image:internal:crafting_recipe>"
stonecutting:
title: "<white><shift:-11><image:internal:stonecutting_recipe>"
smithing-transform:
title: "<white><shift:-11><image:internal:smithing_transform_recipe>"
performance:
# Maximum chain update depth when fixing client visuals

View File

@@ -329,4 +329,70 @@ recipes#11:
B: "minecraft:stick"
result:
id: default:topaz_pickaxe
count: 1
default:topaz_helmet:
type: shaped
pattern:
- "AAA"
- "A A"
ingredients:
A: "default:topaz"
result:
id: default:topaz_helmet
count: 1
default:topaz_chestplate:
type: shaped
pattern:
- "A A"
- "AAA"
- "AAA"
ingredients:
A: "default:topaz"
result:
id: default:topaz_chestplate
count: 1
default:topaz_leggings:
type: shaped
pattern:
- "AAA"
- "A A"
- "A A"
ingredients:
A: "default:topaz"
result:
id: default:topaz_leggings
count: 1
default:topaz_boots:
type: shaped
pattern:
- "A A"
- "A A"
ingredients:
A: "default:topaz"
result:
id: default:topaz_boots
count: 1
default:topaz_bow:
type: smithing_transform
base: minecraft:bow
addition: default:topaz
template-type: default:topaz
result:
id: default:topaz_bow
count: 1
default:topaz_crossbow:
type: smithing_transform
base: minecraft:crossbow
addition: default:topaz
template-type: default:topaz
result:
id: default:topaz_crossbow
count: 1
default:topaz_rod:
type: smithing_transform
base: minecraft:fishing_rod
addition: default:topaz
template-type: default:topaz
result:
id: default:topaz_rod
count: 1

View File

@@ -53,12 +53,18 @@ images:
font: minecraft:internal
file: minecraft:font/gui/custom/stonecutting_recipe.png
char: '\ub008'
internal:smithing_transform_recipe:
height: 142
ascent: 20
font: minecraft:internal
file: minecraft:font/gui/custom/smithing_transform_recipe.png
char: '\ub009'
internal:no_recipe:
height: 140
ascent: 18
font: minecraft:internal
file: minecraft:font/gui/custom/no_recipe.png
char: '\ub009'
char: '\ub00a'
templates:
internal:icon/2d:

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -213,6 +213,7 @@ public class UniversalItemFactory extends BukkitItemFactory {
@Override
protected void maxStackSize(ItemWrapper<ItemStack> item, Integer maxStackSize) {
throw new UnsupportedOperationException("This feature is only available on 1.20.5+");
}
@Override
@@ -229,7 +230,9 @@ public class UniversalItemFactory extends BukkitItemFactory {
@Override
protected ItemWrapper<ItemStack> merge(ItemWrapper<ItemStack> item1, ItemWrapper<ItemStack> item2) {
Object itemStack = ItemObject.copy(item2.getLiteralObject());
ItemObject.setCustomDataTag(itemStack, ItemObject.getCustomDataTag(item1.getLiteralObject()));
ItemObject.setCustomDataTag(itemStack, TagCompound.clone(ItemObject.getCustomDataTag(item1.getLiteralObject())));
// one more step than vanilla
TagCompound.merge(ItemObject.getCustomDataTag(itemStack), ItemObject.getCustomDataTag(item2.getLiteralObject()), true, true);
return new RTagItemWrapper(new RtagItem(ItemObject.asCraftMirror(itemStack)), item2.count());
}
}

View File

@@ -195,7 +195,7 @@ public class BukkitRecipeManager implements RecipeManager<ItemStack> {
CraftEngine.instance().logger().warn("Failed to convert campfire recipe", e);
}
});
BUKKIT_RECIPE_FACTORIES.put(RecipeTypes.STONE_CUTTING, (key, recipe) -> {
BUKKIT_RECIPE_FACTORIES.put(RecipeTypes.STONECUTTING, (key, recipe) -> {
CustomStoneCuttingRecipe<ItemStack> ceRecipe = (CustomStoneCuttingRecipe<ItemStack>) recipe;
List<ItemStack> itemStacks = new ArrayList<>();
for (Holder<Key> item : ceRecipe.ingredient().items()) {
@@ -382,9 +382,13 @@ public class BukkitRecipeManager implements RecipeManager<ItemStack> {
this.byType.computeIfAbsent(recipe.type(), k -> new ArrayList<>()).add(recipe);
this.byId.put(id, recipe);
this.byResult.computeIfAbsent(recipe.result().item().id(), k -> new ArrayList<>()).add(recipe);
HashSet<Key> usedKeys = new HashSet<>();
for (Ingredient<ItemStack> ingredient : recipe.ingredientsInUse()) {
for (Holder<Key> holder : ingredient.items()) {
this.byIngredient.computeIfAbsent(holder.value(), k -> new ArrayList<>()).add(recipe);
Key key = holder.value();
if (usedKeys.add(key)) {
this.byIngredient.computeIfAbsent(key, k -> new ArrayList<>()).add(recipe);
}
}
}
}
@@ -765,7 +769,8 @@ public class BukkitRecipeManager implements RecipeManager<ItemStack> {
templateHolders.isEmpty() ? null : Ingredient.of(templateHolders),
additionHolders.isEmpty() ? null : Ingredient.of(additionHolders),
new CustomRecipeResult<>(new CloneableConstantItem(recipe.result().isCustom() ? Key.of("!internal:custom") : Key.of(recipe.result().id()), result), recipe.result().count()),
List.of(CustomSmithingTransformRecipe.ItemDataProcessor.MERGE_ALL)
true,
List.of()
);
SmithingTransformRecipe transformRecipe = new SmithingTransformRecipe(key, result,

View File

@@ -835,6 +835,7 @@ public class RecipeEventListener implements Listener {
@EventHandler(ignoreCancelled = true)
public void onSmithingTransform(PrepareSmithingEvent event) {
if (!ConfigManager.enableRecipeSystem()) return;
SmithingInventory inventory = event.getInventory();
if (!(inventory.getRecipe() instanceof SmithingTransformRecipe recipe)) return;
@@ -872,6 +873,21 @@ public class RecipeEventListener implements Listener {
CustomSmithingTransformRecipe<ItemStack> transformRecipe = (CustomSmithingTransformRecipe<ItemStack>) ceRecipe;
ItemStack processed = transformRecipe.assemble(new ItemBuildContext(this.plugin.adapt(player), ContextHolder.EMPTY), this.itemManager.wrap(base));
event.setResult(processed);
correctSmithingRecipeUsed(inventory, ceRecipe);
}
private void correctSmithingRecipeUsed(SmithingInventory inventory, Recipe<ItemStack> recipe) {
Object holderOrRecipe = recipeManager.getRecipeHolderByRecipe(recipe);
if (holderOrRecipe == null) {
// it's a vanilla recipe but not injected
return;
}
try {
Object resultInventory = Reflections.field$CraftResultInventory$resultInventory.get(inventory);
Reflections.field$ResultContainer$recipeUsed.set(resultInventory, holderOrRecipe);
} catch (ReflectiveOperationException e) {
plugin.logger().warn("Failed to correct used recipe", e);
}
}
private OptimizedIDItem<ItemStack> getOptimizedIDItem(@Nullable ItemStack itemStack) {

View File

@@ -4414,6 +4414,18 @@ public class Reflections {
)
);
public static final Class<?> clazz$CraftResultInventory = requireNonNull(
ReflectionUtils.getClazz(
BukkitReflectionUtils.assembleCBClass("inventory.CraftResultInventory")
)
);
public static final Field field$CraftResultInventory$resultInventory = requireNonNull(
ReflectionUtils.getDeclaredField(
clazz$CraftResultInventory, clazz$Container, 0
)
);
// 1.20.5+
public static final Method method$ItemStack$hurtAndBreak =
ReflectionUtils.getMethod(

View File

@@ -7,13 +7,16 @@ import net.momirealms.craftengine.core.item.recipe.input.SmithingInput;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.registry.BuiltInRegistries;
import net.momirealms.craftengine.core.registry.Holder;
import net.momirealms.craftengine.core.registry.Registries;
import net.momirealms.craftengine.core.registry.WritableRegistry;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.MiscUtils;
import net.momirealms.craftengine.core.util.ResourceKey;
import net.momirealms.craftengine.core.util.TriConsumer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.function.BiFunction;
public class CustomSmithingTransformRecipe<T> implements Recipe<T> {
public static final Factory<?> FACTORY = new Factory<>();
@@ -22,6 +25,7 @@ public class CustomSmithingTransformRecipe<T> implements Recipe<T> {
private final Ingredient<T> base;
private final Ingredient<T> template;
private final Ingredient<T> addition;
private final boolean mergeComponents;
private final List<ItemDataProcessor> processors;
public CustomSmithingTransformRecipe(Key id,
@@ -29,6 +33,7 @@ public class CustomSmithingTransformRecipe<T> implements Recipe<T> {
@Nullable Ingredient<T> template,
@Nullable Ingredient<T> addition,
CustomRecipeResult<T> result,
boolean mergeComponents,
List<ItemDataProcessor> processors
) {
this.id = id;
@@ -37,6 +42,7 @@ public class CustomSmithingTransformRecipe<T> implements Recipe<T> {
this.template = template;
this.addition = addition;
this.processors = processors;
this.mergeComponents = mergeComponents;
}
@SuppressWarnings("unchecked")
@@ -79,12 +85,12 @@ public class CustomSmithingTransformRecipe<T> implements Recipe<T> {
@Override
public Key id() {
return id;
return this.id;
}
@Override
public T result(ItemBuildContext context) {
return result.buildItemStack(context);
return this.result.buildItemStack(context);
}
@SuppressWarnings("unchecked")
@@ -92,10 +98,13 @@ public class CustomSmithingTransformRecipe<T> implements Recipe<T> {
T result = this.result(context);
Item<T> wrappedResult = (Item<T>) CraftEngine.instance().itemManager().wrap(result);
Item<T> finalResult = wrappedResult;
for (ItemDataProcessor processor : this.processors) {
finalResult = (Item<T>) processor.apply(base, wrappedResult);
if (this.mergeComponents) {
finalResult = base.merge(wrappedResult);
}
return finalResult.getItem();
for (ItemDataProcessor processor : this.processors) {
processor.accept(base, wrappedResult, finalResult);
}
return finalResult.load();
}
@Override
@@ -105,7 +114,7 @@ public class CustomSmithingTransformRecipe<T> implements Recipe<T> {
@Nullable
public Ingredient<T> base() {
return base;
return this.base;
}
@Nullable
@@ -124,12 +133,16 @@ public class CustomSmithingTransformRecipe<T> implements Recipe<T> {
@Override
public Recipe<A> create(Key id, Map<String, Object> arguments) {
List<String> base = MiscUtils.getAsStringList(arguments.get("base"));
List<String> addition = MiscUtils.getAsStringList(arguments.get("addition-input"));
List<String> addition = MiscUtils.getAsStringList(arguments.get("addition"));
List<String> template = MiscUtils.getAsStringList(arguments.get("template-type"));
boolean mergeComponents = (boolean) arguments.getOrDefault("merge-components", true);
@SuppressWarnings("unchecked")
List<Map<String, Object>> processors = (List<Map<String, Object>>) arguments.getOrDefault("post-processors", List.of());
return new CustomSmithingTransformRecipe<>(
id,
toIngredient(base), toIngredient(template),toIngredient(addition), parseResult(arguments),
List.of(ItemDataProcessor.MERGE_ALL)
mergeComponents,
ItemDataProcessors.fromMapList(processors)
);
}
@@ -146,16 +159,42 @@ public class CustomSmithingTransformRecipe<T> implements Recipe<T> {
}
}
@FunctionalInterface
public interface ItemDataProcessor extends BiFunction<Item<?>, Item<?>, Item<?>> {
MergeAllDataProcessor MERGE_ALL = new MergeAllDataProcessor();
public static class ItemDataProcessors {
public static List<ItemDataProcessor> fromMapList(List<Map<String, Object>> mapList) {
if (mapList == null || mapList.isEmpty()) return List.of();
List<ItemDataProcessor> functions = new ArrayList<>();
for (Map<String, Object> map : mapList) {
functions.add(fromMap(map));
}
return functions;
}
public static ItemDataProcessor fromMap(Map<String, Object> map) {
String type = (String) map.get("type");
if (type == null) {
throw new NullPointerException("processor type cannot be null");
}
Key key = Key.withDefaultNamespace(type, "craftengine");
ItemDataProcessor.Factory factory = BuiltInRegistries.SMITHING_RESULT_PROCESSOR_FACTORY.getValue(key);
if (factory == null) {
throw new IllegalArgumentException("Unknown processor type: " + type);
}
return factory.create(map);
}
public static void register(Key key, ItemDataProcessor.Factory factory) {
Holder.Reference<ItemDataProcessor.Factory> holder = ((WritableRegistry<ItemDataProcessor.Factory>) BuiltInRegistries.SMITHING_RESULT_PROCESSOR_FACTORY)
.registerForHolder(new ResourceKey<>(Registries.SMITHING_RESULT_PROCESSOR_FACTORY.location(), key));
holder.bindValue(factory);
}
}
public static class MergeAllDataProcessor implements ItemDataProcessor {
@FunctionalInterface
public interface ItemDataProcessor extends TriConsumer<Item<?>, Item<?>, Item<?>> {
@Override
public Item<?> apply(Item<?> item1, Item<?> item2) {
return item1.merge(item2);
interface Factory {
ItemDataProcessor create(Map<String, Object> arguments);
}
}
}

View File

@@ -36,7 +36,7 @@ public class CustomStoneCuttingRecipe<T> extends AbstractGroupedRecipe<T> {
@Override
public @NotNull Key type() {
return RecipeTypes.STONE_CUTTING;
return RecipeTypes.STONECUTTING;
}
public Ingredient<T> ingredient() {

View File

@@ -16,7 +16,7 @@ public class RecipeTypes {
public static final Key BLASTING = Key.of("minecraft:blasting");
public static final Key SMOKING = Key.of("minecraft:smoking");
public static final Key CAMPFIRE_COOKING = Key.of("minecraft:campfire_cooking");
public static final Key STONE_CUTTING = Key.of("minecraft:stone_cutting");
public static final Key STONECUTTING = Key.of("minecraft:stonecutting");
public static final Key SMITHING_TRANSFORM = Key.of("minecraft:smithing_transform");
public static final Key SMITHING_TRIM = Key.of("minecraft:smithing_trim");
@@ -27,7 +27,7 @@ public class RecipeTypes {
register(SMOKING, CustomSmokingRecipe.FACTORY);
register(BLASTING, CustomBlastingRecipe.FACTORY);
register(CAMPFIRE_COOKING, CustomCampfireRecipe.FACTORY);
register(STONE_CUTTING, CustomStoneCuttingRecipe.FACTORY);
register(STONECUTTING, CustomStoneCuttingRecipe.FACTORY);
register(SMITHING_TRANSFORM, CustomSmithingTransformRecipe.FACTORY);
}

View File

@@ -19,6 +19,6 @@ public class VanillaStoneCuttingRecipe extends VanillaGroupedRecipe {
@Override
public Key type() {
return RecipeTypes.STONE_CUTTING;
return RecipeTypes.STONECUTTING;
}
}

View File

@@ -26,7 +26,7 @@ public class NumberProviders {
holder.bindValue(factory);
}
static List<NumberProvider> fromMapList(List<Map<String, Object>> mapList) {
public static List<NumberProvider> fromMapList(List<Map<String, Object>> mapList) {
if (mapList == null || mapList.isEmpty()) return List.of();
List<NumberProvider> functions = new ArrayList<>();
for (Map<String, Object> map : mapList) {

View File

@@ -249,6 +249,7 @@ public abstract class AbstractPackManager implements PackManager {
plugin.saveResource("resources/internal/resourcepack/assets/minecraft/textures/font/gui/custom/smelting.png");
plugin.saveResource("resources/internal/resourcepack/assets/minecraft/textures/font/gui/custom/campfire.png");
plugin.saveResource("resources/internal/resourcepack/assets/minecraft/textures/font/gui/custom/stonecutting_recipe.png");
plugin.saveResource("resources/internal/resourcepack/assets/minecraft/textures/font/gui/custom/smithing_transform_recipe.png");
plugin.saveResource("resources/internal/resourcepack/assets/minecraft/textures/font/gui/custom/cooking_recipe.png");
plugin.saveResource("resources/internal/resourcepack/assets/minecraft/textures/font/gui/custom/crafting_recipe.png");
plugin.saveResource("resources/internal/resourcepack/assets/minecraft/textures/font/gui/custom/no_recipe.png");

View File

@@ -65,6 +65,7 @@ public interface ItemBrowserManager extends Reloadable, ConfigSectionParser {
public static String RECIPE_CAMPFIRE_TITLE;
public static String RECIPE_CRAFTING_TITLE;
public static String RECIPE_STONECUTTING_TITLE;
public static String RECIPE_SMITHING_TRANSFORM_TITLE;
public static Key RECIPE_BACK;
public static Key RECIPE_EXIT;
public static Key RECIPE_NEXT_PAGE_AVAILABLE;
@@ -100,9 +101,10 @@ public interface ItemBrowserManager extends Reloadable, ConfigSectionParser {
RECIPE_BLASTING_TITLE = getOrThrow(section, "recipe.blasting.title");
RECIPE_SMELTING_TITLE = getOrThrow(section, "recipe.smelting.title");
RECIPE_SMOKING_TITLE = getOrThrow(section, "recipe.smoking.title");
RECIPE_CAMPFIRE_TITLE = getOrThrow(section, "recipe.campfire.title");
RECIPE_CAMPFIRE_TITLE = getOrThrow(section, "recipe.campfire-cooking.title");
RECIPE_CRAFTING_TITLE = getOrThrow(section, "recipe.crafting.title");
RECIPE_STONECUTTING_TITLE = getOrThrow(section, "recipe.stonecutting.title");
RECIPE_SMITHING_TRANSFORM_TITLE = getOrThrow(section, "recipe.smithing-transform.title");
RECIPE_BACK = Key.of(getOrThrow(section, "recipe.page-navigation.return"));
RECIPE_EXIT = Key.of(getOrThrow(section, "recipe.page-navigation.exit"));
RECIPE_NEXT_PAGE_AVAILABLE = Key.of(getOrThrow(section, "recipe.page-navigation.next.available"));

View File

@@ -349,10 +349,215 @@ public class ItemBrowserManagerImpl implements ItemBrowserManager {
openCookingRecipePage(player, (CustomCookingRecipe<Object>) recipe, parentGui, recipes, index, depth, canOpenNoRecipePage);
return;
}
if (recipeType == RecipeTypes.STONE_CUTTING) {
if (recipeType == RecipeTypes.STONECUTTING) {
openStoneCuttingRecipePage(player, (CustomStoneCuttingRecipe<Object>) recipe, parentGui, recipes, index, depth, canOpenNoRecipePage);
return;
}
if (recipeType == RecipeTypes.SMITHING_TRANSFORM) {
openSmithingTransformRecipePage(player, (CustomSmithingTransformRecipe<Object>) recipe, parentGui, recipes, index, depth, canOpenNoRecipePage);
return;
}
}
public void openSmithingTransformRecipePage(Player player, CustomSmithingTransformRecipe<Object> recipe, Gui parentGui, List<Recipe<Object>> recipes, int index, int depth, boolean canOpenNoRecipePage) {
Key previous = index > 0 ? Constants.RECIPE_PREVIOUS_PAGE_AVAILABLE : Constants.RECIPE_PREVIOUS_PAGE_BLOCK;
Key next = index + 1 < recipes.size() ? Constants.RECIPE_NEXT_PAGE_AVAILABLE : Constants.RECIPE_NEXT_PAGE_BLOCK;
Key result = recipe.result().item().id();
GuiLayout layout = new GuiLayout(
" ",
" ",
" ABC X ",
" ^ ",
" ",
" < = > "
)
.addIngredient('X', GuiElement.constant(this.plugin.itemManager().createWrappedItem(result, player).count(recipe.result().count()), (e, c) -> {
c.cancel();
if (MIDDLE_CLICK.contains(c.type()) && player.isCreativeMode() && player.hasPermission(GET_ITEM_PERMISSION) && c.itemOnCursor() == null) {
Item<?> item = this.plugin.itemManager().createWrappedItem(result, player);
item.count(item.maxStackSize());
c.setItemOnCursor(item);
return;
}
if (LEFT_CLICK.contains(c.type())) {
List<Recipe<Object>> inRecipes = this.plugin.recipeManager().getRecipeByResult(result);
if (inRecipes == recipes) return;
player.playSound(Constants.SOUND_CLICK_BUTTON);
if (!inRecipes.isEmpty()) {
openRecipePage(c.clicker(), e.gui(), inRecipes, 0, 0, canOpenNoRecipePage);
} else if (canOpenNoRecipePage) {
openNoRecipePage(player, result, e.gui(), 0);
}
} else if (RIGHT_CLICK.contains(c.type())) {
List<Recipe<Object>> inRecipes = this.plugin.recipeManager().getRecipeByIngredient(result);
if (inRecipes == recipes) return;
player.playSound(Constants.SOUND_CLICK_BUTTON);
if (!inRecipes.isEmpty()) {
openRecipePage(c.clicker(), e.gui(), inRecipes, 0, 0, canOpenNoRecipePage);
}
}
}))
.addIngredient('^', player.hasPermission(GET_ITEM_PERMISSION) ? GuiElement.constant(this.plugin.itemManager().createWrappedItem(Constants.RECIPE_GET_ITEM, player), (e, c) -> {
c.cancel();
player.playSound(Constants.SOUND_PICK_ITEM);
if (LEFT_CLICK.contains(c.type())) {
player.giveItem(this.plugin.itemManager().createWrappedItem(result, player));
} else if (RIGHT_CLICK.contains(c.type())) {
Item<?> item = this.plugin.itemManager().createWrappedItem(result, player);
player.giveItem(item.count(item.maxStackSize()));
}
}) : GuiElement.EMPTY)
.addIngredient('=', GuiElement.constant(this.plugin.itemManager().getCustomItem(parentGui != null ? Constants.RECIPE_BACK : Constants.RECIPE_EXIT)
.map(it -> it.buildItem(ItemBuildContext.of(player, ContextHolder.EMPTY)))
.orElseThrow(() -> new GuiElementMissingException("Can't find gui element " + (parentGui != null ? Constants.RECIPE_BACK : Constants.RECIPE_EXIT))),
((element, click) -> {
click.cancel();
player.playSound(Constants.SOUND_RETURN_PAGE, 0.25f, 1);
if (parentGui != null) {
parentGui.open(player);
} else {
player.closeInventory();
}
}))
)
.addIngredient('>', GuiElement.constant(this.plugin.itemManager()
.getCustomItem(next)
.map(it -> it.buildItem(ItemBuildContext.of(player, ContextHolder.builder()
.withParameter(GuiParameters.CURRENT_PAGE, String.valueOf(index + 1))
.withParameter(GuiParameters.MAX_PAGE, String.valueOf(recipes.size()))
.build())))
.orElseThrow(() -> new GuiElementMissingException("Can't find gui element " + next)), (e, c) -> {
c.cancel();
if (index + 1 < recipes.size()) {
player.playSound(Constants.SOUND_CHANGE_PAGE, 0.25f, 1);
openRecipePage(player, parentGui, recipes, index + 1, depth, canOpenNoRecipePage);
}
}))
.addIngredient('<', GuiElement.constant(this.plugin.itemManager()
.getCustomItem(previous)
.map(it -> it.buildItem(ItemBuildContext.of(player, ContextHolder.builder()
.withParameter(GuiParameters.CURRENT_PAGE, String.valueOf(index + 1))
.withParameter(GuiParameters.MAX_PAGE, String.valueOf(recipes.size()))
.build())))
.orElseThrow(() -> new GuiElementMissingException("Can't find gui element " + previous)), (e, c) -> {
c.cancel();
if (index > 0) {
player.playSound(Constants.SOUND_CHANGE_PAGE, 0.25f, 1);
openRecipePage(player, parentGui, recipes, index - 1, depth, canOpenNoRecipePage);
}
}));
List<Item<?>> templates = new ArrayList<>();
Optional.ofNullable(recipe.template()).ifPresent(it -> {
for (Holder<Key> in : it.items()) {
templates.add(this.plugin.itemManager().createWrappedItem(in.value(), player));
}
});
layout.addIngredient('A', templates.isEmpty() ? GuiElement.EMPTY : GuiElement.recipeIngredient(templates, (e, c) -> {
c.cancel();
if (MIDDLE_CLICK.contains(c.type()) && player.isCreativeMode() && player.hasPermission(GET_ITEM_PERMISSION) && c.itemOnCursor() == null) {
Item<?> item = this.plugin.itemManager().createWrappedItem(e.item().id(), player);
item.count(item.maxStackSize());
c.setItemOnCursor(item);
return;
}
if (LEFT_CLICK.contains(c.type())) {
List<Recipe<Object>> inRecipes = this.plugin.recipeManager().getRecipeByResult(e.item().id());
if (inRecipes == recipes) return;
player.playSound(Constants.SOUND_CLICK_BUTTON);
if (!inRecipes.isEmpty()) {
openRecipePage(c.clicker(), e.gui(), inRecipes, 0, 0, canOpenNoRecipePage);
} else if (canOpenNoRecipePage) {
openNoRecipePage(player, e.item().id(), e.gui(), 0);
}
} else if (RIGHT_CLICK.contains(c.type())) {
List<Recipe<Object>> inRecipes = this.plugin.recipeManager().getRecipeByIngredient(e.item().id());
if (inRecipes == recipes) return;
player.playSound(Constants.SOUND_CLICK_BUTTON);
if (!inRecipes.isEmpty()) {
openRecipePage(c.clicker(), e.gui(), inRecipes, 0, 0, canOpenNoRecipePage);
}
}
}));
List<Item<?>> bases = new ArrayList<>();
Optional.ofNullable(recipe.base()).ifPresent(it -> {
for (Holder<Key> in : it.items()) {
bases.add(this.plugin.itemManager().createWrappedItem(in.value(), player));
}
});
layout.addIngredient('B', bases.isEmpty() ? GuiElement.EMPTY : GuiElement.recipeIngredient(bases, (e, c) -> {
c.cancel();
if (MIDDLE_CLICK.contains(c.type()) && player.isCreativeMode() && player.hasPermission(GET_ITEM_PERMISSION) && c.itemOnCursor() == null) {
Item<?> item = this.plugin.itemManager().createWrappedItem(e.item().id(), player);
item.count(item.maxStackSize());
c.setItemOnCursor(item);
return;
}
if (LEFT_CLICK.contains(c.type())) {
List<Recipe<Object>> inRecipes = this.plugin.recipeManager().getRecipeByResult(e.item().id());
if (inRecipes == recipes) return;
player.playSound(Constants.SOUND_CLICK_BUTTON);
if (!inRecipes.isEmpty()) {
openRecipePage(c.clicker(), e.gui(), inRecipes, 0, 0, canOpenNoRecipePage);
} else if (canOpenNoRecipePage) {
openNoRecipePage(player, e.item().id(), e.gui(), 0);
}
} else if (RIGHT_CLICK.contains(c.type())) {
List<Recipe<Object>> inRecipes = this.plugin.recipeManager().getRecipeByIngredient(e.item().id());
if (inRecipes == recipes) return;
player.playSound(Constants.SOUND_CLICK_BUTTON);
if (!inRecipes.isEmpty()) {
openRecipePage(c.clicker(), e.gui(), inRecipes, 0, 0, canOpenNoRecipePage);
}
}
}));
List<Item<?>> additions = new ArrayList<>();
Optional.ofNullable(recipe.addition()).ifPresent(it -> {
for (Holder<Key> in : it.items()) {
additions.add(this.plugin.itemManager().createWrappedItem(in.value(), player));
}
});
layout.addIngredient('C', additions.isEmpty() ? GuiElement.EMPTY : GuiElement.recipeIngredient(additions, (e, c) -> {
c.cancel();
if (MIDDLE_CLICK.contains(c.type()) && player.isCreativeMode() && player.hasPermission(GET_ITEM_PERMISSION) && c.itemOnCursor() == null) {
Item<?> item = this.plugin.itemManager().createWrappedItem(e.item().id(), player);
item.count(item.maxStackSize());
c.setItemOnCursor(item);
return;
}
if (LEFT_CLICK.contains(c.type())) {
List<Recipe<Object>> inRecipes = this.plugin.recipeManager().getRecipeByResult(e.item().id());
if (inRecipes == recipes) return;
player.playSound(Constants.SOUND_CLICK_BUTTON);
if (!inRecipes.isEmpty()) {
openRecipePage(c.clicker(), e.gui(), inRecipes, 0, 0, canOpenNoRecipePage);
} else if (canOpenNoRecipePage) {
openNoRecipePage(player, e.item().id(), e.gui(), 0);
}
} else if (RIGHT_CLICK.contains(c.type())) {
List<Recipe<Object>> inRecipes = this.plugin.recipeManager().getRecipeByIngredient(e.item().id());
if (inRecipes == recipes) return;
player.playSound(Constants.SOUND_CLICK_BUTTON);
if (!inRecipes.isEmpty()) {
openRecipePage(c.clicker(), e.gui(), inRecipes, 0, 0, canOpenNoRecipePage);
}
}
}));
BasicGui.builder()
.layout(layout)
.inventoryClickConsumer(c -> {
if (MOVE_TO_OTHER_INV.contains(c.type()) || DOUBLE_CLICK.contains(c.type())) {
c.cancel();
}
})
.build()
.title(AdventureHelper.miniMessage().deserialize(Constants.RECIPE_SMITHING_TRANSFORM_TITLE, ItemBuildContext.of(player, ContextHolder.EMPTY).tagResolvers()))
.refresh()
.open(player);
}
public void openStoneCuttingRecipePage(Player player, CustomStoneCuttingRecipe<Object> recipe, Gui parentGui, List<Recipe<Object>> recipes, int index, int depth, boolean canOpenNoRecipePage) {

View File

@@ -4,6 +4,7 @@ import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
import net.momirealms.craftengine.core.block.properties.PropertyFactory;
import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory;
import net.momirealms.craftengine.core.item.recipe.CustomSmithingTransformRecipe;
import net.momirealms.craftengine.core.item.recipe.RecipeFactory;
import net.momirealms.craftengine.core.loot.condition.LootConditionFactory;
import net.momirealms.craftengine.core.loot.entry.LootEntryContainerFactory;
@@ -43,6 +44,7 @@ public class BuiltInRegistries {
public static final Registry<ApplyBonusCountFunction.FormulaFactory> FORMULA_FACTORY = createRegistry(Registries.FORMULA_FACTORY);
public static final Registry<PathMatcherFactory> PATH_MATCHER_FACTORY = createRegistry(Registries.PATH_MATCHER_FACTORY);
public static final Registry<ResolutionFactory> RESOLUTION_FACTORY = createRegistry(Registries.RESOLUTION_FACTORY);
public static final Registry<CustomSmithingTransformRecipe.ItemDataProcessor.Factory> SMITHING_RESULT_PROCESSOR_FACTORY = createRegistry(Registries.SMITHING_RESULT_PROCESSOR_FACTORY);
private static <T> Registry<T> createRegistry(ResourceKey<? extends Registry<T>> key) {
return new MappedRegistry<>(key);

View File

@@ -4,6 +4,7 @@ import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
import net.momirealms.craftengine.core.block.properties.PropertyFactory;
import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory;
import net.momirealms.craftengine.core.item.recipe.CustomSmithingTransformRecipe;
import net.momirealms.craftengine.core.item.recipe.RecipeFactory;
import net.momirealms.craftengine.core.loot.condition.LootConditionFactory;
import net.momirealms.craftengine.core.loot.entry.LootEntryContainerFactory;
@@ -44,4 +45,5 @@ public class Registries {
public static final ResourceKey<Registry<ApplyBonusCountFunction.FormulaFactory>> FORMULA_FACTORY = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("formula_factory"));
public static final ResourceKey<Registry<PathMatcherFactory>> PATH_MATCHER_FACTORY = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("path_matcher_factory"));
public static final ResourceKey<Registry<ResolutionFactory>> RESOLUTION_FACTORY = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("resolution_factory"));
public static final ResourceKey<Registry<CustomSmithingTransformRecipe.ItemDataProcessor.Factory>> SMITHING_RESULT_PROCESSOR_FACTORY = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("smithing_result_processor_factory"));
}