2020-04-15 14:22:03 +02:00
|
|
|
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;
|
2020-04-15 14:22:03 +02:00
|
|
|
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;
|
2020-04-25 15:37:17 +02:00
|
|
|
import com.minelittlepony.unicopia.ability.data.Hit;
|
|
|
|
import com.minelittlepony.unicopia.ability.data.Pos;
|
2023-11-07 19:20:41 +01:00
|
|
|
import com.minelittlepony.unicopia.block.UBlocks;
|
2020-09-22 15:11:20 +02:00
|
|
|
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;
|
2023-11-07 19:20:41 +01:00
|
|
|
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
|
|
|
|
2020-01-27 17:37:22 +01:00
|
|
|
/**
|
|
|
|
* Earth Pony ability to grow crops
|
|
|
|
*/
|
2020-04-25 15:37:17 +02:00
|
|
|
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
|
2020-05-10 17:18:45 +02:00
|
|
|
public boolean canUse(Race race) {
|
2020-10-09 14:37:49 +02:00
|
|
|
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
|
2020-04-25 15:37:17 +02:00
|
|
|
public Hit.Serializer<Pos> getSerializer() {
|
|
|
|
return Pos.SERIALIZER;
|
2020-01-16 12:35:46 +01:00
|
|
|
}
|
|
|
|
|
2020-10-09 19:05:12 +02: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) {
|
2020-09-27 21:23:13 +02:00
|
|
|
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);
|
|
|
|
|
2023-11-07 19:20:41 +01:00
|
|
|
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)) {
|
2024-01-03 23:01:45 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-11-07 19:20:41 +01:00
|
|
|
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());
|
2023-11-07 19:20:41 +01:00
|
|
|
}
|
|
|
|
w.setBlockState(pos, UBlocks.PLUNDER_VINE_BUD.getDefaultState());
|
2024-01-03 23:01:04 +01:00
|
|
|
} 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
|
|
|
}
|
2024-01-03 23:01:04 +01:00
|
|
|
UBlocks.CURING_JOKE.grow(w, state, pos);
|
2023-11-07 19:20:41 +01:00
|
|
|
}
|
|
|
|
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) {
|
2023-08-16 12:57:34 +02:00
|
|
|
player.getMagicalReserves().getExertion().addPercent(30);
|
2020-01-16 12:35:46 +01:00
|
|
|
|
2022-12-19 18:13:15 +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
|
|
|
|
|
|
|
}
|
2023-11-07 19:20:41 +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();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-07 19:20:41 +01:00
|
|
|
public interface Growable {
|
|
|
|
boolean grow(World world, BlockState state, BlockPos pos);
|
|
|
|
}
|
2020-01-16 12:35:46 +01:00
|
|
|
}
|