From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: William Blake Galbreath Date: Thu, 9 May 2019 18:26:06 -0500 Subject: [PATCH] Phantoms attracted to crystals and crystals shoot phantoms diff --git a/src/main/java/net/minecraft/server/DamageSource.java b/src/main/java/net/minecraft/server/DamageSource.java index 6fe5678cff..bd0267ee4b 100644 --- a/src/main/java/net/minecraft/server/DamageSource.java +++ b/src/main/java/net/minecraft/server/DamageSource.java @@ -88,6 +88,7 @@ public class DamageSource { return (new EntityDamageSourceIndirect("thrown", entity, entity1)).c(); } + public static DamageSource indirectMagic(Entity entity, @Nullable Entity owner) { return c(entity, owner); } // Purpur - OBFHELPER public static DamageSource c(Entity entity, @Nullable Entity entity1) { return (new EntityDamageSourceIndirect("indirectMagic", entity, entity1)).setIgnoreArmor().setMagic(); } diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java index ee35d5950f..2d49816311 100644 --- a/src/main/java/net/minecraft/server/Entity.java +++ b/src/main/java/net/minecraft/server/Entity.java @@ -1579,6 +1579,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke return d3 * d3 + d4 * d4 + d5 * d5; } + public double getDistanceSq(Entity entity) { return this.h(entity); } // Purpur - OBFHELPER public double h(Entity entity) { return this.d(entity.getPositionVector()); } @@ -2106,8 +2107,8 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke return this.a(new ItemStack(imaterial), (float) i); } - @Nullable - public EntityItem a(ItemStack itemstack) { + @Nullable public EntityItem dropItem(ItemStack itemstack) { return this.a(itemstack); } // Purpur - OBFHELPER + @Nullable public EntityItem a(ItemStack itemstack) { return this.a(itemstack, 0.0F); } diff --git a/src/main/java/net/minecraft/server/EntityEnderCrystal.java b/src/main/java/net/minecraft/server/EntityEnderCrystal.java index 21dfe7f425..71a440d69d 100644 --- a/src/main/java/net/minecraft/server/EntityEnderCrystal.java +++ b/src/main/java/net/minecraft/server/EntityEnderCrystal.java @@ -13,6 +13,12 @@ public class EntityEnderCrystal extends Entity { private static final DataWatcherObject> c = DataWatcher.a(EntityEnderCrystal.class, DataWatcherRegistry.m); private static final DataWatcherObject d = DataWatcher.a(EntityEnderCrystal.class, DataWatcherRegistry.i); public int b; + // Purpur start + private EntityPhantom targetPhantom; + private int phantomBeamTicks = 0; + private int phantomDamageCooldown = 0; + private int idleCooldown = 0; + // Purpur end public EntityEnderCrystal(EntityTypes entitytypes, World world) { super(entitytypes, world); @@ -51,7 +57,50 @@ public class EntityEnderCrystal extends Entity { } } + // Purpur start + if (world.purpurConfig.phantomAttackedByCrystalRadius <= 0 || --idleCooldown > 0) { + return; // on cooldown + } + + if (targetPhantom == null) { + for (EntityPhantom phantom : world.getEntitiesInAABB(EntityPhantom.class, getBoundingBox().grow(world.purpurConfig.phantomAttackedByCrystalRadius))) { + if (phantom.hasLineOfSight(this)) { + attackPhantom(phantom); + break; + } + } + } else { + setBeamTarget(new BlockPosition(targetPhantom).add(0, -2, 0)); + if (--phantomBeamTicks > 0 && targetPhantom.isAlive()) { + phantomDamageCooldown--; + if (targetPhantom.hasLineOfSight(this)) { + if (phantomDamageCooldown <= 0) { + phantomDamageCooldown = 20; + targetPhantom.damageEntity(DamageSource.indirectMagic(this, this), world.purpurConfig.phantomAttackedByCrystalDamage); + } + } else { + forgetPhantom(); // no longer in sight + } + } else { + forgetPhantom(); // attacked long enough + } + } + } + + private void attackPhantom(EntityPhantom phantom) { + phantomDamageCooldown = 0; + phantomBeamTicks = 60; + targetPhantom = phantom; + } + + private void forgetPhantom() { + targetPhantom = null; + setBeamTarget(null); + phantomBeamTicks = 0; + phantomDamageCooldown = 0; + idleCooldown = 60; } + // Purpur end @Override protected void saveData(NBTTagCompound nbttagcompound) { diff --git a/src/main/java/net/minecraft/server/EntityPhantom.java b/src/main/java/net/minecraft/server/EntityPhantom.java index 3cb78924da..ec09ec241a 100644 --- a/src/main/java/net/minecraft/server/EntityPhantom.java +++ b/src/main/java/net/minecraft/server/EntityPhantom.java @@ -9,9 +9,10 @@ import javax.annotation.Nullable; public class EntityPhantom extends EntityFlying implements IMonster { private static final DataWatcherObject b = DataWatcher.a(EntityPhantom.class, DataWatcherRegistry.b); - private Vec3D c; - private BlockPosition d; - private EntityPhantom.AttackPhase bv; + private Vec3D c; public void setHomeOffset(Vec3D offset) { this.c = offset; } public Vec3D getHomeOffset() { return this.c; } // Purpur - OBFHELPER + private BlockPosition d; public void setHome(BlockPosition home) { this.d = home; } public BlockPosition getHome() { return this.d; } // Purpur - OBFHELPER + private EntityPhantom.AttackPhase bv; public AttackPhase getAttackPhase() { return this.bv; } // Purpur - OBFHELPER + private Vec3D crystalPosition; // Purpur public EntityPhantom(EntityTypes entitytypes, World world) { super(entitytypes, world); @@ -30,11 +31,37 @@ public class EntityPhantom extends EntityFlying implements IMonster { @Override protected void initPathfinder() { - this.goalSelector.a(1, new EntityPhantom.c()); - this.goalSelector.a(2, new EntityPhantom.i()); - this.goalSelector.a(3, new EntityPhantom.e()); - this.targetSelector.a(1, new EntityPhantom.b()); + // Purpur start + if (world.purpurConfig.phantomOrbitCrystalRadius > 0) { + this.goalSelector.a(1, new FindCrystalGoal(this)); + this.goalSelector.a(2, new OrbitCrystalGoal(this)); + } + this.goalSelector.a(3, new EntityPhantom.c()); // PickAttackGoal + this.goalSelector.a(4, new EntityPhantom.i()); // SweepAttackGoal + this.goalSelector.a(5, new EntityPhantom.e()); // OrbitPointGoal + this.targetSelector.a(1, new EntityPhantom.b()); // AttackPlayer Goal + // Purpur end + } + + // Purpur start + @Override + protected LootTableInfo.Builder a(boolean wasRecentlyHit, DamageSource damagesource) { // dropLoot + boolean dropped = false; + if (killer == null && damagesource.getEntity() instanceof EntityEnderCrystal) { + if (random.nextInt(5) < 1) { // 1 out of 5 chance (20%) + dropped = dropItem(new ItemStack(Items.PHANTOM_MEMBRANE)) != null; + } + } + if (!dropped) { + return super.a(wasRecentlyHit, damagesource); // dropLoot + } + return new LootTableInfo.Builder((WorldServer) world); + } + + public boolean isCirclingCrystal() { + return crystalPosition != null; } + // Purpur end @Override protected void initDatawatcher() { @@ -198,6 +225,136 @@ public class EntityPhantom extends EntityFlying implements IMonster { } // Paper end + // Purpur start + class FindCrystalGoal extends PathfinderGoal { + private final EntityPhantom phantom; + private EntityEnderCrystal crystal; + private java.util.Comparator comparator; + + FindCrystalGoal(EntityPhantom phantom) { + this.phantom = phantom; + comparator = java.util.Comparator.comparingDouble(phantom::h); + this.a(EnumSet.of(PathfinderGoal.Type.LOOK)); + } + + @Override + public boolean a() { // shouldExecute + double range = maxTargetRange(); + List crystals = world.getEntitiesInAABB(EntityEnderCrystal.class, phantom.getBoundingBox().grow(range)); + if (crystals.isEmpty()) { + return false; + } + crystals.sort(comparator); + crystal = crystals.get(0); + if (phantom.getDistanceSq(crystal) > range * range) { + crystal = null; + return false; + } + return true; + } + + @Override + public boolean b() { // shouldContinueExecuting + if (crystal == null || !crystal.isAlive()) { + return false; + } + double range = maxTargetRange(); + return phantom.getDistanceSq(crystal) <= (range * range) * 2; + } + + @Override + public void c() { // startExecuting + phantom.crystalPosition = new Vec3D(crystal.locX(), crystal.locY() + (phantom.getRandom().nextInt(10) + 10), crystal.locZ()); + } + + @Override + public void d() { // resetTask + crystal = null; + phantom.crystalPosition = null; + super.d(); + } + + private double maxTargetRange() { + return phantom.world.purpurConfig.phantomOrbitCrystalRadius; + } + + class DistanceComparator implements java.util.Comparator { + private final Entity entity; + + public DistanceComparator(Entity entity) { + this.entity = entity; + } + + public int compare(Entity entity1, Entity entity2) { + return Double.compare(entity.getDistanceSq(entity1), entity.getDistanceSq(entity2)); + } + } + } + + class OrbitCrystalGoal extends PathfinderGoal { + private final EntityPhantom phantom; + private float offset; + private float radius; + private float verticalChange; + private float direction; + + OrbitCrystalGoal(EntityPhantom phantom) { + this.phantom = phantom; + this.a(EnumSet.of(PathfinderGoal.Type.MOVE)); + } + + @Override + public boolean a() { // shouldExecute + return phantom.isCirclingCrystal(); + } + + @Override + public void c() { // startExecuting + this.radius = 5.0F + phantom.random.nextFloat() * 10.0F; + this.verticalChange = -4.0F + phantom.random.nextFloat() * 9.0F; + this.direction = phantom.random.nextBoolean() ? 1.0F : -1.0F; + updateOffset(); + } + + @Override + public void e() { // tick + if (phantom.random.nextInt(350) == 0) { + this.verticalChange = -4.0F + phantom.random.nextFloat() * 9.0F; + } + if (phantom.random.nextInt(250) == 0) { + ++this.radius; + if (this.radius > 15.0F) { + this.radius = 5.0F; + this.direction = -this.direction; + } + } + if (phantom.random.nextInt(450) == 0) { + this.offset = phantom.random.nextFloat() * 2.0F * 3.1415927F; + updateOffset(); + } + if (phantom.getHomeOffset().c(phantom.locX(), phantom.locY(), phantom.locZ()) < 4.0D) { + updateOffset(); + } + if (phantom.getHomeOffset().y < phantom.locY() && !phantom.world.isEmpty((new BlockPosition(phantom)).down(1))) { + this.verticalChange = Math.max(1.0F, this.verticalChange); + updateOffset(); + } + if (phantom.getHomeOffset().y > phantom.locY() && !phantom.world.isEmpty((new BlockPosition(phantom)).up(1))) { + this.verticalChange = Math.min(-1.0F, this.verticalChange); + updateOffset(); + } + } + + private void updateOffset() { + this.offset += this.direction * 15.0F * 0.017453292F; + phantom.setHomeOffset(phantom.crystalPosition.add( + this.radius * MathHelper.cos(this.offset), + -4.0F + this.verticalChange, + this.radius * MathHelper.sin(this.offset))); + } + } + // Purpur end + class b extends PathfinderGoal { private final PathfinderTargetCondition b; @@ -210,6 +367,7 @@ public class EntityPhantom extends EntityFlying implements IMonster { @Override public boolean a() { + if (isCirclingCrystal()) return false; // Purpur - pathfinder does not have a flag if (this.c > 0) { --this.c; return false; @@ -238,6 +396,7 @@ public class EntityPhantom extends EntityFlying implements IMonster { @Override public boolean b() { + if (isCirclingCrystal()) return false; // Purpur - pathfinder does not have a flag EntityLiving entityliving = EntityPhantom.this.getGoalTarget(); return entityliving != null ? EntityPhantom.this.a(entityliving, PathfinderTargetCondition.a) : false; @@ -252,6 +411,7 @@ public class EntityPhantom extends EntityFlying implements IMonster { @Override public boolean a() { + if (isCirclingCrystal()) return false; // Purpur - pathfinder does not have a flag EntityLiving entityliving = EntityPhantom.this.getGoalTarget(); return entityliving != null ? EntityPhantom.this.a(EntityPhantom.this.getGoalTarget(), PathfinderTargetCondition.a) : false; diff --git a/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java b/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java index 3e067c832f..f2ca515c9b 100644 --- a/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java +++ b/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java @@ -324,6 +324,15 @@ public class PurpurWorldConfig { ironGolemCanSwim = getBoolean("mobs.iron_golem.can-swim", ironGolemCanSwim); } + public double phantomAttackedByCrystalRadius = 0.0D; + public float phantomAttackedByCrystalDamage = 1.0F; + public double phantomOrbitCrystalRadius = 0.0D; + private void phantomSettings() { + phantomAttackedByCrystalRadius = getDouble("mobs.phantom.attacked-by-crystal-range", phantomAttackedByCrystalRadius); + phantomAttackedByCrystalDamage = (float) getDouble("mobs.phantom.attacked-by-crystal-damage", phantomAttackedByCrystalDamage); + phantomOrbitCrystalRadius = getDouble("mobs.phantom.orbit-crystal-radius", phantomOrbitCrystalRadius); + } + public boolean pigGiveSaddleBack = false; private void pigSettings() { pigGiveSaddleBack = getBoolean("mobs.pig.give-saddle-back", pigGiveSaddleBack);