Unicopia/src/main/java/com/minelittlepony/unicopia/projectile/MagicProjectileEntity.java

306 lines
9.8 KiB
Java
Raw Normal View History

package com.minelittlepony.unicopia.projectile;
2020-01-16 12:35:46 +01:00
2021-08-25 00:32:39 +02:00
import java.util.function.Consumer;
import org.jetbrains.annotations.Nullable;
2020-09-23 17:19:28 +02:00
import com.minelittlepony.unicopia.Affinity;
2021-03-03 10:33:23 +01:00
import com.minelittlepony.unicopia.ability.magic.Affine;
import com.minelittlepony.unicopia.ability.magic.Caster;
2020-10-02 09:39:00 +02:00
import com.minelittlepony.unicopia.ability.magic.Levelled;
2021-03-05 18:22:27 +01:00
import com.minelittlepony.unicopia.ability.magic.SpellContainer;
import com.minelittlepony.unicopia.ability.magic.SpellContainer.Operation;
import com.minelittlepony.unicopia.ability.magic.SpellPredicate;
import com.minelittlepony.unicopia.ability.magic.spell.Situation;
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
2020-10-14 21:07:36 +02:00
import com.minelittlepony.unicopia.entity.EntityPhysics;
import com.minelittlepony.unicopia.entity.EntityReference;
2020-10-14 21:07:36 +02:00
import com.minelittlepony.unicopia.entity.Physics;
import com.minelittlepony.unicopia.entity.UEntities;
2021-02-14 21:15:30 +01:00
import com.minelittlepony.unicopia.item.UItems;
import com.minelittlepony.unicopia.network.Channel;
import com.minelittlepony.unicopia.network.MsgSpawnProjectile;
import com.minelittlepony.unicopia.network.datasync.EffectSync;
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.projectile.ProjectileEntity;
2020-06-26 11:44:47 +02:00
import net.minecraft.entity.projectile.thrown.ThrownItemEntity;
2020-01-16 12:35:46 +01:00
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
2021-08-04 15:38:03 +02:00
import net.minecraft.nbt.NbtCompound;
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.util.hit.BlockHitResult;
import net.minecraft.util.hit.EntityHitResult;
import net.minecraft.util.hit.HitResult;
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.
*/
2021-03-06 22:11:17 +01:00
public class MagicProjectileEntity extends ThrownItemEntity implements Caster<LivingEntity> {
2020-06-26 11:44:47 +02:00
private static final TrackedData<Float> DAMAGE = DataTracker.registerData(MagicProjectileEntity.class, TrackedDataHandlerRegistry.FLOAT);
private static final TrackedData<Float> GRAVITY = DataTracker.registerData(MagicProjectileEntity.class, TrackedDataHandlerRegistry.FLOAT);
2020-06-26 11:44:47 +02:00
private static final TrackedData<Boolean> HYDROPHOBIC = DataTracker.registerData(MagicProjectileEntity.class, TrackedDataHandlerRegistry.BOOLEAN);
2022-03-26 20:34:15 +01:00
private static final TrackedData<NbtCompound> EFFECT = DataTracker.registerData(MagicProjectileEntity.class, TrackedDataHandlerRegistry.NBT_COMPOUND);
2020-10-02 09:39:00 +02:00
private static final LevelStore LEVELS = Levelled.fixed(1);
2020-01-16 12:35:46 +01:00
public static final byte PROJECTILE_COLLISSION = 3;
2020-01-16 12:35:46 +01:00
private final EffectSync effectDelegate = new EffectSync(this, EFFECT);
private final EntityPhysics<MagicProjectileEntity> physics = new EntityPhysics<>(this, GRAVITY, false);
2020-10-14 21:07:36 +02:00
private final EntityReference<Entity> homingTarget = new EntityReference<>();
2020-06-26 11:44:47 +02:00
public MagicProjectileEntity(EntityType<MagicProjectileEntity> type, World world) {
2020-01-16 12:35:46 +01:00
super(type, world);
}
2022-03-27 16:02:14 +02:00
public MagicProjectileEntity(World world) {
this(UEntities.THROWN_ITEM, world);
}
public MagicProjectileEntity(World world, LivingEntity thrower) {
super(UEntities.THROWN_ITEM, thrower, world);
2020-01-16 12:35:46 +01:00
}
@Override
protected void initDataTracker() {
super.initDataTracker();
getDataTracker().startTracking(GRAVITY, 1F);
getDataTracker().startTracking(DAMAGE, 0F);
2021-08-04 15:38:03 +02:00
getDataTracker().startTracking(EFFECT, new NbtCompound());
2020-01-16 12:35:46 +01:00
getDataTracker().startTracking(HYDROPHOBIC, false);
}
@Override
protected Item getDefaultItem() {
2021-03-03 10:33:23 +01:00
switch (getSpellSlot().get(false).map(Spell::getAffinity).orElse(Affinity.NEUTRAL)) {
2021-03-02 14:40:37 +01:00
case GOOD: return Items.SNOWBALL;
case BAD: return Items.MAGMA_CREAM;
default: return Items.AIR;
}
}
2020-01-16 12:35:46 +01:00
@Override
public Entity getEntity() {
return this;
}
@Override
public void setMaster(LivingEntity owner) {
setOwner(owner);
2020-01-16 12:35:46 +01:00
}
public void setHomingTarget(@Nullable Entity target) {
homingTarget.set(target);
}
2020-01-16 12:35:46 +01:00
@Override
@Nullable
public LivingEntity getMaster() {
return (LivingEntity)getOwner();
2020-01-16 12:35:46 +01:00
}
@Override
2020-10-02 09:39:00 +02:00
public LevelStore getLevel() {
return LEVELS;
2020-01-16 12:35:46 +01:00
}
2020-10-14 21:07:36 +02:00
@Override
public Physics getPhysics() {
return physics;
}
2020-01-16 12:35:46 +01:00
@Override
public Affinity getAffinity() {
2021-03-03 10:33:23 +01:00
return getSpellSlot().get(true).map(Affine::getAffinity).orElse(Affinity.NEUTRAL);
2020-01-16 12:35:46 +01:00
}
@Override
2021-03-05 18:22:27 +01:00
public SpellContainer getSpellSlot() {
2020-05-28 18:27:30 +02:00
return effectDelegate;
2020-01-16 12:35:46 +01:00
}
@Override
public boolean subtractEnergyCost(double amount) {
return Caster.of(getMaster()).filter(c -> c.subtractEnergyCost(amount)).isPresent();
}
2021-12-12 11:49:30 +01:00
public void addThrowDamage(float damage) {
setThrowDamage(getThrowDamage() + damage);
}
2020-01-16 12:35:46 +01:00
public void setThrowDamage(float damage) {
getDataTracker().set(DAMAGE, Math.max(0, damage));
}
public float getThrowDamage() {
return getDataTracker().get(DAMAGE);
}
public void setHydrophobic() {
getDataTracker().set(HYDROPHOBIC, true);
}
public boolean getHydrophobic() {
return getDataTracker().get(HYDROPHOBIC);
}
@Override
public void tick() {
if (!world.isClient() && !homingTarget.isPresent(world)) {
if (getVelocity().length() < 0.01) {
2021-08-25 00:32:39 +02:00
discard();
2020-01-16 12:35:46 +01:00
}
}
super.tick();
if (getOwner() == null) {
return;
2020-01-16 12:35:46 +01:00
}
getSpellSlot().get(true).filter(spell -> spell.tick(this, Situation.PROJECTILE));
2020-01-16 12:35:46 +01:00
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));
}
}
homingTarget.ifPresent(world, e -> {
setNoGravity(true);
noClip = true;
setVelocity(getVelocity().add(e.getPos().subtract(getPos()).normalize().multiply(0.2)).multiply(0.6, 0.6, 0.6));
});
2020-01-16 12:35:46 +01:00
}
private ParticleEffect getParticleParameters() {
ItemStack stack = getItem();
if (stack.isEmpty()) {
return ParticleTypes.ITEM_SNOWBALL;
}
2021-02-14 21:15:30 +01:00
if (stack.getItem() == UItems.FILLED_JAR) {
stack = UItems.EMPTY_JAR.getDefaultStack();
}
2020-01-16 12:35:46 +01:00
return new ItemStackParticleEffect(ParticleTypes.ITEM, stack);
}
@Override
public void handleStatus(byte id) {
if (id == PROJECTILE_COLLISSION) {
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
}
} else {
super.handleStatus(id);
2020-01-16 12:35:46 +01:00
}
}
@Override
2021-08-04 15:38:03 +02:00
public void readCustomDataFromNbt(NbtCompound compound) {
super.readCustomDataFromNbt(compound);
2020-10-14 21:07:36 +02:00
physics.fromNBT(compound);
homingTarget.fromNBT(compound.getCompound("homingTarget"));
2020-04-22 16:28:20 +02:00
if (compound.contains("effect")) {
2021-12-29 17:11:32 +01:00
getSpellSlot().put(Spell.readNbt(compound.getCompound("effect")));
2020-01-16 12:35:46 +01:00
}
}
@Override
2021-08-04 15:38:03 +02:00
public void writeCustomDataToNbt(NbtCompound compound) {
super.writeCustomDataToNbt(compound);
2020-10-14 21:07:36 +02:00
physics.toNBT(compound);
compound.put("homingTarget", homingTarget.toNBT());
2021-03-03 10:33:23 +01:00
getSpellSlot().get(true).ifPresent(effect -> {
2021-12-29 17:11:32 +01:00
compound.put("effect", Spell.writeNbt(effect));
2021-03-03 10:33:23 +01:00
});
2020-01-16 12:35:46 +01:00
}
@Override
protected void onCollision(HitResult result) {
2021-08-04 15:38:03 +02:00
if (!isRemoved()) {
2021-08-25 00:32:39 +02:00
discard();
super.onCollision(result);
if (!world.isClient()) {
world.sendEntityStatus(this, PROJECTILE_COLLISSION);
2021-08-25 00:32:39 +02:00
discard();
}
2020-01-16 12:35:46 +01:00
}
}
@Override
protected void onBlockHit(BlockHitResult hit) {
2021-08-25 00:32:39 +02:00
super.onBlockHit(hit);
2021-08-25 00:32:39 +02:00
forEachDelegates(effect -> effect.onImpact(this, hit.getBlockPos(), world.getBlockState(hit.getBlockPos())));
2020-01-16 12:35:46 +01:00
}
@Override
protected void onEntityHit(EntityHitResult hit) {
2020-01-16 12:35:46 +01:00
Entity entity = hit.getEntity();
if (entity instanceof ProjectileEntity) {
2020-01-16 12:35:46 +01:00
return;
}
if (entity != null) {
2021-08-25 00:32:39 +02:00
float damage = getThrowDamage();
if (damage > 0) {
entity.damage(DamageSource.thrownProjectile(this, getOwner()), getThrowDamage());
}
forEachDelegates(effect -> effect.onImpact(this, entity));
2020-01-16 12:35:46 +01:00
}
2021-08-25 00:32:39 +02:00
}
2022-03-27 16:02:14 +02:00
@SuppressWarnings("unchecked")
protected void forEachDelegates(Consumer<ProjectileDelegate<MagicProjectileEntity>> consumer) {
getSpellSlot().forEach(spell -> {
if (SpellPredicate.HAS_PROJECTILE_EVENTS.test(spell)) {
2022-03-27 16:02:14 +02:00
consumer.accept((ProjectileDelegate<MagicProjectileEntity>)spell);
}
return Operation.SKIP;
}, true);
if (getItem().getItem() instanceof ProjectileDelegate) {
2022-03-27 16:02:14 +02:00
consumer.accept(((ProjectileDelegate<MagicProjectileEntity>)getItem().getItem()));
}
2020-01-16 12:35:46 +01:00
}
@Override
public Packet<?> createSpawnPacket() {
return Channel.SERVER_SPAWN_PROJECTILE.toPacket(new MsgSpawnProjectile(this));
}
2020-01-16 12:35:46 +01:00
}