Skip to content

Commit

Permalink
Move earth ponies' transmutations to recipes
Browse files Browse the repository at this point in the history
  • Loading branch information
Sollace committed Jan 29, 2024
1 parent 78dbc80 commit d4e698f
Show file tree
Hide file tree
Showing 13 changed files with 298 additions and 79 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package com.minelittlepony.unicopia.ability;

import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.DoubleSupplier;
import java.util.function.Supplier;
import com.minelittlepony.unicopia.Race;
Expand All @@ -12,12 +9,12 @@
import com.minelittlepony.unicopia.ability.data.Hit;
import com.minelittlepony.unicopia.ability.data.Pos;
import com.minelittlepony.unicopia.block.UBlocks;
import com.minelittlepony.unicopia.block.state.StateUtil;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.item.TransformCropsRecipe;
import com.minelittlepony.unicopia.item.URecipes;
import com.minelittlepony.unicopia.particle.MagicParticleEffect;
import com.minelittlepony.unicopia.particle.ParticleUtils;
import com.minelittlepony.unicopia.server.world.BlockDestructionManager;
import com.minelittlepony.unicopia.server.world.UTreeGen;
import com.minelittlepony.unicopia.util.TraceHelper;
import com.minelittlepony.unicopia.util.VecHelper;

Expand All @@ -33,7 +30,6 @@
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.random.Random;
import net.minecraft.world.World;
import net.minecraft.world.WorldEvents;

Expand Down Expand Up @@ -139,40 +135,41 @@ protected int applySingle(Pony player, World w, BlockState state, BlockPos pos)
}

private boolean applyDirectly(Pony player, BlockPos pos) {
return TransmutationRecipe.RECIPES.stream()
.filter(recipe -> recipe.matches(player.asWorld(), pos))
.map(recipe -> recipe.checkPattern(player.asWorld(), pos))
.filter(result -> result.matchedLocations().size() + 1 >= TransmutationRecipe.MINIMUM_INPUT)
.filter(result -> {
boolean transform = result.shoudTransform(player.asWorld().random);

player.playSound(USounds.ENTITY_CRYSTAL_SHARDS_AMBIENT, 1);

result.matchedLocations().forEach(cell -> {
spawnConversionParticles(player.asWorld(), cell.up(), false);
BlockDestructionManager manager = BlockDestructionManager.of(player.asWorld());
if (transform) {
if (manager.damageBlock(cell, 8) >= BlockDestructionManager.MAX_DAMAGE || player.asWorld().random.nextInt(20) == 0) {
player.asWorld().setBlockState(cell, Blocks.DIRT.getDefaultState());
player.asWorld().syncWorldEvent(WorldEvents.BLOCK_BROKEN, cell, Block.getRawIdFromState(player.asWorld().getBlockState(cell)));
}
} else {
if (manager.damageBlock(cell, 4) >= BlockDestructionManager.MAX_DAMAGE || player.asWorld().random.nextInt(20) == 0) {
player.asWorld().setBlockState(cell, Blocks.DIRT.getDefaultState());
player.asWorld().syncWorldEvent(WorldEvents.BLOCK_BROKEN, cell, Block.getRawIdFromState(player.asWorld().getBlockState(cell)));
}
}
});

spawnConversionParticles(player.asWorld(), pos, transform);
return player.asWorld().getRecipeManager()
.getAllMatches(URecipes.GROWING, new TransformCropsRecipe.PlacementArea(player, pos), player.asWorld())
.stream()
.map(recipe -> recipe.checkPattern(player.asWorld(), pos))
.filter(result -> result.matchedLocations().size() + 1 >= TransformCropsRecipe.MINIMUM_INPUT)
.filter(result -> {
boolean transform = result.shoudTransform(player.asWorld().random);

player.playSound(USounds.ENTITY_CRYSTAL_SHARDS_AMBIENT, 1);

result.matchedLocations().forEach(cell -> {
spawnConversionParticles(player.asWorld(), cell.up(), false);
BlockDestructionManager manager = BlockDestructionManager.of(player.asWorld());
if (transform) {
player.asWorld().setBlockState(pos, result.recipe().getResult(player.asWorld(), pos));
if (manager.damageBlock(cell, 8) >= BlockDestructionManager.MAX_DAMAGE || player.asWorld().random.nextInt(20) == 0) {
player.asWorld().setBlockState(cell, Blocks.DIRT.getDefaultState());
player.asWorld().syncWorldEvent(WorldEvents.BLOCK_BROKEN, cell, Block.getRawIdFromState(player.asWorld().getBlockState(cell)));
}
} else {
if (manager.damageBlock(cell, 4) >= BlockDestructionManager.MAX_DAMAGE || player.asWorld().random.nextInt(20) == 0) {
player.asWorld().setBlockState(cell, Blocks.DIRT.getDefaultState());
player.asWorld().syncWorldEvent(WorldEvents.BLOCK_BROKEN, cell, Block.getRawIdFromState(player.asWorld().getBlockState(cell)));
}
}
});

spawnConversionParticles(player.asWorld(), pos, transform);
if (transform) {
player.asWorld().setBlockState(pos, result.recipe().getResult(player.asWorld(), pos));
}

return true;
})
.findFirst()
.isPresent();
return true;
})
.findFirst()
.isPresent();
}

