Files
Purpur/patches/server/0064-Phantoms-attracted-to-crystals-and-crystals-shoot-ph.patch
granny c6802b0a27 Updated Upstream (Paper)
Upstream has released updates that appear to apply and compile correctly

Paper Changes:
PaperMC/Paper@9f1fa0b Fix item gravity on inactive items, remove dumb active skipping
PaperMC/Paper@1a1d0cf Use target pitch in teleport (generally the same thing)
PaperMC/Paper@8ba3073 fix "is_freezing" damage type tag
PaperMC/Paper@1523212 don't resend effects when PlayerItemConsumeEvent is cancelled
PaperMC/Paper@1330880 Add Friction API to minecarts
PaperMC/Paper@580a610 Allow using old ender pearl behavior & apply ender pearl exploit patch (#11524)
PaperMC/Paper@40a960d Rebuild patches
PaperMC/Paper@dfedf79 Correctly cancel consumption of consumable
PaperMC/Paper@147b796 get previous redstone level from the right state for experimental wires
PaperMC/Paper@ad9c58e Only expose velocity relative tp flags to API (#11532)
PaperMC/Paper@f273e6e Set updatingMinecraft to false
PaperMC/Paper@c5c1250 [ci skip] Remove leftover todo file (#11540)
PaperMC/Paper@7ee4835 Correctly clear  explosion density cache(#11541)
PaperMC/Paper@52a0590 Updated Upstream (Bukkit/CraftBukkit) (#11543)
PaperMC/Paper@5c0930d Fix fix recipe iterator patch
PaperMC/Paper@1de0130 re-add a dispense fix patch
PaperMC/Paper@16d7d73 bunch more general fixes
PaperMC/Paper@a5d7426 Correctly support RecipeChoice.empty (#11550)
PaperMC/Paper@85c870e Correct update cursor (#11554)
PaperMC/Paper@d19be64 Fix NPE with spark when CraftServer is not init yet (#11558)
PaperMC/Paper@92131ad Decrease dead entity teleport warning (#11559)
2024-11-01 18:25:48 -07:00

282 lines
12 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/world/entity/boss/enderdragon/EndCrystal.java b/src/main/java/net/minecraft/world/entity/boss/enderdragon/EndCrystal.java
index 7cb3d69a69e0e3ef4b7f9f9c8b1eb67edb5d116d..4c9709e680b585316978b6a410600a8a3b4b6e3e 100644
--- a/src/main/java/net/minecraft/world/entity/boss/enderdragon/EndCrystal.java
+++ b/src/main/java/net/minecraft/world/entity/boss/enderdragon/EndCrystal.java
@@ -31,6 +31,12 @@ public class EndCrystal extends Entity {
private static final EntityDataAccessor<Boolean> DATA_SHOW_BOTTOM = SynchedEntityData.defineId(EndCrystal.class, EntityDataSerializers.BOOLEAN);
public int time;
public boolean generatedByDragonFight = false; // Paper - Fix invulnerable end crystals
+ // Purpur start
+ private net.minecraft.world.entity.monster.Phantom targetPhantom;
+ private int phantomBeamTicks = 0;
+ private int phantomDamageCooldown = 0;
+ private int idleCooldown = 0;
+ // Purpur end
public EndCrystal(EntityType<? extends EndCrystal> type, Level world) {
super(type, world);
@@ -82,6 +88,49 @@ public class EndCrystal extends Entity {
// Paper end - Fix invulnerable end crystals
}
+ // Purpur start
+ if (level().purpurConfig.phantomAttackedByCrystalRadius <= 0 || --idleCooldown > 0) {
+ return; // on cooldown
+ }
+
+ if (targetPhantom == null) {
+ for (net.minecraft.world.entity.monster.Phantom phantom : level().getEntitiesOfClass(net.minecraft.world.entity.monster.Phantom.class, getBoundingBox().inflate(level().purpurConfig.phantomAttackedByCrystalRadius))) {
+ if (phantom.hasLineOfSight(this)) {
+ attackPhantom(phantom);
+ break;
+ }
+ }
+ } else {
+ setBeamTarget(new BlockPos(targetPhantom).offset(0, -2, 0));
+ if (--phantomBeamTicks > 0 && targetPhantom.isAlive()) {
+ phantomDamageCooldown--;
+ if (targetPhantom.hasLineOfSight(this)) {
+ if (phantomDamageCooldown <= 0) {
+ phantomDamageCooldown = 20;
+ targetPhantom.hurt(targetPhantom.damageSources().indirectMagic(this, this), level().purpurConfig.phantomAttackedByCrystalDamage);
+ }
+ } else {
+ forgetPhantom(); // no longer in sight
+ }
+ } else {
+ forgetPhantom(); // attacked long enough
+ }
+ }
+ }
+
+ private void attackPhantom(net.minecraft.world.entity.monster.Phantom phantom) {
+ phantomDamageCooldown = 0;
+ phantomBeamTicks = 60;
+ targetPhantom = phantom;
+ }
+
+ private void forgetPhantom() {
+ targetPhantom = null;
+ setBeamTarget(null);
+ phantomBeamTicks = 0;
+ phantomDamageCooldown = 0;
+ idleCooldown = 60;
+ // Purpur end
}
@Override
diff --git a/src/main/java/net/minecraft/world/entity/monster/Phantom.java b/src/main/java/net/minecraft/world/entity/monster/Phantom.java
index e44d6d02ceaf1f133705efcd58b94e36014389ec..bd1c23d849269df504c94da9d0ca0a6846b22c41 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Phantom.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Phantom.java
@@ -49,6 +49,7 @@ public class Phantom extends FlyingMob implements Enemy {
Vec3 moveTargetPoint;
public BlockPos anchorPoint;
Phantom.AttackPhase attackPhase;
+ Vec3 crystalPosition; // Purpur
public Phantom(EntityType<? extends Phantom> type, Level world) {
super(type, world);
@@ -116,6 +117,23 @@ public class Phantom extends FlyingMob implements Enemy {
level().addFreshEntity(flames);
return true;
}
+
+ @Override
+ protected void dropFromLootTable(ServerLevel world, DamageSource damageSource, boolean causedByPlayer) {
+ boolean dropped = false;
+ if (lastHurtByPlayer == null && damageSource.getEntity() instanceof net.minecraft.world.entity.boss.enderdragon.EndCrystal) {
+ if (random.nextInt(5) < 1) {
+ dropped = spawnAtLocation(world, new net.minecraft.world.item.ItemStack(net.minecraft.world.item.Items.PHANTOM_MEMBRANE)) != null;
+ }
+ }
+ if (!dropped) {
+ super.dropFromLootTable(world, damageSource, causedByPlayer);
+ }
+ }
+
+ public boolean isCirclingCrystal() {
+ return crystalPosition != null;
+ }
// Purpur end
@Override
@@ -130,11 +148,17 @@ public class Phantom extends FlyingMob implements Enemy {
@Override
protected void registerGoals() {
- this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
- this.goalSelector.addGoal(1, new Phantom.PhantomAttackStrategyGoal());
- this.goalSelector.addGoal(2, new Phantom.PhantomSweepAttackGoal());
- this.goalSelector.addGoal(3, new Phantom.PhantomCircleAroundAnchorGoal());
- this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
+ // Purpur start
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this));
+ if (level().purpurConfig.phantomOrbitCrystalRadius > 0) {
+ this.goalSelector.addGoal(1, new FindCrystalGoal(this));
+ this.goalSelector.addGoal(2, new OrbitCrystalGoal(this));
+ }
+ this.goalSelector.addGoal(3, new Phantom.PhantomAttackStrategyGoal());
+ this.goalSelector.addGoal(4, new Phantom.PhantomSweepAttackGoal());
+ this.goalSelector.addGoal(5, new Phantom.PhantomCircleAroundAnchorGoal());
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this));
+ // Purpur end
this.targetSelector.addGoal(1, new Phantom.PhantomAttackPlayerTargetGoal());
}
@@ -333,6 +357,124 @@ public class Phantom extends FlyingMob implements Enemy {
private AttackPhase() {}
}
+ // Purpur start
+ class FindCrystalGoal extends Goal {
+ private final Phantom phantom;
+ private net.minecraft.world.entity.boss.enderdragon.EndCrystal crystal;
+ private Comparator<net.minecraft.world.entity.boss.enderdragon.EndCrystal> comparator;
+
+ FindCrystalGoal(Phantom phantom) {
+ this.phantom = phantom;
+ this.comparator = Comparator.comparingDouble(phantom::distanceToSqr);
+ this.setFlags(EnumSet.of(Flag.LOOK));
+ }
+
+ @Override
+ public boolean canUse() {
+ double range = maxTargetRange();
+ List<net.minecraft.world.entity.boss.enderdragon.EndCrystal> crystals = level().getEntitiesOfClass(net.minecraft.world.entity.boss.enderdragon.EndCrystal.class, phantom.getBoundingBox().inflate(range));
+ if (crystals.isEmpty()) {
+ return false;
+ }
+ crystals.sort(comparator);
+ crystal = crystals.get(0);
+ if (phantom.distanceToSqr(crystal) > range * range) {
+ crystal = null;
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public boolean canContinueToUse() {
+ if (crystal == null || !crystal.isAlive()) {
+ return false;
+ }
+ double range = maxTargetRange();
+ return phantom.distanceToSqr(crystal) <= (range * range) * 2;
+ }
+
+ @Override
+ public void start() {
+ phantom.crystalPosition = new Vec3(crystal.getX(), crystal.getY() + (phantom.random.nextInt(10) + 10), crystal.getZ());
+ }
+
+ @Override
+ public void stop() {
+ crystal = null;
+ phantom.crystalPosition = null;
+ super.stop();
+ }
+
+ private double maxTargetRange() {
+ return phantom.level().purpurConfig.phantomOrbitCrystalRadius;
+ }
+ }
+
+ class OrbitCrystalGoal extends Goal {
+ private final Phantom phantom;
+ private float offset;
+ private float radius;
+ private float verticalChange;
+ private float direction;
+
+ OrbitCrystalGoal(Phantom phantom) {
+ this.phantom = phantom;
+ this.setFlags(EnumSet.of(Flag.MOVE));
+ }
+
+ @Override
+ public boolean canUse() {
+ return phantom.isCirclingCrystal();
+ }
+
+ @Override
+ public void start() {
+ 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 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.moveTargetPoint.distanceToSqr(phantom.getX(), phantom.getY(), phantom.getZ()) < 4.0D) {
+ updateOffset();
+ }
+ if (phantom.moveTargetPoint.y < phantom.getY() && !phantom.level().isEmptyBlock(new BlockPos(phantom).below(1))) {
+ this.verticalChange = Math.max(1.0F, this.verticalChange);
+ updateOffset();
+ }
+ if (phantom.moveTargetPoint.y > phantom.getY() && !phantom.level().isEmptyBlock(new BlockPos(phantom).above(1))) {
+ this.verticalChange = Math.min(-1.0F, this.verticalChange);
+ updateOffset();
+ }
+ }
+
+ private void updateOffset() {
+ this.offset += this.direction * 15.0F * 0.017453292F;
+ phantom.moveTargetPoint = phantom.crystalPosition.add(
+ this.radius * Mth.cos(this.offset),
+ -4.0F + this.verticalChange,
+ this.radius * Mth.sin(this.offset));
+ }
+ }
+ // Purpur end
+
private class PhantomMoveControl extends org.purpurmc.purpur.controller.FlyingMoveControllerWASD { // Purpur
private float speed = 0.1F;
diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java
index 7c1f903c41839251a69d38ba58fb4a7038f8901a..50db395f519db1d25174e2bdb930e11a05167603 100644
--- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java
+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java
@@ -1150,6 +1150,9 @@ public class PurpurWorldConfig {
public String phantomAttackDamage = "6 + size";
public Map<Integer, Double> phantomMaxHealthCache = new HashMap<>();
public Map<Integer, Double> phantomAttackDamageCache = new HashMap<>();
+ 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);
@@ -1171,6 +1174,9 @@ public class PurpurWorldConfig {
phantomAttackDamage = getString("mobs.phantom.attributes.attack_damage", phantomAttackDamage);
phantomMaxHealthCache.clear();
phantomAttackDamageCache.clear();
+ 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;