From 8cdbb4d8989ed7eacb30babfb8bdce85ef19c33d Mon Sep 17 00:00:00 2001 From: Ben Kerllenevich Date: Thu, 17 Jun 2021 08:56:42 -0400 Subject: [PATCH] 3 more patches --- build.gradle.kts | 2 +- ...ed-to-crystals-and-crystals-shoot-ph.patch | 0 .../0079-Add-phantom-spawning-options.patch | 0 ...0080-Implement-bed-explosion-options.patch | 0 ...ed-to-crystals-and-crystals-shoot-ph.patch | 333 ++++++++++++++++++ .../0077-Add-phantom-spawning-options.patch | 232 ++++++++++++ ...0078-Implement-bed-explosion-options.patch | 62 ++++ 7 files changed, 628 insertions(+), 1 deletion(-) delete mode 100644 patches/server-unmapped/0078-Phantoms-attracted-to-crystals-and-crystals-shoot-ph.patch delete mode 100644 patches/server-unmapped/0079-Add-phantom-spawning-options.patch delete mode 100644 patches/server-unmapped/0080-Implement-bed-explosion-options.patch create mode 100644 patches/server/0076-Phantoms-attracted-to-crystals-and-crystals-shoot-ph.patch create mode 100644 patches/server/0077-Add-phantom-spawning-options.patch create mode 100644 patches/server/0078-Implement-bed-explosion-options.patch diff --git a/build.gradle.kts b/build.gradle.kts index bd8f17aaa..7346fc175 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,7 +1,7 @@ plugins { java id("com.github.johnrengelman.shadow") version "7.0.0" apply false - id("io.papermc.paperweight.patcher") version "1.0.4-SNAPSHOT" + id("io.papermc.paperweight.patcher") version "1.1.0-SNAPSHOT" } repositories { diff --git a/patches/server-unmapped/0078-Phantoms-attracted-to-crystals-and-crystals-shoot-ph.patch b/patches/server-unmapped/0078-Phantoms-attracted-to-crystals-and-crystals-shoot-ph.patch deleted file mode 100644 index e69de29bb..000000000 diff --git a/patches/server-unmapped/0079-Add-phantom-spawning-options.patch b/patches/server-unmapped/0079-Add-phantom-spawning-options.patch deleted file mode 100644 index e69de29bb..000000000 diff --git a/patches/server-unmapped/0080-Implement-bed-explosion-options.patch b/patches/server-unmapped/0080-Implement-bed-explosion-options.patch deleted file mode 100644 index e69de29bb..000000000 diff --git a/patches/server/0076-Phantoms-attracted-to-crystals-and-crystals-shoot-ph.patch b/patches/server/0076-Phantoms-attracted-to-crystals-and-crystals-shoot-ph.patch new file mode 100644 index 000000000..f5e001fe5 --- /dev/null +++ b/patches/server/0076-Phantoms-attracted-to-crystals-and-crystals-shoot-ph.patch @@ -0,0 +1,333 @@ +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/world/entity/boss/enderdragon/EndCrystal.java b/src/main/java/net/minecraft/world/entity/boss/enderdragon/EndCrystal.java +index b643a2449e329560c936c0a06fb4cc494d0737a7..c90c46e08bb3d664465b01616ae80c133bc3f733 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 +@@ -14,6 +14,7 @@ import net.minecraft.server.level.ServerLevel; + import net.minecraft.world.damagesource.DamageSource; + import net.minecraft.world.entity.Entity; + import net.minecraft.world.entity.EntityType; ++import net.minecraft.world.entity.monster.Phantom; + import net.minecraft.world.item.ItemStack; + import net.minecraft.world.item.Items; + import net.minecraft.world.level.BlockGetter; +@@ -32,6 +33,12 @@ public class EndCrystal extends Entity { + private static final EntityDataAccessor DATA_SHOW_BOTTOM = SynchedEntityData.defineId(EndCrystal.class, EntityDataSerializers.BOOLEAN); + public int time; + public boolean generatedByDragonFight = false; // Paper - Fix invulnerable end crystals ++ // Purpur start ++ private Phantom targetPhantom; ++ private int phantomBeamTicks = 0; ++ private int phantomDamageCooldown = 0; ++ private int idleCooldown = 0; ++ // Purpur end + + public EndCrystal(EntityType type, Level world) { + super(type, world); +@@ -81,7 +88,50 @@ public class EndCrystal extends Entity { + // Paper end + } + ++ // Purpur start ++ if (level.purpurConfig.phantomAttackedByCrystalRadius <= 0 || --idleCooldown > 0) { ++ return; // on cooldown ++ } ++ ++ if (targetPhantom == null) { ++ for (Phantom phantom : level.getEntitiesOfClass(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(DamageSource.indirectMagic(this, this), level.purpurConfig.phantomAttackedByCrystalDamage); ++ } ++ } else { ++ forgetPhantom(); // no longer in sight ++ } ++ } else { ++ forgetPhantom(); // attacked long enough ++ } ++ } ++ } ++ ++ private void attackPhantom(Phantom 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 addAdditionalSaveData(CompoundTag nbt) { +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 1ffe939bb66358391d92d3e5378865b1cc8690fd..6058fb62baa388febc8e36cb680ab2ddcd1306f4 100644 +--- a/src/main/java/net/minecraft/world/entity/monster/Phantom.java ++++ b/src/main/java/net/minecraft/world/entity/monster/Phantom.java +@@ -12,6 +12,7 @@ import net.minecraft.nbt.CompoundTag; + import net.minecraft.network.syncher.EntityDataAccessor; + import net.minecraft.network.syncher.EntityDataSerializers; + import net.minecraft.network.syncher.SynchedEntityData; ++import net.minecraft.server.level.ServerLevel; + import net.minecraft.sounds.SoundEvent; + import net.minecraft.sounds.SoundEvents; + import net.minecraft.sounds.SoundSource; +@@ -36,10 +37,15 @@ import net.minecraft.world.entity.ai.control.MoveControl; + import net.minecraft.world.entity.ai.goal.Goal; + import net.minecraft.world.entity.ai.targeting.TargetingConditions; + import net.minecraft.world.entity.animal.Cat; ++import net.minecraft.world.entity.boss.enderdragon.EndCrystal; + import net.minecraft.world.entity.player.Player; ++import net.minecraft.world.item.ItemStack; ++import net.minecraft.world.item.Items; + import net.minecraft.world.level.Level; + import net.minecraft.world.level.ServerLevelAccessor; + import net.minecraft.world.level.levelgen.Heightmap; ++import net.minecraft.world.level.storage.loot.LootContext; ++import net.minecraft.world.level.storage.loot.parameters.LootContextParams; + import net.minecraft.world.phys.Vec3; + + public class Phantom extends FlyingMob implements Enemy { +@@ -50,6 +56,7 @@ public class Phantom extends FlyingMob implements Enemy { + Vec3 moveTargetPoint; + BlockPos anchorPoint; + Phantom.AttackPhase attackPhase; ++ Vec3 crystalPosition; // Purpur + + public Phantom(EntityType type, Level world) { + super(type, world); +@@ -73,12 +80,38 @@ public class Phantom extends FlyingMob implements Enemy { + + @Override + protected void registerGoals() { +- this.goalSelector.addGoal(1, new Phantom.PhantomAttackStrategyGoal()); +- this.goalSelector.addGoal(2, new Phantom.PhantomSweepAttackGoal()); +- this.goalSelector.addGoal(3, new Phantom.PhantomCircleAroundAnchorGoal()); ++ // Purpur start ++ 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(1, new Phantom.PhantomAttackPlayerTargetGoal()); ++ // Purpur end + } + ++ // Purpur start ++ @Override ++ protected LootContext.Builder createLootContext(boolean causedByPlayer, DamageSource source) { ++ boolean dropped = false; ++ if (lastHurtByPlayer == null && source.getEntity() instanceof EndCrystal) { ++ if (random.nextInt(5) < 1) { ++ dropped = spawnAtLocation(new ItemStack(Items.PHANTOM_MEMBRANE)) != null; ++ } ++ } ++ if (!dropped) { ++ return super.createLootContext(causedByPlayer, source); ++ } ++ return new LootContext.Builder((ServerLevel) level); ++ } ++ ++ public boolean isCirclingCrystal() { ++ return crystalPosition != null; ++ } ++ // Purpur end ++ + @Override + protected void defineSynchedData() { + super.defineSynchedData(); +@@ -263,6 +296,124 @@ public class Phantom extends FlyingMob implements Enemy { + private AttackPhase() {} + } + ++ // Purpur start ++ class FindCrystalGoal extends Goal { ++ private final Phantom phantom; ++ private EndCrystal crystal; ++ private Comparator comparator; ++ ++ FindCrystalGoal(Phantom phantom) { ++ this.phantom = phantom; ++ this.comparator = Comparator.comparingDouble(phantom::distanceToSqr); ++ this.setFlags(EnumSet.of(Flag.LOOK)); ++ } ++ ++ @Override ++ public boolean shouldActivate() { ++ double range = maxTargetRange(); ++ List crystals = level.getEntitiesOfClass(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 shouldStayActive() { ++ 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 shouldActivate() { ++ 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 MoveControl { + + private float speed = 0.1F; +@@ -349,6 +500,7 @@ public class Phantom extends FlyingMob implements Enemy { + + @Override + public boolean canUse() { ++ if (isCirclingCrystal()) return false; + LivingEntity entityliving = Phantom.this.getTarget(); + + return entityliving != null ? Phantom.this.canAttack(Phantom.this.getTarget(), TargetingConditions.DEFAULT) : false; +@@ -538,6 +690,7 @@ public class Phantom extends FlyingMob implements Enemy { + + @Override + public boolean canUse() { ++ if (isCirclingCrystal()) return false; + if (this.nextScanTick > 0) { + --this.nextScanTick; + return false; +@@ -566,6 +719,7 @@ public class Phantom extends FlyingMob implements Enemy { + + @Override + public boolean canContinueToUse() { ++ if (isCirclingCrystal()) return false; + LivingEntity entityliving = Phantom.this.getTarget(); + + return entityliving != null ? Phantom.this.canAttack(entityliving, TargetingConditions.DEFAULT) : false; +diff --git a/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java b/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java +index e39e1ff19f49b093ae27a98cba280ad2643df7a2..8985cc0ca2e5f1f673ef6ae59976095993332559 100644 +--- a/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java ++++ b/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java +@@ -617,6 +617,9 @@ public class PurpurWorldConfig { + public float phantomFlameDamage = 1.0F; + public int phantomFlameFireTime = 8; + public boolean phantomAllowGriefing = 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); +@@ -624,6 +627,9 @@ public class PurpurWorldConfig { + phantomFlameDamage = (float) getDouble("mobs.phantom.flames.damage", phantomFlameDamage); + phantomFlameFireTime = getInt("mobs.phantom.flames.fire-time", phantomFlameFireTime); + phantomAllowGriefing = getBoolean("mobs.phantom.allow-griefing", phantomAllowGriefing); ++ 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; diff --git a/patches/server/0077-Add-phantom-spawning-options.patch b/patches/server/0077-Add-phantom-spawning-options.patch new file mode 100644 index 000000000..24afd7e2b --- /dev/null +++ b/patches/server/0077-Add-phantom-spawning-options.patch @@ -0,0 +1,232 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: William Blake Galbreath +Date: Fri, 3 Jul 2020 00:03:52 -0500 +Subject: [PATCH] Add phantom spawning options + + +diff --git a/src/main/java/net/minecraft/world/level/levelgen/PhantomSpawner.java b/src/main/java/net/minecraft/world/level/levelgen/PhantomSpawner.java +index 79504dc3448402e73b09c4232b1fd0488872cf68..5c7683058b79953aa4f7427d7654b7e4823dbf09 100644 +--- a/src/main/java/net/minecraft/world/level/levelgen/PhantomSpawner.java ++++ b/src/main/java/net/minecraft/world/level/levelgen/PhantomSpawner.java +@@ -2,12 +2,15 @@ package net.minecraft.world.level.levelgen; + + import java.util.Iterator; + import java.util.Random; ++ ++import com.destroystokyo.paper.event.entity.PhantomPreSpawnEvent; + import net.minecraft.core.BlockPos; + import net.minecraft.nbt.CompoundTag; + import net.minecraft.server.MCUtil; + import net.minecraft.server.level.ServerLevel; + import net.minecraft.server.level.ServerPlayer; + import net.minecraft.stats.ServerStatsCounter; ++import net.minecraft.stats.Stat; + import net.minecraft.stats.Stats; + import net.minecraft.util.Mth; + import net.minecraft.world.DifficultyInstance; +@@ -23,84 +26,100 @@ import net.minecraft.world.level.Level; + import net.minecraft.world.level.NaturalSpawner; + import net.minecraft.world.level.block.state.BlockState; + import net.minecraft.world.level.material.FluidState; ++import org.bukkit.event.entity.CreatureSpawnEvent; + + public class PhantomSpawner implements CustomSpawner { + +- private int nextTick; ++ private int spawnDelay; // Purpur + + public PhantomSpawner() {} + + @Override + public int tick(ServerLevel world, boolean spawnMonsters, boolean spawnAnimals) { ++ // Purpur start - rewrite entire thing + if (!spawnMonsters) { + return 0; +- } else if (!world.getGameRules().getBoolean(GameRules.RULE_DOINSOMNIA)) { ++ } ++ ++ if (!world.getGameRules().getBoolean(GameRules.RULE_DOINSOMNIA)) { ++ return 0; ++ } ++ ++ --this.spawnDelay; ++ if (this.spawnDelay > 0) { + return 0; +- } else { +- Random random = world.random; +- +- --this.nextTick; +- if (this.nextTick > 0) { +- return 0; +- } else { +- this.nextTick += (60 + random.nextInt(60)) * 20; +- if (world.getSkyDarken() < 5 && world.dimensionType().hasSkyLight()) { +- return 0; +- } else { +- int i = 0; +- Iterator iterator = world.players().iterator(); +- +- while (iterator.hasNext()) { +- Player entityhuman = (Player) iterator.next(); +- +- if (!entityhuman.isSpectator() && (!world.paperConfig.phantomIgnoreCreative || !entityhuman.isCreative())) { // Paper +- BlockPos blockposition = entityhuman.blockPosition(); +- +- if (!world.dimensionType().hasSkyLight() || blockposition.getY() >= world.getSeaLevel() && world.canSeeSky(blockposition)) { +- DifficultyInstance difficultydamagescaler = world.getCurrentDifficultyAt(blockposition); +- +- if (difficultydamagescaler.isHarderThan(random.nextFloat() * 3.0F)) { +- ServerStatsCounter serverstatisticmanager = ((ServerPlayer) entityhuman).getStats(); +- int j = Mth.clamp(serverstatisticmanager.getValue(Stats.CUSTOM.get(Stats.TIME_SINCE_REST)), 1, Integer.MAX_VALUE); +- boolean flag2 = true; +- +- if (random.nextInt(j) >= 72000) { +- BlockPos blockposition1 = blockposition.above(20 + random.nextInt(15)).east(-10 + random.nextInt(21)).south(-10 + random.nextInt(21)); +- BlockState iblockdata = world.getBlockState(blockposition1); +- FluidState fluid = world.getFluidState(blockposition1); +- +- if (NaturalSpawner.isValidEmptySpawnBlock((BlockGetter) world, blockposition1, iblockdata, fluid, EntityType.PHANTOM)) { +- SpawnGroupData groupdataentity = null; +- int k = 1 + random.nextInt(difficultydamagescaler.getDifficulty().getId() + 1); +- +- for (int l = 0; l < k; ++l) { +- // Paper start +- com.destroystokyo.paper.event.entity.PhantomPreSpawnEvent event = new com.destroystokyo.paper.event.entity.PhantomPreSpawnEvent(MCUtil.toLocation(world, blockposition1), ((ServerPlayer) entityhuman).getBukkitEntity(), org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.NATURAL); +- if (!event.callEvent()) { +- if (event.shouldAbortSpawn()) { +- break; +- } +- continue; +- } +- // Paper end +- Phantom entityphantom = (Phantom) EntityType.PHANTOM.create((Level) world); +- entityphantom.setSpawningEntity(entityhuman.getUUID()); // Paper +- entityphantom.moveTo(blockposition1, 0.0F, 0.0F); +- groupdataentity = entityphantom.finalizeSpawn(world, difficultydamagescaler, MobSpawnType.NATURAL, groupdataentity, (CompoundTag) null); +- world.addAllEntities(entityphantom, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.NATURAL); // CraftBukkit +- } +- +- i += k; +- } +- } +- } +- } +- } ++ } ++ this.spawnDelay += world.purpurConfig.phantomSpawnDelayMin + world.random.nextInt(world.purpurConfig.phantomSpawnDelayMax - world.purpurConfig.phantomSpawnDelayMin); ++ ++ if (!world.dimensionType().hasSkyLight() || world.getSkyDarken() < world.purpurConfig.phantomSpawnMinSkyDarkness) { ++ return 0; ++ } ++ ++ int numberSpawnsAttempted = 0; ++ for (ServerPlayer player : world.players()) { ++ if (player.isSpectator() || (player.isCreative() && world.paperConfig.phantomIgnoreCreative)) { ++ continue; ++ } ++ ++ BlockPos playerPos = player.blockPosition(); ++ if (playerPos.getY() < world.getSeaLevel() && world.purpurConfig.phantomSpawnOnlyAboveSeaLevel) { ++ continue; ++ } ++ ++ if (!world.canSeeSky(playerPos) && world.purpurConfig.phantomSpawnOnlyWithVisibleSky) { ++ continue; ++ } ++ ++ DifficultyInstance difficultyInstance = world.getCurrentDifficultyAt(playerPos); ++ if (!difficultyInstance.isHarderThan(world.random.nextFloat() * (float) world.purpurConfig.phantomSpawnLocalDifficultyChance)) { ++ continue; ++ } ++ ++ ServerStatsCounter stats = player.getStats(); ++ int timeSinceRest = Mth.clamp(stats.getValue(Stats.CUSTOM.get(Stats.TIME_SINCE_REST)), 1, Integer.MAX_VALUE); ++ if (world.random.nextInt(timeSinceRest) < world.purpurConfig.phantomSpawnMinTimeSinceSlept) { ++ continue; ++ } ++ ++ BlockPos spawnPos = playerPos ++ .above(world.purpurConfig.phantomSpawnMinOverhead + world.random.nextInt(world.purpurConfig.phantomSpawnMaxOverhead - world.purpurConfig.phantomSpawnMinOverhead)) ++ .east(-world.purpurConfig.phantomSpawnOverheadRadius + world.random.nextInt(world.purpurConfig.phantomSpawnOverheadRadius + 1)) ++ .south(-world.purpurConfig.phantomSpawnOverheadRadius + world.random.nextInt(world.purpurConfig.phantomSpawnOverheadRadius + 1)); ++ ++ if (!NaturalSpawner.isValidEmptySpawnBlock(world, spawnPos, world.getTypeIfLoaded(spawnPos), world.getFluidState(spawnPos), EntityType.PHANTOM)) { ++ continue; ++ } ++ ++ int difficulty = difficultyInstance.getDifficulty().getId(); ++ int spawnAttempts = world.purpurConfig.phantomSpawnMinPerAttempt + world.random.nextInt((world.purpurConfig.phantomSpawnMaxPerAttempt < 0 ? difficulty : world.purpurConfig.phantomSpawnMaxPerAttempt) + world.purpurConfig.phantomSpawnMinPerAttempt); ++ ++ SpawnGroupData groupData = null; ++ for (int count = 0; count < spawnAttempts; ++count) { ++ // Paper start ++ PhantomPreSpawnEvent event = new PhantomPreSpawnEvent(MCUtil.toLocation(world, spawnPos), player.getBukkitEntity(), CreatureSpawnEvent.SpawnReason.NATURAL); ++ if (!event.callEvent()) { ++ if (event.shouldAbortSpawn()) { ++ break; + } ++ continue; ++ } ++ // Paper end + +- return i; ++ Phantom phantom = EntityType.PHANTOM.create(world); ++ if (phantom == null) { ++ continue; + } ++ ++ phantom.setSpawningEntity(player.getUUID()); ++ phantom.moveTo(playerPos, 0.0F, 0.0F); ++ groupData = phantom.finalizeSpawn(world, difficultyInstance, MobSpawnType.NATURAL, groupData, null); ++ world.addEntity(phantom, CreatureSpawnEvent.SpawnReason.NATURAL); // CraftBukkit + } ++ ++ numberSpawnsAttempted += spawnAttempts; + } ++ ++ return numberSpawnsAttempted; ++ // Purpur end - rewrite entire thing + } + } +diff --git a/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java b/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java +index 8985cc0ca2e5f1f673ef6ae59976095993332559..891b0ef41e57e95ab3aa7b14891ba53abd135171 100644 +--- a/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java ++++ b/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java +@@ -620,6 +620,18 @@ public class PurpurWorldConfig { + public double phantomAttackedByCrystalRadius = 0.0D; + public float phantomAttackedByCrystalDamage = 1.0F; + public double phantomOrbitCrystalRadius = 0.0D; ++ public int phantomSpawnDelayMin = 1200; ++ public int phantomSpawnDelayMax = 2400; ++ public int phantomSpawnMinSkyDarkness = 5; ++ public boolean phantomSpawnOnlyAboveSeaLevel = true; ++ public boolean phantomSpawnOnlyWithVisibleSky = true; ++ public double phantomSpawnLocalDifficultyChance = 3.0D; ++ public int phantomSpawnMinTimeSinceSlept = 72000; ++ public int phantomSpawnMinOverhead = 20; ++ public int phantomSpawnMaxOverhead = 35; ++ public int phantomSpawnOverheadRadius = 10; ++ public int phantomSpawnMinPerAttempt = 1; ++ public int phantomSpawnMaxPerAttempt = -1; + private void phantomSettings() { + phantomRidable = getBoolean("mobs.phantom.ridable", phantomRidable); + phantomRidableInWater = getBoolean("mobs.phantom.ridable-in-water", phantomRidableInWater); +@@ -630,6 +642,18 @@ public class PurpurWorldConfig { + 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); ++ phantomSpawnDelayMin = getInt("mobs.phantom.spawn.delay.min", phantomSpawnDelayMin); ++ phantomSpawnDelayMax = getInt("mobs.phantom.spawn.delay.max", phantomSpawnDelayMax); ++ phantomSpawnMinSkyDarkness = getInt("mobs.phantom.spawn.min-sky-darkness", phantomSpawnMinSkyDarkness); ++ phantomSpawnOnlyAboveSeaLevel = getBoolean("mobs.phantom.spawn.only-above-sea-level", phantomSpawnOnlyAboveSeaLevel); ++ phantomSpawnOnlyWithVisibleSky = getBoolean("mobs.phantom.spawn.only-with-visible-sky", phantomSpawnOnlyWithVisibleSky); ++ phantomSpawnLocalDifficultyChance = getDouble("mobs.phantom.spawn.local-difficulty-chance", phantomSpawnLocalDifficultyChance); ++ phantomSpawnMinTimeSinceSlept = getInt("mobs.phantom.spawn.min-time-since-slept", phantomSpawnMinTimeSinceSlept); ++ phantomSpawnMinOverhead = getInt("mobs.phantom.spawn.overhead.min", phantomSpawnMinOverhead); ++ phantomSpawnMaxOverhead = getInt("mobs.phantom.spawn.overhead.max", phantomSpawnMaxOverhead); ++ phantomSpawnOverheadRadius = getInt("mobs.phantom.spawn.overhead.radius", phantomSpawnOverheadRadius); ++ phantomSpawnMinPerAttempt = getInt("mobs.phantom.spawn.per-attempt.min", phantomSpawnMinPerAttempt); ++ phantomSpawnMaxPerAttempt = getInt("mobs.phantom.spawn.per-attempt.max", phantomSpawnMaxPerAttempt); + } + + public boolean pigRidable = false; diff --git a/patches/server/0078-Implement-bed-explosion-options.patch b/patches/server/0078-Implement-bed-explosion-options.patch new file mode 100644 index 000000000..dfd65c8ca --- /dev/null +++ b/patches/server/0078-Implement-bed-explosion-options.patch @@ -0,0 +1,62 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: William Blake Galbreath +Date: Sat, 4 Jul 2020 13:12:43 -0500 +Subject: [PATCH] Implement bed explosion options + + +diff --git a/src/main/java/net/minecraft/world/level/block/BedBlock.java b/src/main/java/net/minecraft/world/level/block/BedBlock.java +index 163a7861f987c3832aac51cc6df950c768546731..cfe4cdbd28ff11ea2781d47fe6d8c2de7912a3f6 100644 +--- a/src/main/java/net/minecraft/world/level/block/BedBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/BedBlock.java +@@ -97,7 +97,7 @@ public class BedBlock extends HorizontalDirectionalBlock implements EntityBlock + world.removeBlock(blockposition1, false); + } + +- world.explode((Entity) null, DamageSource.badRespawnPointExplosion(), (ExplosionDamageCalculator) null, (double) pos.getX() + 0.5D, (double) pos.getY() + 0.5D, (double) pos.getZ() + 0.5D, 5.0F, true, Explosion.BlockInteraction.DESTROY); ++ if (world.purpurConfig.bedExplode) world.explode((Entity) null, DamageSource.badRespawnPointExplosion(), null, (double) pos.getX() + 0.5D, (double) pos.getY() + 0.5D, (double) pos.getZ() + 0.5D, (float) world.purpurConfig.bedExplosionPower, world.purpurConfig.bedExplosionFire, world.purpurConfig.bedExplosionEffect); // Purpur + return InteractionResult.SUCCESS; + } else if ((Boolean) state.getValue(BedBlock.OCCUPIED)) { + if (!this.kickVillagerOutOfBed(world, pos)) { +diff --git a/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java b/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java +index 891b0ef41e57e95ab3aa7b14891ba53abd135171..d232768e789ea6a7790cada4a4ad2624cd32e4b2 100644 +--- a/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java ++++ b/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java +@@ -4,6 +4,7 @@ import net.minecraft.core.Registry; + import net.minecraft.resources.ResourceLocation; + import net.minecraft.world.item.Item; + import net.minecraft.world.item.Items; ++import net.minecraft.world.level.Explosion; + import net.minecraft.world.level.block.Block; + import net.minecraft.world.level.block.Blocks; + import org.bukkit.configuration.ConfigurationSection; +@@ -12,6 +13,7 @@ import java.util.ArrayList; + import java.util.HashMap; + import java.util.List; + import java.util.Map; ++import java.util.logging.Level; + + import static net.pl3x.purpur.PurpurConfig.log; + +@@ -258,6 +260,22 @@ public class PurpurWorldConfig { + }); + } + ++ public boolean bedExplode = true; ++ public double bedExplosionPower = 5.0D; ++ public boolean bedExplosionFire = true; ++ public Explosion.BlockInteraction bedExplosionEffect = Explosion.BlockInteraction.DESTROY; ++ private void bedSettings() { ++ bedExplode = getBoolean("blocks.bed.explode", bedExplode); ++ bedExplosionPower = getDouble("blocks.bed.explosion-power", bedExplosionPower); ++ bedExplosionFire = getBoolean("blocks.bed.explosion-fire", bedExplosionFire); ++ try { ++ bedExplosionEffect = Explosion.BlockInteraction.valueOf(getString("blocks.bed.explosion-effect", bedExplosionEffect.name())); ++ } catch (IllegalArgumentException e) { ++ log(Level.SEVERE, "Unknown value for `blocks.bed.explosion-effect`! Using default of `DESTROY`"); ++ bedExplosionEffect = Explosion.BlockInteraction.DESTROY; ++ } ++ } ++ + public boolean dispenserApplyCursedArmor = true; + private void dispenserSettings() { + dispenserApplyCursedArmor = getBoolean("blocks.dispenser.apply-cursed-to-armor-slots", dispenserApplyCursedArmor);