Move earth ponies' transmutations to recipes

This commit is contained in:
Sollace 2024-01-29 19:05:20 +00:00
parent 78dbc800ae
commit d4e698fa56
No known key found for this signature in database
GPG key ID: E52FACE7B5C773DB
13 changed files with 296 additions and 77 deletions

View file

@ -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;
@ -12,12 +9,12 @@ import com.minelittlepony.unicopia.UTags;
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;
@ -33,7 +30,6 @@ import net.minecraft.particle.ParticleTypes;
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;
@ -139,40 +135,41 @@ public class EarthPonyGrowAbility implements Ability<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);
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);
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);
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)));
}
}
});
return true;
})
.findFirst()
.isPresent();
spawnConversionParticles(player.asWorld(), pos, transform);
if (transform) {
player.asWorld().setBlockState(pos, result.recipe().getResult(player.asWorld(), pos));
}
return true;
})
.findFirst()
.isPresent();
}
private static void spawnConversionParticles(World w, BlockPos pos, boolean success) {
@ -201,48 +198,6 @@ public class EarthPonyGrowAbility implements Ability<Pos> {
}
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);
}

View file

@ -13,6 +13,7 @@ import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
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;
@ -20,6 +21,7 @@ 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;
@ -33,8 +35,10 @@ import net.minecraft.util.Identifier;
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);
@ -103,5 +107,20 @@ public class Main implements EmiPlugin {
});
}
});
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;
}
});
});
}
}

View file

@ -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();
}
}
}

View file

@ -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);

View file

@ -23,6 +23,7 @@ import net.minecraft.util.collection.DefaultedList;
public interface URecipes {
RecipeType<SpellbookRecipe> SPELLBOOK = RecipeType.register("unicopia:spellbook");
RecipeType<StonecuttingRecipe> CLOUD_SHAPING = RecipeType.register("unicopia:cloud_shaping");
RecipeType<TransformCropsRecipe> GROWING = RecipeType.register("unicopia:growing");
RecipeSerializer<ShapelessRecipe> ZAP_APPLE_SERIALIZER = RecipeSerializer.register("unicopia:crafting_zap_apple", new ZapAppleRecipe.Serializer());
RecipeSerializer<GlowingRecipe> GLOWING_SERIALIZER = RecipeSerializer.register("unicopia:crafting_glowing", new SpecialRecipeSerializer<>(GlowingRecipe::new));
@ -33,6 +34,7 @@ public interface URecipes {
RecipeSerializer<SpellEnhancingRecipe> TRAIT_COMBINING = RecipeSerializer.register("unicopia:spellbook/combining", new SpellEnhancingRecipe.Serializer());
RecipeSerializer<SpellDuplicatingRecipe> SPELL_DUPLICATING = RecipeSerializer.register("unicopia:spellbook/duplicating", new SpellDuplicatingRecipe.Serializer());
RecipeSerializer<CloudShapingRecipe> CLOUD_SHAPING_SERIALIZER = RecipeSerializer.register("unicopia:cloud_shaping", new CuttingRecipe.Serializer<>(CloudShapingRecipe::new) {});
RecipeSerializer<TransformCropsRecipe> TRANSFORM_CROP_SERIALIZER = RecipeSerializer.register("unicopia:transform_crop", new TransformCropsRecipe.Serializer());
static DefaultedList<Ingredient> getIngredients(JsonArray json) {
DefaultedList<Ingredient> defaultedList = DefaultedList.of();

View file

@ -46,6 +46,7 @@
"item.unicopia.spellbook": "Spellbook",
"emi.category.unicopia.spellbook": "Spellbook",
"emi.category.unicopia.cloud_shaping": "Shaping",
"emi.category.unicopia.growing": "Growing",
"item.unicopia.alicorn_badge": "Alicorn Emblem",
"item.unicopia.unicorn_badge": "Unicorn Emblem",
@ -73,6 +74,7 @@
"item.unicopia.love_bucket": "Love Bucket",
"item.unicopia.love_mug": "Mug o' Love",
"item.unicopia.plunder_vine": "Plunder Vine",
"item.unicopia.empty_jar": "Glass Jar",
"item.unicopia.filled_jar": "%s in a Jar",
"item.unicopia.rain_cloud_jar": "Rain in a Jar",

View file

@ -0,0 +1,3 @@
{
"parent": "unicopia:block/plunder_vine_bud"
}

View file

@ -0,0 +1,12 @@
{
"type": "unicopia:transform_crop",
"target": "minecraft:cornflower",
"consume": {
"Name": "minecraft:lapis_block",
"Properties": {}
},
"output": {
"Name": "unicopia:curing_joke",
"Properties": {}
}
}

View file

@ -0,0 +1,12 @@
{
"type": "unicopia:transform_crop",
"target": "minecraft:carrots",
"consume": {
"Name": "minecraft:raw_gold_block",
"Properties": {}
},
"output": {
"Name": "unicopia:gold_root",
"Properties": {}
}
}

View file

@ -0,0 +1,12 @@
{
"type": "unicopia:transform_crop",
"target": "minecraft:oak_sapling",
"consume": {
"Name": "minecraft:raw_gold_block",
"Properties": {}
},
"output": {
"Name": "unicopia:golden_oak_sapling",
"Properties": {}
}
}

View file

@ -0,0 +1,12 @@
{
"type": "unicopia:transform_crop",
"target": "minecraft:wither_rose",
"consume": {
"Name": "minecraft:netherrack",
"Properties": {}
},
"output": {
"Name": "unicopia:plunder_vine_bud",
"Properties": {}
}
}

View file

@ -0,0 +1,12 @@
{
"type": "unicopia:transform_crop",
"target": "minecraft:dark_oak_sapling",
"consume": {
"Name": "unicopia:chitin",
"Properties": {}
},
"output": {
"Name": "unicopia:zapling",
"Properties": {}
}
}

View file

@ -15,6 +15,7 @@
"minecraft:end_portal_frame",
"minecraft:debug_stick",
"minecraft:command_block_minecart",
"unicopia:plunder_vine",
"#unicopia:badges"
]
}