Unicopia/src/main/java/com/minelittlepony/unicopia/power/PowerStomp.java

487 lines
16 KiB
Java
Raw Normal View History

2018-09-12 01:29:49 +02:00
package com.minelittlepony.unicopia.power;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import org.lwjgl.input.Keyboard;
2019-02-07 14:47:33 +01:00
import com.google.common.collect.Lists;
import com.google.gson.annotations.Expose;
2018-09-12 01:29:49 +02:00
import com.minelittlepony.unicopia.Race;
2019-02-07 11:14:41 +01:00
import com.minelittlepony.unicopia.item.ItemApple;
2019-01-28 18:42:18 +01:00
import com.minelittlepony.unicopia.particle.Particles;
2018-09-12 01:29:49 +02:00
import com.minelittlepony.unicopia.player.IPlayer;
import com.minelittlepony.unicopia.player.PlayerSpeciesList;
import com.minelittlepony.unicopia.power.data.Location;
import com.minelittlepony.unicopia.world.UWorld;
2018-09-12 01:29:49 +02:00
import com.minelittlepony.util.MagicalDamageSource;
import com.minelittlepony.util.PosHelper;
2019-02-07 12:38:42 +01:00
import com.minelittlepony.util.WorldEvent;
2018-09-12 01:29:49 +02:00
import com.minelittlepony.util.shape.IShape;
import com.minelittlepony.util.shape.Sphere;
import com.minelittlepony.util.vector.VecHelper;
import net.minecraft.block.Block;
import net.minecraft.block.BlockLeaves;
import net.minecraft.block.BlockLog;
import net.minecraft.block.material.Material;
import net.minecraft.block.properties.IProperty;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.SharedMonsterAttributes;
2018-09-12 01:29:49 +02:00
import net.minecraft.entity.item.EntityItem;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.item.ItemStack;
import net.minecraft.util.DamageSource;
import net.minecraft.util.EnumFacing;
2018-09-12 01:29:49 +02:00
import net.minecraft.util.EnumParticleTypes;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import static net.minecraft.util.EnumFacing.*;
public class PowerStomp implements IPower<PowerStomp.Data> {
2019-02-03 10:45:45 +01:00
private final double rad = 4;
private final AxisAlignedBB areaOfEffect = new AxisAlignedBB(
-rad, -rad, -rad,
rad, rad, rad
);
2018-09-12 01:29:49 +02:00
@Override
public String getKeyName() {
return "unicopia.power.earth";
}
@Override
public int getKeyCode() {
return Keyboard.KEY_M;
}
@Override
public int getWarmupTime(IPlayer player) {
2019-02-07 10:46:59 +01:00
return 3;
2018-09-12 01:29:49 +02:00
}
@Override
public int getCooldownTime(IPlayer player) {
2019-02-07 10:46:59 +01:00
return 50;
2018-09-12 01:29:49 +02:00
}
@Override
public boolean canUse(Race playerSpecies) {
return playerSpecies.canUseEarth();
}
@Override
2019-02-03 10:45:45 +01:00
public PowerStomp.Data tryActivate(IPlayer player) {
2019-02-07 10:46:59 +01:00
RayTraceResult mop = VecHelper.getObjectMouseOver(player.getOwner(), 6, 1);
2018-09-12 01:29:49 +02:00
if (mop != null && mop.typeOfHit == RayTraceResult.Type.BLOCK) {
BlockPos pos = mop.getBlockPos();
2019-02-03 10:45:45 +01:00
IBlockState state = player.getWorld().getBlockState(pos);
2018-09-12 01:29:49 +02:00
if (state.getBlock() instanceof BlockLog) {
2019-02-03 10:45:45 +01:00
pos = getBaseOfTree(player.getWorld(), state, pos);
if (measureTree(player.getWorld(), state, pos) > 0) {
2018-09-12 01:29:49 +02:00
return new Data(pos.getX(), pos.getY(), pos.getZ(), 1);
}
}
}
2019-02-03 10:45:45 +01:00
if (!player.getOwner().onGround && !player.getOwner().capabilities.isFlying) {
player.getOwner().addVelocity(0, -6, 0);
2018-09-12 01:29:49 +02:00
return new Data(0, 0, 0, 0);
}
return null;
}
@Override
public Class<PowerStomp.Data> getPackageType() {
return PowerStomp.Data.class;
}
public static BlockPos getSolidBlockBelow(BlockPos pos, World w) {
while (w.isValid(pos)) {
pos = pos.down();
if (w.getBlockState(pos).isSideSolid(w, pos, EnumFacing.UP)) {
return pos;
}
}
return pos;
}
2018-09-12 01:29:49 +02:00
@Override
2019-02-03 10:45:45 +01:00
public void apply(IPlayer iplayer, Data data) {
2019-02-03 10:45:45 +01:00
EntityPlayer player = iplayer.getOwner();
2018-09-12 01:29:49 +02:00
if (data.hitType == 0) {
BlockPos ppos = player.getPosition();
BlockPos pos = getSolidBlockBelow(ppos, player.getEntityWorld());
player.addVelocity(0, -(ppos.distanceSq(pos)), 0);
2019-02-03 10:45:45 +01:00
iplayer.getWorld().getEntitiesWithinAABBExcludingEntity(player, areaOfEffect.offset(iplayer.getOriginVector())).forEach(i -> {
2018-09-12 01:29:49 +02:00
double dist = Math.sqrt(i.getDistanceSq(pos));
2018-09-12 01:29:49 +02:00
if (dist <= rad + 3) {
double force = dist / 5;
i.addVelocity(
-(player.posX - i.posX) / force,
-(player.posY - i.posY - 2) / force + (dist < 1 ? dist : 0),
-(player.posZ - i.posZ) / force);
2018-09-12 01:29:49 +02:00
DamageSource damage = MagicalDamageSource.causePlayerDamage("smash", player);
double amount = (4 * player.getEntityAttribute(SharedMonsterAttributes.ATTACK_DAMAGE).getAttributeValue()) / (float)dist;
2018-09-12 01:29:49 +02:00
if (i instanceof EntityPlayer) {
Race race = PlayerSpeciesList.instance().getPlayer((EntityPlayer)i).getPlayerSpecies();
if (race.canUseEarth()) {
amount /= 3;
}
if (race.canFly()) {
amount *= 4;
}
2018-09-12 01:29:49 +02:00
}
i.attackEntityFrom(damage, (float)amount);
2018-09-12 01:29:49 +02:00
}
2019-02-03 10:45:45 +01:00
});
2019-02-03 10:45:45 +01:00
BlockPos.getAllInBoxMutable(pos.add(-rad, -rad, -rad), pos.add(rad, rad, rad)).forEach(i -> {
2018-09-12 01:29:49 +02:00
if (i.distanceSqToCenter(player.posX, player.posY, player.posZ) <= rad*rad) {
spawnEffect(player.world, i);
}
2019-02-03 10:45:45 +01:00
});
2018-09-12 01:29:49 +02:00
for (int i = 1; i < 202; i+= 2) {
spawnParticleRing(player, i);
}
iplayer.subtractEnergyCost(rad);
2018-09-12 01:29:49 +02:00
} else if (data.hitType == 1) {
boolean harmed = player.getHealth() < player.getMaxHealth();
if (harmed && player.world.rand.nextInt(30) == 0) {
iplayer.subtractEnergyCost(3);
return;
}
if (harmed || player.world.rand.nextInt(5) == 0) {
if (!harmed || player.world.rand.nextInt(30) == 0) {
UWorld.enqueueTask(w -> removeTree(w, data.pos()));
}
iplayer.subtractEnergyCost(3);
} else {
int cost = dropApples(player.world, data.pos());
if (cost > 0) {
iplayer.subtractEnergyCost(cost * 3);
}
}
2018-09-12 01:29:49 +02:00
}
}
private void spawnEffect(World w, BlockPos pos) {
IBlockState state = w.getBlockState(pos);
if (state.getBlock() != Blocks.AIR) {
if (w.getBlockState(pos.up()).getBlock() == Blocks.AIR) {
2019-02-07 12:38:42 +01:00
WorldEvent.DESTROY_BLOCK.play(w, pos, state);
2018-09-12 01:29:49 +02:00
}
}
}
@Override
public void preApply(IPlayer player) {
player.addExertion(40);
player.getOwner().spawnRunningParticles();
2018-09-12 01:29:49 +02:00
}
@Override
public void postApply(IPlayer player) {
int timeDiff = getCooldownTime(player) - player.getAbilities().getRemainingCooldown();
2018-09-12 01:29:49 +02:00
if (player.getOwner().getEntityWorld().getWorldTime() % 1 == 0 || timeDiff == 0) {
spawnParticleRing(player.getOwner(), timeDiff, 1);
2018-09-12 01:29:49 +02:00
}
}
private void spawnParticleRing(EntityPlayer player, int timeDiff) {
spawnParticleRing(player, timeDiff, 0);
}
private void spawnParticleRing(EntityPlayer player, int timeDiff, double yVel) {
int animationTicks = (int)(timeDiff / 10);
if (animationTicks < 6) {
IShape 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().rand);
Particles.instance().spawnParticle(EnumParticleTypes.BLOCK_CRACK.getParticleID(), false,
player.posX + point.x,
player.posY + y + point.y,
player.posZ + point.z,
0, yVel, 0,
Block.getStateId(Blocks.DIRT.getDefaultState()));
}
}
}
private void removeTree(World w, BlockPos pos) {
IBlockState log = w.getBlockState(pos);
int size = measureTree(w, log, pos);
if (size > 0) {
pos = ascendTrunk(new ArrayList<BlockPos>(), w, pos, log, 0);
removeTreePart( w, log, pos, 0);
2018-09-12 01:29:49 +02:00
}
}
private BlockPos ascendTrunk(List<BlockPos> done, World w, BlockPos pos, IBlockState log, int level) {
if (level < 3 && !done.contains(pos)) {
done.add(pos);
BlockPos result = ascendTree(w, log, pos, true);
if (variantAndBlockEquals(w.getBlockState(pos.east()), log)) {
result = ascendTrunk(done, w, pos.east(), log, level + 1);
}
if (variantAndBlockEquals(w.getBlockState(pos.west()), log)) {
result = ascendTrunk(done, w, pos.west(), log, level + 1);
}
if (variantAndBlockEquals(w.getBlockState(pos.north()), log)) {
result = ascendTrunk(done, w, pos.north(), log, level + 1);
}
if (variantAndBlockEquals(w.getBlockState(pos.south()), log)) {
result = ascendTrunk(done, w, pos.south(), log, level + 1);
}
2018-09-12 01:29:49 +02:00
return result;
}
return pos;
}
private void removeTreePart(World w, IBlockState log, BlockPos pos, int level) {
if (level < 10 && isWoodOrLeaf(w, log, pos)) {
if (level < 5) {
w.destroyBlock(pos, true);
} else {
IBlockState state = w.getBlockState(pos);
state.getBlock().dropBlockAsItem(w, pos, state, 0);
w.setBlockState(pos, Blocks.AIR.getDefaultState(), 3);
}
PosHelper.all(pos, p -> {
removeTreePart(w, log, p, level + 1);
}, UP, NORTH, SOUTH, EAST, WEST);
}
}
private BlockPos ascendTree(World w, IBlockState log, BlockPos pos, boolean remove) {
int breaks = 0;
IBlockState state;
while (variantAndBlockEquals(w.getBlockState(pos.up()), log)) {
if (PosHelper.some(pos, p -> isLeaves(w.getBlockState(p), log), HORIZONTALS)) {
break;
}
if (remove) {
if (breaks < 10) {
w.destroyBlock(pos, true);
} else {
state = w.getBlockState(pos);
state.getBlock().dropBlockAsItem(w, pos, state, 0);
w.setBlockState(pos, Blocks.AIR.getDefaultState(), 3);
}
breaks++;
}
pos = pos.up();
}
return pos;
}
private int dropApples(World w, BlockPos pos) {
2018-09-12 01:29:49 +02:00
IBlockState log = w.getBlockState(pos);
int size = measureTree(w, log, pos);
if (size > 0) {
2019-02-07 14:47:33 +01:00
List<EntityItem> capturedDrops = Lists.newArrayList();
dropApplesPart(capturedDrops, new ArrayList<BlockPos>(), w, log, pos, 0);
UWorld.enqueueTask(wo -> {
capturedDrops.forEach(item -> {
item.setNoPickupDelay();
wo.spawnEntity(item);
});
});
return capturedDrops.size() / 3;
2018-09-12 01:29:49 +02:00
}
return 0;
2018-09-12 01:29:49 +02:00
}
2019-02-07 14:47:33 +01:00
private void dropApplesPart(List<EntityItem> drops, List<BlockPos> done, World w, IBlockState log, BlockPos pos, int level) {
2018-09-12 01:29:49 +02:00
if (!done.contains(pos)) {
done.add(pos);
pos = ascendTree(w, log, pos, false);
if (level < 10 && isWoodOrLeaf(w, log, pos)) {
IBlockState state = w.getBlockState(pos);
2019-02-03 10:45:45 +01:00
2018-09-12 01:29:49 +02:00
if (state.getBlock() instanceof BlockLeaves && w.getBlockState(pos.down()).getMaterial() == Material.AIR) {
2019-02-07 12:38:42 +01:00
WorldEvent.DESTROY_BLOCK.play(w, pos, state);
2019-02-03 10:45:45 +01:00
2018-09-12 01:29:49 +02:00
EntityItem item = new EntityItem(w);
item.setPosition(pos.getX() + w.rand.nextFloat(), pos.getY() - 0.5, pos.getZ() + w.rand.nextFloat());
2019-01-13 21:05:40 +01:00
item.setItem(getApple(w, log));
2019-02-07 14:47:33 +01:00
drops.add(item);
2018-09-12 01:29:49 +02:00
}
PosHelper.all(pos, p -> {
2019-02-07 14:47:33 +01:00
dropApplesPart(drops, done, w, log, p, level + 1);
2018-09-12 01:29:49 +02:00
}, UP, NORTH, SOUTH, EAST, WEST);
}
}
}
2019-01-13 21:05:40 +01:00
private ItemStack getApple(World w, IBlockState log) {
2019-02-07 11:14:41 +01:00
return ItemApple.getRandomItemStack(getVariant(log));
2018-09-12 01:29:49 +02:00
}
private int measureTree(World w, IBlockState log, BlockPos pos) {
List<BlockPos> logs = new ArrayList<BlockPos>();
List<BlockPos> leaves = new ArrayList<BlockPos>();
2019-02-03 10:45:45 +01:00
2018-09-12 01:29:49 +02:00
countParts(logs, leaves, w, log, pos);
2019-02-03 10:45:45 +01:00
2018-09-12 01:29:49 +02:00
return logs.size() <= (leaves.size() / 2) ? logs.size() + leaves.size() : 0;
}
private BlockPos getBaseOfTree(World w, IBlockState log, BlockPos pos) {
return getBaseOfTreePart(new ArrayList<BlockPos>(), w, log, pos);
}
private BlockPos getBaseOfTreePart(List<BlockPos> done, World w, IBlockState log, BlockPos pos) {
if (done.contains(pos) || !variantAndBlockEquals(w.getBlockState(pos), log)) {
return null;
}
done.add(pos);
while (variantAndBlockEquals(w.getBlockState(pos.down()), log)) {
pos = pos.down();
done.add(pos);
}
BlockPos adjacent = getBaseOfTreePart(done, w, log, pos.north());
if (adjacent != null && adjacent.getY() < pos.getY()) {
pos = adjacent;
}
adjacent = getBaseOfTreePart(done, w, log, pos.south());
if (adjacent != null && adjacent.getY() < pos.getY()) {
pos = adjacent;
}
adjacent = getBaseOfTreePart(done, w, log, pos.east());
if (adjacent != null && adjacent.getY() < pos.getY()) {
pos = adjacent;
}
adjacent = getBaseOfTreePart(done, w, log, pos.west());
if (adjacent != null && adjacent.getY() < pos.getY()) {
pos = adjacent;
}
if (!done.contains(pos)) {
done.add(pos);
}
return pos;
}
private boolean isWoodOrLeaf(World w, IBlockState log, BlockPos pos) {
IBlockState state = w.getBlockState(pos);
return variantAndBlockEquals(state, log) || (isLeaves(state, log) && ((Boolean)state.getValue(BlockLeaves.DECAYABLE)).booleanValue());
}
private void countParts(List<BlockPos> logs, List<BlockPos> leaves, World w, IBlockState log, BlockPos pos) {
if (logs.contains(pos) || leaves.contains(pos)) {
return;
}
IBlockState state = w.getBlockState(pos);
boolean yay = false;
if (state.getBlock() instanceof BlockLeaves && ((Boolean)state.getValue(BlockLeaves.DECAYABLE)).booleanValue() && variantEquals(state, log)) {
leaves.add(pos);
yay = true;
} else if (variantAndBlockEquals(state, log)) {
logs.add(pos);
yay = true;
}
if (yay) {
PosHelper.all(pos, p -> {
countParts(logs, leaves, w, log, p);
}, UP, NORTH, SOUTH, EAST, WEST);
}
}
private boolean isLeaves(IBlockState state, IBlockState log) {
return state.getBlock() instanceof BlockLeaves && variantEquals(state, log);
}
private boolean variantAndBlockEquals(IBlockState one, IBlockState two) {
return (one.getBlock() == two.getBlock()) && variantEquals(one, two);
}
private boolean variantEquals(IBlockState one, IBlockState two) {
return getVariant(one) == getVariant(two);
}
private Object getVariant(IBlockState state) {
2019-02-03 10:45:45 +01:00
if (state.getBlock() instanceof BlockLeaves) {
return ((BlockLeaves)state.getBlock()).getWoodType(state.getBlock().getMetaFromState(state));
}
2018-09-12 01:29:49 +02:00
for (Entry<IProperty<?>, ?> i : state.getProperties().entrySet()) {
if (i.getKey().getName().contentEquals("variant")) {
return i.getValue();
}
}
return null;
}
protected static class Data extends Location {
@Expose
2018-09-12 01:29:49 +02:00
public int hitType;
public Data(int x, int y, int z, int hit) {
super(x, y, z);
hitType = hit;
}
}
}