Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import net.errorcraft.itematic.access.network.listener.ClientPlayPacketListenerAccess;
import net.errorcraft.itematic.item.ItemKeys;
import net.errorcraft.itematic.item.component.ItemComponentTypes;
import net.errorcraft.itematic.item.event.ItemEvents;
import net.errorcraft.itematic.network.packet.s2c.play.TwirlS2CPacket;
import net.errorcraft.itematic.world.action.actions.TwirlPlayerAction;
import net.minecraft.client.MinecraftClient;
Expand Down Expand Up @@ -36,8 +36,8 @@ protected ClientPlayNetworkHandlerExtender(MinecraftClient client, ClientConnect
target = "Lnet/minecraft/item/ItemStack;isOf(Lnet/minecraft/item/Item;)Z"
)
)
private static boolean isOfForTotemOfUndyingUseItemComponentCheck(ItemStack instance, Item item) {
return instance.itematic$hasComponent(ItemComponentTypes.LIFE_SAVING);
private static boolean isOfForTotemOfUndyingUseEventListenerCheck(ItemStack instance, Item item) {
return instance.itematic$hasEventListener(ItemEvents.BEFORE_DEATH_HOLDER);
}

@Redirect(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,33 +1,67 @@
package net.errorcraft.itematic.data.server;

import it.unimi.dsi.fastutil.chars.Char2ObjectMap;
import it.unimi.dsi.fastutil.chars.Char2ObjectOpenHashMap;
import net.errorcraft.itematic.item.ItemKeys;
import net.errorcraft.itematic.item.ItematicItemTags;
import net.errorcraft.itematic.recipe.ItemColoringRecipe;
import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput;
import net.fabricmc.fabric.api.datagen.v1.provider.FabricCodecDataProvider;
import net.minecraft.data.DataOutput;
import net.minecraft.data.server.recipe.CraftingRecipeJsonBuilder;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.recipe.Ingredient;
import net.minecraft.recipe.Recipe;
import net.minecraft.recipe.*;
import net.minecraft.recipe.book.CraftingRecipeCategory;
import net.minecraft.recipe.book.RecipeCategory;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.registry.RegistryWrapper;
import net.minecraft.registry.entry.RegistryEntry;
import net.minecraft.util.DyeColor;
import net.minecraft.util.Identifier;
import net.minecraft.util.collection.DefaultedList;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiConsumer;

@SuppressWarnings("SameParameterValue")
public class RecipeProvider extends FabricCodecDataProvider<Recipe<?>> {
public RecipeProvider(FabricDataOutput dataOutput, CompletableFuture<RegistryWrapper.WrapperLookup> registriesFuture) {
super(dataOutput, registriesFuture, DataOutput.OutputType.DATA_PACK, "recipes", Recipe.CODEC);
super(dataOutput, registriesFuture, DataOutput.OutputType.DATA_PACK, "recipe", Recipe.CODEC);
}

@Override
protected void configure(BiConsumer<Identifier, Recipe<?>> provider, RegistryWrapper.WrapperLookup lookup) {
RegistryWrapper.Impl<Item> items = lookup.getWrapperOrThrow(RegistryKeys.ITEM);
provider.accept(
Identifier.ofVanilla("honey_block"),
shapedRecipe(RecipeCategory.FOOD, items.getOrThrow(ItemKeys.HONEY_BLOCK))
.input('#', items.getOrThrow(ItemKeys.HONEY_BOTTLE), items.getOrThrow(ItemKeys.GLASS_BOTTLE))
.pattern("##")
.pattern("##")
.build()
);
provider.accept(
Identifier.ofVanilla("sugar_from_honey_bottle"),
shapelessRecipe(RecipeCategory.MISC, items.getOrThrow(ItemKeys.SUGAR), 3)
.input(items.getOrThrow(ItemKeys.HONEY_BOTTLE), 1, items.getOrThrow(ItemKeys.GLASS_BOTTLE))
.build()
);
provider.accept(
Identifier.ofVanilla("cake"),
shapedRecipe(RecipeCategory.FOOD, items.getOrThrow(ItemKeys.CAKE))
.input('A', items.getOrThrow(ItemKeys.MILK_BUCKET), items.getOrThrow(ItemKeys.BUCKET))
.input('B', items.getOrThrow(ItemKeys.SUGAR))
.input('C', items.getOrThrow(ItemKeys.WHEAT))
.input('E', items.getOrThrow(ItemKeys.EGG))
.pattern("AAA")
.pattern("BEB")
.pattern("CCC")
.build()
);
provider.accept(Identifier.ofVanilla("shulker_box_coloring_black"), colorShulkerBox(
DyeColor.BLACK,
items.getOrThrow(ItemKeys.BLACK_SHULKER_BOX)
Expand Down Expand Up @@ -102,4 +136,94 @@ public String getName() {
private static ItemColoringRecipe colorShulkerBox(DyeColor color, RegistryEntry<Item> result) {
return new ItemColoringRecipe(CraftingRecipeCategory.MISC, Ingredient.fromTag(ItematicItemTags.SHULKER_BOXES), color, new ItemStack(result));
}

private static ShapelessRecipeBuilder shapelessRecipe(RecipeCategory category, RegistryEntry<Item> result, int count) {
return new ShapelessRecipeBuilder(new ItemStack(result, count), category);
}

private static ShapedRecipeBuilder shapedRecipe(RecipeCategory category, RegistryEntry<Item> result) {
return new ShapedRecipeBuilder(new ItemStack(result), category);
}

private static class ShapelessRecipeBuilder {
private final ItemStack result;
private final RecipeCategory category;
private final DefaultedList<Ingredient> inputs = DefaultedList.of();

private ShapelessRecipeBuilder(ItemStack result, RecipeCategory category) {
this.result = result;
this.category = category;
}

public ShapelessRecipe build() {
return new ShapelessRecipe(
"",
CraftingRecipeJsonBuilder.toCraftingCategory(this.category),
this.result,
this.inputs
);
}

public ShapelessRecipeBuilder input(RegistryEntry<Item> input) {
this.inputs.add(Ingredient.ofStacks(new ItemStack(input)));
return this;
}

public ShapelessRecipeBuilder input(RegistryEntry<Item> input, int count) {
for (int i = 0; i < count; i++) {
this.inputs.add(Ingredient.ofStacks(new ItemStack(input)));
}

return this;
}

public ShapelessRecipeBuilder input(RegistryEntry<Item> input, int count, RegistryEntry<Item> remainder) {
for (int i = 0; i < count; i++) {
Ingredient ingredient = Ingredient.ofStacks(new ItemStack(input));
ingredient.itematic$setRemainder(Optional.of(new ItemStack(remainder)));
this.inputs.add(ingredient);
}

return this;
}
}

private static class ShapedRecipeBuilder {
private final ItemStack result;
private final RecipeCategory category;
private final Char2ObjectMap<Ingredient> inputs = new Char2ObjectOpenHashMap<>();
private final List<String> pattern = new ArrayList<>();

private ShapedRecipeBuilder(ItemStack result, RecipeCategory category) {
this.result = result;
this.category = category;
}

public ShapedRecipe build() {
return new ShapedRecipe(
"",
CraftingRecipeJsonBuilder.toCraftingCategory(this.category),
RawShapedRecipe.create(this.inputs, this.pattern),
this.result,
true
);
}

public ShapedRecipeBuilder input(char key, RegistryEntry<Item> input) {
this.inputs.put(key, Ingredient.ofStacks(new ItemStack(input)));
return this;
}

public ShapedRecipeBuilder input(char key, RegistryEntry<Item> input, RegistryEntry<Item> remainder) {
Ingredient ingredient = Ingredient.ofStacks(new ItemStack(input));
ingredient.itematic$setRemainder(Optional.of(new ItemStack(remainder)));
this.inputs.put(key, ingredient);
return this;
}

public ShapedRecipeBuilder pattern(String pattern) {
this.pattern.add(pattern);
return this;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
package net.errorcraft.itematic.gametest.block;

import net.errorcraft.itematic.component.PotionContentsComponentUtil;
import net.errorcraft.itematic.gametest.Assert;
import net.errorcraft.itematic.gametest.TestUtil;
import net.errorcraft.itematic.item.ItemKeys;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.potion.Potions;
import net.minecraft.screen.BrewingStandScreenHandler;
import net.minecraft.screen.ScreenHandlerType;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.test.GameTest;
import net.minecraft.test.TestContext;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.GameMode;

public class BrewingStandBlockTestSuite {
private static final BlockPos BLOCK_POSITION = new BlockPos(1, 2, 1);

@GameTest(templateName = "itematic:block.brewing_stand", tickLimit = 401)
public void brewingWaterBottleWithNetherWartTurnsItIntoAwkwardPotion(TestContext context) {
ServerWorld world = context.getWorld();
PlayerEntity player = context.createMockPlayer(GameMode.SURVIVAL);
BrewingStandScreenHandler brewingStandMenu = TestUtil.getMenuFromBlock(context, BLOCK_POSITION, player, ScreenHandlerType.BREWING_STAND);
brewingStandMenu.getSlot(0)
.setStack(PotionContentsComponentUtil.setPotion(world.itematic$createStack(ItemKeys.POTION), Potions.WATER));
brewingStandMenu.getSlot(3)
.setStack(world.itematic$createStack(ItemKeys.NETHER_WART));
brewingStandMenu.getSlot(4)
.setStack(world.itematic$createStack(ItemKeys.BLAZE_POWDER));
context.createTimedTaskRunner()
.expectMinDurationAndRun(
401,
() -> {
ItemStack resultPotion = brewingStandMenu.getSlot(0).getStack();
Assert.itemStackIsOf(resultPotion, ItemKeys.POTION);
Assert.itemStackHasPotion(resultPotion, Potions.AWKWARD);
}
)
.completeIfSuccessful();
}

@GameTest(templateName = "itematic:block.brewing_stand", tickLimit = 401)
public void brewingAwkwardPotionWithSugarTurnsItIntoSwiftnessPotion(TestContext context) {
ServerWorld world = context.getWorld();
PlayerEntity player = context.createMockPlayer(GameMode.SURVIVAL);
BrewingStandScreenHandler brewingStandMenu = TestUtil.getMenuFromBlock(context, BLOCK_POSITION, player, ScreenHandlerType.BREWING_STAND);
brewingStandMenu.getSlot(0)
.setStack(PotionContentsComponentUtil.setPotion(world.itematic$createStack(ItemKeys.POTION), Potions.AWKWARD));
brewingStandMenu.getSlot(3)
.setStack(world.itematic$createStack(ItemKeys.SUGAR));
brewingStandMenu.getSlot(4)
.setStack(world.itematic$createStack(ItemKeys.BLAZE_POWDER));
context.createTimedTaskRunner()
.expectMinDurationAndRun(
401,
() -> {
ItemStack resultPotion = brewingStandMenu.getSlot(0).getStack();
Assert.itemStackIsOf(resultPotion, ItemKeys.POTION);
Assert.itemStackHasPotion(resultPotion, Potions.SWIFTNESS);
}
)
.completeIfSuccessful();
}

@GameTest(templateName = "itematic:block.brewing_stand", tickLimit = 401)
public void brewingSwiftnessPotionWithGlowstoneDustTurnsItIntoStrongSwiftnessPotion(TestContext context) {
ServerWorld world = context.getWorld();
PlayerEntity player = context.createMockPlayer(GameMode.SURVIVAL);
BrewingStandScreenHandler brewingStandMenu = TestUtil.getMenuFromBlock(context, BLOCK_POSITION, player, ScreenHandlerType.BREWING_STAND);
brewingStandMenu.getSlot(0)
.setStack(PotionContentsComponentUtil.setPotion(world.itematic$createStack(ItemKeys.POTION), Potions.SWIFTNESS));
brewingStandMenu.getSlot(3)
.setStack(world.itematic$createStack(ItemKeys.GLOWSTONE_DUST));
brewingStandMenu.getSlot(4)
.setStack(world.itematic$createStack(ItemKeys.BLAZE_POWDER));
context.createTimedTaskRunner()
.expectMinDurationAndRun(
401,
() -> {
ItemStack resultPotion = brewingStandMenu.getSlot(0).getStack();
Assert.itemStackIsOf(resultPotion, ItemKeys.POTION);
Assert.itemStackHasPotion(resultPotion, Potions.STRONG_SWIFTNESS);
}
)
.completeIfSuccessful();
}

@GameTest(templateName = "itematic:block.brewing_stand", tickLimit = 401)
public void brewingSwiftnessPotionWithRedstoneTurnsItIntoLongSwiftnessPotion(TestContext context) {
ServerWorld world = context.getWorld();
PlayerEntity player = context.createMockPlayer(GameMode.SURVIVAL);
BrewingStandScreenHandler brewingStandMenu = TestUtil.getMenuFromBlock(context, BLOCK_POSITION, player, ScreenHandlerType.BREWING_STAND);
brewingStandMenu.getSlot(0)
.setStack(PotionContentsComponentUtil.setPotion(world.itematic$createStack(ItemKeys.POTION), Potions.SWIFTNESS));
brewingStandMenu.getSlot(3)
.setStack(world.itematic$createStack(ItemKeys.REDSTONE));
brewingStandMenu.getSlot(4)
.setStack(world.itematic$createStack(ItemKeys.BLAZE_POWDER));
context.createTimedTaskRunner()
.expectMinDurationAndRun(
401,
() -> {
ItemStack resultPotion = brewingStandMenu.getSlot(0).getStack();
Assert.itemStackIsOf(resultPotion, ItemKeys.POTION);
Assert.itemStackHasPotion(resultPotion, Potions.LONG_SWIFTNESS);
}
)
.completeIfSuccessful();
}

@GameTest(templateName = "itematic:block.brewing_stand", tickLimit = 401)
public void brewingSwiftnessPotionWithGunpowderTurnsItIntoSwiftnessSplashPotion(TestContext context) {
ServerWorld world = context.getWorld();
PlayerEntity player = context.createMockPlayer(GameMode.SURVIVAL);
BrewingStandScreenHandler brewingStandMenu = TestUtil.getMenuFromBlock(context, BLOCK_POSITION, player, ScreenHandlerType.BREWING_STAND);
brewingStandMenu.getSlot(0)
.setStack(PotionContentsComponentUtil.setPotion(world.itematic$createStack(ItemKeys.POTION), Potions.SWIFTNESS));
brewingStandMenu.getSlot(3)
.setStack(world.itematic$createStack(ItemKeys.GUNPOWDER));
brewingStandMenu.getSlot(4)
.setStack(world.itematic$createStack(ItemKeys.BLAZE_POWDER));
context.createTimedTaskRunner()
.expectMinDurationAndRun(
401,
() -> {
ItemStack resultPotion = brewingStandMenu.getSlot(0).getStack();
Assert.itemStackIsOf(resultPotion, ItemKeys.SPLASH_POTION);
Assert.itemStackHasPotion(resultPotion, Potions.SWIFTNESS);
}
)
.completeIfSuccessful();
}

@GameTest(templateName = "itematic:block.brewing_stand", tickLimit = 401)
public void brewingSwiftnessSplashPotionWithDragonBreathTurnsItIntoSwiftnessLingeringPotionAndLeavesGlassBottle(TestContext context) {
ServerWorld world = context.getWorld();
PlayerEntity player = context.createMockPlayer(GameMode.SURVIVAL);
BrewingStandScreenHandler brewingStandMenu = TestUtil.getMenuFromBlock(context, BLOCK_POSITION, player, ScreenHandlerType.BREWING_STAND);
brewingStandMenu.getSlot(0)
.setStack(PotionContentsComponentUtil.setPotion(world.itematic$createStack(ItemKeys.SPLASH_POTION), Potions.SWIFTNESS));
brewingStandMenu.getSlot(3)
.setStack(world.itematic$createStack(ItemKeys.DRAGON_BREATH));
brewingStandMenu.getSlot(4)
.setStack(world.itematic$createStack(ItemKeys.BLAZE_POWDER));
context.createTimedTaskRunner()
.expectMinDurationAndRun(
401,
() -> {
ItemStack resultPotion = brewingStandMenu.getSlot(0).getStack();
Assert.itemStackIsOf(resultPotion, ItemKeys.LINGERING_POTION);
Assert.itemStackHasPotion(resultPotion, Potions.SWIFTNESS);
ItemStack ingredientRemainder = brewingStandMenu.getSlot(3).getStack();
Assert.itemStackIsOf(ingredientRemainder, ItemKeys.GLASS_BOTTLE);
}
)
.completeIfSuccessful();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package net.errorcraft.itematic.gametest.item;

import net.errorcraft.itematic.gametest.Assert;
import net.errorcraft.itematic.item.ItemKeys;
import net.fabricmc.fabric.api.gametest.v1.FabricGameTest;
import net.minecraft.entity.effect.StatusEffects;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.test.GameTest;
import net.minecraft.test.TestContext;
import net.minecraft.util.Hand;
import net.minecraft.world.GameMode;

public class TotemOfUndyingTestSuite {
@GameTest(templateName = FabricGameTest.EMPTY_STRUCTURE)
public void holdingTotemOfUndyingSavesHolderFromDeath(TestContext context) {
PlayerEntity player = context.createMockPlayer(GameMode.SURVIVAL);
ServerWorld world = context.getWorld();
ItemStack stack = world.itematic$createStack(ItemKeys.TOTEM_OF_UNDYING);
player.setStackInHand(Hand.MAIN_HAND, stack);
player.damage(world.getDamageSources().fall(), Float.MAX_VALUE);
context.addInstantFinalTask(() -> {
Assert.areFloatsEqual(player.getHealth(), 1.0f, (value, expected) -> "Expected health to be " + expected + ", got " + value + " instead");
context.expectEntityHasEffect(player, StatusEffects.REGENERATION, 1);
context.expectEntityHasEffect(player, StatusEffects.ABSORPTION, 1);
context.expectEntityHasEffect(player, StatusEffects.FIRE_RESISTANCE, 0);
Assert.itemStackIsEmpty(player.getStackInHand(Hand.MAIN_HAND));
});
}
}
Loading