diff --git a/src/main/java/com/minelittlepony/unicopia/entity/EntitySpell.java b/src/main/java/com/minelittlepony/unicopia/entity/EntitySpell.java index 61ab417f..33a75785 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/EntitySpell.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/EntitySpell.java @@ -1,5 +1,7 @@ package com.minelittlepony.unicopia.entity; +import javax.annotation.Nullable; + import org.apache.commons.lang3.StringUtils; import com.minelittlepony.unicopia.Predicates; @@ -79,12 +81,15 @@ public class EntitySpell extends EntityLiving implements IMagicals, ICaster extends EntityAIBase { - protected final ICaster entity; + protected final ICaster casted; - private static Field __followPredicate; + protected final EntityLiving entity; - static { - try { - Field f = EntityAIFollow.class.getDeclaredFields()[1]; - f.setAccessible(true); - __followPredicate = RegistryLockSpinner.makeNonFinal(f); - } catch (IllegalArgumentException | IllegalAccessException e) { - e.printStackTrace(); - __followPredicate = null; + protected EntityLivingBase owner; + + protected final World world; + + public final double followSpeed; + + private final PathNavigate petPathfinder; + + private int timeout; + + public float maxDist; + public float minDist; + + private float oldWaterCost; + + public EntityAIFollowCaster(ICaster casted, double followSpeed, float minDist, float maxDist) { + this.casted = casted; + + this.entity = (EntityLiving)casted.getEntity(); + this.world = casted.getWorld(); + + this.followSpeed = followSpeed; + this.petPathfinder = entity.getNavigator(); + this.minDist = minDist; + this.maxDist = maxDist; + this.setMutexBits(3); + + if (!(petPathfinder instanceof PathNavigateGround || petPathfinder instanceof PathNavigateFlying)) { + throw new IllegalArgumentException("Unsupported mob type for FollowOwnerGoal"); } } - public EntityAIFollowCaster(ICaster caster, double walkSpeed, float maxDistance, float area) { - super((EntityLiving)caster.getEntity(), walkSpeed, maxDistance, area); + /** + * Returns whether the EntityAIBase should begin execution. + */ + public boolean shouldExecute() { + EntityLivingBase owner = casted.getOwner(); - entity = caster; + if (owner == null + || (owner instanceof EntityPlayer && ((EntityPlayer)owner).isSpectator()) + || entity.getDistanceSq(owner) < (minDist * minDist)) { + return false; + } - if (__followPredicate != null) { - try { - __followPredicate.set(this, (Predicate)(e -> { - Entity owner = caster.getOwner(); - return e != null && (e == owner || (owner != null && owner.getUniqueID().equals(e.getUniqueID()))); - })); - } catch (IllegalArgumentException | IllegalAccessException e) { - e.printStackTrace(); + this.owner = owner; + + return true; + } + + @Override + public boolean shouldContinueExecuting() { + return !petPathfinder.noPath() + && entity.getDistanceSq(owner) > (maxDist * maxDist); + } + + @Override + public void startExecuting() { + timeout = 0; + oldWaterCost = entity.getPathPriority(PathNodeType.WATER); + entity.setPathPriority(PathNodeType.WATER, 0); + } + + @Override + public void resetTask() { + owner = null; + petPathfinder.clearPath(); + entity.setPathPriority(PathNodeType.WATER, oldWaterCost); + } + + @Override + public void updateTask() { + entity.getLookHelper().setLookPositionWithEntity(owner, 10, entity.getVerticalFaceSpeed()); + + if (--timeout > 0) { + return; + } + + timeout = 10; + + if (petPathfinder.tryMoveToEntityLiving(owner, followSpeed) + || entity.getLeashed() + || entity.isRiding() + || entity.getDistanceSq(owner) < 144) { + return; + } + + int x = MathHelper.floor(owner.posX) - 2; + int y = MathHelper.floor(owner.getEntityBoundingBox().minY); + int z = MathHelper.floor(owner.posZ) - 2; + + for (int offX = 0; offX <= 4; offX++) { + for (int offZ = 0; offZ <= 4; offZ++) { + if (canTeleport(x, y, z, offX, offZ)) { + + entity.setLocationAndAngles((x + offX) + 0.5F, y, (z + offZ) + 0.5F, entity.rotationYaw, entity.rotationPitch); + petPathfinder.clearPath(); + + return; + } } } } -} + + protected boolean canTeleport(int x, int y, int z, int xOffset, int zOffset) { + if (xOffset < 1 || zOffset < 1 || xOffset > 3 || zOffset > 3) { + return true; + } + + BlockPos pos = new BlockPos(x + xOffset, y - 1, z + zOffset); + IBlockState state = world.getBlockState(pos); + + return state.getBlockFaceShape(world, pos, EnumFacing.DOWN) == BlockFaceShape.SOLID + && state.canEntitySpawn(entity) + && world.isAirBlock(pos.up()) + && world.isAirBlock(pos.up(2)); + } +} \ No newline at end of file diff --git a/src/main/java/com/minelittlepony/unicopia/model/ModelGem.java b/src/main/java/com/minelittlepony/unicopia/model/ModelGem.java index a893cf8f..85d9e6c2 100644 --- a/src/main/java/com/minelittlepony/unicopia/model/ModelGem.java +++ b/src/main/java/com/minelittlepony/unicopia/model/ModelGem.java @@ -104,7 +104,9 @@ public class ModelGem extends ModelBase { setLightingConditionsBrightness(0xF0F0); - Color.glColor(spell.getEffect().getTint(), 1); + if (spell.hasEffect()) { + Color.glColor(spell.getEffect().getTint(), 1); + } int tiers = Math.min(spell.getCurrentLevel(), 5); @@ -144,10 +146,7 @@ public class ModelGem extends ModelBase { } protected void renderOverlay(float grow, float scale) { - - body.render(scale); - } private void setLightingConditionsBrightness(int brightness) { diff --git a/src/main/java/com/minelittlepony/unicopia/network/EffectSync.java b/src/main/java/com/minelittlepony/unicopia/network/EffectSync.java index 6b4d5bdc..f9fb62ce 100644 --- a/src/main/java/com/minelittlepony/unicopia/network/EffectSync.java +++ b/src/main/java/com/minelittlepony/unicopia/network/EffectSync.java @@ -1,5 +1,7 @@ package com.minelittlepony.unicopia.network; +import javax.annotation.Nullable; + import com.minelittlepony.unicopia.spell.ICaster; import com.minelittlepony.unicopia.spell.IMagicEffect; import com.minelittlepony.unicopia.spell.SpellRegistry; @@ -9,6 +11,8 @@ import net.minecraft.nbt.NBTTagCompound; import net.minecraft.network.datasync.DataParameter; public class EffectSync { + + @Nullable private IMagicEffect effect; private final ICaster owned; @@ -49,7 +53,7 @@ public class EffectSync { return effect; } - public void set(IMagicEffect effect) { + public void set(@Nullable IMagicEffect effect) { if (this.effect != null && this.effect != effect) { this.effect.setDead(); } diff --git a/src/main/java/com/minelittlepony/unicopia/player/PlayerCapabilities.java b/src/main/java/com/minelittlepony/unicopia/player/PlayerCapabilities.java index 8d1c1c15..74624e97 100644 --- a/src/main/java/com/minelittlepony/unicopia/player/PlayerCapabilities.java +++ b/src/main/java/com/minelittlepony/unicopia/player/PlayerCapabilities.java @@ -327,12 +327,13 @@ class PlayerCapabilities implements IPlayer { } @Override - public void setEffect(IMagicEffect effect) { + public void setEffect(@Nullable IMagicEffect effect) { effectDelegate.set(effect); sendCapabilities(true); } + @Nullable @Override public IMagicEffect getEffect() { return effectDelegate.get(); diff --git a/src/main/java/com/minelittlepony/unicopia/spell/AbstractSpell.java b/src/main/java/com/minelittlepony/unicopia/spell/AbstractSpell.java index 456223aa..57c2769e 100644 --- a/src/main/java/com/minelittlepony/unicopia/spell/AbstractSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/spell/AbstractSpell.java @@ -1,17 +1,21 @@ package com.minelittlepony.unicopia.spell; +import net.minecraft.nbt.NBTTagCompound; + public abstract class AbstractSpell implements IMagicEffect { protected boolean isDead = false; + private int strength = 0; + @Override public int getCurrentLevel() { - return 0; + return strength; } @Override public void setCurrentLevel(int level) { - + strength = level; } @Override @@ -28,4 +32,16 @@ public abstract class AbstractSpell implements IMagicEffect { public boolean getDead() { return isDead; } + + @Override + public void writeToNBT(NBTTagCompound compound) { + compound.setBoolean("dead", isDead); + compound.setInteger("spell_strength", strength); + } + + @Override + public void readFromNBT(NBTTagCompound compound) { + isDead = compound.getBoolean("dead"); + strength = compound.getInteger("spell_strength"); + } } diff --git a/src/main/java/com/minelittlepony/unicopia/spell/ICaster.java b/src/main/java/com/minelittlepony/unicopia/spell/ICaster.java index 6fecbbd7..94bc2961 100644 --- a/src/main/java/com/minelittlepony/unicopia/spell/ICaster.java +++ b/src/main/java/com/minelittlepony/unicopia/spell/ICaster.java @@ -5,6 +5,8 @@ import java.util.UUID; import java.util.function.Consumer; import java.util.stream.Stream; +import javax.annotation.Nullable; + import com.minelittlepony.unicopia.player.IOwned; import com.minelittlepony.util.shape.IShape; import com.minelittlepony.util.vector.VecHelper; @@ -17,8 +19,9 @@ import net.minecraft.world.World; public interface ICaster extends IOwned, ILevelled, IAligned { - void setEffect(IMagicEffect effect); + void setEffect(@Nullable IMagicEffect effect); + @Nullable IMagicEffect getEffect(); default boolean hasEffect() { diff --git a/src/main/java/com/minelittlepony/unicopia/spell/IMagicEffect.java b/src/main/java/com/minelittlepony/unicopia/spell/IMagicEffect.java index fd3e05de..cad455c1 100644 --- a/src/main/java/com/minelittlepony/unicopia/spell/IMagicEffect.java +++ b/src/main/java/com/minelittlepony/unicopia/spell/IMagicEffect.java @@ -83,4 +83,8 @@ public interface IMagicEffect extends InbtSerialisable, ILevelled, IAligned { default boolean allowAI() { return false; } + + default IMagicEffect copy() { + return SpellRegistry.instance().copyInstance(this); + } } diff --git a/src/main/java/com/minelittlepony/unicopia/spell/SpellCharge.java b/src/main/java/com/minelittlepony/unicopia/spell/SpellCharge.java index e4304cfe..d3c77daf 100644 --- a/src/main/java/com/minelittlepony/unicopia/spell/SpellCharge.java +++ b/src/main/java/com/minelittlepony/unicopia/spell/SpellCharge.java @@ -18,8 +18,6 @@ import net.minecraft.util.math.Vec3d; public class SpellCharge extends AbstractSpell { - private int desiredLevel = 0; - boolean searching = true; private UUID targettedEntityId; @@ -115,30 +113,21 @@ public class SpellCharge extends AbstractSpell { return 2; } - @Override - public int getCurrentLevel() { - return desiredLevel; - } - - @Override - public void setCurrentLevel(int level) { - desiredLevel = level; - } - @Override public void writeToNBT(NBTTagCompound compound) { + super.writeToNBT(compound); + if (targettedEntityId != null) { compound.setUniqueId("target", targettedEntityId); } - compound.setInteger("level", desiredLevel); } @Override public void readFromNBT(NBTTagCompound compound) { + super.readFromNBT(compound); + if (compound.hasKey("target")) { targettedEntityId = compound.getUniqueId("target"); } - - desiredLevel = compound.getInteger("level"); } } diff --git a/src/main/java/com/minelittlepony/unicopia/spell/SpellDisguise.java b/src/main/java/com/minelittlepony/unicopia/spell/SpellDisguise.java index 0d84f6e0..03d66c4c 100644 --- a/src/main/java/com/minelittlepony/unicopia/spell/SpellDisguise.java +++ b/src/main/java/com/minelittlepony/unicopia/spell/SpellDisguise.java @@ -208,6 +208,7 @@ public class SpellDisguise extends AbstractSpell { @Override public void writeToNBT(NBTTagCompound compound) { + super.writeToNBT(compound); compound.setString("entityId", entityId); compound.setBoolean("dead", getDead()); @@ -225,6 +226,8 @@ public class SpellDisguise extends AbstractSpell { @Override public void readFromNBT(NBTTagCompound compound) { + super.readFromNBT(compound); + String newId = compound.getString("entityId"); if (!newId.contentEquals(entityId)) { diff --git a/src/main/java/com/minelittlepony/unicopia/spell/SpellDrake.java b/src/main/java/com/minelittlepony/unicopia/spell/SpellDrake.java index a27045de..613b39c5 100644 --- a/src/main/java/com/minelittlepony/unicopia/spell/SpellDrake.java +++ b/src/main/java/com/minelittlepony/unicopia/spell/SpellDrake.java @@ -49,7 +49,7 @@ public class SpellDrake extends AbstractSpell { } public boolean getDead() { - return super.getDead() || (piggyBackSpell != null && piggyBackSpell.getDead()); + return super.getDead(); } @Override @@ -58,28 +58,36 @@ public class SpellDrake extends AbstractSpell { if (firstUpdate) { firstUpdate = false; - if (source.getOwner() instanceof EntitySpell) { - EntitySpell living = (EntitySpell)source.getOwner(); + if (source.getEntity() instanceof EntitySpell) { + EntitySpell living = (EntitySpell)source.getEntity(); ((PathNavigateGround)living.getNavigator()).setCanSwim(false); living.tasks.addTask(1, new EntityAISwimming(living)); - living.tasks.addTask(2, new EntityAIFollowCaster(source, 1, 4, 6)); + living.tasks.addTask(2, new EntityAIFollowCaster<>(source, 1, 4, 70)); } } - if (piggyBackSpell == null) { - AxisAlignedBB bb = EFFECT_BOUNDS.offset(source.getOriginVector()); + if (!source.getWorld().isRemote) { - source.getWorld().getEntitiesInAABBexcluding(source.getEntity(), bb, e -> e instanceof EntitySpell).stream() - .map(i -> (EntitySpell)i) - .filter(i -> i.getEffect() != null && !(i.getEffect() instanceof SpellDrake)) - .findFirst().ifPresent(i -> { - piggyBackSpell = i.getEffect(); - i.setEffect(null); - }); + if (piggyBackSpell == null) { + AxisAlignedBB bb = EFFECT_BOUNDS.offset(source.getOriginVector()); + + source.getWorld().getEntitiesInAABBexcluding(source.getEntity(), bb, e -> e instanceof EntitySpell).stream() + .map(i -> (EntitySpell)i) + .filter(i -> i.hasEffect() && !(i.getEffect() instanceof SpellDrake)) + .findFirst().ifPresent(i -> { + piggyBackSpell = i.getEffect().copy(); + piggyBackSpell.onPlaced(source); + i.setEffect(null); + }); + } } - return piggyBackSpell != null && piggyBackSpell.update(source, level); + if (piggyBackSpell != null) { + piggyBackSpell.update(source, level); + } + + return true; } @Override @@ -89,7 +97,10 @@ public class SpellDrake extends AbstractSpell { } } + @Override public void writeToNBT(NBTTagCompound compound) { + super.writeToNBT(compound); + if (piggyBackSpell != null) { compound.setTag("effect", SpellRegistry.instance().serializeEffectToNBT(piggyBackSpell)); } @@ -97,6 +108,8 @@ public class SpellDrake extends AbstractSpell { @Override public void readFromNBT(NBTTagCompound compound) { + super.readFromNBT(compound); + if (compound.hasKey("effect")) { piggyBackSpell = SpellRegistry.instance().createEffectFromNBT(compound.getCompoundTag("effect")); } diff --git a/src/main/java/com/minelittlepony/unicopia/spell/SpellPortal.java b/src/main/java/com/minelittlepony/unicopia/spell/SpellPortal.java index 1ea36894..3fefa21f 100644 --- a/src/main/java/com/minelittlepony/unicopia/spell/SpellPortal.java +++ b/src/main/java/com/minelittlepony/unicopia/spell/SpellPortal.java @@ -290,6 +290,8 @@ public class SpellPortal extends AbstractSpell implements IUseAction { @Override public void writeToNBT(NBTTagCompound compound) { + super.writeToNBT(compound); + if (destinationPos != null) { compound.setTag("destination", InbtSerialisable.writeBlockPos(destinationPos)); } @@ -307,6 +309,8 @@ public class SpellPortal extends AbstractSpell implements IUseAction { @Override public void readFromNBT(NBTTagCompound compound) { + super.readFromNBT(compound); + if (compound.hasKey("destination")) { destinationPos = InbtSerialisable.readBlockPos(compound.getCompoundTag("destination")); } diff --git a/src/main/java/com/minelittlepony/unicopia/spell/SpellRegistry.java b/src/main/java/com/minelittlepony/unicopia/spell/SpellRegistry.java index 8b5e8d2f..bec7e705 100644 --- a/src/main/java/com/minelittlepony/unicopia/spell/SpellRegistry.java +++ b/src/main/java/com/minelittlepony/unicopia/spell/SpellRegistry.java @@ -53,6 +53,11 @@ public class SpellRegistry { return null; } + @SuppressWarnings("unchecked") + public T copyInstance(T effect) { + return (T)createEffectFromNBT(serializeEffectToNBT(effect)); + } + public IMagicEffect createEffectFromNBT(NBTTagCompound compound) { if (compound.hasKey("effect_id")) { IMagicEffect effect = getSpellFromName(compound.getString("effect_id")); diff --git a/src/main/java/com/minelittlepony/unicopia/spell/SpellShield.java b/src/main/java/com/minelittlepony/unicopia/spell/SpellShield.java index 60b424e3..1b8142a3 100644 --- a/src/main/java/com/minelittlepony/unicopia/spell/SpellShield.java +++ b/src/main/java/com/minelittlepony/unicopia/spell/SpellShield.java @@ -13,23 +13,10 @@ import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.SoundEvents; -import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.math.Vec3d; public class SpellShield extends AbstractSpell { - private int strength = 0; - - @Override - public int getCurrentLevel() { - return strength; - } - - @Override - public void setCurrentLevel(int level) { - strength = level; - } - @Override public String getName() { return "shield"; @@ -52,43 +39,51 @@ public class SpellShield extends AbstractSpell { @Override public void render(ICaster source, int level) { - spawnParticles(source, 4 + (level * 2)); - } + level = 4 + (level * 2); - protected void spawnParticles(ICaster source, int strength) { - source.spawnParticles(new Sphere(true, strength), strength * 6, pos -> { - Particles.instance().spawnParticle(UParticles.UNICORN_MAGIC, false, pos, 0, 0, 0); - }); + source.spawnParticles(new Sphere(true, level), level * 6, pos -> { + Particles.instance().spawnParticle(UParticles.UNICORN_MAGIC, false, pos, 0, 0, 0); + }); } @Override public boolean updateOnPerson(ICaster source) { - update(source, strength); - - if (source.getEntity().getEntityWorld().getWorldTime() % 50 == 0) { - double radius = 4 + (strength * 2); - if (!IPower.takeFromPlayer((EntityPlayer)source.getOwner(), radius/4)) { - setDead(); - } - } + if (super.updateOnPerson(source)) { + if (source.getEntity().getEntityWorld().getWorldTime() % 50 == 0) { + double radius = 4 + (getCurrentLevel() * 2); + if (!IPower.takeFromPlayer((EntityPlayer)source.getOwner(), radius/4)) { + setDead(); + } + } + } return !getDead(); } + protected double getDrawDropOffRange(int level) { + return 4 + (level * 2); + } + @Override public boolean update(ICaster source, int level) { - double radius = 4 + (level * 2); + double radius = getDrawDropOffRange(level); Entity owner = source.getOwner(); boolean ownerIsValid = source.getAffinity() != SpellAffinity.BAD && Predicates.MAGI.test(owner); + Vec3d origin = source.getOriginVector(); + source.findAllEntitiesInRange(radius) .filter(entity -> !(ownerIsValid && entity.equals(owner))) .forEach(i -> { - double dist = Math.sqrt(i.getDistanceSq(source.getOrigin())); + try { + double dist = i.getPositionVector().distanceTo(origin); - applyRadialEffect(source, i, dist, radius); + applyRadialEffect(source, i, dist, radius); + } catch (Throwable e) { + e.printStackTrace(); + } }); return true; @@ -107,10 +102,12 @@ public class SpellShield extends AbstractSpell { } } } else if (target instanceof EntityLivingBase) { - double force = Math.min(0.25F, distance); + double force = Math.max(0.1, radius / 4); if (source.getAffinity() != SpellAffinity.BAD && target instanceof EntityPlayer) { force *= calculateAdjustedForce(PlayerSpeciesList.instance().getPlayer((EntityPlayer)target)); + } else { + force *= 0.75; } applyForce(pos, target, force, distance); @@ -121,12 +118,12 @@ public class SpellShield extends AbstractSpell { * Applies a force to the given entity based on distance from the source. */ protected void applyForce(Vec3d pos, Entity target, double force, double distance) { - pos = target.getPositionVector().subtract(pos); + pos = target.getPositionVector().subtract(pos).normalize().scale(force); target.addVelocity( - force / pos.x, - force / pos.y + (distance < 1 ? distance : 0), - force / pos.z + pos.x, + pos.y + (distance < 1 ? distance : 0), + pos.z ); } @@ -163,14 +160,4 @@ public class SpellShield extends AbstractSpell { ProjectileUtil.setThrowableHeading(projectile, normal, (float)motion.length(), 0); } } - - @Override - public void writeToNBT(NBTTagCompound compound) { - compound.setInteger("spell_strength", strength); - } - - @Override - public void readFromNBT(NBTTagCompound compound) { - strength = compound.getInteger("spell_strength"); - } } diff --git a/src/main/java/com/minelittlepony/unicopia/spell/SpellVortex.java b/src/main/java/com/minelittlepony/unicopia/spell/SpellVortex.java index 2725ce00..ef689573 100644 --- a/src/main/java/com/minelittlepony/unicopia/spell/SpellVortex.java +++ b/src/main/java/com/minelittlepony/unicopia/spell/SpellVortex.java @@ -23,19 +23,25 @@ public class SpellVortex extends SpellShield { } @Override - protected void spawnParticles(ICaster source, int strength) { + public void render(ICaster source, int level) { + level = 4 + (level * 2); Vec3d pos = source.getOriginVector(); - source.spawnParticles(new Sphere(false, strength), strength * 9, p -> { + source.spawnParticles(new Sphere(false, level), level * 9, p -> { Particles.instance().spawnParticle(UParticles.UNICORN_MAGIC, false, p, p.subtract(pos)); }); } + @Override + protected double getDrawDropOffRange(int level) { + return 10 + (level * 2); + } + @Override protected void applyRadialEffect(ICaster source, Entity target, double distance, double radius) { Vec3d pos = source.getOriginVector(); - double force = 4 / distance; + double force = 2.5F / distance; if (source.getAffinity() != SpellAffinity.BAD && target instanceof EntityPlayer) { force *= calculateAdjustedForce(PlayerSpeciesList.instance().getPlayer((EntityPlayer)target));