Files
Purpur/patches/server/0081-Phantoms-attracted-to-crystals-and-crystals-shoot-ph.patch
BillyGalbreath 643d9681ff Updated Upstream (Paper & Tuinity)
Upstream has released updates that appear to apply and compile correctly

Paper Changes:
8fa15382b [Auto] Updated Upstream (CraftBukkit)
732b2f6b5 [CI-SKIP] [Auto] Rebuild Patches
c5a39ffa2 Add BlockPreDispenseEvent (#5075)
8aeb4c9c3 Correctly skip pathfinder ticks for inactive entities (#5085)
0e420049c Fix console spam when removing chests in water
56b7935bb Add PlayerChangeBeaconEffectEvent
2eda45c34 added Wither API
97d0c571b Added Vanilla Entity Tags
fccac282a fix NPE in getDisplayNameComponent
24e140372 Configurable max leash distance

Tuinity Changes:
d3ee22224 Merge dev/lighting
2021-01-20 23:52:16 -06:00

343 lines
15 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: William Blake Galbreath <blake.galbreath@gmail.com>
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 6fe5678cffc2487fe00c953d772f764bb37a4b11..bd0267ee4b3782f6d1ec39cba7966ba4f62f1adf 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 f5159b76dee6127d7db2addfcb512b5b1f5b9c41..905e3a98fe900c82053b1514122bfe6cf1000dfa 100644
--- a/src/main/java/net/minecraft/server/Entity.java
+++ b/src/main/java/net/minecraft/server/Entity.java
@@ -2138,8 +2138,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 362ca695d88a7c788421cc557b8110b954d8153a..1942fae27051af79b6eb1d790a219da100bf889e 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<Optional<BlockPosition>> c = DataWatcher.a(EntityEnderCrystal.class, DataWatcherRegistry.m);
private static final DataWatcherObject<Boolean> 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<? extends EntityEnderCrystal> 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 3a70900c73e3a6c4f40cf74406534f4bfd3eb67b..dfce36368da9bdd9285c490a802f7a0cc4a339f6 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<Integer> b = DataWatcher.a(EntityPhantom.class, DataWatcherRegistry.b);
- private Vec3D c;
- private BlockPosition d;
- private EntityPhantom.AttackPhase bo;
+ 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 bo; public AttackPhase getAttackPhase() { return this.bo; } // Purpur - OBFHELPER
+ private Vec3D crystalPosition; // Purpur
public EntityPhantom(EntityTypes<? extends EntityPhantom> 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<EntityEnderCrystal> 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<EntityEnderCrystal> crystals = world.getEntitiesInAABB(EntityEnderCrystal.class, phantom.getBoundingBox().grow(range));
+ if (crystals.isEmpty()) {
+ return false;
+ }
+ crystals.sort(comparator);
+ crystal = crystals.get(0);
+ if (phantom.getDistanceSquared(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.getDistanceSquared(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<Entity> {
+ private final Entity entity;
+
+ public DistanceComparator(Entity entity) {
+ this.entity = entity;
+ }
+
+ public int compare(Entity entity1, Entity entity2) {
+ return Double.compare(entity.getDistanceSquared(entity1), entity.getDistanceSquared(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 54afa5afa20f317d55b36df0aa325e0ac3e814a7..4d12719b4b695328e364d4c39f5d54cd00522f09 100644
--- a/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java
+++ b/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java
@@ -339,6 +339,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);