private static void spawnConversionParticles(World w, BlockPos pos, boolean success) {
Expand Down Expand Up @@ -201,48 +198,6 @@ public void coolDown(Pony player, AbilitySlot slot) {

}

private static record TransmutationRecipe(Block input, BlockState output, BlockState material) {
static final List<TransmutationRecipe> RECIPES = List.of(
new TransmutationRecipe(Blocks.OAK_SAPLING, UTreeGen.GOLDEN_APPLE_TREE.sapling().get().getDefaultState(), Blocks.RAW_GOLD_BLOCK.getDefaultState()),
new TransmutationRecipe(Blocks.CARROTS, UBlocks.GOLD_ROOT.getDefaultState(), Blocks.RAW_GOLD_BLOCK.getDefaultState()),
new TransmutationRecipe(Blocks.CORNFLOWER, UBlocks.CURING_JOKE.getDefaultState(), Blocks.LAPIS_BLOCK.getDefaultState()),
new TransmutationRecipe(Blocks.WITHER_ROSE, UBlocks.PLUNDER_VINE_BUD.getDefaultState(), Blocks.NETHERRACK.getDefaultState())
);
static final int RADIUS = 3;
static final int SIDE_LENGTH = (2 * RADIUS) + 1;
static final int AREA = (SIDE_LENGTH * SIDE_LENGTH) - 1;
static final int MINIMUM_INPUT = 9;

public boolean matches(World world, BlockPos pos) {
return world.getBlockState(pos).isOf(input);
}

public Result checkPattern(World world, BlockPos pos) {
BlockPos center = pos.down();
Set<BlockPos> matches = new HashSet<>();
for (BlockPos cell : BlockPos.iterateInSquare(center, RADIUS, Direction.EAST, Direction.NORTH)) {
if (cell.equals(center)) {
continue;
}
if (!world.getBlockState(cell).equals(material)) {
break;
}
matches.add(cell.toImmutable());
}
return new Result(this, matches);
}

public BlockState getResult(World world, BlockPos pos) {
return StateUtil.copyState(world.getBlockState(pos), output);
}

record Result (TransmutationRecipe recipe, Set<BlockPos> matchedLocations) {
public boolean shoudTransform(Random random) {
return random.nextInt(TransmutationRecipe.AREA) < matchedLocations().size();
}
}
}

public interface Growable {
boolean grow(World world, BlockState state, BlockPos pos);
}
Expand Down
19 changes: 19 additions & 0 deletions src/main/java/com/minelittlepony/unicopia/compat/emi/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
import com.minelittlepony.unicopia.block.UBlocks;
import com.minelittlepony.unicopia.item.EnchantableItem;
import com.minelittlepony.unicopia.item.TransformCropsRecipe;
import com.minelittlepony.unicopia.item.UItems;
import com.minelittlepony.unicopia.item.URecipes;
import com.minelittlepony.unicopia.item.group.MultiItem;

import dev.emi.emi.api.EmiPlugin;
import dev.emi.emi.api.EmiRegistry;
import dev.emi.emi.api.recipe.EmiRecipeCategory;
import dev.emi.emi.api.recipe.EmiWorldInteractionRecipe;
import dev.emi.emi.api.render.EmiTexture;
import dev.emi.emi.api.stack.Comparison;
import dev.emi.emi.api.stack.EmiStack;
Expand All @@ -33,8 +35,10 @@
public class Main implements EmiPlugin {
static final EmiStack SPELL_BOOK_STATION = EmiStack.of(UItems.SPELLBOOK);
static final EmiStack CLOUD_SHAPING_STATION = EmiStack.of(UBlocks.SHAPING_BENCH);
static final EmiStack GROWING_STATION = EmiStack.of(UItems.EARTH_BADGE);
static final EmiRecipeCategory SPELL_BOOK_CATEGORY = new EmiRecipeCategory(Unicopia.id("spellbook"), SPELL_BOOK_STATION, SPELL_BOOK_STATION);
static final EmiRecipeCategory CLOUD_SHAPING_CATEGORY = new EmiRecipeCategory(Unicopia.id("cloud_shaping"), CLOUD_SHAPING_STATION, CLOUD_SHAPING_STATION);
static final EmiRecipeCategory GROWING_CATEGORY = new EmiRecipeCategory(Unicopia.id("growing"), GROWING_STATION, GROWING_STATION);

static final Identifier WIDGETS = Unicopia.id("textures/gui/widgets.png");
static final EmiTexture EMPTY_ARROW = new EmiTexture(WIDGETS, 44, 0, 24, 17);
Expand Down Expand Up @@ -103,5 +107,20 @@ public EmiRecipeCategory getCategory() {
});
}
});

