2020-04-15 14:22:03 +02:00
|
|
|
package com.minelittlepony.unicopia.entity;
|
2020-01-16 12:35:46 +01:00
|
|
|
|
|
|
|
import java.util.UUID;
|
|
|
|
|
2020-04-15 14:22:03 +02:00
|
|
|
import com.minelittlepony.unicopia.magic.Affinity;
|
2020-04-15 18:12:00 +02:00
|
|
|
import com.minelittlepony.unicopia.magic.Caster;
|
2020-05-28 18:09:34 +02:00
|
|
|
import com.minelittlepony.unicopia.magic.EtherialListener;
|
2020-04-15 18:12:00 +02:00
|
|
|
import com.minelittlepony.unicopia.magic.MagicEffect;
|
|
|
|
import com.minelittlepony.unicopia.magic.TossedMagicEffect;
|
2020-04-15 14:22:03 +02:00
|
|
|
import com.minelittlepony.unicopia.magic.spell.SpellRegistry;
|
2020-05-03 22:42:16 +02:00
|
|
|
import com.minelittlepony.unicopia.network.Channel;
|
2020-04-15 14:22:03 +02:00
|
|
|
import com.minelittlepony.unicopia.network.EffectSync;
|
2020-05-03 22:42:16 +02:00
|
|
|
import com.minelittlepony.unicopia.network.MsgSpawnProjectile;
|
2020-04-15 18:12:00 +02:00
|
|
|
import com.minelittlepony.unicopia.util.projectile.AdvancedProjectile;
|
|
|
|
import com.minelittlepony.unicopia.util.projectile.Tossable;
|
|
|
|
import com.minelittlepony.unicopia.util.projectile.TossableItem;
|
2020-01-16 12:35:46 +01:00
|
|
|
|
|
|
|
import net.minecraft.entity.Entity;
|
|
|
|
import net.minecraft.entity.EntityType;
|
|
|
|
import net.minecraft.entity.LivingEntity;
|
|
|
|
import net.minecraft.entity.damage.DamageSource;
|
|
|
|
import net.minecraft.entity.data.DataTracker;
|
|
|
|
import net.minecraft.entity.data.TrackedData;
|
|
|
|
import net.minecraft.entity.data.TrackedDataHandlerRegistry;
|
|
|
|
import net.minecraft.entity.thrown.ThrownItemEntity;
|
|
|
|
import net.minecraft.item.Item;
|
|
|
|
import net.minecraft.item.ItemStack;
|
|
|
|
import net.minecraft.item.Items;
|
|
|
|
import net.minecraft.nbt.CompoundTag;
|
2020-05-03 22:42:16 +02:00
|
|
|
import net.minecraft.network.Packet;
|
2020-01-16 12:35:46 +01:00
|
|
|
import net.minecraft.particle.ItemStackParticleEffect;
|
|
|
|
import net.minecraft.particle.ParticleEffect;
|
|
|
|
import net.minecraft.particle.ParticleTypes;
|
|
|
|
import net.minecraft.server.world.ServerWorld;
|
|
|
|
import net.minecraft.util.hit.BlockHitResult;
|
|
|
|
import net.minecraft.util.hit.EntityHitResult;
|
|
|
|
import net.minecraft.util.hit.HitResult;
|
2020-05-28 18:09:34 +02:00
|
|
|
import net.minecraft.util.math.BlockPos;
|
2020-01-16 12:35:46 +01:00
|
|
|
import net.minecraft.util.math.Vec3d;
|
|
|
|
import net.minecraft.world.World;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A generalised version of Mojang's projectile entity class with added support for a custom appearance and water phobia.
|
|
|
|
*
|
|
|
|
* Can also carry a spell if needed.
|
|
|
|
*/
|
2020-04-15 18:12:00 +02:00
|
|
|
public class ProjectileEntity extends ThrownItemEntity implements IMagicals, AdvancedProjectile, Caster<LivingEntity> {
|
2020-01-16 12:35:46 +01:00
|
|
|
|
2020-01-27 11:05:22 +01:00
|
|
|
private static final TrackedData<Float> DAMAGE = DataTracker.registerData(ProjectileEntity.class, TrackedDataHandlerRegistry.FLOAT);
|
|
|
|
private static final TrackedData<Boolean> HYDROPHOBIC = DataTracker.registerData(ProjectileEntity.class, TrackedDataHandlerRegistry.BOOLEAN);
|
|
|
|
private static final TrackedData<CompoundTag> EFFECT = DataTracker.registerData(ProjectileEntity.class, TrackedDataHandlerRegistry.TAG_COMPOUND);
|
2020-01-16 12:35:46 +01:00
|
|
|
|
|
|
|
private final EffectSync effectDelegate = new EffectSync(this, EFFECT);
|
|
|
|
|
|
|
|
private UUID ownerUuid;
|
|
|
|
|
2020-05-28 18:09:34 +02:00
|
|
|
private BlockPos lastBlockPos;
|
|
|
|
|
2020-01-27 11:05:22 +01:00
|
|
|
public ProjectileEntity(EntityType<ProjectileEntity> type, World world) {
|
2020-01-16 12:35:46 +01:00
|
|
|
super(type, world);
|
|
|
|
}
|
|
|
|
|
2020-01-27 11:05:22 +01:00
|
|
|
public ProjectileEntity(EntityType<ProjectileEntity> type, World world, LivingEntity thrower) {
|
2020-04-22 16:28:20 +02:00
|
|
|
this(type, world, thrower.getX(), thrower.getY() + thrower.getStandingEyeHeight(), thrower.getZ());
|
2020-01-16 12:35:46 +01:00
|
|
|
setOwner(thrower);
|
|
|
|
}
|
|
|
|
|
2020-01-27 11:05:22 +01:00
|
|
|
public ProjectileEntity(EntityType<ProjectileEntity> type, World world, double x, double y, double z) {
|
2020-01-16 12:35:46 +01:00
|
|
|
super(type, world);
|
|
|
|
|
2020-04-22 16:28:20 +02:00
|
|
|
setPos(x, y, z);
|
2020-01-16 12:35:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void initDataTracker() {
|
|
|
|
super.initDataTracker();
|
|
|
|
getDataTracker().startTracking(DAMAGE, (float)0);
|
|
|
|
getDataTracker().startTracking(EFFECT, new CompoundTag());
|
|
|
|
getDataTracker().startTracking(HYDROPHOBIC, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected Item getDefaultItem() {
|
|
|
|
return Items.AIR;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Entity getEntity() {
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void setOwner(LivingEntity owner) {
|
|
|
|
ownerUuid = owner == null ? null : owner.getUuid();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public LivingEntity getOwner() {
|
|
|
|
if (ownerUuid == null || !(world instanceof ServerWorld)) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (LivingEntity) ((ServerWorld)world).getEntity(ownerUuid);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int getCurrentLevel() {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void setCurrentLevel(int level) {
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Affinity getAffinity() {
|
|
|
|
return hasEffect() ? Affinity.NEUTRAL : getEffect().getAffinity();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void setGravity(boolean gravity) {
|
|
|
|
setNoGravity(gravity);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-04-15 18:12:00 +02:00
|
|
|
public void setEffect(TossedMagicEffect effect) {
|
|
|
|
setEffect((MagicEffect)effect);
|
2020-01-16 12:35:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-04-15 18:12:00 +02:00
|
|
|
public void setEffect(MagicEffect effect) {
|
2020-01-16 12:35:46 +01:00
|
|
|
effectDelegate.set(effect);
|
|
|
|
|
|
|
|
if (effect != null) {
|
|
|
|
effect.onPlaced(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-05-28 18:09:34 +02:00
|
|
|
public <T> T getEffect(Class<T> type, boolean update) {
|
2020-01-16 12:35:46 +01:00
|
|
|
return effectDelegate.get(type, update);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean hasEffect() {
|
|
|
|
return effectDelegate.has();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void setThrowDamage(float damage) {
|
|
|
|
getDataTracker().set(DAMAGE, Math.max(0, damage));
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public float getThrowDamage() {
|
|
|
|
return getDataTracker().get(DAMAGE);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void setHydrophobic() {
|
|
|
|
getDataTracker().set(HYDROPHOBIC, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean getHydrophobic() {
|
|
|
|
return getDataTracker().get(HYDROPHOBIC);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void tick() {
|
|
|
|
|
|
|
|
if (!world.isClient()) {
|
|
|
|
if (Math.abs(getVelocity().x) < 0.01 && Math.abs(getVelocity().x) < 0.01 && Math.abs(getVelocity().y) < 0.01) {
|
|
|
|
remove();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
super.tick();
|
|
|
|
|
|
|
|
if (age % 1000 == 0) {
|
|
|
|
setNoGravity(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hasEffect()) {
|
2020-05-28 18:09:34 +02:00
|
|
|
if (lastBlockPos == null || !lastBlockPos.equals(getBlockPos())) {
|
|
|
|
notifyNearbySpells(getEffect(), lastBlockPos, 6, EtherialListener.REMOVED);
|
|
|
|
lastBlockPos = getBlockPos();
|
|
|
|
notifyNearbySpells(getEffect(), 6, EtherialListener.ADDED);
|
|
|
|
}
|
|
|
|
|
2020-01-17 14:27:26 +01:00
|
|
|
if (getEffect().isDead()) {
|
2020-01-16 12:35:46 +01:00
|
|
|
remove();
|
|
|
|
} else {
|
|
|
|
getEffect().update(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (world.isClient()) {
|
|
|
|
getEffect().render(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (getHydrophobic()) {
|
|
|
|
if (world.getBlockState(getBlockPos()).getMaterial().isLiquid()) {
|
|
|
|
Vec3d vel = getVelocity();
|
|
|
|
|
|
|
|
double velY = vel.y;
|
|
|
|
|
|
|
|
velY *= -1;
|
|
|
|
|
|
|
|
if (!hasNoGravity()) {
|
|
|
|
velY += 0.16;
|
|
|
|
}
|
|
|
|
|
|
|
|
setVelocity(new Vec3d(vel.x, velY, vel.z));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void launch(Entity entityThrower, float pitch, float yaw, float wobble, float velocity, float inaccuracy) {
|
2020-05-03 22:42:16 +02:00
|
|
|
setProperties(entityThrower, pitch, yaw, wobble, velocity, inaccuracy);
|
2020-01-16 12:35:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private ParticleEffect getParticleParameters() {
|
|
|
|
ItemStack stack = getItem();
|
|
|
|
|
|
|
|
if (stack.isEmpty()) {
|
|
|
|
return ParticleTypes.ITEM_SNOWBALL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return new ItemStackParticleEffect(ParticleTypes.ITEM, stack);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-05-07 13:15:53 +02:00
|
|
|
public void handleStatus(byte id) {
|
|
|
|
if (id == 3) {
|
2020-01-16 12:35:46 +01:00
|
|
|
ParticleEffect effect = getParticleParameters();
|
|
|
|
|
|
|
|
for(int i = 0; i < 8; i++) {
|
2020-04-22 16:28:20 +02:00
|
|
|
world.addParticle(effect, getX(), getY(), getZ(), 0, 0, 0);
|
2020-01-16 12:35:46 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void readCustomDataFromTag(CompoundTag compound) {
|
|
|
|
super.readCustomDataFromTag(compound);
|
|
|
|
|
2020-04-22 16:28:20 +02:00
|
|
|
if (compound.contains("effect")) {
|
2020-01-16 12:35:46 +01:00
|
|
|
setEffect(SpellRegistry.instance().createEffectFromNBT(compound.getCompound("effect")));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void writeCustomDataToTag(CompoundTag compound) {
|
|
|
|
super.writeCustomDataToTag(compound);
|
|
|
|
|
|
|
|
if (hasEffect()) {
|
|
|
|
compound.put("effect", SpellRegistry.instance().serializeEffectToNBT(getEffect()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void onCollision(HitResult result) {
|
2020-05-07 13:15:53 +02:00
|
|
|
if (!removed) {
|
|
|
|
remove();
|
|
|
|
|
|
|
|
if (result.getType() == HitResult.Type.BLOCK) {
|
|
|
|
onHitBlock((BlockHitResult)result);
|
|
|
|
} else if (result.getType() == HitResult.Type.ENTITY) {
|
|
|
|
onHitEntity((EntityHitResult)result);
|
|
|
|
}
|
2020-01-16 12:35:46 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected void onHitBlock(BlockHitResult hit) {
|
|
|
|
Item item = getItem().getItem();
|
|
|
|
|
2020-04-15 18:12:00 +02:00
|
|
|
if (item instanceof TossableItem) {
|
|
|
|
((TossableItem)item).onImpact(this, hit.getBlockPos(), world.getBlockState(hit.getBlockPos()));
|
2020-01-16 12:35:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (hasEffect()) {
|
2020-04-15 18:12:00 +02:00
|
|
|
MagicEffect effect = getEffect();
|
2020-01-16 12:35:46 +01:00
|
|
|
|
2020-04-15 18:12:00 +02:00
|
|
|
if (effect instanceof Tossable) {
|
|
|
|
((Tossable<?>)effect).onImpact(this, hit.getBlockPos(), world.getBlockState(hit.getBlockPos()));
|
2020-01-16 12:35:46 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected void onHitEntity(EntityHitResult hit) {
|
|
|
|
Entity entity = hit.getEntity();
|
|
|
|
|
2020-04-15 18:12:00 +02:00
|
|
|
if (entity instanceof AdvancedProjectile) {
|
2020-01-16 12:35:46 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (entity != null) {
|
|
|
|
entity.damage(DamageSource.thrownProjectile(this, getOwner()), getThrowDamage());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!world.isClient()) {
|
|
|
|
world.sendEntityStatus(this, (byte)3);
|
|
|
|
remove();
|
|
|
|
}
|
|
|
|
}
|
2020-05-03 22:42:16 +02:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public Packet<?> createSpawnPacket() {
|
|
|
|
return Channel.SPAWN_PROJECTILE.toPacket(new MsgSpawnProjectile(this));
|
|
|
|
}
|
2020-01-16 12:35:46 +01:00
|
|
|
}
|