diff --git a/src/main/java/com/minelittlepony/unicopia/ability/Abilities.java b/src/main/java/com/minelittlepony/unicopia/ability/Abilities.java index 6be7cd9f..b8a1ebaa 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/Abilities.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/Abilities.java @@ -20,6 +20,7 @@ public interface Abilities { Ability SHOOT = register(new UnicornProjectileAbility(), "shoot", AbilitySlot.TERTIARY); // earth / alicorn + Ability KICK = register(new EarthPonyKickAbility(), "kick", AbilitySlot.PRIMARY); Ability GROW = register(new EarthPonyGrowAbility(), "grow", AbilitySlot.SECONDARY); Ability STOMP = register(new EarthPonyStompAbility(), "stomp", AbilitySlot.TERTIARY); diff --git a/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyKickAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyKickAbility.java new file mode 100644 index 00000000..77f9cfa8 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyKickAbility.java @@ -0,0 +1,190 @@ +package com.minelittlepony.unicopia.ability; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import javax.annotation.Nullable; + +import com.google.common.collect.Lists; +import com.minelittlepony.unicopia.Race; +import com.minelittlepony.unicopia.TreeTraverser; +import com.minelittlepony.unicopia.TreeType; +import com.minelittlepony.unicopia.ability.data.Hit; +import com.minelittlepony.unicopia.ability.data.Pos; +import com.minelittlepony.unicopia.entity.player.Pony; +import com.minelittlepony.unicopia.util.PosHelper; +import com.minelittlepony.unicopia.util.RayTraceHelper; +import com.minelittlepony.unicopia.util.WorldEvent; +import com.minelittlepony.unicopia.util.shape.Shape; +import com.minelittlepony.unicopia.util.shape.Sphere; + +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.block.LeavesBlock; +import net.minecraft.entity.ItemEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.particle.BlockStateParticleEffect; +import net.minecraft.particle.ParticleTypes; +import net.minecraft.tag.BlockTags; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.World; + +/** + * Earth Pony kicking ability + */ +public class EarthPonyKickAbility implements Ability { + + @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(); + } + + @Override + public double getCostEstimate(Pony player) { + return 3; + } + + @Nullable + @Override + public Pos tryActivate(Pony player) { + Optional p = RayTraceHelper.doTrace(player.getMaster(), 6, 1).getBlockPos(); + + if (p.isPresent()) { + BlockPos pos = p.get(); + BlockState state = player.getWorld().getBlockState(pos); + + if (state.getBlock().isIn(BlockTags.LOGS)) { + pos = TreeTraverser.Descender.descendTree(player.getWorld(), state, pos).get(); + if (TreeTraverser.Measurer.measureTree(player.getWorld(), state, pos) > 0) { + return new Pos(pos); + } + } + } + + return null; + } + + @Override + public Hit.Serializer getSerializer() { + return Pos.SERIALIZER; + } + + @Override + public void apply(Pony iplayer, Pos data) { + PlayerEntity player = iplayer.getMaster(); + + boolean harmed = player.getHealth() < player.getMaxHealth(); + + if (harmed && player.world.random.nextInt(30) == 0) { + iplayer.subtractEnergyCost(3); + return; + } + + if (harmed || player.world.random.nextInt(5) == 0) { + + if (!harmed || player.world.random.nextInt(30) == 0) { + TreeTraverser.Remover.removeTree(player.world, data.pos()); + } + + iplayer.subtractEnergyCost(3); + } else { + int cost = dropApples(player.world, data.pos()); + + if (cost > 0) { + iplayer.subtractEnergyCost(cost * 3); + } + } + } + + @Override + public void preApply(Pony player, AbilitySlot slot) { + player.getMagicalReserves().getExertion().add(40); + } + + @Override + public void postApply(Pony player, AbilitySlot slot) { + int timeDiff = getCooldownTime(player) - player.getAbilities().getStat(slot).getRemainingCooldown(); + + if (player.getMaster().getEntityWorld().getTime() % 1 == 0 || timeDiff == 0) { + spawnParticleRing(player.getMaster(), timeDiff, 1); + } + } + + private void spawnParticleRing(PlayerEntity player, int timeDiff, double yVel) { + int animationTicks = timeDiff / 10; + if (animationTicks < 6) { + Shape shape = new Sphere(true, animationTicks, 1, 0, 1); + + double y = 0.5 + (Math.sin(animationTicks) * 1.5); + + yVel *= y * 5; + + for (int i = 0; i < shape.getVolumeOfSpawnableSpace(); i++) { + Vec3d point = shape.computePoint(player.getEntityWorld().random).add(player.getPos()); + player.world.addParticle(new BlockStateParticleEffect(ParticleTypes.BLOCK, Blocks.DIRT.getDefaultState()), + point.x, + point.y + y, + point.z, + 0, yVel, 0 + ); + } + } + } + + private int dropApples(World w, BlockPos pos) { + BlockState log = w.getBlockState(pos); + int size = TreeTraverser.Measurer.measureTree(w, log, pos); + if (size > 0) { + + List capturedDrops = Lists.newArrayList(); + + dropApplesPart(capturedDrops, new ArrayList(), w, log, pos, 0); + + capturedDrops.forEach(item -> { + item.setToDefaultPickupDelay(); + w.spawnEntity(item); + }); + + return capturedDrops.size() / 3; + } + + return 0; + } + + private static void dropApplesPart(List drops, List done, World w, BlockState log, BlockPos pos, int level) { + if (!done.contains(pos)) { + done.add(pos); + pos = TreeTraverser.Ascender.ascendTree(w, log, pos, false); + if (level < 10 && TreeTraverser.isWoodOrLeaf(w, log, pos)) { + BlockState state = w.getBlockState(pos); + + if (state.getBlock() instanceof LeavesBlock && w.getBlockState(pos.down()).isAir()) { + WorldEvent.play(WorldEvent.DESTROY_BLOCK, w, pos, state); + drops.add(new ItemEntity(w, + pos.getX() + w.random.nextFloat(), + pos.getY() - 0.5, + pos.getZ() + w.random.nextFloat(), + TreeType.get(log).pickRandomStack() + )); + } + + PosHelper.all(pos, p -> { + dropApplesPart(drops, done, w, log, p, level + 1); + }, Direction.UP, Direction.NORTH, Direction.SOUTH, Direction.EAST, Direction.WEST); + } + } + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyStompAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyStompAbility.java index 52495dff..7db679f3 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyStompAbility.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyStompAbility.java @@ -1,21 +1,11 @@ package com.minelittlepony.unicopia.ability; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; - import javax.annotation.Nullable; -import com.google.common.collect.Lists; import com.minelittlepony.unicopia.Race; -import com.minelittlepony.unicopia.TreeTraverser; -import com.minelittlepony.unicopia.TreeType; import com.minelittlepony.unicopia.ability.data.Hit; -import com.minelittlepony.unicopia.ability.data.Multi; import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.util.MagicalDamageSource; -import com.minelittlepony.unicopia.util.PosHelper; -import com.minelittlepony.unicopia.util.RayTraceHelper; import com.minelittlepony.unicopia.util.WorldEvent; import com.minelittlepony.unicopia.util.shape.Shape; import com.minelittlepony.unicopia.util.shape.Sphere; @@ -23,26 +13,22 @@ import com.minelittlepony.unicopia.util.shape.Sphere; import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.block.Blocks; -import net.minecraft.block.LeavesBlock; import net.minecraft.block.ShapeContext; -import net.minecraft.entity.ItemEntity; import net.minecraft.entity.attribute.EntityAttributes; import net.minecraft.entity.damage.DamageSource; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.particle.BlockStateParticleEffect; import net.minecraft.particle.ParticleTypes; -import net.minecraft.tag.BlockTags; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Box; import net.minecraft.util.math.Direction; import net.minecraft.util.math.Vec3d; -import net.minecraft.util.math.Vec3i; import net.minecraft.world.World; /** * Earth Pony stomping ability */ -public class EarthPonyStompAbility implements Ability { +public class EarthPonyStompAbility implements Ability { private final double rad = 4; @@ -73,109 +59,69 @@ public class EarthPonyStompAbility implements Ability { @Nullable @Override - public Multi tryActivate(Pony player) { - Optional p = RayTraceHelper.doTrace(player.getMaster(), 6, 1).getBlockPos(); - - if (p.isPresent()) { - BlockPos pos = p.get(); - BlockState state = player.getWorld().getBlockState(pos); - - if (state.getBlock().isIn(BlockTags.LOGS)) { - pos = TreeTraverser.Descender.descendTree(player.getWorld(), state, pos).get(); - if (TreeTraverser.Measurer.measureTree(player.getWorld(), state, pos) > 0) { - return new Multi(pos, 1); - } - } - } - + public Hit tryActivate(Pony player) { if (!player.getMaster().isOnGround() && !player.getMaster().abilities.flying) { player.getMaster().addVelocity(0, -6, 0); - return new Multi(Vec3i.ZERO, 0); + return Hit.INSTANCE; } return null; } @Override - public Hit.Serializer getSerializer() { - return Multi.SERIALIZER; + public Hit.Serializer getSerializer() { + return Hit.SERIALIZER; } - @Override - public void apply(Pony iplayer, Multi data) { + public void apply(Pony iplayer, Hit data) { PlayerEntity player = iplayer.getMaster(); - if (data.hitType == 0) { - BlockPos ppos = player.getBlockPos(); - BlockPos pos = getSolidBlockBelow(ppos, player.getEntityWorld()); + BlockPos ppos = player.getBlockPos(); + BlockPos pos = getSolidBlockBelow(ppos, player.getEntityWorld()); - player.addVelocity(0, -(ppos.getSquaredDistance(pos)), 0); + player.addVelocity(0, -(ppos.getSquaredDistance(pos)), 0); - iplayer.getWorld().getOtherEntities(player, areaOfEffect.offset(iplayer.getOriginVector())).forEach(i -> { - double dist = Math.sqrt(pos.getSquaredDistance(i.getBlockPos())); + iplayer.getWorld().getOtherEntities(player, areaOfEffect.offset(iplayer.getOriginVector())).forEach(i -> { + double dist = Math.sqrt(pos.getSquaredDistance(i.getBlockPos())); - if (dist <= rad + 3) { - double force = dist / 5; - i.addVelocity( - -(player.getX() - i.getX()) / force, - -(player.getY() - i.getY() - 2) / force + (dist < 1 ? dist : 0), - -(player.getZ() - i.getZ()) / force); + if (dist <= rad + 3) { + double force = dist / 5; + i.addVelocity( + -(player.getX() - i.getX()) / force, + -(player.getY() - i.getY() - 2) / force + (dist < 1 ? dist : 0), + -(player.getZ() - i.getZ()) / force); - DamageSource damage = MagicalDamageSource.create("smash", player); + DamageSource damage = MagicalDamageSource.create("smash", player); - double amount = (4 * player.getAttributeInstance(EntityAttributes.GENERIC_ATTACK_DAMAGE).getValue()) / (float)dist; + double amount = (4 * player.getAttributeInstance(EntityAttributes.GENERIC_ATTACK_DAMAGE).getValue()) / (float)dist; - if (i instanceof PlayerEntity) { - Race race = Pony.of((PlayerEntity)i).getSpecies(); - if (race.canUseEarth()) { - amount /= 3; - } - - if (race.canFly()) { - amount *= 4; - } + if (i instanceof PlayerEntity) { + Race race = Pony.of((PlayerEntity)i).getSpecies(); + if (race.canUseEarth()) { + amount /= 3; } - i.damage(damage, (float)amount); + if (race.canFly()) { + amount *= 4; + } } - }); - BlockPos.iterate(pos.add(-rad, -rad, -rad), pos.add(rad, rad, rad)).forEach(i -> { - if (i.getSquaredDistance(player.getX(), player.getY(), player.getZ(), true) <= rad*rad) { - spawnEffect(player.world, i); - } - }); - - for (int i = 1; i < 202; i+= 2) { - spawnParticleRing(player, i, 0); + i.damage(damage, (float)amount); } + }); - iplayer.subtractEnergyCost(rad); - } else if (data.hitType == 1) { - - boolean harmed = player.getHealth() < player.getMaxHealth(); - - if (harmed && player.world.random.nextInt(30) == 0) { - iplayer.subtractEnergyCost(3); - return; + BlockPos.iterate(pos.add(-rad, -rad, -rad), pos.add(rad, rad, rad)).forEach(i -> { + if (i.getSquaredDistance(player.getX(), player.getY(), player.getZ(), true) <= rad*rad) { + spawnEffect(player.world, i); } + }); - if (harmed || player.world.random.nextInt(5) == 0) { - - if (!harmed || player.world.random.nextInt(30) == 0) { - TreeTraverser.Remover.removeTree(player.world, data.pos()); - } - - iplayer.subtractEnergyCost(3); - } else { - int cost = dropApples(player.world, data.pos()); - - if (cost > 0) { - iplayer.subtractEnergyCost(cost * 3); - } - } + for (int i = 1; i < 202; i+= 2) { + spawnParticleRing(player, i, 0); } + + iplayer.subtractEnergyCost(rad); } private void spawnEffect(World w, BlockPos pos) { @@ -221,50 +167,6 @@ public class EarthPonyStompAbility implements Ability { } } - private int dropApples(World w, BlockPos pos) { - BlockState log = w.getBlockState(pos); - int size = TreeTraverser.Measurer.measureTree(w, log, pos); - if (size > 0) { - - List capturedDrops = Lists.newArrayList(); - - dropApplesPart(capturedDrops, new ArrayList(), w, log, pos, 0); - - capturedDrops.forEach(item -> { - item.setToDefaultPickupDelay(); - w.spawnEntity(item); - }); - - return capturedDrops.size() / 3; - } - - return 0; - } - - private static void dropApplesPart(List drops, List done, World w, BlockState log, BlockPos pos, int level) { - if (!done.contains(pos)) { - done.add(pos); - pos = TreeTraverser.Ascender.ascendTree(w, log, pos, false); - if (level < 10 && TreeTraverser.isWoodOrLeaf(w, log, pos)) { - BlockState state = w.getBlockState(pos); - - if (state.getBlock() instanceof LeavesBlock && w.getBlockState(pos.down()).isAir()) { - WorldEvent.play(WorldEvent.DESTROY_BLOCK, w, pos, state); - drops.add(new ItemEntity(w, - pos.getX() + w.random.nextFloat(), - pos.getY() - 0.5, - pos.getZ() + w.random.nextFloat(), - TreeType.get(log).pickRandomStack() - )); - } - - PosHelper.all(pos, p -> { - dropApplesPart(drops, done, w, log, p, level + 1); - }, Direction.UP, Direction.NORTH, Direction.SOUTH, Direction.EAST, Direction.WEST); - } - } - } - private static BlockPos getSolidBlockBelow(BlockPos pos, World w) { while (!World.isHeightInvalid(pos)) { pos = pos.down(); diff --git a/src/main/resources/assets/unicopia/lang/en_us.json b/src/main/resources/assets/unicopia/lang/en_us.json index 4e83dd5f..676a9ba4 100644 --- a/src/main/resources/assets/unicopia/lang/en_us.json +++ b/src/main/resources/assets/unicopia/lang/en_us.json @@ -130,6 +130,7 @@ "ability.unicopia.teleport": "Teleport", "ability.unicopia.grow": "Earthly Nourishment", "ability.unicopia.stomp": "Ground Pound", + "ability.unicopia.kick": "Crushing Blow", "ability.unicopia.pummel": "Crushing Blow", "ability.unicopia.carry": "Pickup/Drop Passenger", "ability.unicopia.hang": "Cling to Ceiling", diff --git a/src/main/resources/assets/unicopia/textures/gui/ability/kick.png b/src/main/resources/assets/unicopia/textures/gui/ability/kick.png new file mode 100644 index 00000000..de75f52f Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/gui/ability/kick.png differ