Files
Purpur/patches/server/0024-Phantoms-attracted-to-crystals-and-crystals-shoot-ph.patch
William Blake Galbreath d085a5b222 Updated Upstream (Paper)
Upstream has released updates that appears to apply and compile correctly

Paper Changes:
cb15cfa4 Improve Async Login so pending connections dont get exposed
f275e9cb Optimize Hoppers - Major Boost - Got2GoFast!
0106485c Improvements to watchdog changes
65934b1f Fix build for last commit. 5am commits are great
3f436029 Don't process watchdog until server has fully started and ticked.
938bd972 Don't fire BlockFade on worldgen threads - Fixes #3208
509a828e Fix loading spawn chunks when async chunks is off
8a91bfd2 Improvements to async login
bf698865 Revert "Re-track players that dismount from other players"
82b98418 Fix some issues with async login as well another source of sync loads
aa241d2b Allow multiple callbacks to schedule for Callback Executor
a2064a41 Add PlayerAttackEntityCooldownResetEvent This event is called when processing a player's attack on an entity right before their attack strength cd is reset, there are no existing events that fire within this period of time so it was impossible to capture the players attack strength via API prior to this commit.
f48d4299 Allow sleeping players to float
eeb2f67d Fix Bed respawn deviating too far from vanilla (#3195)
68a7b9fe Move player to spawn point if spawn in unloaded world
2020-04-24 08:22:02 -05:00

391 lines
18 KiB
Diff

From 6e163b10eb444f0fc0573636727586bf44298e98 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
---
.../net/minecraft/server/DamageSource.java | 1 +
.../java/net/minecraft/server/Entity.java | 8 +-
.../minecraft/server/EntityEnderCrystal.java | 49 +++++
.../net/minecraft/server/EntityPhantom.java | 172 +++++++++++++++++-
.../net/minecraft/server/IEntityAccess.java | 1 +
.../net/pl3x/purpur/PurpurWorldConfig.java | 6 +
6 files changed, 226 insertions(+), 11 deletions(-)
diff --git a/src/main/java/net/minecraft/server/DamageSource.java b/src/main/java/net/minecraft/server/DamageSource.java
index f7344d3a..f6c0165f 100644
--- a/src/main/java/net/minecraft/server/DamageSource.java
+++ b/src/main/java/net/minecraft/server/DamageSource.java
@@ -82,6 +82,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 585ebc37..217ab1be 100644
--- a/src/main/java/net/minecraft/server/Entity.java
+++ b/src/main/java/net/minecraft/server/Entity.java
@@ -1427,6 +1427,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.c(entity.getPositionVector());
}
@@ -1956,14 +1957,13 @@ 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);
}
@Nullable public final EntityItem dropItem(ItemStack itemstack, float offset) { return this.a(itemstack, offset); } // Paper - OBFHELPER
- @Nullable
- public EntityItem a(ItemStack itemstack, float f) {
+ @Nullable public EntityItem a(ItemStack itemstack, float f) {
if (itemstack.isEmpty()) {
return null;
} else if (this.world.isClientSide) {
diff --git a/src/main/java/net/minecraft/server/EntityEnderCrystal.java b/src/main/java/net/minecraft/server/EntityEnderCrystal.java
index a57d0089..02952c3a 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.getEntitiesByClass(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 b(NBTTagCompound nbttagcompound) {
diff --git a/src/main/java/net/minecraft/server/EntityPhantom.java b/src/main/java/net/minecraft/server/EntityPhantom.java
index 9d331f9a..77b69cd3 100644
--- a/src/main/java/net/minecraft/server/EntityPhantom.java
+++ b/src/main/java/net/minecraft/server/EntityPhantom.java
@@ -8,9 +8,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 bw;
+ 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 bw; public AttackPhase getAttackPhase() { return this.bw; } // Purpur - OBFHELPER
+ private BlockPosition crystalPosition; // Purpur
public EntityPhantom(EntityTypes<? extends EntityPhantom> entitytypes, World world) {
super(entitytypes, world);
@@ -63,12 +64,18 @@ public class EntityPhantom extends EntityFlying implements IMonster {
@Override
protected void initPathfinder() {
+ // Purpur start
this.goalSelector.a(0, new net.pl3x.purpur.pathfinder.PathfinderGoalHasRider(this)); // Purpur
- this.goalSelector.a(1, new EntityPhantom.c());
- this.goalSelector.a(2, new EntityPhantom.i());
- this.goalSelector.a(3, new EntityPhantom.e());
+ 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(0, new net.pl3x.purpur.pathfinder.PathfinderGoalHasRider(this)); // Purpur
- this.targetSelector.a(1, new EntityPhantom.b());
+ this.targetSelector.a(1, new EntityPhantom.b()); // AttackPlayerGoal
+ // Purpur end
}
@Override
@@ -158,6 +165,24 @@ public class EntityPhantom extends EntityFlying implements IMonster {
}
// 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;
+ }
+
@Override
public boolean onSpacebar() {
if (hasRider() && getRider().getBukkitEntity().hasPermission("allow.special.phantom")) {
@@ -266,6 +291,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.getEntitiesByClass(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 BlockPosition(crystal).add(0, phantom.getRandom().nextInt(10) + 10, 0);
+ }
+
+ @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.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(new Vec3D(phantom.crystalPosition).add(
+ (double) (this.radius * MathHelper.cos(this.offset)),
+ (double) (-4.0F + this.verticalChange),
+ (double) (this.radius * MathHelper.sin(this.offset))));
+ }
+ }
+ // Purpur end
+
class b extends PathfinderGoal {
private final PathfinderTargetCondition b;
@@ -279,6 +434,7 @@ public class EntityPhantom extends EntityFlying implements IMonster {
@Override
public boolean a() {
if (getRider() != null) return false; // Purpur - pathfinder does not have a flag
+ if (isCirclingCrystal()) return false; // Purpur - pathfinder does not have a flag
if (this.c > 0) {
--this.c;
return false;
@@ -310,6 +466,7 @@ public class EntityPhantom extends EntityFlying implements IMonster {
@Override
public boolean b() {
if (getRider() != null) return false; // Purpur - pathfinder does not have a flag
+ 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;
@@ -325,6 +482,7 @@ public class EntityPhantom extends EntityFlying implements IMonster {
@Override
public boolean a() {
if (getRider() != null) return false; // Purpur - pathfinder does not have a flag
+ 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/minecraft/server/IEntityAccess.java b/src/main/java/net/minecraft/server/IEntityAccess.java
index 5135308f..89961900 100644
--- a/src/main/java/net/minecraft/server/IEntityAccess.java
+++ b/src/main/java/net/minecraft/server/IEntityAccess.java
@@ -34,6 +34,7 @@ public interface IEntityAccess {
});
}
+ default <T extends Entity> List<T> getEntitiesByClass(Class<? extends T> oclass, AxisAlignedBB axisalignedbb) { return a(oclass, axisalignedbb); } // Purpur - OBFHELPER
default <T extends Entity> List<T> a(Class<? extends T> oclass, AxisAlignedBB axisalignedbb) {
return this.a(oclass, axisalignedbb, IEntitySelector.f);
}
diff --git a/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java b/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java
index 12cd8196..e341c492 100644
--- a/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java
+++ b/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java
@@ -404,6 +404,9 @@ public class PurpurWorldConfig {
public int phantomFlameFireTime = 8;
public boolean phantomIgnoreCreative = false;
public boolean phantomOnlyAttackInsomniacs = false;
+ public double phantomAttackedByCrystalRadius = 0.0D;
+ public float phantomAttackedByCrystalDamage = 1.0F;
+ public double phantomOrbitCrystalRadius = 0.0D;
private void phantomSettings() {
phantomRidable = getBoolean("mobs.phantom.ridable", phantomRidable);
phantomRidableInWater = getBoolean("mobs.phantom.ridable-in-water", phantomRidableInWater);
@@ -413,6 +416,9 @@ public class PurpurWorldConfig {
phantomFlameFireTime = getInt("mobs.phantom.flames.fire-time", phantomFlameFireTime);
phantomIgnoreCreative = getBoolean("mobs.phantom.do-not-spawn-on-creative-players", phantomIgnoreCreative);
phantomOnlyAttackInsomniacs = getBoolean("mobs.phantom.only-attack-insomniacs", phantomOnlyAttackInsomniacs);
+ 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 pigRidable = false;
--
2.24.0