registry.addCategory(GROWING_CATEGORY);
registry.addWorkstation(GROWING_CATEGORY, GROWING_STATION);
registry.getRecipeManager().listAllOfType(URecipes.GROWING).forEach(recipe -> {
registry.addRecipe(new EmiWorldInteractionRecipe(EmiWorldInteractionRecipe.builder()
.id(recipe.getId())
.leftInput(EmiStack.of(recipe.getTargetAsItem()))
.rightInput(EmiStack.of(recipe.getCatalyst(), TransformCropsRecipe.MINIMUM_INPUT), true)
.output(EmiStack.of(recipe.getOutput()))) {
@Override
public EmiRecipeCategory getCategory() {
return GROWING_CATEGORY;
}
});
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
package com.minelittlepony.unicopia.item;

import java.util.HashSet;
import java.util.Set;

import com.google.gson.JsonObject;
import com.minelittlepony.unicopia.block.state.StateUtil;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.mojang.datafixers.util.Pair;
import com.mojang.serialization.Codec;
import com.mojang.serialization.JsonOps;
import com.mojang.serialization.codecs.RecordCodecBuilder;

import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.inventory.SingleStackInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.recipe.Recipe;
import net.minecraft.recipe.RecipeSerializer;
import net.minecraft.recipe.RecipeType;
import net.minecraft.registry.DynamicRegistryManager;
import net.minecraft.registry.Registries;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.random.Random;
import net.minecraft.world.EmptyBlockView;
import net.minecraft.world.World;

public class TransformCropsRecipe implements Recipe<TransformCropsRecipe.PlacementArea> {
public static final int RADIUS = 3;
public static final int SIDE_LENGTH = (2 * RADIUS) + 1;
public static final int AREA = (SIDE_LENGTH * SIDE_LENGTH) - 1;
public static final int MINIMUM_INPUT = 9;

private final Identifier id;

private final Block target;
private final BlockState catalyst;
private final BlockState output;

public TransformCropsRecipe(Identifier id, Block target, BlockState catalyst, BlockState output) {
this.id = id;
this.output = output;
this.target = target;
this.catalyst = catalyst;
}

public ItemStack getTargetAsItem() {
return target.asItem().getDefaultStack();
}

public ItemStack getCatalyst() {
return catalyst.getBlock().getPickStack(EmptyBlockView.INSTANCE, BlockPos.ORIGIN, catalyst);
}

public ItemStack getOutput() {
return output.getBlock().getPickStack(EmptyBlockView.INSTANCE, BlockPos.ORIGIN, output);
}

@Override
public Identifier getId() {
return id;
}

@Override
public RecipeSerializer<?> getSerializer() {
return URecipes.TRANSFORM_CROP_SERIALIZER;
}

@Override
public RecipeType<?> getType() {
return URecipes.GROWING;
}

@Override
public boolean matches(PlacementArea inventory, World world) {
return world.getBlockState(inventory.position()).isOf(target);
}

@Override
public ItemStack craft(PlacementArea inventory, DynamicRegistryManager manager) {
return getOutput(manager);
}

@Override
public ItemStack getOutput(DynamicRegistryManager manager) {
return output.getBlock().asItem().getDefaultStack();
}

public Result checkPattern(World world, BlockPos pos) {
BlockPos center = pos.down();
Set<BlockPos> matches = new HashSet<>();
for (BlockPos cell : BlockPos.iterateInSquare(center, RADIUS, Direction.EAST, Direction.NORTH)) {
if (cell.equals(center)) {
continue;
}
if (!world.getBlockState(cell).equals(catalyst)) {
break;
}
matches.add(cell.toImmutable());
}
return new Result(this, matches);
}

public BlockState getResult(World world, BlockPos pos) {
return StateUtil.copyState(world.getBlockState(pos), output);
}

@Override
public boolean fits(int width, int height) {
return width >= SIDE_LENGTH && height >= SIDE_LENGTH;
}

public static class Serializer implements RecipeSerializer<TransformCropsRecipe> {
record Intermediate(Block target, BlockState fuel, BlockState output) {}
private static final Codec<Intermediate> CODEC = RecordCodecBuilder.create(instance -> instance.group(
Registries.BLOCK.getCodec().fieldOf("target").forGetter(Intermediate::target),
BlockState.CODEC.fieldOf("consume").forGetter(Intermediate::fuel),
BlockState.CODEC.fieldOf("output").forGetter(Intermediate::output)
).apply(instance, Intermediate::new));

@Override
public TransformCropsRecipe read(Identifier id, JsonObject json) {
Intermediate content = CODEC.decode(JsonOps.INSTANCE, json).result().map(Pair::getFirst).get();
return new TransformCropsRecipe(id, content.target(), content.fuel(), content.output());
}

@Override
public TransformCropsRecipe read(Identifier id, PacketByteBuf buffer) {
return new TransformCropsRecipe(id,
buffer.readRegistryValue(Registries.BLOCK),
Block.getStateFromRawId(buffer.readInt()),
Block.getStateFromRawId(buffer.readInt())
);
}

@Override
public void write(PacketByteBuf buffer, TransformCropsRecipe recipe) {
buffer.writeRegistryValue(Registries.BLOCK, recipe.target);
buffer.writeInt(Block.getRawIdFromState(recipe.catalyst));
buffer.writeInt(Block.getRawIdFromState(recipe.output));
}
}

public static record PlacementArea (Pony pony, BlockPos position) implements SingleStackInventory {
@Override
public ItemStack getStack(int var1) {
return ItemStack.EMPTY;
}

@Override
public ItemStack removeStack(int slot, int count) {
return ItemStack.EMPTY;
}

@Override
public void setStack(int slot, ItemStack stack) { }

@Override
public void markDirty() { }

@Override
public boolean canPlayerUse(PlayerEntity player) {
return true;
}
}

public record Result (TransformCropsRecipe recipe, Set<BlockPos> matchedLocations) {
public boolean shoudTransform(Random random) {
return random.nextInt(AREA) < matchedLocations().size();
}
}
}
1 change: 1 addition & 0 deletions src/main/java/com/minelittlepony/unicopia/item/UItems.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public interface UItems {

FriendshipBraceletItem FRIENDSHIP_BRACELET = register("friendship_bracelet", new FriendshipBraceletItem(new FabricItemSettings().rarity(Rarity.UNCOMMON)), ItemGroups.TOOLS);

Item PLUNDER_VINE = register("plunder_vine", new BlockItem(UBlocks.PLUNDER_VINE_BUD, new Item.Settings()));
Item EMPTY_JAR = register("empty_jar", new JarItem(new Item.Settings().maxCount(16).fireproof(), false, false, false), ItemGroups.FUNCTIONAL);
FilledJarItem FILLED_JAR = register("filled_jar", new FilledJarItem(new Item.Settings().maxCount(1).recipeRemainder(EMPTY_JAR)));
Item RAIN_CLOUD_JAR = register("rain_cloud_jar", new JarItem(new Item.Settings().maxCount(1).fireproof().recipeRemainder(EMPTY_JAR), true, false, false), ItemGroups.FUNCTIONAL);
Expand Down
Loading

0 comments on commit d4e698f

Please sign in to comment.