2020-10-11 13:14:41 +02:00
|
|
|
package com.minelittlepony.unicopia.ability;
|
|
|
|
|
2022-09-25 13:58:10 +02:00
|
|
|
import java.util.*;
|
|
|
|
import java.util.stream.Stream;
|
2020-10-11 13:14:41 +02:00
|
|
|
|
2021-08-04 15:38:03 +02:00
|
|
|
import org.jetbrains.annotations.Nullable;
|
2020-10-11 13:14:41 +02:00
|
|
|
|
|
|
|
import com.minelittlepony.unicopia.Race;
|
|
|
|
import com.minelittlepony.unicopia.ability.data.Hit;
|
|
|
|
import com.minelittlepony.unicopia.ability.data.Pos;
|
2021-08-20 22:25:46 +02:00
|
|
|
import com.minelittlepony.unicopia.ability.data.tree.TreeType;
|
2022-09-20 23:50:15 +02:00
|
|
|
import com.minelittlepony.unicopia.client.minelittlepony.MineLPDelegate;
|
2022-01-03 21:54:07 +01:00
|
|
|
import com.minelittlepony.unicopia.client.render.PlayerPoser.Animation;
|
2022-09-26 21:11:28 +02:00
|
|
|
import com.minelittlepony.unicopia.entity.Living;
|
2023-06-02 21:20:30 +02:00
|
|
|
import com.minelittlepony.unicopia.entity.damage.UDamageTypes;
|
2020-10-11 13:14:41 +02:00
|
|
|
import com.minelittlepony.unicopia.entity.player.Pony;
|
2022-01-04 17:00:06 +01:00
|
|
|
import com.minelittlepony.unicopia.particle.ParticleUtils;
|
|
|
|
import com.minelittlepony.unicopia.particle.UParticles;
|
2023-04-30 11:46:33 +02:00
|
|
|
import com.minelittlepony.unicopia.server.world.BlockDestructionManager;
|
2022-09-21 22:58:07 +02:00
|
|
|
import com.minelittlepony.unicopia.util.*;
|
2020-10-11 13:14:41 +02:00
|
|
|
|
2021-02-13 12:51:01 +01:00
|
|
|
import net.minecraft.block.BeehiveBlock;
|
2021-02-13 12:50:24 +01:00
|
|
|
import net.minecraft.block.Block;
|
2021-02-13 12:51:01 +01:00
|
|
|
import net.minecraft.block.BlockState;
|
2020-10-11 13:14:41 +02:00
|
|
|
import net.minecraft.block.Blocks;
|
2021-02-13 12:51:01 +01:00
|
|
|
import net.minecraft.block.entity.BeehiveBlockEntity;
|
2020-10-11 13:14:41 +02:00
|
|
|
import net.minecraft.entity.ItemEntity;
|
2022-09-21 22:58:07 +02:00
|
|
|
import net.minecraft.entity.LivingEntity;
|
2021-02-13 12:51:01 +01:00
|
|
|
import net.minecraft.entity.passive.BeeEntity;
|
2020-10-11 13:14:41 +02:00
|
|
|
import net.minecraft.entity.player.PlayerEntity;
|
2021-08-20 23:52:06 +02:00
|
|
|
import net.minecraft.item.ItemStack;
|
2022-09-21 22:58:07 +02:00
|
|
|
import net.minecraft.predicate.entity.EntityPredicates;
|
2022-09-25 00:14:29 +02:00
|
|
|
import net.minecraft.server.world.ServerWorld;
|
2023-09-03 23:44:26 +02:00
|
|
|
import net.minecraft.util.Identifier;
|
2020-10-11 13:14:41 +02:00
|
|
|
import net.minecraft.util.math.BlockPos;
|
2021-02-13 12:51:01 +01:00
|
|
|
import net.minecraft.util.math.Box;
|
2020-10-11 13:14:41 +02:00
|
|
|
import net.minecraft.util.math.Vec3d;
|
2022-09-21 22:58:07 +02:00
|
|
|
import net.minecraft.world.World;
|
2021-08-25 14:22:11 +02:00
|
|
|
import net.minecraft.world.WorldEvents;
|
2020-10-11 13:14:41 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Earth Pony kicking ability
|
|
|
|
*/
|
|
|
|
public class EarthPonyKickAbility implements Ability<Pos> {
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int getWarmupTime(Pony player) {
|
|
|
|
return 3;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int getCooldownTime(Pony player) {
|
|
|
|
return 50;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean canUse(Race race) {
|
|
|
|
return race.canUseEarth();
|
|
|
|
}
|
|
|
|
|
2023-09-03 23:44:26 +02:00
|
|
|
@Override
|
|
|
|
public Identifier getIcon(Pony player) {
|
|
|
|
Identifier id = Abilities.REGISTRY.getId(this);
|
|
|
|
return new Identifier(id.getNamespace(), "textures/gui/ability/" + id.getPath()
|
|
|
|
+ "_" + player.getObservedSpecies().getId().getPath()
|
|
|
|
+ ".png");
|
|
|
|
}
|
|
|
|
|
2020-10-11 13:14:41 +02:00
|
|
|
@Override
|
|
|
|
public double getCostEstimate(Pony player) {
|
2023-08-16 20:42:31 +02:00
|
|
|
return TraceHelper.findBlock(player.asEntity(), getKickDirection(player) * 6, 1)
|
2022-12-19 18:13:15 +01:00
|
|
|
.filter(pos -> TreeType.at(pos, player.asWorld()) != TreeType.NONE)
|
2022-01-04 17:00:06 +01:00
|
|
|
.isPresent() ? 3 : 1;
|
2020-10-11 13:14:41 +02:00
|
|
|
}
|
|
|
|
|
2022-09-21 22:58:07 +02:00
|
|
|
@Override
|
2022-09-23 16:05:04 +02:00
|
|
|
public Optional<Pos> prepareQuickAction(Pony player, ActivationType type) {
|
|
|
|
return Optional.of(getDefaultKickLocation(player));
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean onQuickAction(Pony player, ActivationType type, Optional<Pos> data) {
|
2022-09-21 22:58:07 +02:00
|
|
|
if (type == ActivationType.TAP) {
|
|
|
|
|
|
|
|
if (!player.isClient()) {
|
2022-09-23 16:05:04 +02:00
|
|
|
data.ifPresent(kickLocation -> {
|
|
|
|
Vec3d origin = player.getOriginVector();
|
2022-12-19 18:13:15 +01:00
|
|
|
World w = player.asWorld();
|
2022-09-23 16:05:04 +02:00
|
|
|
|
2023-09-12 12:52:01 +02:00
|
|
|
player.asEntity().addExhaustion(3);
|
|
|
|
|
2022-12-19 16:03:35 +01:00
|
|
|
for (var e : VecHelper.findInRange(player.asEntity(), w, kickLocation.vec(), 2, EntityPredicates.EXCEPT_CREATIVE_OR_SPECTATOR)) {
|
2022-09-23 16:05:04 +02:00
|
|
|
if (e instanceof LivingEntity entity) {
|
|
|
|
float calculatedStrength = 0.5F * (1 + player.getLevel().getScaled(9));
|
2023-06-02 21:20:30 +02:00
|
|
|
|
|
|
|
entity.damage(player.damageOf(UDamageTypes.KICK, player), player.asWorld().random.nextBetween(2, 10) + calculatedStrength);
|
2022-09-23 16:05:04 +02:00
|
|
|
entity.takeKnockback(calculatedStrength, origin.x - entity.getX(), origin.z - entity.getZ());
|
2022-09-26 21:11:28 +02:00
|
|
|
Living.updateVelocity(entity);
|
2022-09-23 16:05:04 +02:00
|
|
|
player.subtractEnergyCost(3);
|
2023-08-05 16:45:36 +02:00
|
|
|
player.setAnimation(Animation.KICK, Animation.Recipient.ANYONE);
|
2022-09-23 16:05:04 +02:00
|
|
|
return;
|
|
|
|
}
|
2022-09-21 22:58:07 +02:00
|
|
|
}
|
|
|
|
|
2022-09-23 16:05:04 +02:00
|
|
|
BlockPos pos = kickLocation.pos();
|
2022-12-19 16:03:35 +01:00
|
|
|
EarthPonyStompAbility.stompBlock(w, pos, 10 * (1 + player.getLevel().getScaled(5)) * w.getBlockState(pos).calcBlockBreakingDelta(player.asEntity(), w, pos));
|
2023-08-05 16:45:36 +02:00
|
|
|
player.setAnimation(Animation.KICK, Animation.Recipient.ANYONE);
|
2022-09-23 16:05:04 +02:00
|
|
|
});
|
2022-09-21 22:58:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-04-30 16:35:44 +02:00
|
|
|
if (type == ActivationType.DOUBLE_TAP && player.asEntity().isOnGround() && player.getMagicalReserves().getMana().get() > 40) {
|
|
|
|
player.getPhysics().dashForward((float)player.asWorld().random.nextTriangular(3.5F, 0.3F));
|
|
|
|
player.subtractEnergyCost(4);
|
2023-09-12 12:52:01 +02:00
|
|
|
player.asEntity().addExhaustion(5);
|
2023-04-30 16:35:44 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-09-21 22:58:07 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-10-11 13:14:41 +02:00
|
|
|
@Nullable
|
|
|
|
@Override
|
2023-08-16 00:18:41 +02:00
|
|
|
public Optional<Pos> prepare(Pony player) {
|
2022-12-19 16:03:35 +01:00
|
|
|
return TraceHelper.findBlock(player.asEntity(), 6 * getKickDirection(player), 1)
|
2022-12-19 18:13:15 +01:00
|
|
|
.filter(pos -> TreeType.at(pos, player.asWorld()) != TreeType.NONE)
|
2022-01-04 17:00:06 +01:00
|
|
|
.map(Pos::new)
|
2023-08-16 00:18:41 +02:00
|
|
|
.or(() -> Optional.of(getDefaultKickLocation(player)));
|
2022-01-04 17:00:06 +01:00
|
|
|
}
|
|
|
|
|
2022-09-21 22:58:07 +02:00
|
|
|
private int getKickDirection(Pony player) {
|
2023-09-12 12:52:01 +02:00
|
|
|
return MineLPDelegate.getInstance().getPlayerPonyRace(player.asEntity()).isEquine() && player.asEntity().isInSneakingPose() ? -1 : 1;
|
2022-09-21 22:58:07 +02:00
|
|
|
}
|
|
|
|
|
2022-01-04 17:00:06 +01:00
|
|
|
private Pos getDefaultKickLocation(Pony player) {
|
2022-12-19 16:03:35 +01:00
|
|
|
Vec3d kickVector = player.asEntity().getRotationVector().multiply(1, 0, 1);
|
|
|
|
|
2023-01-21 01:28:59 +01:00
|
|
|
if (MineLPDelegate.getInstance().getPlayerPonyRace(player.asEntity()).isEquine()) {
|
2022-01-04 17:00:06 +01:00
|
|
|
kickVector = kickVector.rotateY((float)Math.PI);
|
|
|
|
}
|
2023-06-02 21:20:30 +02:00
|
|
|
return new Pos(BlockPos.ofFloored(player.getOriginVector().add(kickVector)));
|
2021-08-20 22:22:28 +02:00
|
|
|
}
|
2020-10-11 13:14:41 +02:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public Hit.Serializer<Pos> getSerializer() {
|
|
|
|
return Pos.SERIALIZER;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2023-08-16 00:18:41 +02:00
|
|
|
public boolean apply(Pony iplayer, Pos data) {
|
|
|
|
|
2022-01-04 17:00:06 +01:00
|
|
|
BlockPos pos = data.pos();
|
2023-09-12 12:25:41 +02:00
|
|
|
TreeType treeType = TreeType.at(pos, iplayer.asWorld());
|
2023-08-16 00:18:41 +02:00
|
|
|
|
2023-08-05 16:45:36 +02:00
|
|
|
iplayer.setAnimation(Animation.KICK, Animation.Recipient.ANYONE);
|
2023-09-12 12:25:41 +02:00
|
|
|
iplayer.subtractEnergyCost(treeType == TreeType.NONE ? 1 : 3);
|
2023-09-12 12:52:01 +02:00
|
|
|
iplayer.asEntity().addExhaustion(3);
|
2020-10-11 13:14:41 +02:00
|
|
|
|
2023-09-12 12:25:41 +02:00
|
|
|
return treeType.collectBlocks(iplayer.asWorld(), pos).filter(tree -> {
|
|
|
|
ParticleUtils.spawnParticle(iplayer.asWorld(), UParticles.GROUND_POUND, data.vec(), Vec3d.ZERO);
|
2023-05-25 14:10:18 +02:00
|
|
|
|
2023-09-12 12:25:41 +02:00
|
|
|
PlayerEntity player = iplayer.asEntity();
|
2022-01-03 21:54:07 +01:00
|
|
|
|
2023-09-12 12:25:41 +02:00
|
|
|
if (BlockDestructionManager.of(player.getWorld()).getBlockDestruction(pos) + 4 >= BlockDestructionManager.MAX_DAMAGE) {
|
|
|
|
if (player.getWorld().random.nextInt(30) == 0) {
|
2023-09-12 12:52:01 +02:00
|
|
|
tree.logs().forEach(player.getWorld(), (w, state, p) -> w.breakBlock(p, true));
|
2023-09-12 12:25:41 +02:00
|
|
|
tree.leaves().forEach(player.getWorld(), (w, state, p) -> {
|
2021-02-13 12:50:24 +01:00
|
|
|
Block.dropStacks(w.getBlockState(p), w, p);
|
2023-09-12 12:25:41 +02:00
|
|
|
w.setBlockState(p, Blocks.AIR.getDefaultState(), Block.NOTIFY_ALL);
|
|
|
|
});
|
|
|
|
}
|
2020-10-11 13:14:41 +02:00
|
|
|
|
2023-09-12 12:25:41 +02:00
|
|
|
iplayer.subtractEnergyCost(3);
|
|
|
|
} else {
|
2023-09-12 12:52:01 +02:00
|
|
|
tree.leaves().forEach(player.getWorld(), (w, state, p) -> {
|
|
|
|
if (w.random.nextInt(30) == 0) {
|
|
|
|
w.syncWorldEvent(WorldEvents.BLOCK_BROKEN, p, Block.getRawIdFromState(state));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2023-09-12 12:25:41 +02:00
|
|
|
int cost = dropApples(player, pos);
|
2020-10-11 13:14:41 +02:00
|
|
|
|
2023-09-12 12:25:41 +02:00
|
|
|
if (cost > 0) {
|
|
|
|
iplayer.subtractEnergyCost(cost / 7F);
|
|
|
|
}
|
2020-10-11 13:14:41 +02:00
|
|
|
}
|
2023-08-16 00:18:41 +02:00
|
|
|
|
2023-09-12 12:25:41 +02:00
|
|
|
return true;
|
|
|
|
}).isPresent();
|
2020-10-11 13:14:41 +02: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(40);
|
2020-10-11 13:14:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2023-08-16 00:18:41 +02:00
|
|
|
public void coolDown(Pony player, AbilitySlot slot) {
|
2023-09-12 12:25:41 +02:00
|
|
|
player.asEntity().getHungerManager().addExhaustion(0.1F);
|
2020-10-11 13:14:41 +02:00
|
|
|
}
|
|
|
|
|
2021-02-13 12:50:24 +01:00
|
|
|
private int dropApples(PlayerEntity player, BlockPos pos) {
|
2023-09-12 12:25:41 +02:00
|
|
|
TreeType treeType = TreeType.at(pos, player.getWorld());
|
|
|
|
return treeType.collectBlocks(player.getWorld(), pos).map(tree -> {
|
|
|
|
tree.logs().forEach(player.getWorld(), (world, state, position) -> {
|
2021-02-13 12:50:24 +01:00
|
|
|
affectBlockChange(player, position);
|
2023-09-12 12:25:41 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
int[] dropCount = {0};
|
|
|
|
tree.leaves().forEach(player.getWorld(), (world, state, position) -> {
|
2021-02-13 12:50:24 +01:00
|
|
|
affectBlockChange(player, position);
|
2023-09-12 12:25:41 +02:00
|
|
|
if (!buckBlock(treeType, state, world, position)
|
2022-09-25 13:58:10 +02:00
|
|
|
.filter(i -> !i.isEmpty())
|
2023-09-12 12:25:41 +02:00
|
|
|
.map(stack -> createDrop(stack, position, world, dropCount))
|
|
|
|
.toList().isEmpty()) {
|
2022-09-25 13:58:10 +02:00
|
|
|
world.syncWorldEvent(WorldEvents.BLOCK_BROKEN, position, Block.getRawIdFromState(state));
|
2021-02-13 12:50:24 +01:00
|
|
|
}
|
|
|
|
});
|
2020-10-11 13:14:41 +02:00
|
|
|
|
2023-09-12 12:25:41 +02:00
|
|
|
return dropCount[0] / 3;
|
|
|
|
}).orElse(0);
|
2020-10-11 13:14:41 +02:00
|
|
|
}
|
|
|
|
|
2023-09-12 12:25:41 +02:00
|
|
|
private ItemEntity createDrop(ItemStack stack, BlockPos pos, World world, int[] dropCount) {
|
2022-09-25 13:58:10 +02:00
|
|
|
ItemEntity entity = new ItemEntity(world,
|
|
|
|
pos.getX() + world.random.nextFloat(),
|
|
|
|
pos.getY() - 0.5,
|
|
|
|
pos.getZ() + world.random.nextFloat(),
|
|
|
|
stack
|
|
|
|
);
|
|
|
|
entity.setToDefaultPickupDelay();
|
2023-09-12 12:25:41 +02:00
|
|
|
world.spawnEntity(entity);
|
|
|
|
dropCount[0]++;
|
2022-09-25 13:58:10 +02:00
|
|
|
return entity;
|
|
|
|
}
|
|
|
|
|
2023-09-12 12:25:41 +02:00
|
|
|
private Stream<ItemStack> buckBlock(TreeType treeType, BlockState treeState, World world, BlockPos position) {
|
2022-09-25 13:58:10 +02:00
|
|
|
|
|
|
|
if (treeState.getBlock() instanceof Buckable buckable) {
|
|
|
|
return buckable.onBucked((ServerWorld)world, treeState, position).stream();
|
|
|
|
}
|
|
|
|
|
|
|
|
BlockPos down = position.down();
|
|
|
|
BlockState below = world.getBlockState(down);
|
|
|
|
|
|
|
|
if (below.isAir()) {
|
2023-09-12 12:25:41 +02:00
|
|
|
return Stream.of(treeType.pickRandomStack(world.random, treeState));
|
2022-09-25 13:58:10 +02:00
|
|
|
}
|
|
|
|
|
2023-05-25 14:10:18 +02:00
|
|
|
if (below.getBlock() instanceof Buckable buckable) {
|
|
|
|
return buckable.onBucked((ServerWorld)world, below, down).stream();
|
|
|
|
}
|
|
|
|
|
2022-09-25 13:58:10 +02:00
|
|
|
return Stream.empty();
|
|
|
|
}
|
|
|
|
|
2021-02-13 12:50:24 +01:00
|
|
|
private void affectBlockChange(PlayerEntity player, BlockPos position) {
|
2023-06-03 13:40:54 +02:00
|
|
|
BlockDestructionManager.of(player.getWorld()).damageBlock(position, 4);
|
2020-10-11 13:14:41 +02:00
|
|
|
|
2023-09-12 12:25:41 +02:00
|
|
|
PosHelper.fastAll(position, p -> {
|
2023-06-03 13:40:54 +02:00
|
|
|
BlockState s = player.getWorld().getBlockState(p);
|
2021-02-13 12:51:01 +01:00
|
|
|
|
|
|
|
if (s.getBlock() instanceof BeehiveBlock) {
|
2023-06-03 13:40:54 +02:00
|
|
|
if (player.getWorld().getBlockEntity(p) instanceof BeehiveBlockEntity hive) {
|
2022-09-25 13:58:10 +02:00
|
|
|
hive.angerBees(player, s, BeehiveBlockEntity.BeeState.EMERGENCY);
|
2021-02-13 12:51:01 +01:00
|
|
|
}
|
|
|
|
|
2023-06-03 13:40:54 +02:00
|
|
|
player.getWorld().updateComparators(position, s.getBlock());
|
2022-09-25 13:58:10 +02:00
|
|
|
|
2021-02-13 12:51:01 +01:00
|
|
|
Box area = new Box(position).expand(8, 6, 8);
|
2023-06-03 13:40:54 +02:00
|
|
|
List<BeeEntity> nearbyBees = player.getWorld().getNonSpectatingEntities(BeeEntity.class, area);
|
2021-02-13 12:51:01 +01:00
|
|
|
|
|
|
|
if (!nearbyBees.isEmpty()) {
|
2023-06-03 13:40:54 +02:00
|
|
|
List<PlayerEntity> nearbyPlayers = player.getWorld().getNonSpectatingEntities(PlayerEntity.class, area);
|
2022-09-25 13:58:10 +02:00
|
|
|
int i = nearbyPlayers.size();
|
|
|
|
|
|
|
|
for (BeeEntity bee : nearbyBees) {
|
|
|
|
if (bee.getTarget() == null) {
|
2023-06-03 13:40:54 +02:00
|
|
|
bee.setTarget(nearbyPlayers.get(player.getWorld().random.nextInt(i)));
|
2022-09-25 13:58:10 +02:00
|
|
|
}
|
|
|
|
}
|
2021-02-13 12:51:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}, PosHelper.HORIZONTAL);
|
2020-10-11 13:14:41 +02:00
|
|
|
}
|
2022-09-25 00:14:29 +02:00
|
|
|
|
|
|
|
public interface Buckable {
|
|
|
|
List<ItemStack> onBucked(ServerWorld world, BlockState state, BlockPos pos);
|
|
|
|
}
|
2020-10-11 13:14:41 +02:00
|
|
|
}
|