Skip to content

Commit

Permalink
feat: Add a new WarpPlateRecipe type for configurable shard recipes (#…
Browse files Browse the repository at this point in the history
…749)

* Add a new WarpPlateRecipe type for configurable shard recipes

---------

Co-authored-by: BlayTheNinth <1933180+BlayTheNinth@users.noreply.github.com>
  • Loading branch information
edralzar and BlayTheNinth authored Nov 25, 2023
1 parent c4aa6c8 commit c6e10a6
Show file tree
Hide file tree
Showing 5 changed files with 246 additions and 25 deletions.
2 changes: 2 additions & 0 deletions shared/src/main/java/net/blay09/mods/waystones/Waystones.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import net.blay09.mods.waystones.item.ModItems;
import net.blay09.mods.waystones.menu.ModMenus;
import net.blay09.mods.waystones.network.ModNetworking;
import net.blay09.mods.waystones.recipe.ModRecipes;
import net.blay09.mods.waystones.stats.ModStats;
import net.blay09.mods.waystones.worldgen.ModWorldGen;

Expand All @@ -27,5 +28,6 @@ public static void initialize() {
ModItems.initialize(Balm.getItems());
ModMenus.initialize(Balm.getMenus());
ModWorldGen.initialize(Balm.getWorldGen());
ModRecipes.initialize(Balm.getRecipes());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,21 @@
import net.blay09.mods.balm.api.Balm;
import net.blay09.mods.balm.api.container.ImplementedContainer;
import net.blay09.mods.balm.api.menu.BalmMenuProvider;
import net.blay09.mods.waystones.Waystones;
import net.blay09.mods.waystones.api.*;
import net.blay09.mods.waystones.block.WarpPlateBlock;
import net.blay09.mods.waystones.config.WaystonesConfig;
import net.blay09.mods.waystones.menu.WarpPlateContainer;
import net.blay09.mods.waystones.core.*;
import net.blay09.mods.waystones.item.ModItems;
import net.blay09.mods.waystones.menu.WarpPlateContainer;
import net.blay09.mods.waystones.recipe.ModRecipes;
import net.blay09.mods.waystones.recipe.WarpPlateRecipe;
import net.blay09.mods.waystones.worldgen.namegen.NameGenerationMode;
import net.blay09.mods.waystones.worldgen.namegen.NameGenerator;
import net.minecraft.ChatFormatting;
import net.minecraft.client.Minecraft;
import net.minecraft.core.BlockPos;
import net.minecraft.core.NonNullList;
import net.minecraft.core.RegistryAccess;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.Component;
Expand All @@ -31,19 +35,24 @@
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.ContainerData;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.item.crafting.RecipeHolder;
import net.minecraft.world.level.ServerLevelAccessor;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.AABB;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;


public class WarpPlateBlockEntity extends WaystoneBlockEntityBase implements ImplementedContainer {

private static final Logger logger = LoggerFactory.getLogger(WarpPlateBlockEntity.class);

private final WeakHashMap<Entity, Integer> ticksPassedPerEntity = new WeakHashMap<>();

private final Random random = new Random();
Expand Down Expand Up @@ -106,7 +115,7 @@ public void initializeFromExisting(ServerLevelAccessor world, Waystone existingW
completedFirstAttunement = tag != null && tag.getBoolean("CompletedFirstAttunement");

if (!completedFirstAttunement) {
initializeInventory();
initializeInventory(world);
}
}

Expand All @@ -123,15 +132,28 @@ public void initializeWaystone(ServerLevelAccessor world, @Nullable LivingEntity

WaystoneSyncManager.sendWaystoneUpdateToAll(world.getServer(), waystone);

initializeInventory();
initializeInventory(world);
}

private void initializeInventory() {
setItem(0, new ItemStack(Items.FLINT));
setItem(1, new ItemStack(ModItems.warpDust));
setItem(2, new ItemStack(ModItems.warpDust));
setItem(3, new ItemStack(ModItems.warpDust));
setItem(4, new ItemStack(ModItems.warpDust));
private void initializeInventory(ServerLevelAccessor levelAccessor) {
WarpPlateRecipe initializingRecipe = levelAccessor.getLevel().getRecipeManager().getAllRecipesFor(ModRecipes.warpPlateRecipeType)
.stream()
.filter(holder -> holder.id().getNamespace().equals(Waystones.MOD_ID) && holder.id().getPath().equals("attuned_shard"))
.map(RecipeHolder::value)
.findFirst()
.orElse(null);
if (initializingRecipe == null) {
logger.error("Failed to find Warp Plate recipe for initial attunement");
completedFirstAttunement = true;
return;
}

for (int i = 0; i < 5; i++) {
final var ingredient = initializingRecipe.getIngredients().get(i);
final var ingredientItems = ingredient.getItems();
final var ingredientItem = ingredientItems.length > 0 ? ingredientItems[0] : ItemStack.EMPTY;
setItem(i, ingredientItem);
}
}

@Override
Expand Down Expand Up @@ -198,18 +220,26 @@ private boolean isEntityOnWarpPlate(Entity entity) {
}

public void serverTick() {
if (isReadyForAttunement()) {
WarpPlateRecipe recipe = trySelectRecipe();
if (recipe != null) {
attunementTicks++;

if (attunementTicks >= getMaxAttunementTicks()) {
attunementTicks = 0;
ItemStack attunedShard = new ItemStack(ModItems.attunedShard);
ItemStack attunedShard = recipe.assemble(this, RegistryAccess.EMPTY);
WaystonesAPI.setBoundWaystone(attunedShard, getWaystone());
ItemStack centerStack = getItem(0);
if (centerStack.getCount() > 1) {
centerStack = centerStack.copyWithCount(centerStack.getCount() - 1);
if (!Minecraft.getInstance().player.getInventory().add(centerStack)) {
Minecraft.getInstance().player.drop(centerStack, false);
}
}
setItem(0, attunedShard);
for (int i = 1; i <= 4; i++) {
getItem(i).shrink(1);
}
completedFirstAttunement = true;
this.completedFirstAttunement = true;
}
} else {
attunementTicks = 0;
Expand Down Expand Up @@ -260,10 +290,6 @@ public void serverTick() {
entry.setValue(ticksPassed + 1);
}
}

if (getItem(0).getItem() != Items.FLINT) {
completedFirstAttunement = true;
}
}

private int getWarpPlateUseTime() {
Expand Down Expand Up @@ -341,13 +367,14 @@ private void applyWarpPlateEffects(Entity entity) {
}
}

private boolean isReadyForAttunement() {
return readyForAttunement
&& getItem(0).getItem() == Items.FLINT
&& getItem(1).getItem() == ModItems.warpDust
&& getItem(2).getItem() == ModItems.warpDust
&& getItem(3).getItem() == ModItems.warpDust
&& getItem(4).getItem() == ModItems.warpDust;
@Nullable
private WarpPlateRecipe trySelectRecipe() {
if (!readyForAttunement || level == null) {
return null;
}

return level.getRecipeManager().getRecipeFor(ModRecipes.warpPlateRecipeType, this, level)
.map(RecipeHolder::value).orElse(null);
}

@Nullable
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package net.blay09.mods.waystones.recipe;

import net.blay09.mods.balm.api.recipe.BalmRecipes;
import net.blay09.mods.waystones.Waystones;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.crafting.RecipeType;

public class ModRecipes {

public static final String WARP_PLATE_RECIPE_GROUP = "warp_plate";
public static final ResourceLocation WARP_PLATE_RECIPE_TYPE = new ResourceLocation(Waystones.MOD_ID, WARP_PLATE_RECIPE_GROUP);

public static RecipeType<WarpPlateRecipe> warpPlateRecipeType;

public static void initialize(BalmRecipes registry) {
registry.registerRecipeType(() -> warpPlateRecipeType = new RecipeType<>() {
@Override
public String toString() {
return WARP_PLATE_RECIPE_GROUP;
}
},
WarpPlateRecipe.Serializer::new, WARP_PLATE_RECIPE_TYPE);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
package net.blay09.mods.waystones.recipe;

import com.mojang.serialization.Codec;
import com.mojang.serialization.DataResult;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.blay09.mods.waystones.block.ModBlocks;
import net.minecraft.core.NonNullList;
import net.minecraft.core.RegistryAccess;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.world.Container;
import net.minecraft.world.entity.player.StackedContents;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.*;
import net.minecraft.world.level.Level;

public class WarpPlateRecipe implements Recipe<Container> {

private final ItemStack resultItem;
private final Ingredient primaryIngredient;
private final NonNullList<Ingredient> secondaryIngredients;
private final NonNullList<Ingredient> combinedIngredients;

public WarpPlateRecipe(ItemStack resultItem, Ingredient primaryIngredient, NonNullList<Ingredient> secondaryIngredients) {
this.resultItem = resultItem;

this.primaryIngredient = primaryIngredient;
this.secondaryIngredients = secondaryIngredients;

this.combinedIngredients = NonNullList.createWithCapacity(5);
this.combinedIngredients.add(primaryIngredient);
this.combinedIngredients.addAll(secondaryIngredients);
}

@Override
public boolean matches(Container inventory, Level level) {
// Short-circuit if the primary ingredient is not present to ensure it's actually in the right slot and avoid unnecessary processing
if (!primaryIngredient.test(inventory.getItem(0))) {
return false;
}

StackedContents stackedContents = new StackedContents();
int foundInputs = 0;
// canCraft uses getIngredients, so we need to include the primary ingredient here as well
for (int i = 0; i < combinedIngredients.size(); i++) {
ItemStack itemStack = inventory.getItem(i);
if (!itemStack.isEmpty()) {
foundInputs++;
stackedContents.accountStack(itemStack, 1);
}
}
return foundInputs == combinedIngredients.size() && stackedContents.canCraft(this, null);
}

@Override
public ItemStack assemble(Container inventory, RegistryAccess registryAccess) {
return this.resultItem.copy();
}

@Override
public ItemStack getResultItem(RegistryAccess registryAccess) {
return this.resultItem;
}

@Override
public boolean canCraftInDimensions(int width, int height) {
return true;
}

@Override
public NonNullList<Ingredient> getIngredients() {
return this.combinedIngredients;
}

@Override
public boolean isSpecial() {
return true;
}

@Override
public String getGroup() {
return ModRecipes.WARP_PLATE_RECIPE_GROUP;
}

@Override
public ItemStack getToastSymbol() {
return new ItemStack(ModBlocks.warpPlate);
}

@Override
public RecipeSerializer<?> getSerializer() {
return new Serializer();
}

@Override
public RecipeType<?> getType() {
return ModRecipes.warpPlateRecipeType;
}

static class Serializer implements RecipeSerializer<WarpPlateRecipe> {

private static final Codec<WarpPlateRecipe> CODEC = RecordCodecBuilder.create(instance -> instance.group(
CraftingRecipeCodecs.ITEMSTACK_OBJECT_CODEC.fieldOf("result").forGetter(recipe -> recipe.resultItem),
Ingredient.CODEC.fieldOf("primary").forGetter(recipe -> recipe.primaryIngredient),
Ingredient.CODEC.listOf().fieldOf("secondary")
.flatXmap(secondary -> {
Ingredient[] ingredients = secondary.stream().filter((ingredient) -> !ingredient.isEmpty()).toArray(Ingredient[]::new);
if (ingredients.length == 0) {
return DataResult.error(() -> "No secondary ingredients for warp plate recipe");
} else {
return ingredients.length > 4 ? DataResult.error(() -> "Too many secondary ingredients for warp plate recipe") : DataResult.success(
NonNullList.of(Ingredient.EMPTY, ingredients));
}
}, DataResult::success)
.forGetter(recipe -> recipe.secondaryIngredients)
)
.apply(instance, WarpPlateRecipe::new));

@Override
public Codec<WarpPlateRecipe> codec() {
return CODEC;
}

@Override
public WarpPlateRecipe fromNetwork(FriendlyByteBuf buf) {
final var resultItem = buf.readItem();
final var primaryIngredient = Ingredient.fromNetwork(buf);
final NonNullList<Ingredient> secondaryIngredients = NonNullList.createWithCapacity(4);
for (int i = 0; i < secondaryIngredients.size(); i++) {
secondaryIngredients.add(Ingredient.fromNetwork(buf));
}
return new WarpPlateRecipe(resultItem, primaryIngredient, secondaryIngredients);
}

@Override
public void toNetwork(FriendlyByteBuf buf, WarpPlateRecipe recipe) {
buf.writeItem(recipe.resultItem);
recipe.primaryIngredient.toNetwork(buf);
for (Ingredient ingredient : recipe.secondaryIngredients) {
ingredient.toNetwork(buf);
}
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"type": "waystones:warp_plate",
"result": {
"item": "waystones:attuned_shard",
"count": 1
},
"primary": {
"item": "minecraft:flint"
},
"secondary": [
{
"item": "waystones:warp_dust"
},
{
"item": "waystones:warp_dust"
},
{
"item": "waystones:warp_dust"
},
{
"item": "waystones:warp_dust"
}
]
}

0 comments on commit c6e10a6

Please sign in to comment.