Unicopia/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyGrowAbility.java

259 lines
9.9 KiB
Java
Raw Normal View History

package com.minelittlepony.unicopia.ability;
2020-01-16 12:35:46 +01:00
2024-01-18 20:52:59 +01:00
import java.util.HashSet;
import java.util.List;
2023-08-16 00:18:41 +02:00
import java.util.Optional;
2024-01-18 20:52:59 +01:00
import java.util.Set;
2024-01-04 22:41:27 +01:00
import java.util.function.DoubleSupplier;
import java.util.function.Supplier;
import com.minelittlepony.unicopia.Race;
2024-01-18 20:52:59 +01:00
import com.minelittlepony.unicopia.USounds;
2024-01-04 22:41:27 +01:00
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.entity.player.Pony;
import com.minelittlepony.unicopia.particle.MagicParticleEffect;
2024-01-04 22:41:27 +01:00
import com.minelittlepony.unicopia.particle.ParticleUtils;
2024-01-18 20:52:59 +01:00
import com.minelittlepony.unicopia.server.world.BlockDestructionManager;
import com.minelittlepony.unicopia.server.world.UTreeGen;
2022-10-01 18:20:53 +02:00
import com.minelittlepony.unicopia.util.TraceHelper;
2024-01-04 22:41:27 +01:00
import com.minelittlepony.unicopia.util.VecHelper;
2024-01-18 20:52:59 +01:00
import net.minecraft.block.Block;
2020-01-16 12:35:46 +01:00
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
2024-01-04 22:41:27 +01:00
import net.minecraft.block.CarrotsBlock;
2024-01-03 21:51:24 +01:00
import net.minecraft.block.FarmlandBlock;
2020-01-16 12:35:46 +01:00
import net.minecraft.item.BoneMealItem;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
2024-01-04 22:41:27 +01:00
import net.minecraft.particle.ParticleTypes;
2024-01-18 20:52:59 +01:00
import net.minecraft.state.property.Property;
2020-01-16 12:35:46 +01:00
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
2024-01-04 22:41:27 +01:00
import net.minecraft.util.math.Vec3d;
2024-01-18 20:52:59 +01:00
import net.minecraft.util.math.random.Random;
2020-01-16 12:35:46 +01:00
import net.minecraft.world.World;
2024-01-18 20:52:59 +01:00
import net.minecraft.world.WorldEvents;
2020-01-16 12:35:46 +01:00
/**
* Earth Pony ability to grow crops
*/
public class EarthPonyGrowAbility implements Ability<Pos> {
2020-01-16 12:35:46 +01:00
@Override
2020-04-15 18:12:00 +02:00
public int getWarmupTime(Pony player) {
2020-01-16 12:35:46 +01:00
return 10;
}
@Override
2020-04-15 18:12:00 +02:00
public int getCooldownTime(Pony player) {
2020-01-16 12:35:46 +01:00
return 50;
}
@Override
public boolean canUse(Race race) {
return race.canUseEarth();
2020-01-16 12:35:46 +01:00
}
@Override
2023-08-16 00:18:41 +02:00
public Optional<Pos> prepare(Pony player) {
return TraceHelper.findBlock(player.asEntity(), 3, 1).map(Pos::new);
2020-01-16 12:35:46 +01:00
}
@Override
public Hit.Serializer<Pos> getSerializer() {
return Pos.SERIALIZER;
2020-01-16 12:35:46 +01:00
}
@Override
public double getCostEstimate(Pony player) {
return 10;
}
2020-01-16 12:35:46 +01:00
@Override
2023-08-16 00:18:41 +02:00
public boolean apply(Pony player, Pos data) {
2020-01-16 12:35:46 +01:00
int count = 0;
2024-01-18 20:52:59 +01:00
if (!applyDirectly(player, data.pos())) {
for (BlockPos pos : BlockPos.iterate(
data.pos().add(-2, -2, -2),
data.pos().add( 2, 2, 2))) {
count += applySingle(player, player.asWorld(), player.asWorld().getBlockState(pos), pos);
}
} else {
count = 1;
2020-01-16 12:35:46 +01:00
}
if (count > 0) {
player.subtractEnergyCost(count / 5D);
2020-01-16 12:35:46 +01:00
}
2023-08-16 00:18:41 +02:00
return true;
2020-01-16 12:35:46 +01:00
}
2024-01-04 22:41:27 +01:00
protected int applySingle(Pony player, World w, BlockState state, BlockPos pos) {
2020-01-16 12:35:46 +01:00
ItemStack stack = new ItemStack(Items.BONE_MEAL);
if (state.getBlock() instanceof Growable growable) {
return growable.grow(w, state, pos) ? 1 : 0;
}
2024-01-04 22:41:27 +01:00
if (state.isOf(Blocks.CARROTS)) {
if (state.get(CarrotsBlock.AGE) == CarrotsBlock.MAX_AGE) {
boolean transform = w.random.nextInt(3) == 0;
2024-01-18 20:52:59 +01:00
spawnConversionParticles(w, pos, transform);
2024-01-04 22:41:27 +01:00
if (transform) {
w.setBlockState(pos, UBlocks.GOLD_ROOT.getDefaultState().with(CarrotsBlock.AGE, CarrotsBlock.MAX_AGE));
}
return 5;
}
}
if (w.getBlockState(pos).isIn(UTags.UNAFFECTED_BY_GROW_ABILITY)) {
return 0;
}
if (BoneMealItem.useOnFertilizable(stack, w, pos)) {
if (w.random.nextInt(350) == 0) {
if (w.getBlockState(pos.down()).isOf(Blocks.FARMLAND)) {
2024-01-04 22:41:27 +01:00
FarmlandBlock.setToDirt(null, state, w, pos.down());
}
w.setBlockState(pos, UBlocks.PLUNDER_VINE_BUD.getDefaultState());
} else if (w.random.nextInt(5000) == 0) {
2024-01-02 23:03:41 +01:00
if (w.getBlockState(pos.down()).isOf(Blocks.FARMLAND)) {
2024-01-03 21:51:24 +01:00
FarmlandBlock.setToDirt(null, state, w, pos.down());
2024-01-02 23:03:41 +01:00
}
UBlocks.CURING_JOKE.grow(w, state, pos);
}
return 1;
}
if (BoneMealItem.useOnGround(stack, w, pos, Direction.UP)) {
2020-01-16 12:35:46 +01:00
return 1;
}
return 0;
}
2024-01-18 20:52:59 +01:00
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);
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) {
DoubleSupplier vecComponentFactory = () -> w.random.nextTriangular(0, 0.5);
Supplier<Vec3d> posSupplier = () -> pos.toCenterPos().add(VecHelper.supply(vecComponentFactory));
for (int i = 0; i < 25; i++) {
ParticleUtils.spawnParticle(w, new MagicParticleEffect(0xFFFF00), posSupplier.get(), Vec3d.ZERO);
if (success) {
ParticleUtils.spawnParticle(w, ParticleTypes.CLOUD, posSupplier.get(), Vec3d.ZERO);
}
}
}
2020-01-16 12:35:46 +01:00
@Override
2023-08-16 00:18:41 +02:00
public void warmUp(Pony player, AbilitySlot slot) {
player.getMagicalReserves().getExertion().addPercent(30);
2020-01-16 12:35:46 +01:00
if (player.asWorld().isClient()) {
2020-04-22 16:28:20 +02:00
player.spawnParticles(MagicParticleEffect.UNICORN, 1);
2020-01-16 12:35:46 +01:00
}
}
@Override
2023-08-16 00:18:41 +02:00
public void coolDown(Pony player, AbilitySlot slot) {
2020-01-16 12:35:46 +01:00
}
2024-01-18 20:52:59 +01:00
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) {
BlockState input = world.getBlockState(pos);
BlockState output = this.output;
for (var property : input.getProperties()) {
output = copyProperty(input, output, property);
}
return output;
}
private <T extends Comparable<T>> BlockState copyProperty(BlockState from, BlockState to, Property<T> property) {
return to.withIfExists(property, from.get(property));
}
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);
}
2020-01-16 12:35:46 +01:00
}