Purpur/patches/server/0008-Ridables.patch
granny 969064029c
Updated Upstream (Paper)
Upstream has released updates that appear to apply and compile correctly

Paper Changes:
PaperMC/Paper@d129162 Properly handle BlockBreakEvent#isDropItems (#8936)
PaperMC/Paper@67a1a8c Fix Inventory#getLocation not working for the composter (#9596)
PaperMC/Paper@5b34a09 Add option to disable chorus plant and mushroom block updates (#9442)
PaperMC/Paper@ed8401c Fix rotation for spawning display entities (#9552)
PaperMC/Paper@bd67b83 Determine lava and water fluid explosion resistance by their block explosion resistance (#9482)
PaperMC/Paper@d928dda Don't tab-complete namespaced commands if `send-namespaced` is false (#9366)
PaperMC/Paper@f43a19c Fix CME in CraftPersistentDataTypeRegistry (#6701)
PaperMC/Paper@45ba652 [ci skip] Rebuild patches
PaperMC/Paper@990765b Fire entity death event for ender dragon (#9495)
PaperMC/Paper@1259b93 Fixed CraftItemStack/CraftItemMeta enchantment level inconsistency (#8792)
PaperMC/Paper@88891c3  Add Entity Coordinate and Rotation API (#9461)
PaperMC/Paper@5bf82aa Add cancellability to PlayerTrackEntityEvent (#8605)
PaperMC/Paper@1b96c64 Improve cancelling PreCreatureSpawnEvent with per player mob spawns (#9400)
PaperMC/Paper@7232506 Add entity tracker min Y distance config option (#9406)
PaperMC/Paper@00a68b1 Player listing API (#8782)
PaperMC/Paper@af6142e [ci skip] Update Gradle wrapper
PaperMC/Paper@281855c Remove unused lag-compensate-block-breaking option (#9635)
PaperMC/Paper@35ef053 [ci skip] update editorconfig and gitattributes (#9608)
PaperMC/Paper@97b9c4a Fix NPE on Boat#getStatus (#9115)
PaperMC/Paper@2be57c6 Expand Pose API (#8781)
PaperMC/Paper@b29ecd4 Expose clicked BlockFace during BlockDamageEvent (#9632)
PaperMC/Paper@e3f29f4 MerchantRecipe: add copy constructor (#8703)
PaperMC/Paper@fed9042 More DragonBattle API (#5655)
PaperMC/Paper@27d39ca Properly clone custom nbt tags inside ItemMeta (#7849)
PaperMC/Paper@0c7385b Add PlayerPickItemEvent (#5590)
PaperMC/Paper@9395aa6 Improve performance of mass crafts (#9603)
PaperMC/Paper@7386a13 Fix endgateway teleportation offset (#9517)
PaperMC/Paper@a712766 Allow custom damage for trident (#8132)
PaperMC/Paper@3716832 Prevent overfilled bundles from duplicating items (#9633)
PaperMC/Paper@9cbad8e Expose Hand during BlockCanBuildEvent (#9636)
2023-08-22 18:41:13 -07:00

6697 lines
310 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: William Blake Galbreath <Blake.Galbreath@GMail.com>
Date: Sun, 5 Jul 2020 22:19:49 -0500
Subject: [PATCH] Ridables
diff --git a/src/main/java/net/minecraft/core/BlockPos.java b/src/main/java/net/minecraft/core/BlockPos.java
index 83cab746d1d6fe25c043c8aee28c39412b90c127..ec6b58dae525c81bbb1c0e2d96fbded6f00a45b5 100644
--- a/src/main/java/net/minecraft/core/BlockPos.java
+++ b/src/main/java/net/minecraft/core/BlockPos.java
@@ -48,6 +48,12 @@ public class BlockPos extends Vec3i {
private static final int X_OFFSET = 38;
// Paper end
+ // Purpur start
+ public BlockPos(net.minecraft.world.entity.Entity entity) {
+ super(entity.getBlockX(), entity.getBlockY(), entity.getBlockZ());
+ }
+ // Purpur end
+
public BlockPos(int x, int y, int z) {
super(x, y, z);
}
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index 94f95539b22f1673022d25fd5558b85ea3fa80cb..81eca1ab647ee7cfd5ab6ffa2538a451964774ed 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -1540,6 +1540,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
worldserver.hasPhysicsEvent = org.bukkit.event.block.BlockPhysicsEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper
net.minecraft.world.level.block.entity.HopperBlockEntity.skipHopperEvents = worldserver.paperConfig().hopper.disableMoveEvent || org.bukkit.event.inventory.InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0; // Paper
worldserver.hasEntityMoveEvent = io.papermc.paper.event.entity.EntityMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper
+ worldserver.hasRidableMoveEvent = org.purpurmc.purpur.event.entity.RidableMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Purpur
this.profiler.push(() -> {
return worldserver + " " + worldserver.dimension().location();
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
index dbb59969cf55eda997588f4c3ef7dc899ea619bb..4c3b5a26a6b04afff3a707929ced3c62b5256a67 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -223,6 +223,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
public boolean hasPhysicsEvent = true; // Paper
public boolean hasEntityMoveEvent = false; // Paper
private final alternate.current.wire.WireHandler wireHandler = new alternate.current.wire.WireHandler(this); // Paper - optimize redstone (Alternate Current)
+ public boolean hasRidableMoveEvent = false; // Purpur
public static Throwable getAddToWorldStackTrace(Entity entity) {
final Throwable thr = new Throwable(entity + " Added to world at " + new java.util.Date());
io.papermc.paper.util.StacktraceDeobfuscator.INSTANCE.deobfuscateThrowable(thr);
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
index bf88c86deaf677a10f2f8b67fe48a199e625fea9..3aff5f410220c2f3dee7acfa4339b9b5d0a5e7dd 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
@@ -752,6 +752,15 @@ public class ServerPlayer extends Player {
this.trackStartFallingPosition();
this.trackEnteredOrExitedLavaOnVehicle();
this.advancements.flushDirty(this);
+
+ // Purpur start
+ if (this.level().purpurConfig.useNightVisionWhenRiding && this.getVehicle() != null && this.getVehicle().getRider() == this && this.level().getGameTime() % 100 == 0) { // 5 seconds
+ MobEffectInstance nightVision = this.getEffect(MobEffects.NIGHT_VISION);
+ if (nightVision == null || nightVision.getDuration() <= 300) { // 15 seconds
+ this.addEffect(new MobEffectInstance(MobEffects.NIGHT_VISION, 400, 0)); // 20 seconds
+ }
+ }
+ // Purpur end
}
public void doTick() {
diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index 63eac8b250b8bae40de432a0ffd4792122cbae74..e936fa6e638a7ff4b087ecb4247c467be6fe9c57 100644
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -2870,6 +2870,8 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
ServerGamePacketListenerImpl.this.cserver.getPluginManager().callEvent(event);
+ player.processClick(enumhand); // Purpur
+
// Entity in bucket - SPIGOT-4048 and SPIGOT-6859a
if ((entity instanceof Bucketable && entity instanceof LivingEntity && origItem != null && origItem.asItem() == Items.WATER_BUCKET) && (event.isCancelled() || ServerGamePacketListenerImpl.this.player.getInventory().getSelected() == null || ServerGamePacketListenerImpl.this.player.getInventory().getSelected().getItem() != origItem)) {
entity.getEntityData().resendPossiblyDesyncedEntity(player); // Paper - The entire mob gets deleted, so resend it.
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
index 6951b47abb5d36cdd9fe200152a93155c31aedaf..14624fc143c9553930f181f7db93caecf24c0495 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -376,7 +376,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
private final Set<String> tags;
private final double[] pistonDeltas;
private long pistonDeltasGameTime;
- private EntityDimensions dimensions;
+ protected EntityDimensions dimensions; // Purpur - private -> protected
private float eyeHeight;
public boolean isInPowderSnow;
public boolean wasInPowderSnow;
@@ -2942,6 +2942,13 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
this.passengers = ImmutableList.copyOf(list);
}
+ // Purpur start
+ if (isRidable() && this.passengers.get(0) == passenger && passenger instanceof Player player) {
+ onMount(player);
+ this.rider = player;
+ }
+ // Purpur end
+
this.gameEvent(GameEvent.ENTITY_MOUNT, passenger);
}
}
@@ -2982,6 +2989,14 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
return false;
}
// Spigot end
+
+ // Purpur start
+ if (this.rider != null && this.passengers.get(0) == this.rider) {
+ onDismount(this.rider);
+ this.rider = null;
+ }
+ // Purpur end
+
if (this.passengers.size() == 1 && this.passengers.get(0) == entity) {
this.passengers = ImmutableList.of();
} else {
@@ -4883,4 +4898,45 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
return ((net.minecraft.server.level.ServerChunkCache) level.getChunkSource()).isPositionTicking(this);
}
// Paper end
+
+ // Purpur start
+ @Nullable
+ private Player rider = null;
+
+ @Nullable
+ public Player getRider() {
+ return rider;
+ }
+
+ public boolean isRidable() {
+ return false;
+ }
+
+ public boolean isControllable() {
+ return true;
+ }
+
+ public void onMount(Player rider) {
+ if (this instanceof Mob) {
+ ((Mob) this).setTarget(null, null, false);
+ ((Mob) this).getNavigation().stop();
+ }
+ rider.setJumping(false); // fixes jump on mount
+ }
+
+ public void onDismount(Player player) {
+ }
+
+ public boolean onSpacebar() {
+ return false;
+ }
+
+ public boolean onClick(InteractionHand hand) {
+ return false;
+ }
+
+ public boolean processClick(InteractionHand hand) {
+ return false;
+ }
+ // Purpur end
}
diff --git a/src/main/java/net/minecraft/world/entity/GlowSquid.java b/src/main/java/net/minecraft/world/entity/GlowSquid.java
index 759713f7c646aaf1a918c87a2834a1d405385dad..c6a06e07f0b4bb29b5f4c70dfa53ff6db2e4e6ea 100644
--- a/src/main/java/net/minecraft/world/entity/GlowSquid.java
+++ b/src/main/java/net/minecraft/world/entity/GlowSquid.java
@@ -23,6 +23,19 @@ public class GlowSquid extends Squid {
super(type, world);
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.glowSquidRidable;
+ }
+
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.glowSquidControllable;
+ }
+ // Purpur end
+
@Override
protected ParticleOptions getInkParticle() {
return ParticleTypes.GLOW_SQUID_INK;
diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
index e9a31314424d9db911cd9806741222397c3072d7..3c2892b9b2ade76d9d4b081ad5bce4991df11dda 100644
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
@@ -218,9 +218,9 @@ public abstract class LivingEntity extends Entity implements Attackable {
protected int deathScore;
public float lastHurt;
public boolean jumping;
- public float xxa;
- public float yya;
- public float zza;
+ public float xxa; public float getStrafeMot() { return xxa; } public void setStrafeMot(float strafe) { xxa = strafe; } // Purpur - OBFHELPER
+ public float yya; public float getVerticalMot() { return yya; } public void setVerticalMot(float vertical) { yya = vertical; } // Purpur - OBFHELPER
+ public float zza; public float getForwardMot() { return zza; } public void setForwardMot(float forward) { zza = forward; } // Purpur - OBFHELPER
protected int lerpSteps;
protected double lerpX;
protected double lerpY;
@@ -286,7 +286,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
this.effectsDirty = true;
this.useItem = ItemStack.EMPTY;
this.lastClimbablePos = Optional.empty();
- this.attributes = new AttributeMap(DefaultAttributes.getSupplier(type));
+ this.attributes = new AttributeMap(DefaultAttributes.getSupplier(type), this); // Purpur
this.craftAttributes = new CraftAttributeMap(this.attributes); // CraftBukkit
// CraftBukkit - setHealth(getMaxHealth()) inlined and simplified to skip the instanceof check for EntityPlayer, as getBukkitEntity() is not initialized in constructor
this.entityData.set(LivingEntity.DATA_HEALTH_ID, (float) this.getAttribute(Attributes.MAX_HEALTH).getValue());
@@ -337,6 +337,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
public static AttributeSupplier.Builder createLivingAttributes() {
return AttributeSupplier.builder().add(Attributes.MAX_HEALTH).add(Attributes.KNOCKBACK_RESISTANCE).add(Attributes.MOVEMENT_SPEED).add(Attributes.ARMOR).add(Attributes.ARMOR_TOUGHNESS);
}
+ public boolean shouldSendAttribute(Attribute attribute) { return true; } // Purpur
@Override
protected void checkFallDamage(double heightDifference, boolean onGround, BlockState state, BlockPos landedPosition) {
@@ -2686,7 +2687,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
}
protected long lastJumpTime = 0L; // Paper
- protected void jumpFromGround() {
+ public void jumpFromGround() { // Purpur - protected -> public
Vec3 vec3d = this.getDeltaMovement();
// Paper start
long time = System.nanoTime();
@@ -3458,8 +3459,10 @@ public abstract class LivingEntity extends Entity implements Attackable {
this.pushEntities();
this.level().getProfiler().pop();
// Paper start
- if (((ServerLevel) this.level()).hasEntityMoveEvent && !(this instanceof net.minecraft.world.entity.player.Player)) {
- if (this.xo != this.getX() || this.yo != this.getY() || this.zo != this.getZ() || this.yRotO != this.getYRot() || this.xRotO != this.getXRot()) {
+ // Purpur start
+ if (this.xo != this.getX() || this.yo != this.getY() || this.zo != this.getZ() || this.yRotO != this.getYRot() || this.xRotO != this.getXRot()) {
+ if (((ServerLevel) this.level()).hasEntityMoveEvent && !(this instanceof net.minecraft.world.entity.player.Player)) {
+ // Purpur end
Location from = new Location(this.level().getWorld(), this.xo, this.yo, this.zo, this.yRotO, this.xRotO);
Location to = new Location (this.level().getWorld(), this.getX(), this.getY(), this.getZ(), this.getYRot(), this.getXRot());
io.papermc.paper.event.entity.EntityMoveEvent event = new io.papermc.paper.event.entity.EntityMoveEvent(this.getBukkitLivingEntity(), from, to.clone());
@@ -3469,6 +3472,21 @@ public abstract class LivingEntity extends Entity implements Attackable {
this.absMoveTo(event.getTo().getX(), event.getTo().getY(), event.getTo().getZ(), event.getTo().getYaw(), event.getTo().getPitch());
}
}
+ // Purpur start
+ if (getRider() != null) {
+ getRider().resetLastActionTime();
+ if (((ServerLevel) level()).hasRidableMoveEvent && this instanceof Mob) {
+ Location from = new Location(level().getWorld(), xo, yo, zo, this.yRotO, this.xRotO);
+ Location to = new Location(level().getWorld(), getX(), getY(), getZ(), this.getYRot(), this.getXRot());
+ org.purpurmc.purpur.event.entity.RidableMoveEvent event = new org.purpurmc.purpur.event.entity.RidableMoveEvent((org.bukkit.entity.Mob) getBukkitLivingEntity(), (Player) getRider().getBukkitEntity(), from, to.clone());
+ if (!event.callEvent()) {
+ absMoveTo(from.getX(), from.getY(), from.getZ(), from.getYaw(), from.getPitch());
+ } else if (!to.equals(event.getTo())) {
+ absMoveTo(to.getX(), to.getY(), to.getZ(), to.getYaw(), to.getPitch());
+ }
+ }
+ }
+ // Purpur end
}
// Paper end
if (!this.level().isClientSide && this.isSensitiveToWater() && this.isInWaterRainOrBubble()) {
diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java
index f6eb032897c6d5d16ab5c8c287e49e189c24571c..1f4c41521c9d6fd781f96c7d9552c8e55bbf347b 100644
--- a/src/main/java/net/minecraft/world/entity/Mob.java
+++ b/src/main/java/net/minecraft/world/entity/Mob.java
@@ -145,8 +145,8 @@ public abstract class Mob extends LivingEntity implements Targeting {
this.restrictRadius = -1.0F;
this.goalSelector = new GoalSelector(world.getProfilerSupplier());
this.targetSelector = new GoalSelector(world.getProfilerSupplier());
- this.lookControl = new LookControl(this);
- this.moveControl = new MoveControl(this);
+ this.lookControl = new org.purpurmc.purpur.controller.LookControllerWASD(this); // Purpur
+ this.moveControl = new org.purpurmc.purpur.controller.MoveControllerWASD(this); // Purpur
this.jumpControl = new JumpControl(this);
this.bodyRotationControl = this.createBodyControl();
this.navigation = this.createNavigation(world);
@@ -1381,7 +1381,7 @@ public abstract class Mob extends LivingEntity implements Targeting {
protected void onOffspringSpawnedFromEgg(Player player, Mob child) {}
protected InteractionResult mobInteract(Player player, InteractionHand hand) {
- return InteractionResult.PASS;
+ return tryRide(player, hand); // Purpur
}
public boolean isWithinRestriction() {
@@ -1759,4 +1759,56 @@ public abstract class Mob extends LivingEntity implements Targeting {
return itemmonsteregg == null ? null : new ItemStack(itemmonsteregg);
}
+
+ // Purpur start
+ public double getMaxY() {
+ return level().getHeight();
+ }
+
+ public InteractionResult tryRide(Player player, InteractionHand hand) {
+ return tryRide(player, hand, InteractionResult.PASS);
+ }
+
+ public InteractionResult tryRide(Player player, InteractionHand hand, InteractionResult result) {
+ if (!isRidable()) {
+ return result;
+ }
+ if (hand != InteractionHand.MAIN_HAND) {
+ return InteractionResult.PASS;
+ }
+ if (player.isShiftKeyDown()) {
+ return InteractionResult.PASS;
+ }
+ if (!player.getItemInHand(hand).isEmpty()) {
+ return InteractionResult.PASS;
+ }
+ if (!passengers.isEmpty() || player.isPassenger()) {
+ return InteractionResult.PASS;
+ }
+ if (this instanceof TamableAnimal tamable) {
+ if (tamable.isTame() && !tamable.isOwnedBy(player)) {
+ return InteractionResult.PASS;
+ }
+ if (!tamable.isTame() && !level().purpurConfig.untamedTamablesAreRidable) {
+ return InteractionResult.PASS;
+ }
+ }
+ if (this instanceof AgeableMob ageable) {
+ if (ageable.isBaby() && !level().purpurConfig.babiesAreRidable) {
+ return InteractionResult.PASS;
+ }
+ }
+ if (!player.getBukkitEntity().hasPermission("allow.ride." + net.minecraft.core.registries.BuiltInRegistries.ENTITY_TYPE.getKey(getType()).getPath())) {
+ player.sendMiniMessage(org.purpurmc.purpur.PurpurConfig.cannotRideMob);
+ return InteractionResult.PASS;
+ }
+ player.setYRot(this.getYRot());
+ player.setXRot(this.getXRot());
+ if (player.startRiding(this)) {
+ return InteractionResult.SUCCESS;
+ } else {
+ return InteractionResult.PASS;
+ }
+ }
+ // Purpur end
}
diff --git a/src/main/java/net/minecraft/world/entity/ai/attributes/AttributeMap.java b/src/main/java/net/minecraft/world/entity/ai/attributes/AttributeMap.java
index e283eb57c25f7de222f9d09dca851169f5f6e488..210a0bee1227e4671909dd553ab22027cfc868fb 100644
--- a/src/main/java/net/minecraft/world/entity/ai/attributes/AttributeMap.java
+++ b/src/main/java/net/minecraft/world/entity/ai/attributes/AttributeMap.java
@@ -24,14 +24,21 @@ public class AttributeMap {
private final Set<AttributeInstance> dirtyAttributes = Sets.newHashSet();
private final AttributeSupplier supplier;
private final java.util.function.Function<Attribute, AttributeInstance> createInstance; // Pufferfish
+ private final net.minecraft.world.entity.LivingEntity entity; // Purpur
public AttributeMap(AttributeSupplier defaultAttributes) {
+ // Purpur start
+ this(defaultAttributes, null);
+ }
+ public AttributeMap(AttributeSupplier defaultAttributes, net.minecraft.world.entity.LivingEntity entity) {
+ this.entity = entity;
+ // Purpur end
this.supplier = defaultAttributes;
this.createInstance = attribute -> this.supplier.createInstance(this::onAttributeModified, attribute); // Pufferfish
}
private void onAttributeModified(AttributeInstance instance) {
- if (instance.getAttribute().isClientSyncable()) {
+ if (instance.getAttribute().isClientSyncable() && (entity == null || entity.shouldSendAttribute(instance.getAttribute()))) { // Purpur
this.dirtyAttributes.add(instance);
}
@@ -43,7 +50,7 @@ public class AttributeMap {
public Collection<AttributeInstance> getSyncableAttributes() {
return this.attributes.values().stream().filter((attribute) -> {
- return attribute.getAttribute().isClientSyncable();
+ return attribute.getAttribute().isClientSyncable() && (entity == null || entity.shouldSendAttribute(attribute.getAttribute())); // Purpur
}).collect(Collectors.toList());
}
diff --git a/src/main/java/net/minecraft/world/entity/ai/attributes/DefaultAttributes.java b/src/main/java/net/minecraft/world/entity/ai/attributes/DefaultAttributes.java
index 8a720f9ae81d7ea856e28cb27a66adcf04bcb0eb..e0b70d9732a2b7d96999b7e4a497ffa1d8cf86a7 100644
--- a/src/main/java/net/minecraft/world/entity/ai/attributes/DefaultAttributes.java
+++ b/src/main/java/net/minecraft/world/entity/ai/attributes/DefaultAttributes.java
@@ -80,7 +80,88 @@ import org.slf4j.Logger;
public class DefaultAttributes {
private static final Logger LOGGER = LogUtils.getLogger();
- private static final Map<EntityType<? extends LivingEntity>, AttributeSupplier> SUPPLIERS = ImmutableMap.<EntityType<? extends LivingEntity>, AttributeSupplier>builder().put(EntityType.ALLAY, Allay.createAttributes().build()).put(EntityType.ARMOR_STAND, LivingEntity.createLivingAttributes().build()).put(EntityType.AXOLOTL, Axolotl.createAttributes().build()).put(EntityType.BAT, Bat.createAttributes().build()).put(EntityType.BEE, Bee.createAttributes().build()).put(EntityType.BLAZE, Blaze.createAttributes().build()).put(EntityType.CAT, Cat.createAttributes().build()).put(EntityType.CAMEL, Camel.createAttributes().build()).put(EntityType.CAVE_SPIDER, CaveSpider.createCaveSpider().build()).put(EntityType.CHICKEN, Chicken.createAttributes().build()).put(EntityType.COD, AbstractFish.createAttributes().build()).put(EntityType.COW, Cow.createAttributes().build()).put(EntityType.CREEPER, Creeper.createAttributes().build()).put(EntityType.DOLPHIN, Dolphin.createAttributes().build()).put(EntityType.DONKEY, AbstractChestedHorse.createBaseChestedHorseAttributes().build()).put(EntityType.DROWNED, Zombie.createAttributes().build()).put(EntityType.ELDER_GUARDIAN, ElderGuardian.createAttributes().build()).put(EntityType.ENDERMAN, EnderMan.createAttributes().build()).put(EntityType.ENDERMITE, Endermite.createAttributes().build()).put(EntityType.ENDER_DRAGON, EnderDragon.createAttributes().build()).put(EntityType.EVOKER, Evoker.createAttributes().build()).put(EntityType.FOX, Fox.createAttributes().build()).put(EntityType.FROG, Frog.createAttributes().build()).put(EntityType.GHAST, Ghast.createAttributes().build()).put(EntityType.GIANT, Giant.createAttributes().build()).put(EntityType.GLOW_SQUID, GlowSquid.createAttributes().build()).put(EntityType.GOAT, Goat.createAttributes().build()).put(EntityType.GUARDIAN, Guardian.createAttributes().build()).put(EntityType.HOGLIN, Hoglin.createAttributes().build()).put(EntityType.HORSE, AbstractHorse.createBaseHorseAttributes().build()).put(EntityType.HUSK, Zombie.createAttributes().build()).put(EntityType.ILLUSIONER, Illusioner.createAttributes().build()).put(EntityType.IRON_GOLEM, IronGolem.createAttributes().build()).put(EntityType.LLAMA, Llama.createAttributes().build()).put(EntityType.MAGMA_CUBE, MagmaCube.createAttributes().build()).put(EntityType.MOOSHROOM, Cow.createAttributes().build()).put(EntityType.MULE, AbstractChestedHorse.createBaseChestedHorseAttributes().build()).put(EntityType.OCELOT, Ocelot.createAttributes().build()).put(EntityType.PANDA, Panda.createAttributes().build()).put(EntityType.PARROT, Parrot.createAttributes().build()).put(EntityType.PHANTOM, Monster.createMonsterAttributes().build()).put(EntityType.PIG, Pig.createAttributes().build()).put(EntityType.PIGLIN, Piglin.createAttributes().build()).put(EntityType.PIGLIN_BRUTE, PiglinBrute.createAttributes().build()).put(EntityType.PILLAGER, Pillager.createAttributes().build()).put(EntityType.PLAYER, Player.createAttributes().build()).put(EntityType.POLAR_BEAR, PolarBear.createAttributes().build()).put(EntityType.PUFFERFISH, AbstractFish.createAttributes().build()).put(EntityType.RABBIT, Rabbit.createAttributes().build()).put(EntityType.RAVAGER, Ravager.createAttributes().build()).put(EntityType.SALMON, AbstractFish.createAttributes().build()).put(EntityType.SHEEP, Sheep.createAttributes().build()).put(EntityType.SHULKER, Shulker.createAttributes().build()).put(EntityType.SILVERFISH, Silverfish.createAttributes().build()).put(EntityType.SKELETON, AbstractSkeleton.createAttributes().build()).put(EntityType.SKELETON_HORSE, SkeletonHorse.createAttributes().build()).put(EntityType.SLIME, Monster.createMonsterAttributes().build()).put(EntityType.SNIFFER, Sniffer.createAttributes().build()).put(EntityType.SNOW_GOLEM, SnowGolem.createAttributes().build()).put(EntityType.SPIDER, Spider.createAttributes().build()).put(EntityType.SQUID, Squid.createAttributes().build()).put(EntityType.STRAY, AbstractSkeleton.createAttributes().build()).put(EntityType.STRIDER, Strider.createAttributes().build()).put(EntityType.TADPOLE, Tadpole.createAttributes().build()).put(EntityType.TRADER_LLAMA, Llama.createAttributes().build()).put(EntityType.TROPICAL_FISH, AbstractFish.createAttributes().build()).put(EntityType.TURTLE, Turtle.createAttributes().build()).put(EntityType.VEX, Vex.createAttributes().build()).put(EntityType.VILLAGER, Villager.createAttributes().build()).put(EntityType.VINDICATOR, Vindicator.createAttributes().build()).put(EntityType.WARDEN, Warden.createAttributes().build()).put(EntityType.WANDERING_TRADER, Mob.createMobAttributes().build()).put(EntityType.WITCH, Witch.createAttributes().build()).put(EntityType.WITHER, WitherBoss.createAttributes().build()).put(EntityType.WITHER_SKELETON, AbstractSkeleton.createAttributes().build()).put(EntityType.WOLF, Wolf.createAttributes().build()).put(EntityType.ZOGLIN, Zoglin.createAttributes().build()).put(EntityType.ZOMBIE, Zombie.createAttributes().build()).put(EntityType.ZOMBIE_HORSE, ZombieHorse.createAttributes().build()).put(EntityType.ZOMBIE_VILLAGER, Zombie.createAttributes().build()).put(EntityType.ZOMBIFIED_PIGLIN, ZombifiedPiglin.createAttributes().build()).build();
+ private static final Map<EntityType<? extends LivingEntity>, AttributeSupplier> SUPPLIERS = ImmutableMap.<EntityType<? extends LivingEntity>, AttributeSupplier>builder()
+ .put(EntityType.ALLAY, Allay.createAttributes().build())
+ .put(EntityType.ARMOR_STAND, LivingEntity.createLivingAttributes().build())
+ .put(EntityType.AXOLOTL, Axolotl.createAttributes().build())
+ .put(EntityType.BAT, Bat.createAttributes().build())
+ .put(EntityType.BEE, Bee.createAttributes().build())
+ .put(EntityType.BLAZE, Blaze.createAttributes().build())
+ .put(EntityType.CAT, Cat.createAttributes().build())
+ .put(EntityType.CAMEL, Camel.createAttributes().build())
+ .put(EntityType.CAVE_SPIDER, CaveSpider.createCaveSpider().build())
+ .put(EntityType.CHICKEN, Chicken.createAttributes().build())
+ .put(EntityType.COD, AbstractFish.createAttributes().build())
+ .put(EntityType.COW, Cow.createAttributes().build())
+ .put(EntityType.CREEPER, Creeper.createAttributes().build())
+ .put(EntityType.DOLPHIN, Dolphin.createAttributes().build())
+ .put(EntityType.DONKEY, AbstractChestedHorse.createBaseChestedHorseAttributes().build())
+ .put(EntityType.DROWNED, Zombie.createAttributes().build())
+ .put(EntityType.ELDER_GUARDIAN, ElderGuardian.createAttributes().build())
+ .put(EntityType.ENDERMAN, EnderMan.createAttributes().build())
+ .put(EntityType.ENDERMITE, Endermite.createAttributes().build())
+ .put(EntityType.ENDER_DRAGON, EnderDragon.createAttributes().build())
+ .put(EntityType.EVOKER, Evoker.createAttributes().build())
+ .put(EntityType.FOX, Fox.createAttributes().build())
+ .put(EntityType.FROG, Frog.createAttributes().build())
+ .put(EntityType.GHAST, Ghast.createAttributes().build())
+ .put(EntityType.GIANT, Giant.createAttributes().build())
+ .put(EntityType.GLOW_SQUID, GlowSquid.createAttributes().build())
+ .put(EntityType.GOAT, Goat.createAttributes().build())
+ .put(EntityType.GUARDIAN, Guardian.createAttributes().build())
+ .put(EntityType.HOGLIN, Hoglin.createAttributes().build())
+ .put(EntityType.HORSE, AbstractHorse.createBaseHorseAttributes().build())
+ .put(EntityType.HUSK, Zombie.createAttributes().build())
+ .put(EntityType.ILLUSIONER, Illusioner.createAttributes().build())
+ .put(EntityType.IRON_GOLEM, IronGolem.createAttributes().build())
+ .put(EntityType.LLAMA, Llama.createAttributes().build())
+ .put(EntityType.MAGMA_CUBE, MagmaCube.createAttributes().build())
+ .put(EntityType.MOOSHROOM, Cow.createAttributes().build())
+ .put(EntityType.MULE, AbstractChestedHorse.createBaseChestedHorseAttributes().build())
+ .put(EntityType.OCELOT, Ocelot.createAttributes().build())
+ .put(EntityType.PANDA, Panda.createAttributes().build())
+ .put(EntityType.PARROT, Parrot.createAttributes().build())
+ .put(EntityType.PHANTOM, net.minecraft.world.entity.monster.Phantom.createAttributes().build()) // Purpur
+ .put(EntityType.PIG, Pig.createAttributes().build())
+ .put(EntityType.PIGLIN, Piglin.createAttributes().build())
+ .put(EntityType.PIGLIN_BRUTE, PiglinBrute.createAttributes().build())
+ .put(EntityType.PILLAGER, Pillager.createAttributes().build())
+ .put(EntityType.PLAYER, Player.createAttributes().build())
+ .put(EntityType.POLAR_BEAR, PolarBear.createAttributes().build())
+ .put(EntityType.PUFFERFISH, AbstractFish.createAttributes().build())
+ .put(EntityType.RABBIT, Rabbit.createAttributes().build())
+ .put(EntityType.RAVAGER, Ravager.createAttributes().build())
+ .put(EntityType.SALMON, AbstractFish.createAttributes().build())
+ .put(EntityType.SHEEP, Sheep.createAttributes().build())
+ .put(EntityType.SHULKER, Shulker.createAttributes().build())
+ .put(EntityType.SILVERFISH, Silverfish.createAttributes().build())
+ .put(EntityType.SKELETON, AbstractSkeleton.createAttributes().build())
+ .put(EntityType.SKELETON_HORSE, SkeletonHorse.createAttributes().build())
+ .put(EntityType.SLIME, Monster.createMonsterAttributes().build())
+ .put(EntityType.SNIFFER, Sniffer.createAttributes().build())
+ .put(EntityType.SNOW_GOLEM, SnowGolem.createAttributes().build())
+ .put(EntityType.SPIDER, Spider.createAttributes().build())
+ .put(EntityType.SQUID, Squid.createAttributes().build())
+ .put(EntityType.STRAY, AbstractSkeleton.createAttributes().build())
+ .put(EntityType.STRIDER, Strider.createAttributes().build())
+ .put(EntityType.TADPOLE, Tadpole.createAttributes().build())
+ .put(EntityType.TRADER_LLAMA, Llama.createAttributes().build())
+ .put(EntityType.TROPICAL_FISH, AbstractFish.createAttributes().build())
+ .put(EntityType.TURTLE, Turtle.createAttributes().build())
+ .put(EntityType.VEX, Vex.createAttributes().build())
+ .put(EntityType.VILLAGER, Villager.createAttributes().build())
+ .put(EntityType.VINDICATOR, Vindicator.createAttributes().build())
+ .put(EntityType.WARDEN, Warden.createAttributes().build())
+ .put(EntityType.WANDERING_TRADER, Mob.createMobAttributes().build())
+ .put(EntityType.WITCH, Witch.createAttributes().build())
+ .put(EntityType.WITHER, WitherBoss.createAttributes().build())
+ .put(EntityType.WITHER_SKELETON, AbstractSkeleton.createAttributes().build())
+ .put(EntityType.WOLF, Wolf.createAttributes().build())
+ .put(EntityType.ZOGLIN, Zoglin.createAttributes().build())
+ .put(EntityType.ZOMBIE, Zombie.createAttributes().build())
+ .put(EntityType.ZOMBIE_HORSE, ZombieHorse.createAttributes().build())
+ .put(EntityType.ZOMBIE_VILLAGER, Zombie.createAttributes().build())
+ .put(EntityType.ZOMBIFIED_PIGLIN, ZombifiedPiglin.createAttributes().build()).build();
public static AttributeSupplier getSupplier(EntityType<? extends LivingEntity> type) {
return SUPPLIERS.get(type);
diff --git a/src/main/java/net/minecraft/world/entity/ai/control/MoveControl.java b/src/main/java/net/minecraft/world/entity/ai/control/MoveControl.java
index 03d77c6d4697d4934ca65e3329982f0efe089820..ce7e3e90993b76225dc820a21e04267e488f5f7b 100644
--- a/src/main/java/net/minecraft/world/entity/ai/control/MoveControl.java
+++ b/src/main/java/net/minecraft/world/entity/ai/control/MoveControl.java
@@ -29,6 +29,20 @@ public class MoveControl implements Control {
this.mob = entity;
}
+ // Purpur start
+ public void setSpeedModifier(double speed) {
+ this.speedModifier = speed;
+ }
+
+ public void setForward(float forward) {
+ this.strafeForwards = forward;
+ }
+
+ public void setStrafe(float strafe) {
+ this.strafeRight = strafe;
+ }
+ // Purpur end
+
public boolean hasWanted() {
return this.operation == MoveControl.Operation.MOVE_TO;
}
diff --git a/src/main/java/net/minecraft/world/entity/ai/control/SmoothSwimmingLookControl.java b/src/main/java/net/minecraft/world/entity/ai/control/SmoothSwimmingLookControl.java
index 7df56705a4a0de2dc4ff7ab133fc26612c219162..384bed4505b6cabb1ae151cd2c4eb5e56f24563f 100644
--- a/src/main/java/net/minecraft/world/entity/ai/control/SmoothSwimmingLookControl.java
+++ b/src/main/java/net/minecraft/world/entity/ai/control/SmoothSwimmingLookControl.java
@@ -3,7 +3,7 @@ package net.minecraft.world.entity.ai.control;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Mob;
-public class SmoothSwimmingLookControl extends LookControl {
+public class SmoothSwimmingLookControl extends org.purpurmc.purpur.controller.LookControllerWASD { // Purpur
private final int maxYRotFromCenter;
private static final int HEAD_TILT_X = 10;
private static final int HEAD_TILT_Y = 20;
@@ -14,7 +14,7 @@ public class SmoothSwimmingLookControl extends LookControl {
}
@Override
- public void tick() {
+ public void vanillaTick() { // Purpur
if (this.lookAtCooldown > 0) {
--this.lookAtCooldown;
this.getYRotD().ifPresent((yaw) -> {
diff --git a/src/main/java/net/minecraft/world/entity/ambient/Bat.java b/src/main/java/net/minecraft/world/entity/ambient/Bat.java
index 2c91fe46355c9a201507de5577f693ed4f5fb974..e184d2a89a89d4bf77a32a2d610175c5bbd38a03 100644
--- a/src/main/java/net/minecraft/world/entity/ambient/Bat.java
+++ b/src/main/java/net/minecraft/world/entity/ambient/Bat.java
@@ -18,6 +18,7 @@ import net.minecraft.world.entity.EntityDimensions;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.MobSpawnType;
+import net.minecraft.world.entity.MoverType;
import net.minecraft.world.entity.Pose;
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
import net.minecraft.world.entity.ai.attributes.Attributes;
@@ -43,12 +44,59 @@ public class Bat extends AmbientCreature {
public Bat(EntityType<? extends Bat> type, Level world) {
super(type, world);
+ this.moveControl = new org.purpurmc.purpur.controller.FlyingWithSpacebarMoveControllerWASD(this, 0.075F); // Purpur
if (!world.isClientSide) {
this.setResting(true);
}
}
+ // Purpur start
+ @Override
+ public boolean shouldSendAttribute(net.minecraft.world.entity.ai.attributes.Attribute attribute) { return attribute != Attributes.FLYING_SPEED; } // Fixes log spam on clients
+
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.batRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.batRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.batControllable;
+ }
+
+ @Override
+ public double getMaxY() {
+ return level().purpurConfig.batMaxY;
+ }
+
+ @Override
+ public void onMount(Player rider) {
+ super.onMount(rider);
+ if (isResting()) {
+ setResting(false);
+ level().levelEvent(null, 1025, new BlockPos(this).above(), 0);
+ }
+ }
+
+ @Override
+ public void travel(Vec3 vec3) {
+ super.travel(vec3);
+ if (getRider() != null && this.isControllable() && !onGround) {
+ float speed = (float) getAttributeValue(Attributes.FLYING_SPEED) * 2;
+ setSpeed(speed);
+ Vec3 mot = getDeltaMovement();
+ move(MoverType.SELF, mot.multiply(speed, 0.25, speed));
+ setDeltaMovement(mot.scale(0.9D));
+ }
+ }
+ // Purpur end
+
@Override
public boolean isFlapping() {
return !this.isResting() && this.tickCount % Bat.TICKS_PER_FLAP == 0;
@@ -98,7 +146,7 @@ public class Bat extends AmbientCreature {
protected void pushEntities() {}
public static AttributeSupplier.Builder createAttributes() {
- return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 6.0D);
+ return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 6.0D).add(Attributes.FLYING_SPEED, 0.6D); // Purpur
}
public boolean isResting() {
@@ -130,6 +178,14 @@ public class Bat extends AmbientCreature {
@Override
protected void customServerAiStep() {
+ // Purpur start
+ if (getRider() != null && this.isControllable()) {
+ Vec3 mot = getDeltaMovement();
+ setDeltaMovement(mot.x(), mot.y() + (getVerticalMot() > 0 ? 0.07D : 0.0D), mot.z());
+ return;
+ }
+ // Purpur end
+
super.customServerAiStep();
BlockPos blockposition = this.blockPosition();
BlockPos blockposition1 = blockposition.above();
diff --git a/src/main/java/net/minecraft/world/entity/animal/AbstractFish.java b/src/main/java/net/minecraft/world/entity/animal/AbstractFish.java
index 2249fc6dd98afb8d52623b5864955fdd3b3fc042..2ccfaab0a02cf5ff9779e250fb79a75a9852e10d 100644
--- a/src/main/java/net/minecraft/world/entity/animal/AbstractFish.java
+++ b/src/main/java/net/minecraft/world/entity/animal/AbstractFish.java
@@ -94,7 +94,7 @@ public abstract class AbstractFish extends WaterAnimal implements Bucketable {
@Override
protected void registerGoals() {
super.registerGoals();
- this.goalSelector.addGoal(0, new PanicGoal(this, 1.25D));
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.goalSelector.addGoal(2, new AvoidEntityGoal<>(this, Player.class, 8.0F, 1.6D, 1.4D, EntitySelector.NO_SPECTATORS::test));
this.goalSelector.addGoal(4, new AbstractFish.FishSwimGoal(this));
}
@@ -107,7 +107,7 @@ public abstract class AbstractFish extends WaterAnimal implements Bucketable {
@Override
public void travel(Vec3 movementInput) {
if (this.isEffectiveAi() && this.isInWater()) {
- this.moveRelative(0.01F, movementInput);
+ this.moveRelative(getRider() != null ? getSpeed() : 0.01F, movementInput); // Purpur
this.move(MoverType.SELF, this.getDeltaMovement());
this.setDeltaMovement(this.getDeltaMovement().scale(0.9D));
if (this.getTarget() == null) {
@@ -166,7 +166,7 @@ public abstract class AbstractFish extends WaterAnimal implements Bucketable {
protected void playStepSound(BlockPos pos, BlockState state) {
}
- static class FishMoveControl extends MoveControl {
+ static class FishMoveControl extends org.purpurmc.purpur.controller.WaterMoveControllerWASD { // Purpur
private final AbstractFish fish;
FishMoveControl(AbstractFish owner) {
@@ -174,14 +174,22 @@ public abstract class AbstractFish extends WaterAnimal implements Bucketable {
this.fish = owner;
}
+ // Purpur start
@Override
- public void tick() {
+ public void purpurTick(Player rider) {
+ super.purpurTick(rider);
+ fish.setDeltaMovement(fish.getDeltaMovement().add(0.0D, 0.005D, 0.0D));
+ }
+ // Purpur end
+
+ @Override
+ public void vanillaTick() { // Purpur
if (this.fish.isEyeInFluid(FluidTags.WATER)) {
this.fish.setDeltaMovement(this.fish.getDeltaMovement().add(0.0D, 0.005D, 0.0D));
}
if (this.operation == MoveControl.Operation.MOVE_TO && !this.fish.getNavigation().isDone()) {
- float f = (float)(this.speedModifier * this.fish.getAttributeValue(Attributes.MOVEMENT_SPEED));
+ float f = (float)(this.getSpeedModifier() * this.fish.getAttributeValue(Attributes.MOVEMENT_SPEED)); // Purpur
this.fish.setSpeed(Mth.lerp(0.125F, this.fish.getSpeed(), f));
double d = this.wantedX - this.fish.getX();
double e = this.wantedY - this.fish.getY();
diff --git a/src/main/java/net/minecraft/world/entity/animal/Bee.java b/src/main/java/net/minecraft/world/entity/animal/Bee.java
index 55026e1731e41b4e3e4c6a8fef5d96a32051a556..706ae64b894709601dccfb621d3c215f073e98e9 100644
--- a/src/main/java/net/minecraft/world/entity/animal/Bee.java
+++ b/src/main/java/net/minecraft/world/entity/animal/Bee.java
@@ -43,6 +43,7 @@ import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.MobType;
+import net.minecraft.world.entity.MoverType;
import net.minecraft.world.entity.NeutralMob;
import net.minecraft.world.entity.PathfinderMob;
import net.minecraft.world.entity.Pose;
@@ -147,6 +148,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal {
public Bee(EntityType<? extends Bee> type, Level world) {
super(type, world);
this.remainingCooldownBeforeLocatingNewFlower = Mth.nextInt(this.random, 20, 60);
+ final org.purpurmc.purpur.controller.FlyingMoveControllerWASD flyingController = new org.purpurmc.purpur.controller.FlyingMoveControllerWASD(this, 0.25F, 1.0F, false); // Purpur
// Paper start - apply gravity to bees when they get stuck in the void, fixes MC-167279
class BeeFlyingMoveControl extends FlyingMoveControl {
public BeeFlyingMoveControl(final Mob entity, final int maxPitchChange, final boolean noGravity) {
@@ -155,11 +157,24 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal {
@Override
public void tick() {
+ // Purpur start
+ if (mob.getRider() != null && mob.isControllable()) {
+ flyingController.purpurTick(mob.getRider());
+ return;
+ }
+ // Purpur end
if (this.mob.getY() <= Bee.this.level().getMinBuildHeight()) {
this.mob.setNoGravity(false);
}
super.tick();
}
+
+ // Purpur start
+ @Override
+ public boolean hasWanted() {
+ return mob.getRider() != null || !mob.isControllable() || super.hasWanted();
+ }
+ // Purpur end
}
this.moveControl = new BeeFlyingMoveControl(this, 20, true);
// Paper end
@@ -171,6 +186,40 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal {
this.setPathfindingMalus(BlockPathTypes.FENCE, -1.0F);
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.beeRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.beeRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.beeControllable;
+ }
+
+ @Override
+ public double getMaxY() {
+ return level().purpurConfig.beeMaxY;
+ }
+
+ @Override
+ public void travel(Vec3 vec3) {
+ super.travel(vec3);
+ if (getRider() != null && this.isControllable() && !onGround) {
+ float speed = (float) getAttributeValue(Attributes.FLYING_SPEED) * 2;
+ setSpeed(speed);
+ Vec3 mot = getDeltaMovement();
+ move(MoverType.SELF, mot.multiply(speed, speed, speed));
+ setDeltaMovement(mot.scale(0.9D));
+ }
+ }
+ // Purpur end
+
@Override
protected void defineSynchedData() {
super.defineSynchedData();
@@ -185,6 +234,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal {
@Override
protected void registerGoals() {
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.goalSelector.addGoal(0, new Bee.BeeAttackGoal(this, 1.399999976158142D, true));
this.goalSelector.addGoal(1, new Bee.BeeEnterHiveGoal());
this.goalSelector.addGoal(2, new BreedGoal(this, 1.0D));
@@ -200,6 +250,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal {
this.goalSelector.addGoal(7, new Bee.BeeGrowCropGoal());
this.goalSelector.addGoal(8, new Bee.BeeWanderGoal());
this.goalSelector.addGoal(9, new FloatGoal(this));
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.targetSelector.addGoal(1, (new Bee.BeeHurtByOtherGoal(this)).setAlertOthers(new Class[0]));
this.targetSelector.addGoal(2, new Bee.BeeBecomeAngryTargetGoal(this));
this.targetSelector.addGoal(3, new ResetUniversalAngerTargetGoal<>(this, true));
@@ -881,16 +932,16 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal {
}
}
- private class BeeLookControl extends LookControl {
+ private class BeeLookControl extends org.purpurmc.purpur.controller.LookControllerWASD { // Purpur
BeeLookControl(Mob entity) {
super(entity);
}
@Override
- public void tick() {
+ public void vanillaTick() { // Purpur
if (!Bee.this.isAngry()) {
- super.tick();
+ super.vanillaTick(); // Purpur
}
}
diff --git a/src/main/java/net/minecraft/world/entity/animal/Cat.java b/src/main/java/net/minecraft/world/entity/animal/Cat.java
index 90ce201bc7c47cef9bc59d7b535a7453854bac75..90ec526b135a6de6e7ba16fdd3a113ef92b42ef6 100644
--- a/src/main/java/net/minecraft/world/entity/animal/Cat.java
+++ b/src/main/java/net/minecraft/world/entity/animal/Cat.java
@@ -97,6 +97,31 @@ public class Cat extends TamableAnimal implements VariantHolder<CatVariant> {
super(type, world);
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.catRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.catRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.catControllable;
+ }
+
+ @Override
+ public void onMount(Player rider) {
+ super.onMount(rider);
+ setInSittingPose(false);
+ setLying(false);
+ setRelaxStateOne(false);
+ }
+ // Purpur end
+
public ResourceLocation getResourceLocation() {
return this.getVariant().texture();
}
@@ -105,6 +130,7 @@ public class Cat extends TamableAnimal implements VariantHolder<CatVariant> {
protected void registerGoals() {
this.temptGoal = new Cat.CatTemptGoal(this, 0.6D, Cat.TEMPT_INGREDIENT, true);
this.goalSelector.addGoal(1, new FloatGoal(this));
+ this.goalSelector.addGoal(1, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.goalSelector.addGoal(1, new PanicGoal(this, 1.5D));
this.goalSelector.addGoal(2, new SitWhenOrderedToGoal(this));
this.goalSelector.addGoal(3, new Cat.CatRelaxOnOwnerGoal(this));
@@ -117,6 +143,7 @@ public class Cat extends TamableAnimal implements VariantHolder<CatVariant> {
this.goalSelector.addGoal(10, new BreedGoal(this, 0.8D));
this.goalSelector.addGoal(11, new WaterAvoidingRandomStrollGoal(this, 0.8D, 1.0000001E-5F));
this.goalSelector.addGoal(12, new LookAtPlayerGoal(this, Player.class, 10.0F));
+ this.targetSelector.addGoal(1, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.targetSelector.addGoal(1, new NonTameRandomTargetGoal<>(this, Rabbit.class, false, (Predicate) null));
this.targetSelector.addGoal(1, new NonTameRandomTargetGoal<>(this, Turtle.class, false, Turtle.BABY_ON_LAND_SELECTOR));
}
@@ -373,6 +400,7 @@ public class Cat extends TamableAnimal implements VariantHolder<CatVariant> {
@Override
public InteractionResult mobInteract(Player player, InteractionHand hand) {
+ if (getRider() != null) return InteractionResult.PASS; // Purpur
ItemStack itemstack = player.getItemInHand(hand);
Item item = itemstack.getItem();
diff --git a/src/main/java/net/minecraft/world/entity/animal/Chicken.java b/src/main/java/net/minecraft/world/entity/animal/Chicken.java
index 84cefbc339ca368ac306d521012ce2f826a660b5..600b7b4e510871de0dee4051158d344734f04966 100644
--- a/src/main/java/net/minecraft/world/entity/animal/Chicken.java
+++ b/src/main/java/net/minecraft/world/entity/animal/Chicken.java
@@ -54,9 +54,27 @@ public class Chicken extends Animal {
this.setPathfindingMalus(BlockPathTypes.WATER, 0.0F);
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.chickenRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.chickenRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.chickenControllable;
+ }
+ // Purpur end
+
@Override
protected void registerGoals() {
this.goalSelector.addGoal(0, new FloatGoal(this));
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.goalSelector.addGoal(1, new PanicGoal(this, 1.4D));
this.goalSelector.addGoal(2, new BreedGoal(this, 1.0D));
this.goalSelector.addGoal(3, new TemptGoal(this, 1.0D, Chicken.FOOD_ITEMS, false));
diff --git a/src/main/java/net/minecraft/world/entity/animal/Cod.java b/src/main/java/net/minecraft/world/entity/animal/Cod.java
index 824e5e4fe7619ae46061c3c978c9a044db8c84ab..e2a98b45e56a368de19bb65e304370a5998c7cb9 100644
--- a/src/main/java/net/minecraft/world/entity/animal/Cod.java
+++ b/src/main/java/net/minecraft/world/entity/animal/Cod.java
@@ -13,6 +13,18 @@ public class Cod extends AbstractSchoolingFish {
super(type, world);
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.codRidable;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.codControllable;
+ }
+ // Purpur end
+
@Override
public ItemStack getBucketItemStack() {
return new ItemStack(Items.COD_BUCKET);
diff --git a/src/main/java/net/minecraft/world/entity/animal/Cow.java b/src/main/java/net/minecraft/world/entity/animal/Cow.java
index 0263acf81330ada1bb8ae7d52a3b04e08d0c4dcb..11082cf272b72123d8917baa9a0d0e5367dca66b 100644
--- a/src/main/java/net/minecraft/world/entity/animal/Cow.java
+++ b/src/main/java/net/minecraft/world/entity/animal/Cow.java
@@ -42,9 +42,27 @@ public class Cow extends Animal {
super(type, world);
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.cowRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.cowRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.cowControllable;
+ }
+ // Purpur end
+
@Override
protected void registerGoals() {
this.goalSelector.addGoal(0, new FloatGoal(this));
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.goalSelector.addGoal(1, new PanicGoal(this, 2.0D));
this.goalSelector.addGoal(2, new BreedGoal(this, 1.0D));
this.goalSelector.addGoal(3, new TemptGoal(this, 1.25D, Ingredient.of(Items.WHEAT), false));
@@ -85,6 +103,7 @@ public class Cow extends Animal {
@Override
public InteractionResult mobInteract(Player player, InteractionHand hand) {
+ if (getRider() != null) return InteractionResult.PASS; // Purpur
ItemStack itemstack = player.getItemInHand(hand);
if (itemstack.is(Items.BUCKET) && !this.isBaby()) {
@@ -92,7 +111,7 @@ public class Cow extends Animal {
PlayerBucketFillEvent event = CraftEventFactory.callPlayerBucketFillEvent((ServerLevel) player.level(), player, this.blockPosition(), this.blockPosition(), null, itemstack, Items.MILK_BUCKET, hand);
if (event.isCancelled()) {
- return InteractionResult.PASS;
+ return tryRide(player, hand); // Purpur
}
// CraftBukkit end
diff --git a/src/main/java/net/minecraft/world/entity/animal/Dolphin.java b/src/main/java/net/minecraft/world/entity/animal/Dolphin.java
index 8448c5d778998390cf2b683f36e4e18ca7ffdc34..3dab78e2f39855f71bb7294521be71b3faa59b81 100644
--- a/src/main/java/net/minecraft/world/entity/animal/Dolphin.java
+++ b/src/main/java/net/minecraft/world/entity/animal/Dolphin.java
@@ -82,14 +82,82 @@ public class Dolphin extends WaterAnimal {
public static final Predicate<ItemEntity> ALLOWED_ITEMS = (entityitem) -> {
return !entityitem.hasPickUpDelay() && entityitem.isAlive() && entityitem.isInWater();
};
+ private int spitCooldown; // Purpur
public Dolphin(EntityType<? extends Dolphin> type, Level world) {
super(type, world);
- this.moveControl = new SmoothSwimmingMoveControl(this, 85, 10, 0.02F, 0.1F, true);
+ // Purpur start
+ class DolphinMoveControl extends SmoothSwimmingMoveControl {
+ private final org.purpurmc.purpur.controller.WaterMoveControllerWASD waterMoveControllerWASD;
+ private final Dolphin dolphin;
+
+ public DolphinMoveControl(Dolphin dolphin, int pitchChange, int yawChange, float speedInWater, float speedInAir, boolean buoyant) {
+ super(dolphin, pitchChange, yawChange, speedInWater, speedInAir, buoyant);
+ this.dolphin = dolphin;
+ this.waterMoveControllerWASD = new org.purpurmc.purpur.controller.WaterMoveControllerWASD(dolphin);
+ }
+
+ @Override
+ public void tick() {
+ if (dolphin.getRider() != null && dolphin.isControllable()) {
+ purpurTick(dolphin.getRider());
+ } else {
+ super.tick();
+ }
+ }
+
+ public void purpurTick(Player rider) {
+ if (dolphin.getAirSupply() < 150) {
+ // if drowning override player WASD controls to find air
+ super.tick();
+ } else {
+ waterMoveControllerWASD.purpurTick(rider);
+ dolphin.setDeltaMovement(dolphin.getDeltaMovement().add(0.0D, 0.005D, 0.0D));
+ }
+ }
+ };
+ this.moveControl = new DolphinMoveControl(this, 85, 10, 0.02F, 0.1F, true);
+ // Purpur end
this.lookControl = new SmoothSwimmingLookControl(this, 10);
this.setCanPickUpLoot(true);
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.dolphinRidable;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.dolphinControllable;
+ }
+
+ @Override
+ public boolean onSpacebar() {
+ if (spitCooldown == 0 && getRider() != null) {
+ spitCooldown = level().purpurConfig.dolphinSpitCooldown;
+
+ org.bukkit.craftbukkit.entity.CraftPlayer player = (org.bukkit.craftbukkit.entity.CraftPlayer) getRider().getBukkitEntity();
+ if (!player.hasPermission("allow.special.dolphin")) {
+ return false;
+ }
+
+ org.bukkit.Location loc = player.getEyeLocation();
+ loc.setPitch(loc.getPitch() - 10);
+ org.bukkit.util.Vector target = loc.getDirection().normalize().multiply(10).add(loc.toVector());
+
+ org.purpurmc.purpur.entity.DolphinSpit spit = new org.purpurmc.purpur.entity.DolphinSpit(level(), this);
+ spit.shoot(target.getX() - getX(), target.getY() - getY(), target.getZ() - getZ(), level().purpurConfig.dolphinSpitSpeed, 5.0F);
+
+ level().addFreshEntity(spit);
+ playSound(SoundEvents.DOLPHIN_ATTACK, 1.0F, 1.0F + (random.nextFloat() - random.nextFloat()) * 0.2F);
+ return true;
+ }
+ return false;
+ }
+ // Purpur end
+
@Nullable
@Override
public SpawnGroupData finalizeSpawn(ServerLevelAccessor world, DifficultyInstance difficulty, MobSpawnType spawnReason, @Nullable SpawnGroupData entityData, @Nullable CompoundTag entityNbt) {
@@ -164,6 +232,7 @@ public class Dolphin extends WaterAnimal {
protected void registerGoals() {
this.goalSelector.addGoal(0, new BreathAirGoal(this));
this.goalSelector.addGoal(0, new TryFindWaterGoal(this));
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.goalSelector.addGoal(1, new Dolphin.DolphinSwimToTreasureGoal(this));
this.goalSelector.addGoal(2, new Dolphin.DolphinSwimWithPlayerGoal(this, 4.0D));
this.goalSelector.addGoal(4, new RandomSwimmingGoal(this, 1.0D, 10));
@@ -174,6 +243,7 @@ public class Dolphin extends WaterAnimal {
this.goalSelector.addGoal(8, new Dolphin.PlayWithItemsGoal());
this.goalSelector.addGoal(8, new FollowBoatGoal(this));
this.goalSelector.addGoal(9, new AvoidEntityGoal<>(this, Guardian.class, 8.0F, 1.0D, 1.0D));
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.targetSelector.addGoal(1, (new HurtByTargetGoal(this, new Class[]{Guardian.class})).setAlertOthers());
}
@@ -225,7 +295,7 @@ public class Dolphin extends WaterAnimal {
@Override
protected boolean canRide(Entity entity) {
- return true;
+ return boardingCooldown <= 0; // Purpur - make dolphin honor ride cooldown like all other non-boss mobs;
}
@Override
@@ -260,6 +330,11 @@ public class Dolphin extends WaterAnimal {
@Override
public void tick() {
super.tick();
+ // Purpur start
+ if (spitCooldown > 0) {
+ spitCooldown--;
+ }
+ // Purpur end
if (this.isNoAi()) {
this.setAirSupply(this.getMaxAirSupply());
} else {
diff --git a/src/main/java/net/minecraft/world/entity/animal/Fox.java b/src/main/java/net/minecraft/world/entity/animal/Fox.java
index 62604ed2e82afd3603ccac5d9c6e285c431542b1..e0a21a0e8ff29f5b634fcb312881a1a93c6c3a44 100644
--- a/src/main/java/net/minecraft/world/entity/animal/Fox.java
+++ b/src/main/java/net/minecraft/world/entity/animal/Fox.java
@@ -141,6 +141,44 @@ public class Fox extends Animal implements VariantHolder<Fox.Type> {
this.setCanPickUpLoot(true);
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.foxRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.foxRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.foxControllable;
+ }
+
+ @Override
+ public float getJumpPower() {
+ return getRider() != null && this.isControllable() ? 0.5F : super.getJumpPower();
+ }
+
+ @Override
+ public void onMount(Player rider) {
+ super.onMount(rider);
+ setCanPickUpLoot(false);
+ clearStates();
+ setIsPouncing(false);
+ spitOutItem(getItemBySlot(EquipmentSlot.MAINHAND));
+ setItemSlot(EquipmentSlot.MAINHAND, ItemStack.EMPTY);
+ }
+
+ @Override
+ public void onDismount(Player rider) {
+ super.onDismount(rider);
+ setCanPickUpLoot(true);
+ }
+ // Purpur end
+
@Override
protected void defineSynchedData() {
super.defineSynchedData();
@@ -160,6 +198,7 @@ public class Fox extends Animal implements VariantHolder<Fox.Type> {
return entityliving instanceof AbstractSchoolingFish;
});
this.goalSelector.addGoal(0, new Fox.FoxFloatGoal());
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.goalSelector.addGoal(0, new ClimbOnTopOfPowderSnowGoal(this, this.level()));
this.goalSelector.addGoal(1, new Fox.FaceplantGoal());
this.goalSelector.addGoal(2, new Fox.FoxPanicGoal(2.2D));
@@ -186,6 +225,7 @@ public class Fox extends Animal implements VariantHolder<Fox.Type> {
this.goalSelector.addGoal(11, new Fox.FoxSearchForItemsGoal());
this.goalSelector.addGoal(12, new Fox.FoxLookAtPlayerGoal(this, Player.class, 24.0F));
this.goalSelector.addGoal(13, new Fox.PerchAndSearchGoal());
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.targetSelector.addGoal(3, new Fox.DefendTrustedTargetGoal(LivingEntity.class, false, false, (entityliving) -> {
return Fox.TRUSTED_TARGET_SELECTOR.test(entityliving) && !this.trusts(entityliving.getUUID());
}));
@@ -758,16 +798,16 @@ public class Fox extends Animal implements VariantHolder<Fox.Type> {
return new Vec3(0.0D, (double) (0.55F * this.getEyeHeight()), (double) (this.getBbWidth() * 0.4F));
}
- public class FoxLookControl extends LookControl {
+ public class FoxLookControl extends org.purpurmc.purpur.controller.LookControllerWASD { // Purpur
public FoxLookControl() {
super(Fox.this);
}
@Override
- public void tick() {
+ public void vanillaTick() { // Purpur
if (!Fox.this.isSleeping()) {
- super.tick();
+ super.vanillaTick(); // Purpur
}
}
@@ -778,16 +818,16 @@ public class Fox extends Animal implements VariantHolder<Fox.Type> {
}
}
- private class FoxMoveControl extends MoveControl {
+ private class FoxMoveControl extends org.purpurmc.purpur.controller.MoveControllerWASD { // Purpur
public FoxMoveControl() {
super(Fox.this);
}
@Override
- public void tick() {
+ public void vanillaTick() { // Purpur
if (Fox.this.canMove()) {
- super.tick();
+ super.vanillaTick(); // Purpur
}
}
diff --git a/src/main/java/net/minecraft/world/entity/animal/IronGolem.java b/src/main/java/net/minecraft/world/entity/animal/IronGolem.java
index f383928fc5b331ddf128bdcb6a23010d8fe088d3..6815d7350a82c6d32f60aa6116466ebd06a920f9 100644
--- a/src/main/java/net/minecraft/world/entity/animal/IronGolem.java
+++ b/src/main/java/net/minecraft/world/entity/animal/IronGolem.java
@@ -66,8 +66,27 @@ public class IronGolem extends AbstractGolem implements NeutralMob {
this.setMaxUpStep(1.0F);
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.ironGolemRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.ironGolemRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.ironGolemControllable;
+ }
+ // Purpur end
+
@Override
protected void registerGoals() {
+ if (level().purpurConfig.ironGolemCanSwim) this.goalSelector.addGoal(0, new net.minecraft.world.entity.ai.goal.FloatGoal(this)); // Purpur
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.goalSelector.addGoal(1, new MeleeAttackGoal(this, 1.0D, true));
this.goalSelector.addGoal(2, new MoveTowardsTargetGoal(this, 0.9D, 32.0F));
this.goalSelector.addGoal(2, new MoveBackToVillageGoal(this, 0.6D, false));
@@ -75,6 +94,7 @@ public class IronGolem extends AbstractGolem implements NeutralMob {
this.goalSelector.addGoal(5, new OfferFlowerGoal(this));
this.goalSelector.addGoal(7, new LookAtPlayerGoal(this, Player.class, 6.0F));
this.goalSelector.addGoal(8, new RandomLookAroundGoal(this));
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.targetSelector.addGoal(1, new DefendVillageTargetGoal(this));
this.targetSelector.addGoal(2, new HurtByTargetGoal(this, new Class[0]));
this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, this::isAngryAt));
@@ -270,13 +290,13 @@ public class IronGolem extends AbstractGolem implements NeutralMob {
ItemStack itemstack = player.getItemInHand(hand);
if (!itemstack.is(Items.IRON_INGOT)) {
- return InteractionResult.PASS;
+ return tryRide(player, hand); // Purpur
} else {
float f = this.getHealth();
this.heal(25.0F);
if (this.getHealth() == f) {
- return InteractionResult.PASS;
+ return tryRide(player, hand); // Purpur
} else {
float f1 = 1.0F + (this.random.nextFloat() - this.random.nextFloat()) * 0.2F;
diff --git a/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java b/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java
index a04374f91f2fbb31219d86b6ae63bcf8fdf7318c..7b0e3b46e3d318ee856d53cfc5532f3432447438 100644
--- a/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java
+++ b/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java
@@ -63,6 +63,23 @@ public class MushroomCow extends Cow implements Shearable, VariantHolder<Mushroo
super(type, world);
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.mooshroomRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.mooshroomRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.mooshroomControllable;
+ }
+ // Purpur end
+
@Override
public float getWalkTargetValue(BlockPos pos, LevelReader world) {
return world.getBlockState(pos.below()).is(Blocks.MYCELIUM) ? 10.0F : world.getPathfindingCostFromLightLevels(pos);
@@ -124,7 +141,7 @@ public class MushroomCow extends Cow implements Shearable, VariantHolder<Mushroo
} else if (itemstack.is(Items.SHEARS) && this.readyForShearing()) {
// CraftBukkit start
if (!CraftEventFactory.handlePlayerShearEntityEvent(player, this, itemstack, hand)) {
- return InteractionResult.PASS;
+ return tryRide(player, hand); // Purpur
}
// CraftBukkit end
this.shear(SoundSource.PLAYERS);
@@ -145,7 +162,7 @@ public class MushroomCow extends Cow implements Shearable, VariantHolder<Mushroo
Optional<Pair<MobEffect, Integer>> optional = this.getEffectFromItemStack(itemstack);
if (!optional.isPresent()) {
- return InteractionResult.PASS;
+ return tryRide(player, hand); // Purpur
}
Pair<MobEffect, Integer> pair = (Pair) optional.get();
diff --git a/src/main/java/net/minecraft/world/entity/animal/Ocelot.java b/src/main/java/net/minecraft/world/entity/animal/Ocelot.java
index 924ea172cab5178e5754bfe09cc7b83c1a66faa6..c25549d516072bdb362f78efce3da730e08f3ccc 100644
--- a/src/main/java/net/minecraft/world/entity/animal/Ocelot.java
+++ b/src/main/java/net/minecraft/world/entity/animal/Ocelot.java
@@ -68,6 +68,23 @@ public class Ocelot extends Animal {
this.reassessTrustingGoals();
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.ocelotRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.ocelotRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.ocelotControllable;
+ }
+ // Purpur end
+
public boolean isTrusting() {
return (Boolean) this.entityData.get(Ocelot.DATA_TRUSTING);
}
@@ -99,12 +116,14 @@ public class Ocelot extends Animal {
protected void registerGoals() {
this.temptGoal = new Ocelot.OcelotTemptGoal(this, 0.6D, Ocelot.TEMPT_INGREDIENT, true);
this.goalSelector.addGoal(1, new FloatGoal(this));
+ this.goalSelector.addGoal(1, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.goalSelector.addGoal(3, this.temptGoal);
this.goalSelector.addGoal(7, new LeapAtTargetGoal(this, 0.3F));
this.goalSelector.addGoal(8, new OcelotAttackGoal(this));
this.goalSelector.addGoal(9, new BreedGoal(this, 0.8D));
this.goalSelector.addGoal(10, new WaterAvoidingRandomStrollGoal(this, 0.8D, 1.0000001E-5F));
this.goalSelector.addGoal(11, new LookAtPlayerGoal(this, Player.class, 10.0F));
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, Chicken.class, false));
this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, Turtle.class, 10, false, false, Turtle.BABY_ON_LAND_SELECTOR));
}
diff --git a/src/main/java/net/minecraft/world/entity/animal/Panda.java b/src/main/java/net/minecraft/world/entity/animal/Panda.java
index f43c4fed59c4c75540008284ddb197d9a6b5487b..cc12b93b5c95202c4e3417e3a35e5f5c361b9767 100644
--- a/src/main/java/net/minecraft/world/entity/animal/Panda.java
+++ b/src/main/java/net/minecraft/world/entity/animal/Panda.java
@@ -111,6 +111,32 @@ public class Panda extends Animal {
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.pandaRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.pandaRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.pandaControllable;
+ }
+
+ @Override
+ public void onMount(Player rider) {
+ super.onMount(rider);
+ setForwardMot(0.0F);
+ sit(false);
+ eat(false);
+ setOnBack(false);
+ }
+ // Purpur end
+
@Override
public boolean canTakeItem(ItemStack stack) {
EquipmentSlot enumitemslot = Mob.getEquipmentSlotForItem(stack);
@@ -272,6 +298,7 @@ public class Panda extends Animal {
@Override
protected void registerGoals() {
this.goalSelector.addGoal(0, new FloatGoal(this));
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.goalSelector.addGoal(2, new Panda.PandaPanicGoal(this, 2.0D));
this.goalSelector.addGoal(2, new Panda.PandaBreedGoal(this, 1.0D));
this.goalSelector.addGoal(3, new Panda.PandaAttackGoal(this, 1.2000000476837158D, true));
@@ -287,6 +314,7 @@ public class Panda extends Animal {
this.goalSelector.addGoal(12, new Panda.PandaRollGoal(this));
this.goalSelector.addGoal(13, new FollowParentGoal(this, 1.25D));
this.goalSelector.addGoal(14, new WaterAvoidingRandomStrollGoal(this, 1.0D));
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.targetSelector.addGoal(1, (new Panda.PandaHurtByTargetGoal(this, new Class[0])).setAlertOthers(new Class[0]));
}
@@ -633,7 +661,7 @@ public class Panda extends Animal {
ItemStack itemstack = player.getItemInHand(hand);
if (this.isScared()) {
- return InteractionResult.PASS;
+ return tryRide(player, hand); // Purpur
} else if (this.isOnBack()) {
this.setOnBack(false);
return InteractionResult.sidedSuccess(this.level().isClientSide);
@@ -650,7 +678,7 @@ public class Panda extends Animal {
this.setInLove(player);
} else {
if (this.level().isClientSide || this.isSitting() || this.isInWater()) {
- return InteractionResult.PASS;
+ return tryRide(player, hand); // Purpur
}
this.tryToSit();
@@ -669,7 +697,7 @@ public class Panda extends Animal {
return InteractionResult.SUCCESS;
} else {
- return InteractionResult.PASS;
+ return tryRide(player, hand); // Purpur
}
}
@@ -709,7 +737,7 @@ public class Panda extends Animal {
return !this.isOnBack() && !this.isScared() && !this.isEating() && !this.isRolling() && !this.isSitting();
}
- private static class PandaMoveControl extends MoveControl {
+ private static class PandaMoveControl extends org.purpurmc.purpur.controller.MoveControllerWASD { // Purpur
private final Panda panda;
@@ -719,9 +747,9 @@ public class Panda extends Animal {
}
@Override
- public void tick() {
+ public void vanillaTick() { // Purpur
if (this.panda.canPerformAction()) {
- super.tick();
+ super.vanillaTick(); // Purpur
}
}
}
diff --git a/src/main/java/net/minecraft/world/entity/animal/Parrot.java b/src/main/java/net/minecraft/world/entity/animal/Parrot.java
index e1874aaa0065a6e8e6ae043713c22f08b58b5e21..dc6bc1ecb11a8d1776be488de4df7243d196f330 100644
--- a/src/main/java/net/minecraft/world/entity/animal/Parrot.java
+++ b/src/main/java/net/minecraft/world/entity/animal/Parrot.java
@@ -129,12 +129,68 @@ public class Parrot extends ShoulderRidingEntity implements VariantHolder<Parrot
public Parrot(EntityType<? extends Parrot> type, Level world) {
super(type, world);
- this.moveControl = new FlyingMoveControl(this, 10, false);
+ // Purpur start
+ final org.purpurmc.purpur.controller.FlyingWithSpacebarMoveControllerWASD flyingController = new org.purpurmc.purpur.controller.FlyingWithSpacebarMoveControllerWASD(this, 0.3F);
+ class ParrotMoveControl extends FlyingMoveControl {
+ public ParrotMoveControl(Mob entity, int maxPitchChange, boolean noGravity) {
+ super(entity, maxPitchChange, noGravity);
+ }
+
+ @Override
+ public void tick() {
+ if (mob.getRider() != null && mob.isControllable()) {
+ flyingController.purpurTick(mob.getRider());
+ } else {
+ super.tick();
+ }
+ }
+
+ @Override
+ public boolean hasWanted() {
+ return mob.getRider() != null && mob.isControllable() ? getForwardMot() != 0 || getStrafeMot() != 0 : super.hasWanted();
+ }
+ }
+ this.moveControl = new ParrotMoveControl(this, 10, false);
+ // Purpur end
this.setPathfindingMalus(BlockPathTypes.DANGER_FIRE, -1.0F);
this.setPathfindingMalus(BlockPathTypes.DAMAGE_FIRE, -1.0F);
this.setPathfindingMalus(BlockPathTypes.COCOA, -1.0F);
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.parrotRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.parrotRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.parrotControllable;
+ }
+
+ @Override
+ public double getMaxY() {
+ return level().purpurConfig.parrotMaxY;
+ }
+
+ @Override
+ public void travel(Vec3 vec3) {
+ super.travel(vec3);
+ if (getRider() != null && this.isControllable() && !onGround) {
+ float speed = (float) getAttributeValue(Attributes.FLYING_SPEED) * 2;
+ setSpeed(speed);
+ Vec3 mot = getDeltaMovement();
+ move(net.minecraft.world.entity.MoverType.SELF, mot.multiply(speed, 0.25, speed));
+ setDeltaMovement(mot.scale(0.9D));
+ }
+ }
+ // Purpur end
+
@Nullable
@Override
public SpawnGroupData finalizeSpawn(ServerLevelAccessor world, DifficultyInstance difficulty, MobSpawnType spawnReason, @Nullable SpawnGroupData entityData, @Nullable CompoundTag entityNbt) {
@@ -153,8 +209,10 @@ public class Parrot extends ShoulderRidingEntity implements VariantHolder<Parrot
@Override
protected void registerGoals() {
- this.goalSelector.addGoal(0, new PanicGoal(this, 1.25D));
+ //this.goalSelector.addGoal(0, new PanicGoal(this, 1.25D)); // Purpur - move down
this.goalSelector.addGoal(0, new FloatGoal(this));
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
+ this.goalSelector.addGoal(1, new PanicGoal(this, 1.25D)); // Purpur
this.goalSelector.addGoal(1, new LookAtPlayerGoal(this, Player.class, 8.0F));
this.goalSelector.addGoal(2, new SitWhenOrderedToGoal(this));
this.goalSelector.addGoal(2, new FollowOwnerGoal(this, 1.0D, 5.0F, 1.0F, true));
diff --git a/src/main/java/net/minecraft/world/entity/animal/Pig.java b/src/main/java/net/minecraft/world/entity/animal/Pig.java
index 4175767f248b1389c5c2a896edde5dc036eb4345..5e4712f90e6412f741b0ec47a86f238b8736af62 100644
--- a/src/main/java/net/minecraft/world/entity/animal/Pig.java
+++ b/src/main/java/net/minecraft/world/entity/animal/Pig.java
@@ -64,9 +64,27 @@ public class Pig extends Animal implements ItemSteerable, Saddleable {
this.steering = new ItemBasedSteering(this.entityData, Pig.DATA_BOOST_TIME, Pig.DATA_SADDLE_ID);
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.pigRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.pigRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.pigControllable;
+ }
+ // Purpur end
+
@Override
protected void registerGoals() {
this.goalSelector.addGoal(0, new FloatGoal(this));
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.goalSelector.addGoal(1, new PanicGoal(this, 1.25D));
this.goalSelector.addGoal(3, new BreedGoal(this, 1.0D));
this.goalSelector.addGoal(4, new TemptGoal(this, 1.2D, Ingredient.of(Items.CARROT_ON_A_STICK), false));
diff --git a/src/main/java/net/minecraft/world/entity/animal/PolarBear.java b/src/main/java/net/minecraft/world/entity/animal/PolarBear.java
index 04f07ecb74d53b6ecf6a9a6055488d5a3023b535..7ee4d9f8fc21a6357e713097f2060cbf1a1cbbed 100644
--- a/src/main/java/net/minecraft/world/entity/animal/PolarBear.java
+++ b/src/main/java/net/minecraft/world/entity/animal/PolarBear.java
@@ -60,11 +60,40 @@ public class PolarBear extends Animal implements NeutralMob {
private int remainingPersistentAngerTime;
@Nullable
private UUID persistentAngerTarget;
+ private int standTimer = 0; // Purpur
public PolarBear(EntityType<? extends PolarBear> type, Level world) {
super(type, world);
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.polarBearRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.polarBearRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.polarBearControllable;
+ }
+
+ @Override
+ public boolean onSpacebar() {
+ if (!isStanding()) {
+ if (getRider() != null && getRider().getForwardMot() == 0 && getRider().getStrafeMot() == 0) {
+ setStanding(true);
+ playSound(SoundEvents.POLAR_BEAR_WARNING, 1.0F, 1.0F);
+ }
+ }
+ return false;
+ }
+ // Purpur end
+
@Nullable
@Override
public AgeableMob getBreedOffspring(ServerLevel world, AgeableMob entity) {
@@ -80,12 +109,14 @@ public class PolarBear extends Animal implements NeutralMob {
protected void registerGoals() {
super.registerGoals();
this.goalSelector.addGoal(0, new FloatGoal(this));
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.goalSelector.addGoal(1, new PolarBear.PolarBearMeleeAttackGoal());
this.goalSelector.addGoal(1, new PolarBear.PolarBearPanicGoal());
this.goalSelector.addGoal(4, new FollowParentGoal(this, 1.25D));
this.goalSelector.addGoal(5, new RandomStrollGoal(this, 1.0D));
this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 6.0F));
this.goalSelector.addGoal(7, new RandomLookAroundGoal(this));
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.targetSelector.addGoal(1, new PolarBear.PolarBearHurtByTargetGoal());
this.targetSelector.addGoal(2, new PolarBear.PolarBearAttackPlayersGoal());
this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, this::isAngryAt));
@@ -202,6 +233,11 @@ public class PolarBear extends Animal implements NeutralMob {
this.updatePersistentAnger((ServerLevel)this.level(), true);
}
+ // Purpur start
+ if (isStanding() && --standTimer <= 0) {
+ setStanding(false);
+ }
+ // Purpur end
}
@Override
@@ -231,6 +267,7 @@ public class PolarBear extends Animal implements NeutralMob {
public void setStanding(boolean warning) {
this.entityData.set(DATA_STANDING_ID, warning);
+ standTimer = warning ? 20 : -1; // Purpur
}
public float getStandingAnimationScale(float tickDelta) {
diff --git a/src/main/java/net/minecraft/world/entity/animal/Pufferfish.java b/src/main/java/net/minecraft/world/entity/animal/Pufferfish.java
index a197337f2e09f53cf382022569c8836745d78769..95383e246a8e1b311e4f26a66372966b6bc51de5 100644
--- a/src/main/java/net/minecraft/world/entity/animal/Pufferfish.java
+++ b/src/main/java/net/minecraft/world/entity/animal/Pufferfish.java
@@ -45,6 +45,18 @@ public class Pufferfish extends AbstractFish {
this.refreshDimensions();
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.pufferfishRidable;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.pufferfishControllable;
+ }
+ // Purpur end
+
@Override
protected void defineSynchedData() {
super.defineSynchedData();
diff --git a/src/main/java/net/minecraft/world/entity/animal/Rabbit.java b/src/main/java/net/minecraft/world/entity/animal/Rabbit.java
index 082351bd5f98d8738334b9164375f63fdc890455..8e747771fe023dfa4cf61e5166d017368ed746b0 100644
--- a/src/main/java/net/minecraft/world/entity/animal/Rabbit.java
+++ b/src/main/java/net/minecraft/world/entity/animal/Rabbit.java
@@ -86,6 +86,7 @@ public class Rabbit extends Animal implements VariantHolder<Rabbit.Variant> {
private boolean wasOnGround;
private int jumpDelayTicks;
public int moreCarrotTicks;
+ private boolean actualJump; // Purpur
public Rabbit(EntityType<? extends Rabbit> type, Level world) {
super(type, world);
@@ -93,9 +94,55 @@ public class Rabbit extends Animal implements VariantHolder<Rabbit.Variant> {
this.moveControl = new Rabbit.RabbitMoveControl(this);
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.rabbitRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.rabbitRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.rabbitControllable;
+ }
+
+ @Override
+ public boolean onSpacebar() {
+ if (onGround) {
+ actualJump = true;
+ jumpFromGround();
+ actualJump = false;
+ }
+ return true;
+ }
+
+ private void handleJumping() {
+ if (onGround) {
+ RabbitJumpControl jumpController = (RabbitJumpControl) jumpControl;
+ if (!wasOnGround) {
+ setJumping(false);
+ jumpController.setCanJump(false);
+ }
+ if (!jumpController.wantJump()) {
+ if (moveControl.hasWanted()) {
+ startJumping();
+ }
+ } else if (!jumpController.canJump()) {
+ jumpController.setCanJump(true);
+ }
+ }
+ wasOnGround = onGround;
+ }
+ // Purpur end
+
@Override
public void registerGoals() {
this.goalSelector.addGoal(1, new FloatGoal(this));
+ this.goalSelector.addGoal(1, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.goalSelector.addGoal(1, new ClimbOnTopOfPowderSnowGoal(this, this.level()));
this.goalSelector.addGoal(1, new Rabbit.RabbitPanicGoal(this, 2.2D));
this.goalSelector.addGoal(2, new BreedGoal(this, 0.8D));
@@ -110,6 +157,14 @@ public class Rabbit extends Animal implements VariantHolder<Rabbit.Variant> {
@Override
protected float getJumpPower() {
+ // Purpur start
+ if (getRider() != null && this.isControllable()) {
+ if (getForwardMot() < 0) {
+ setSpeed(getForwardMot() * 2F);
+ }
+ return actualJump ? 0.5F : 0.3F;
+ }
+ // Purpur end
float f = 0.3F;
if (this.horizontalCollision || this.moveControl.hasWanted() && this.moveControl.getWantedY() > this.getY() + 0.5D) {
@@ -134,7 +189,7 @@ public class Rabbit extends Animal implements VariantHolder<Rabbit.Variant> {
}
@Override
- protected void jumpFromGround() {
+ public void jumpFromGround() { // Purpur - protected -> public
super.jumpFromGround();
double d0 = this.moveControl.getSpeedModifier();
@@ -184,6 +239,13 @@ public class Rabbit extends Animal implements VariantHolder<Rabbit.Variant> {
@Override
public void customServerAiStep() {
+ // Purpur start
+ if (getRider() != null && this.isControllable()) {
+ handleJumping();
+ return;
+ }
+ // Purpur end
+
if (this.jumpDelayTicks > 0) {
--this.jumpDelayTicks;
}
@@ -468,7 +530,7 @@ public class Rabbit extends Animal implements VariantHolder<Rabbit.Variant> {
}
}
- private static class RabbitMoveControl extends MoveControl {
+ private static class RabbitMoveControl extends org.purpurmc.purpur.controller.MoveControllerWASD { // Purpur
private final Rabbit rabbit;
private double nextJumpSpeed;
@@ -479,14 +541,14 @@ public class Rabbit extends Animal implements VariantHolder<Rabbit.Variant> {
}
@Override
- public void tick() {
+ public void vanillaTick() { // Purpur
if (this.rabbit.onGround() && !this.rabbit.jumping && !((Rabbit.RabbitJumpControl) this.rabbit.jumpControl).wantJump()) {
this.rabbit.setSpeedModifier(0.0D);
} else if (this.hasWanted()) {
this.rabbit.setSpeedModifier(this.nextJumpSpeed);
}
- super.tick();
+ super.vanillaTick(); // Purpur
}
@Override
diff --git a/src/main/java/net/minecraft/world/entity/animal/Salmon.java b/src/main/java/net/minecraft/world/entity/animal/Salmon.java
index 0af79daa357f53a8871e293b57e16c099e5d3f64..87c442fb198cad8671ad1419e589a5a67c4fdca8 100644
--- a/src/main/java/net/minecraft/world/entity/animal/Salmon.java
+++ b/src/main/java/net/minecraft/world/entity/animal/Salmon.java
@@ -13,6 +13,18 @@ public class Salmon extends AbstractSchoolingFish {
super(type, world);
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.salmonRidable;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.salmonControllable;
+ }
+ // Purpur end
+
@Override
public int getMaxSchoolSize() {
return 5;
diff --git a/src/main/java/net/minecraft/world/entity/animal/Sheep.java b/src/main/java/net/minecraft/world/entity/animal/Sheep.java
index c0e89262c596fbdd0bb3c3f76baccb17a1bb5fcd..0e2f10fa133281477ab96ba743266ade4d5f2119 100644
--- a/src/main/java/net/minecraft/world/entity/animal/Sheep.java
+++ b/src/main/java/net/minecraft/world/entity/animal/Sheep.java
@@ -117,10 +117,28 @@ public class Sheep extends Animal implements Shearable {
super(type, world);
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.sheepRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.sheepRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.sheepControllable;
+ }
+ // Purpur end
+
@Override
protected void registerGoals() {
this.eatBlockGoal = new EatBlockGoal(this);
this.goalSelector.addGoal(0, new FloatGoal(this));
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.goalSelector.addGoal(1, new PanicGoal(this, 1.25D));
this.goalSelector.addGoal(2, new BreedGoal(this, 1.0D));
this.goalSelector.addGoal(3, new TemptGoal(this, 1.1D, Ingredient.of(Items.WHEAT), false));
diff --git a/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java b/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java
index 8adcfc8f6772a32b5915e4a07100e8eb735f907a..04927f5f06d6dc14ad01319dd22583632c2c511b 100644
--- a/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java
+++ b/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java
@@ -54,12 +54,31 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM
super(type, world);
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.snowGolemRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.snowGolemRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.snowGolemControllable;
+ }
+ // Purpur end
+
@Override
protected void registerGoals() {
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.goalSelector.addGoal(1, new RangedAttackGoal(this, 1.25D, 20, 10.0F));
this.goalSelector.addGoal(2, new WaterAvoidingRandomStrollGoal(this, 1.0D, 1.0000001E-5F));
this.goalSelector.addGoal(3, new LookAtPlayerGoal(this, Player.class, 6.0F));
this.goalSelector.addGoal(4, new RandomLookAroundGoal(this));
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, Mob.class, 10, true, false, (entityliving) -> {
return entityliving instanceof Enemy;
}));
@@ -107,6 +126,7 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM
return;
}
+ if (getRider() != null && this.isControllable() && !level().purpurConfig.snowGolemLeaveTrailWhenRidden) return; // Purpur - don't leave snow trail when being ridden
BlockState iblockdata = Blocks.SNOW.defaultBlockState();
for (int i = 0; i < 4; ++i) {
@@ -154,7 +174,7 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM
if (itemstack.is(Items.SHEARS) && this.readyForShearing()) {
// CraftBukkit start
if (!CraftEventFactory.handlePlayerShearEntityEvent(player, this, itemstack, hand)) {
- return InteractionResult.PASS;
+ return tryRide(player, hand); // Purpur
}
// CraftBukkit end
this.shear(SoundSource.PLAYERS);
@@ -167,7 +187,7 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM
return InteractionResult.sidedSuccess(this.level().isClientSide);
} else {
- return InteractionResult.PASS;
+ return tryRide(player, hand); // Purpur
}
}
diff --git a/src/main/java/net/minecraft/world/entity/animal/Squid.java b/src/main/java/net/minecraft/world/entity/animal/Squid.java
index f60c4cd0543fd5d50fa7e2c1a9e8381227adb540..7fa03dbc442942b70f374096e346d54089d8f0a0 100644
--- a/src/main/java/net/minecraft/world/entity/animal/Squid.java
+++ b/src/main/java/net/minecraft/world/entity/animal/Squid.java
@@ -48,9 +48,32 @@ public class Squid extends WaterAnimal {
this.tentacleSpeed = 1.0F / (this.random.nextFloat() + 1.0F) * 0.2F;
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.squidRidable;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.squidControllable;
+ }
+
+ protected void rotateVectorAroundY(org.bukkit.util.Vector vector, double degrees) {
+ double rad = Math.toRadians(degrees);
+ double cos = Math.cos(rad);
+ double sine = Math.sin(rad);
+ double x = vector.getX();
+ double z = vector.getZ();
+ vector.setX(cos * x - sine * z);
+ vector.setZ(sine * x + cos * z);
+ }
+ // Purpur end
+
@Override
protected void registerGoals() {
this.goalSelector.addGoal(0, new Squid.SquidRandomMovementGoal(this));
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.goalSelector.addGoal(1, new Squid.SquidFleeGoal());
}
@@ -298,6 +321,37 @@ public class Squid extends WaterAnimal {
@Override
public void tick() {
+ // Purpur start
+ Player rider = squid.getRider();
+ if (rider != null && squid.isControllable()) {
+ if (rider.jumping) {
+ squid.onSpacebar();
+ }
+ float forward = rider.getForwardMot();
+ float strafe = rider.getStrafeMot();
+ float speed = (float) squid.getAttributeValue(Attributes.MOVEMENT_SPEED) * 10F;
+ if (forward < 0.0F) {
+ speed *= -0.5;
+ }
+ org.bukkit.util.Vector dir = rider.getBukkitEntity().getEyeLocation().getDirection().normalize().multiply(speed / 20.0F);
+ if (strafe != 0.0F) {
+ if (forward == 0.0F) {
+ dir.setY(0);
+ rotateVectorAroundY(dir, strafe > 0.0F ? -90 : 90);
+ } else if (forward < 0.0F) {
+ rotateVectorAroundY(dir, strafe > 0.0F ? 45 : -45);
+ } else {
+ rotateVectorAroundY(dir, strafe > 0.0F ? -45 : 45);
+ }
+ }
+ if (forward != 0.0F || strafe != 0.0F) {
+ squid.setMovementVector((float) dir.getX(), (float) dir.getY(), (float) dir.getZ());
+ } else {
+ squid.setMovementVector(0.0F, 0.0F, 0.0F);
+ }
+ return;
+ }
+ // Purpur end
int i = this.squid.getNoActionTime();
if (i > 100) {
this.squid.setMovementVector(0.0F, 0.0F, 0.0F);
diff --git a/src/main/java/net/minecraft/world/entity/animal/TropicalFish.java b/src/main/java/net/minecraft/world/entity/animal/TropicalFish.java
index b05b560b7570e97bc234b75f26233909fcf575b3..71234b258157579d3a47064e7e299bb7fb90908f 100644
--- a/src/main/java/net/minecraft/world/entity/animal/TropicalFish.java
+++ b/src/main/java/net/minecraft/world/entity/animal/TropicalFish.java
@@ -42,6 +42,18 @@ public class TropicalFish extends AbstractSchoolingFish implements VariantHolder
super(type, world);
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.tropicalFishRidable;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.tropicalFishControllable;
+ }
+ // Purpur end
+
public static String getPredefinedName(int variant) {
return "entity.minecraft.tropical_fish.predefined." + variant;
}
diff --git a/src/main/java/net/minecraft/world/entity/animal/Turtle.java b/src/main/java/net/minecraft/world/entity/animal/Turtle.java
index d3c15d029d5f003cba3c89f7ea1f3ed4f943f2bd..46b71439efdab39c28d29684913bec2a1ba6491a 100644
--- a/src/main/java/net/minecraft/world/entity/animal/Turtle.java
+++ b/src/main/java/net/minecraft/world/entity/animal/Turtle.java
@@ -83,6 +83,23 @@ public class Turtle extends Animal {
this.setMaxUpStep(1.0F);
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.turtleRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.turtleRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.turtleControllable;
+ }
+ // Purpur end
+
public void setHomePos(BlockPos pos) {
this.entityData.set(Turtle.HOME_POS, pos.immutable()); // Paper - called with mutablepos...
}
@@ -185,6 +202,7 @@ public class Turtle extends Animal {
@Override
protected void registerGoals() {
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.goalSelector.addGoal(0, new Turtle.TurtlePanicGoal(this, 1.2D));
this.goalSelector.addGoal(1, new Turtle.TurtleBreedGoal(this, 1.0D));
this.goalSelector.addGoal(1, new Turtle.TurtleLayEggGoal(this, 1.0D));
@@ -342,13 +360,15 @@ public class Turtle extends Animal {
org.bukkit.craftbukkit.event.CraftEventFactory.entityDamage = null; // CraftBukkit
}
- private static class TurtleMoveControl extends MoveControl {
+ private static class TurtleMoveControl extends org.purpurmc.purpur.controller.MoveControllerWASD { // Purpur
private final Turtle turtle;
+ private final org.purpurmc.purpur.controller.WaterMoveControllerWASD waterController; // Purpur
TurtleMoveControl(Turtle turtle) {
super(turtle);
this.turtle = turtle;
+ waterController = new org.purpurmc.purpur.controller.WaterMoveControllerWASD(turtle, 0.25D); // Purpur
}
private void updateSpeed() {
@@ -367,8 +387,18 @@ public class Turtle extends Animal {
}
+ // Purpur start
+ public void purpurTick(Player rider) {
+ if (turtle.isInWater()) {
+ waterController.purpurTick(rider);
+ } else {
+ super.purpurTick(rider);
+ }
+ }
+ // Purpur end
+
@Override
- public void tick() {
+ public void vanillaTick() { // Purpur
this.updateSpeed();
if (this.operation == MoveControl.Operation.MOVE_TO && !this.turtle.getNavigation().isDone()) {
double d0 = this.wantedX - this.turtle.getX();
@@ -384,7 +414,7 @@ public class Turtle extends Animal {
this.turtle.setYRot(this.rotlerp(this.turtle.getYRot(), f, 90.0F));
this.turtle.yBodyRot = this.turtle.getYRot();
- float f1 = (float) (this.speedModifier * this.turtle.getAttributeValue(Attributes.MOVEMENT_SPEED));
+ float f1 = (float) (this.getSpeedModifier() * this.turtle.getAttributeValue(Attributes.MOVEMENT_SPEED));
this.turtle.setSpeed(Mth.lerp(0.125F, this.turtle.getSpeed(), f1));
this.turtle.setDeltaMovement(this.turtle.getDeltaMovement().add(0.0D, (double) this.turtle.getSpeed() * d1 * 0.1D, 0.0D));
diff --git a/src/main/java/net/minecraft/world/entity/animal/Wolf.java b/src/main/java/net/minecraft/world/entity/animal/Wolf.java
index 57ceec70bb150afaa66962090b142048d5b50c2f..40d647d6b3132f8273d43a1addcb8c8116c7ec19 100644
--- a/src/main/java/net/minecraft/world/entity/animal/Wolf.java
+++ b/src/main/java/net/minecraft/world/entity/animal/Wolf.java
@@ -103,9 +103,32 @@ public class Wolf extends TamableAnimal implements NeutralMob {
this.setPathfindingMalus(BlockPathTypes.DANGER_POWDER_SNOW, -1.0F);
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.wolfRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.wolfRidableInWater;
+ }
+
+ public void onMount(Player rider) {
+ super.onMount(rider);
+ setInSittingPose(false);
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.wolfControllable;
+ }
+ // Purpur end
+
@Override
protected void registerGoals() {
this.goalSelector.addGoal(1, new FloatGoal(this));
+ this.goalSelector.addGoal(1, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.goalSelector.addGoal(1, new Wolf.WolfPanicGoal(1.5D));
this.goalSelector.addGoal(2, new SitWhenOrderedToGoal(this));
this.goalSelector.addGoal(3, new Wolf.WolfAvoidEntityGoal<>(this, Llama.class, 24.0F, 1.5D, 1.5D));
@@ -117,6 +140,7 @@ public class Wolf extends TamableAnimal implements NeutralMob {
this.goalSelector.addGoal(9, new BegGoal(this, 8.0F));
this.goalSelector.addGoal(10, new LookAtPlayerGoal(this, Player.class, 8.0F));
this.goalSelector.addGoal(10, new RandomLookAroundGoal(this));
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.targetSelector.addGoal(1, new OwnerHurtByTargetGoal(this));
this.targetSelector.addGoal(2, new OwnerHurtTargetGoal(this));
this.targetSelector.addGoal(3, (new HurtByTargetGoal(this, new Class[0])).setAlertOthers());
diff --git a/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java b/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java
index 1a0eee3b766a5ce5623c32ed9c023a0f80db1d1a..6c2129b93dbeaeb1e8e8db58fce8670ef8ce5716 100644
--- a/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java
+++ b/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java
@@ -101,10 +101,23 @@ public class Allay extends PathfinderMob implements InventoryCarrier, VibrationS
private float spinningAnimationTicks;
private float spinningAnimationTicks0;
public boolean forceDancing = false; // CraftBukkit
+ private org.purpurmc.purpur.controller.FlyingMoveControllerWASD purpurController; // Purpur
public Allay(EntityType<? extends Allay> type, Level world) {
super(type, world);
- this.moveControl = new FlyingMoveControl(this, 20, true);
+ // Purpur start
+ this.purpurController = new org.purpurmc.purpur.controller.FlyingMoveControllerWASD(this, 0.1F, 0.5F);
+ this.moveControl = new FlyingMoveControl(this, 20, true) {
+ @Override
+ public void tick() {
+ if (mob.getRider() != null && mob.isControllable()) {
+ purpurController.purpurTick(mob.getRider());
+ } else {
+ super.tick();
+ }
+ }
+ };
+ // Purpur end
this.setCanPickUpLoot(this.canPickUpLoot());
this.vibrationUser = new Allay.VibrationUser();
this.vibrationData = new VibrationSystem.Data();
@@ -118,6 +131,28 @@ public class Allay extends PathfinderMob implements InventoryCarrier, VibrationS
}
// CraftBukkit end
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.allayRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.allayRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.allayControllable;
+ }
+
+ @Override
+ protected void registerGoals() {
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
+ }
+ // Purpur end
+
@Override
protected Brain.Provider<Allay> brainProvider() {
return Brain.provider(Allay.MEMORY_TYPES, Allay.SENSOR_TYPES);
@@ -226,7 +261,7 @@ public class Allay extends PathfinderMob implements InventoryCarrier, VibrationS
@Override
protected void customServerAiStep() {
this.level().getProfiler().push("allayBrain");
- if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish
+ if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider
this.getBrain().tick((ServerLevel) this.level(), this);
this.level().getProfiler().pop();
this.level().getProfiler().push("allayActivityUpdate");
diff --git a/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java b/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java
index 42e22a4b9cb6841de04862cc81454da3232aa65a..b76bead9c107889e9b2f11bdc24ad7da811f97ed 100644
--- a/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java
+++ b/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java
@@ -98,6 +98,23 @@ public class Axolotl extends Animal implements LerpingModel, VariantHolder<Axolo
this.setMaxUpStep(1.0F);
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.axolotlRidable;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.axolotlControllable;
+ }
+
+ @Override
+ protected void registerGoals() {
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
+ }
+ // Purpur end
+
@Override
public Map<String, Vector3f> getModelRotationValues() {
return this.modelRotationValues;
@@ -289,7 +306,7 @@ public class Axolotl extends Animal implements LerpingModel, VariantHolder<Axolo
@Override
protected void customServerAiStep() {
this.level().getProfiler().push("axolotlBrain");
- if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish
+ if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider
this.getBrain().tick((ServerLevel) this.level(), this);
this.level().getProfiler().pop();
this.level().getProfiler().push("axolotlActivityUpdate");
@@ -521,14 +538,22 @@ public class Axolotl extends Animal implements LerpingModel, VariantHolder<Axolo
private static class AxolotlMoveControl extends SmoothSwimmingMoveControl {
private final Axolotl axolotl;
+ private final org.purpurmc.purpur.controller.WaterMoveControllerWASD waterController; // Purpur
public AxolotlMoveControl(Axolotl axolotl) {
super(axolotl, 85, 10, 0.1F, 0.5F, false);
this.axolotl = axolotl;
+ waterController = new org.purpurmc.purpur.controller.WaterMoveControllerWASD(axolotl, 0.5D); // Purpur
}
@Override
public void tick() {
+ // Purpur start
+ if (axolotl.getRider() != null && axolotl.isControllable()) {
+ waterController.purpurTick(axolotl.getRider());
+ return;
+ }
+ // Purpur end
if (!this.axolotl.isPlayingDead()) {
super.tick();
}
@@ -543,9 +568,9 @@ public class Axolotl extends Animal implements LerpingModel, VariantHolder<Axolo
}
@Override
- public void tick() {
+ public void vanillaTick() { // Purpur
if (!Axolotl.this.isPlayingDead()) {
- super.tick();
+ super.vanillaTick(); // Purpur
}
}
diff --git a/src/main/java/net/minecraft/world/entity/animal/camel/Camel.java b/src/main/java/net/minecraft/world/entity/animal/camel/Camel.java
index 4efa7e331cc974008c653a04687a336e97626445..76a38d235de3499ca19c3ccacd9289c7355012db 100644
--- a/src/main/java/net/minecraft/world/entity/animal/camel/Camel.java
+++ b/src/main/java/net/minecraft/world/entity/animal/camel/Camel.java
@@ -84,6 +84,13 @@ public class Camel extends AbstractHorse implements PlayerRideableJumping, Rider
groundPathNavigation.setCanWalkOverFences(true);
}
+ // Purpur start
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.camelRidableInWater;
+ }
+ // Purpur end
+
@Override
public void addAdditionalSaveData(CompoundTag nbt) {
super.addAdditionalSaveData(nbt);
diff --git a/src/main/java/net/minecraft/world/entity/animal/frog/Frog.java b/src/main/java/net/minecraft/world/entity/animal/frog/Frog.java
index 80ddb3059d6484c2b90c55ef601043798f1a4b50..f189e0493286d58fbe16a30912b3a1dc4cd4c3e5 100644
--- a/src/main/java/net/minecraft/world/entity/animal/frog/Frog.java
+++ b/src/main/java/net/minecraft/world/entity/animal/frog/Frog.java
@@ -77,16 +77,65 @@ public class Frog extends Animal implements VariantHolder<FrogVariant> {
public final AnimationState croakAnimationState = new AnimationState();
public final AnimationState tongueAnimationState = new AnimationState();
public final AnimationState swimIdleAnimationState = new AnimationState();
+ private org.purpurmc.purpur.controller.MoveControllerWASD purpurLandController; // Purpur
+ private org.purpurmc.purpur.controller.WaterMoveControllerWASD purpurWaterController; // Purpur
public Frog(EntityType<? extends Animal> type, Level world) {
super(type, world);
this.lookControl = new Frog.FrogLookControl(this);
this.setPathfindingMalus(BlockPathTypes.WATER, 4.0F);
this.setPathfindingMalus(BlockPathTypes.TRAPDOOR, -1.0F);
- this.moveControl = new SmoothSwimmingMoveControl(this, 85, 10, 0.02F, 0.1F, true);
+ // Purpur start
+ this.purpurLandController = new org.purpurmc.purpur.controller.MoveControllerWASD(this, 0.2F);
+ this.purpurWaterController = new org.purpurmc.purpur.controller.WaterMoveControllerWASD(this, 0.5F);
+ this.moveControl = new SmoothSwimmingMoveControl(this, 85, 10, 0.02F, 0.1F, true) {
+ @Override
+ public void tick() {
+ net.minecraft.world.entity.player.Player rider = mob.getRider();
+ if (rider != null && mob.isControllable()) {
+ if (mob.isInWater()) {
+ purpurWaterController.purpurTick(rider);
+ mob.setDeltaMovement(mob.getDeltaMovement().add(0.0D, -0.005D, 0.0D));
+ } else {
+ purpurLandController.purpurTick(rider);
+ }
+ } else {
+ super.tick();
+ }
+ }
+ };
+ // Purpur end
this.setMaxUpStep(1.0F);
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.frogRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.frogRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.frogControllable;
+ }
+
+ @Override
+ protected void registerGoals() {
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
+ }
+
+ @Override
+ public float getJumpPower() {
+ return (getRider() != null && isControllable()) ? level().purpurConfig.frogRidableJumpHeight * this.getBlockJumpFactor() : super.getJumpPower();
+ }
+ // Purpur end
+
@Override
protected Brain.Provider<Frog> brainProvider() {
return Brain.provider(MEMORY_TYPES, SENSOR_TYPES);
@@ -166,7 +215,7 @@ public class Frog extends Animal implements VariantHolder<FrogVariant> {
@Override
protected void customServerAiStep() {
this.level().getProfiler().push("frogBrain");
- if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish
+ if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider
this.getBrain().tick((ServerLevel)this.level(), this);
this.level().getProfiler().pop();
this.level().getProfiler().push("frogActivityUpdate");
@@ -347,7 +396,7 @@ public class Frog extends Animal implements VariantHolder<FrogVariant> {
return world.getBlockState(pos.below()).is(BlockTags.FROGS_SPAWNABLE_ON) && isBrightEnoughToSpawn(world, pos);
}
- class FrogLookControl extends LookControl {
+ class FrogLookControl extends org.purpurmc.purpur.controller.LookControllerWASD { // Purpur
FrogLookControl(Mob entity) {
super(entity);
}
diff --git a/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java b/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java
index 6ed4ac06c76b8d0d6e8db778cade15dbd1e3e5f5..af8438ae8c805d3276ef2d82eb39b08880fcc8a1 100644
--- a/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java
+++ b/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java
@@ -45,13 +45,50 @@ public class Tadpole extends AbstractFish {
protected static final ImmutableList<SensorType<? extends Sensor<? super Tadpole>>> SENSOR_TYPES = ImmutableList.of(SensorType.NEAREST_LIVING_ENTITIES, SensorType.NEAREST_PLAYERS, SensorType.HURT_BY, SensorType.FROG_TEMPTATIONS);
protected static final ImmutableList<MemoryModuleType<?>> MEMORY_TYPES = ImmutableList.of(MemoryModuleType.LOOK_TARGET, MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES, MemoryModuleType.WALK_TARGET, MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE, MemoryModuleType.PATH, MemoryModuleType.NEAREST_VISIBLE_ADULT, MemoryModuleType.TEMPTATION_COOLDOWN_TICKS, MemoryModuleType.IS_TEMPTED, MemoryModuleType.TEMPTING_PLAYER, MemoryModuleType.BREED_TARGET, MemoryModuleType.IS_PANICKING);
public boolean ageLocked; // Paper
+ private org.purpurmc.purpur.controller.WaterMoveControllerWASD purpurController; // Purpur
public Tadpole(EntityType<? extends AbstractFish> type, Level world) {
super(type, world);
- this.moveControl = new SmoothSwimmingMoveControl(this, 85, 10, 0.02F, 0.1F, true);
+ // Purpur start
+ this.purpurController = new org.purpurmc.purpur.controller.WaterMoveControllerWASD(this, 0.5F);
+ this.moveControl = new SmoothSwimmingMoveControl(this, 85, 10, 0.02F, 0.1F, true) {
+ @Override
+ public void tick() {
+ Player rider = mob.getRider();
+ if (rider != null && mob.isControllable()) {
+ purpurController.purpurTick(rider);
+ mob.setDeltaMovement(mob.getDeltaMovement().add(0.0D, 0.002D, 0.0D));
+ } else {
+ super.tick();
+ }
+ }
+ };
+ // Purpur end
this.lookControl = new SmoothSwimmingLookControl(this, 10);
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.tadpoleRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.tadpoleRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.tadpoleControllable;
+ }
+
+ @Override
+ protected void registerGoals() {
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
+ }
+ // Purpur end
+
@Override
protected PathNavigation createNavigation(Level world) {
return new WaterBoundPathNavigation(this, world);
@@ -81,7 +118,7 @@ public class Tadpole extends AbstractFish {
@Override
protected void customServerAiStep() {
this.level().getProfiler().push("tadpoleBrain");
- if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish
+ if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider
this.getBrain().tick((ServerLevel) this.level(), this);
this.level().getProfiler().pop();
this.level().getProfiler().push("tadpoleActivityUpdate");
diff --git a/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java b/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java
index 7f21d1d400c8a5615ed1a787dcb0680338f8c28d..ec956339b02fc9da53563d67c0eeaa91a3c3a8ee 100644
--- a/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java
+++ b/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java
@@ -90,6 +90,23 @@ public class Goat extends Animal {
return InstrumentItem.create(Items.GOAT_HORN, (Holder) holderset.getRandomElement(randomsource).get());
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.goatRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.goatRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.goatControllable;
+ }
+ // Purpur end
+
@Override
protected Brain.Provider<Goat> brainProvider() {
return Brain.provider(Goat.MEMORY_TYPES, Goat.SENSOR_TYPES);
@@ -193,7 +210,7 @@ public class Goat extends Animal {
@Override
protected void customServerAiStep() {
this.level().getProfiler().push("goatBrain");
- if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish
+ if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider // Purpur - TODO: Uncomment when pufferfish patch
this.getBrain().tick((ServerLevel) this.level(), this);
this.level().getProfiler().pop();
this.level().getProfiler().push("goatActivityUpdate");
diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/AbstractHorse.java b/src/main/java/net/minecraft/world/entity/animal/horse/AbstractHorse.java
index 49d7109b6ca63c8073db777549a65b2fcb70967e..4b656f4e1660978e3dd284093241003cabff4f23 100644
--- a/src/main/java/net/minecraft/world/entity/animal/horse/AbstractHorse.java
+++ b/src/main/java/net/minecraft/world/entity/animal/horse/AbstractHorse.java
@@ -147,12 +147,22 @@ public abstract class AbstractHorse extends Animal implements ContainerListener,
protected AbstractHorse(EntityType<? extends AbstractHorse> type, Level world) {
super(type, world);
+ this.moveControl = new net.minecraft.world.entity.ai.control.MoveControl(this); // Purpur - use vanilla controller
+ this.lookControl = new net.minecraft.world.entity.ai.control.LookControl(this); // Purpur - use vanilla controller
this.setMaxUpStep(1.0F);
this.createInventory();
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return false; // vanilla handles
+ }
+ // Purpur end
+
@Override
protected void registerGoals() {
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HorseHasRider(this)); // Purpur
this.goalSelector.addGoal(1, new PanicGoal(this, 1.2D));
this.goalSelector.addGoal(1, new RunAroundLikeCrazyGoal(this, 1.2D));
this.goalSelector.addGoal(2, new BreedGoal(this, 1.0D, AbstractHorse.class));
@@ -163,6 +173,7 @@ public abstract class AbstractHorse extends Animal implements ContainerListener,
if (this.canPerformRearing()) {
this.goalSelector.addGoal(9, new RandomStandGoal(this));
}
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HorseHasRider(this)); // Purpur
this.addBehaviourGoals();
}
diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/Donkey.java b/src/main/java/net/minecraft/world/entity/animal/horse/Donkey.java
index e0dfee0e0ce091d5ae0ec740e939c2c50915c104..6a5fb18523545ccc788b09ab4fbee1b14a64bc63 100644
--- a/src/main/java/net/minecraft/world/entity/animal/horse/Donkey.java
+++ b/src/main/java/net/minecraft/world/entity/animal/horse/Donkey.java
@@ -15,6 +15,13 @@ public class Donkey extends AbstractChestedHorse {
super(type, world);
}
+ // Purpur start
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.donkeyRidableInWater;
+ }
+ // Purpur end
+
@Override
protected SoundEvent getAmbientSound() {
return SoundEvents.DONKEY_AMBIENT;
diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/Horse.java b/src/main/java/net/minecraft/world/entity/animal/horse/Horse.java
index 5f5dc651d570989ec1294c31a14dcfede466b80a..3e50581033e88e8eddcbd85bfa890cbe0b88a7e6 100644
--- a/src/main/java/net/minecraft/world/entity/animal/horse/Horse.java
+++ b/src/main/java/net/minecraft/world/entity/animal/horse/Horse.java
@@ -40,6 +40,13 @@ public class Horse extends AbstractHorse implements VariantHolder<Variant> {
super(type, world);
}
+ // Purpur start
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.horseRidableInWater;
+ }
+ // Purpur end
+
@Override
protected void randomizeAttributes(RandomSource random) {
this.getAttribute(Attributes.MAX_HEALTH).setBaseValue((double)generateMaxHealth(random::nextInt));
diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/Llama.java b/src/main/java/net/minecraft/world/entity/animal/horse/Llama.java
index d28089c37707f323f73e60cb0ebed4e3cdf8c0fd..42d5b435728aa497dc34f0ac984e8637fe96343d 100644
--- a/src/main/java/net/minecraft/world/entity/animal/horse/Llama.java
+++ b/src/main/java/net/minecraft/world/entity/animal/horse/Llama.java
@@ -76,7 +76,51 @@ public class Llama extends AbstractChestedHorse implements VariantHolder<Llama.V
public Llama(EntityType<? extends Llama> type, Level world) {
super(type, world);
+ // Purpur start
+ this.moveControl = new org.purpurmc.purpur.controller.MoveControllerWASD(this) {
+ @Override
+ public void tick() {
+ if (entity.getRider() != null && entity.isControllable() && isSaddled()) {
+ purpurTick(entity.getRider());
+ } else {
+ vanillaTick();
+ }
+ }
+ };
+ this.lookControl = new org.purpurmc.purpur.controller.LookControllerWASD(this) {
+ @Override
+ public void tick() {
+ if (entity.getRider() != null && entity.isControllable() && isSaddled()) {
+ purpurTick(entity.getRider());
+ } else {
+ vanillaTick();
+ }
+ }
+ };
+ // Purpur end
+ }
+
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.llamaRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.llamaRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.llamaControllable;
+ }
+
+ @Override
+ public boolean isSaddled() {
+ return super.isSaddled() || (isTamed() && getSwag() != null);
}
+ // Purpur end
public boolean isTraderLlama() {
return false;
@@ -127,6 +171,7 @@ public class Llama extends AbstractChestedHorse implements VariantHolder<Llama.V
@Override
protected void registerGoals() {
this.goalSelector.addGoal(0, new FloatGoal(this));
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.LlamaHasRider(this)); // Purpur
this.goalSelector.addGoal(1, new RunAroundLikeCrazyGoal(this, 1.2D));
this.goalSelector.addGoal(2, new LlamaFollowCaravanGoal(this, 2.0999999046325684D));
this.goalSelector.addGoal(3, new RangedAttackGoal(this, 1.25D, 40, 20.0F));
@@ -137,6 +182,7 @@ public class Llama extends AbstractChestedHorse implements VariantHolder<Llama.V
this.goalSelector.addGoal(7, new WaterAvoidingRandomStrollGoal(this, 0.7D));
this.goalSelector.addGoal(8, new LookAtPlayerGoal(this, Player.class, 6.0F));
this.goalSelector.addGoal(9, new RandomLookAroundGoal(this));
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.LlamaHasRider(this)); // Purpur
this.targetSelector.addGoal(1, new Llama.LlamaHurtByTargetGoal(this));
this.targetSelector.addGoal(2, new Llama.LlamaAttackWolfGoal(this));
}
diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/Mule.java b/src/main/java/net/minecraft/world/entity/animal/horse/Mule.java
index a6601f70890f90691923c0e6a9f10ea597ccabc2..ae7bcfa608d8bdd2a2320618225294de0314ce53 100644
--- a/src/main/java/net/minecraft/world/entity/animal/horse/Mule.java
+++ b/src/main/java/net/minecraft/world/entity/animal/horse/Mule.java
@@ -14,6 +14,13 @@ public class Mule extends AbstractChestedHorse {
super(type, world);
}
+ // Purpur start
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.muleRidableInWater;
+ }
+ // Purpur end
+
@Override
protected SoundEvent getAmbientSound() {
return SoundEvents.MULE_AMBIENT;
diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/SkeletonHorse.java b/src/main/java/net/minecraft/world/entity/animal/horse/SkeletonHorse.java
index d2007d4461967551c873b7c2020613c5c6d49b1d..aad898d61861ed6fc215864b598a57f4cedf5339 100644
--- a/src/main/java/net/minecraft/world/entity/animal/horse/SkeletonHorse.java
+++ b/src/main/java/net/minecraft/world/entity/animal/horse/SkeletonHorse.java
@@ -28,6 +28,13 @@ public class SkeletonHorse extends AbstractHorse {
super(type, world);
}
+ // Purpur start
+ @Override
+ public boolean isTamed() {
+ return true;
+ }
+ // Purpur end
+
public static AttributeSupplier.Builder createAttributes() {
return createBaseHorseAttributes().add(Attributes.MAX_HEALTH, 15.0D).add(Attributes.MOVEMENT_SPEED, (double)0.2F);
}
@@ -39,6 +46,7 @@ public class SkeletonHorse extends AbstractHorse {
@Override
protected void addBehaviourGoals() {
+ if (level().purpurConfig.skeletonHorseCanSwim) goalSelector.addGoal(0, new net.minecraft.world.entity.ai.goal.FloatGoal(this));
}
@Override
diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/TraderLlama.java b/src/main/java/net/minecraft/world/entity/animal/horse/TraderLlama.java
index b016f53c6644c6411b3a91e09049892131187179..5fc37e2322188e0db12f7679e40b1a3d40268ca7 100644
--- a/src/main/java/net/minecraft/world/entity/animal/horse/TraderLlama.java
+++ b/src/main/java/net/minecraft/world/entity/animal/horse/TraderLlama.java
@@ -27,6 +27,28 @@ public class TraderLlama extends Llama {
super(type, world);
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.traderLlamaRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.traderLlamaRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.traderLlamaControllable;
+ }
+
+ @Override
+ public boolean isSaddled() {
+ return super.isSaddled() || isTamed();
+ }
+ // Purpur end
+
@Override
public boolean isTraderLlama() {
return true;
diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/ZombieHorse.java b/src/main/java/net/minecraft/world/entity/animal/horse/ZombieHorse.java
index e2935115c8d41af1d623da4f0d4f73de80386129..86029716d5538e28a8845375cc23d4bf408073ff 100644
--- a/src/main/java/net/minecraft/world/entity/animal/horse/ZombieHorse.java
+++ b/src/main/java/net/minecraft/world/entity/animal/horse/ZombieHorse.java
@@ -21,6 +21,18 @@ public class ZombieHorse extends AbstractHorse {
super(type, world);
}
+ // Purpur start
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.zombieHorseRidableInWater;
+ }
+
+ @Override
+ public boolean isTamed() {
+ return true;
+ }
+ // Purpur end
+
public static AttributeSupplier.Builder createAttributes() {
return createBaseHorseAttributes().add(Attributes.MAX_HEALTH, 15.0D).add(Attributes.MOVEMENT_SPEED, (double)0.2F);
}
@@ -63,5 +75,6 @@ public class ZombieHorse extends AbstractHorse {
@Override
protected void addBehaviourGoals() {
+ if (level().purpurConfig.zombieHorseCanSwim) goalSelector.addGoal(0, new net.minecraft.world.entity.ai.goal.FloatGoal(this)); // Purpur
}
}
diff --git a/src/main/java/net/minecraft/world/entity/animal/sniffer/Sniffer.java b/src/main/java/net/minecraft/world/entity/animal/sniffer/Sniffer.java
index a6d98f64910c816a5c11867d12698f5cd63c751a..76e4b87ffc470b7b205fba0a254668f15a394034 100644
--- a/src/main/java/net/minecraft/world/entity/animal/sniffer/Sniffer.java
+++ b/src/main/java/net/minecraft/world/entity/animal/sniffer/Sniffer.java
@@ -90,6 +90,23 @@ public class Sniffer extends Animal {
this.setPathfindingMalus(BlockPathTypes.DAMAGE_CAUTIOUS, -1.0F);
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.snifferRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.snifferRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.snifferControllable;
+ }
+ // Purpur end
+
// CraftBukkit start - SPIGOT-7295: moved from constructor to appropriate location
@Override
protected void defineSynchedData() {
@@ -334,7 +351,7 @@ public class Sniffer extends Animal {
}
@Override
- protected void jumpFromGround() {
+ public void jumpFromGround() { // Purpur - protected -> public
super.jumpFromGround();
double d0 = this.moveControl.getSpeedModifier();
diff --git a/src/main/java/net/minecraft/world/entity/boss/EnderDragonPart.java b/src/main/java/net/minecraft/world/entity/boss/EnderDragonPart.java
index a6f3fba3b02b0b4d2a4e9e5205301c6f52d0188a..4eebfc27ded55e4d764d04f35d3e9c9e0791c89f 100644
--- a/src/main/java/net/minecraft/world/entity/boss/EnderDragonPart.java
+++ b/src/main/java/net/minecraft/world/entity/boss/EnderDragonPart.java
@@ -24,6 +24,13 @@ public class EnderDragonPart extends Entity {
this.name = name;
}
+ // Purpur start
+ @Override
+ public net.minecraft.world.InteractionResult interact(net.minecraft.world.entity.player.Player player, net.minecraft.world.InteractionHand hand) {
+ return parentMob.isAlive() ? parentMob.tryRide(player, hand) : net.minecraft.world.InteractionResult.PASS;
+ }
+ // Purpur end
+
@Override
protected void defineSynchedData() {
}
diff --git a/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java b/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java
index a24ae93efcdb2da5782d342c7697a1bb253400c7..c5100d3d8c36bf0c07f2028c5d3ec4bbe6e92256 100644
--- a/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java
+++ b/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java
@@ -105,6 +105,7 @@ public class EnderDragon extends Mob implements Enemy {
@Nullable
private BlockPos podium;
// Paper end
+ private boolean hadRider; // Purpur
public EnderDragon(EntityType<? extends EnderDragon> entitytypes, Level world) {
super(EntityType.ENDER_DRAGON, world);
@@ -126,7 +127,37 @@ public class EnderDragon extends Mob implements Enemy {
this.noPhysics = true;
this.noCulling = true;
this.phaseManager = new EnderDragonPhaseManager(this);
- this.explosionSource = new Explosion(world, this, null, null, Double.NaN, Double.NaN, Double.NaN, Float.NaN, true, Explosion.BlockInteraction.DESTROY); // CraftBukkit
+
+ // Purpur start
+ this.moveControl = new org.purpurmc.purpur.controller.FlyingMoveControllerWASD(this) {
+ @Override
+ public void vanillaTick() {
+ // dragon doesn't use the controller. do nothing
+ }
+ };
+ this.lookControl = new org.purpurmc.purpur.controller.LookControllerWASD(this) {
+ @Override
+ public void vanillaTick() {
+ // dragon doesn't use the controller. do nothing
+ }
+
+ @Override
+ public void purpurTick(Player rider) {
+ setYawPitch(rider.getYRot() - 180F, rider.xRotO * 0.5F);
+ }
+ };
+ // Purpur end
+ }
+
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.enderDragonRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.enderDragonRidableInWater;
}
public void setDragonFight(EndDragonFight fight) {
@@ -141,6 +172,17 @@ public class EnderDragon extends Mob implements Enemy {
return this.fightOrigin;
}
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.enderDragonControllable;
+ }
+
+ @Override
+ public double getMaxY() {
+ return level().purpurConfig.enderDragonMaxY;
+ }
+ // Purpur end
+
public static AttributeSupplier.Builder createAttributes() {
return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 200.0D);
}
@@ -202,6 +244,37 @@ public class EnderDragon extends Mob implements Enemy {
@Override
public void aiStep() {
+ // Purpur start
+ boolean hasRider = getRider() != null && this.isControllable();
+ if (hasRider) {
+ if (!hadRider) {
+ hadRider = true;
+ noPhysics = false;
+ this.dimensions = net.minecraft.world.entity.EntityDimensions.scalable(4.0F, 2.0F);
+ }
+
+ // dragon doesn't use controllers, so must tick manually
+ moveControl.tick();
+ lookControl.tick();
+
+ moveRelative((float) getAttributeValue(Attributes.MOVEMENT_SPEED) * 0.1F, new Vec3(-getStrafeMot(), getVerticalMot(), -getForwardMot()));
+ Vec3 mot = getDeltaMovement();
+ setDeltaMovement(mot);
+ move(MoverType.PLAYER, mot);
+
+ mot = mot.multiply(0.9F, 0.9F, 0.9F);
+ setDeltaMovement(mot);
+
+ // control wing flap speed on client
+ phaseManager.setPhase(mot.x() * mot.x() + mot.z() * mot.z() < 0.005F ? EnderDragonPhase.HOVERING : EnderDragonPhase.HOLDING_PATTERN);
+ } else if (hadRider) {
+ hadRider = false;
+ noPhysics = true;
+ this.dimensions = net.minecraft.world.entity.EntityDimensions.scalable(16.0F, 8.0F);
+ phaseManager.setPhase(EnderDragonPhase.HOLDING_PATTERN); // HoldingPattern
+ }
+ // Purpur end
+
this.processFlappingMovement();
if (this.level().isClientSide) {
this.setHealth(this.getHealth());
@@ -228,6 +301,8 @@ public class EnderDragon extends Mob implements Enemy {
float f;
if (this.isDeadOrDying()) {
+ if (hasRider) ejectPassengers(); // Purpur
+
float f1 = (this.random.nextFloat() - 0.5F) * 8.0F;
f = (this.random.nextFloat() - 0.5F) * 4.0F;
@@ -240,9 +315,9 @@ public class EnderDragon extends Mob implements Enemy {
f = 0.2F / ((float) vec3d.horizontalDistance() * 10.0F + 1.0F);
f *= (float) Math.pow(2.0D, vec3d.y);
- if (this.phaseManager.getCurrentPhase().isSitting()) {
+ if (!hasRider && this.phaseManager.getCurrentPhase().isSitting()) { // Purpur
this.flapTime += 0.1F;
- } else if (this.inWall) {
+ } else if (!hasRider && this.inWall) { // Purpur
this.flapTime += f * 0.5F;
} else {
this.flapTime += f;
@@ -287,7 +362,7 @@ public class EnderDragon extends Mob implements Enemy {
}
this.phaseManager.getCurrentPhase().doClientTick();
- } else {
+ } else if (!hasRider) { // Purpur
DragonPhaseInstance idragoncontroller = this.phaseManager.getCurrentPhase();
idragoncontroller.doServerTick();
@@ -356,7 +431,7 @@ public class EnderDragon extends Mob implements Enemy {
this.tickPart(this.body, (double) (f11 * 0.5F), 0.0D, (double) (-f12 * 0.5F));
this.tickPart(this.wing1, (double) (f12 * 4.5F), 2.0D, (double) (f11 * 4.5F));
this.tickPart(this.wing2, (double) (f12 * -4.5F), 2.0D, (double) (f11 * -4.5F));
- if (!this.level().isClientSide && this.hurtTime == 0) {
+ if (!hasRider && !this.level().isClientSide && this.hurtTime == 0) { // Purpur
this.knockBack(this.level().getEntities((Entity) this, this.wing1.getBoundingBox().inflate(4.0D, 2.0D, 4.0D).move(0.0D, -2.0D, 0.0D), EntitySelector.NO_CREATIVE_OR_SPECTATOR));
this.knockBack(this.level().getEntities((Entity) this, this.wing2.getBoundingBox().inflate(4.0D, 2.0D, 4.0D).move(0.0D, -2.0D, 0.0D), EntitySelector.NO_CREATIVE_OR_SPECTATOR));
this.hurt(this.level().getEntities((Entity) this, this.head.getBoundingBox().inflate(1.0D), EntitySelector.NO_CREATIVE_OR_SPECTATOR));
@@ -400,7 +475,7 @@ public class EnderDragon extends Mob implements Enemy {
}
if (!this.level().isClientSide) {
- this.inWall = this.checkWalls(this.head.getBoundingBox()) | this.checkWalls(this.neck.getBoundingBox()) | this.checkWalls(this.body.getBoundingBox());
+ this.inWall = !hasRider && this.checkWalls(this.head.getBoundingBox()) | this.checkWalls(this.neck.getBoundingBox()) | this.checkWalls(this.body.getBoundingBox()); // Purpur
if (this.dragonFight != null) {
this.dragonFight.updateDragon(this);
}
diff --git a/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java b/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java
index 1e07febcf7a3dfb281728cc5e3e4f15dd776d7e0..6c86411658ef0bf64cb8cf4f213112b65f2d1d90 100644
--- a/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java
+++ b/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java
@@ -84,16 +84,30 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob
return entityliving.getMobType() != MobType.UNDEAD && entityliving.attackable();
};
private static final TargetingConditions TARGETING_CONDITIONS = TargetingConditions.forCombat().range(20.0D).selector(WitherBoss.LIVING_ENTITY_SELECTOR);
+ private int shootCooldown = 0; // Purpur
// Paper start
private boolean canPortal = false;
public void setCanTravelThroughPortals(boolean canPortal) { this.canPortal = canPortal; }
// Paper end
+ private org.purpurmc.purpur.controller.FlyingWithSpacebarMoveControllerWASD purpurController; // Purpur
public WitherBoss(EntityType<? extends WitherBoss> type, Level world) {
super(type, world);
this.bossEvent = (ServerBossEvent) (new ServerBossEvent(this.getDisplayName(), BossEvent.BossBarColor.PURPLE, BossEvent.BossBarOverlay.PROGRESS)).setDarkenScreen(true);
- this.moveControl = new FlyingMoveControl(this, 10, false);
+ // Purpur start
+ this.purpurController = new org.purpurmc.purpur.controller.FlyingWithSpacebarMoveControllerWASD(this, 0.1F);
+ this.moveControl = new FlyingMoveControl(this, 10, false) {
+ @Override
+ public void tick() {
+ if (mob.getRider() != null && mob.isControllable()) {
+ purpurController.purpurTick(mob.getRider());
+ } else {
+ super.tick();
+ }
+ }
+ };
+ // Purpur end
this.setHealth(this.getMaxHealth());
this.xpReward = 50;
}
@@ -108,13 +122,113 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob
return navigationflying;
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.witherRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.witherRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.witherControllable;
+ }
+
+ @Override
+ public double getMaxY() {
+ return level().purpurConfig.witherMaxY;
+ }
+
+ @Override
+ public void travel(Vec3 vec3) {
+ super.travel(vec3);
+ if (getRider() != null && this.isControllable() && !onGround) {
+ float speed = (float) getAttributeValue(Attributes.FLYING_SPEED) * 5F;
+ setSpeed(speed);
+ Vec3 mot = getDeltaMovement();
+ move(net.minecraft.world.entity.MoverType.SELF, mot.multiply(speed, 0.5, speed));
+ setDeltaMovement(mot.scale(0.9D));
+ }
+ }
+
+ @Override
+ public void onMount(Player rider) {
+ super.onMount(rider);
+ this.entityData.set(DATA_TARGETS.get(0), 0);
+ this.entityData.set(DATA_TARGETS.get(1), 0);
+ this.entityData.set(DATA_TARGETS.get(2), 0);
+ getNavigation().stop();
+ shootCooldown = 20;
+ }
+
+ @Override
+ public boolean onClick(net.minecraft.world.InteractionHand hand) {
+ return shoot(getRider(), hand == net.minecraft.world.InteractionHand.MAIN_HAND ? new int[]{1} : new int[]{2});
+ }
+
+ public boolean shoot(@Nullable Player rider, int[] heads) {
+ if (shootCooldown > 0) {
+ return false;
+ }
+
+ shootCooldown = 20;
+ if (rider == null) {
+ return false;
+ }
+
+ org.bukkit.craftbukkit.entity.CraftHumanEntity player = rider.getBukkitEntity();
+ if (!player.hasPermission("allow.special.wither")) {
+ return false;
+ }
+
+ net.minecraft.world.phys.HitResult rayTrace = getRayTrace(120, net.minecraft.world.level.ClipContext.Fluid.NONE);
+ if (rayTrace == null) {
+ return false;
+ }
+
+ Vec3 loc;
+ if (rayTrace.getType() == net.minecraft.world.phys.HitResult.Type.BLOCK) {
+ BlockPos pos = ((net.minecraft.world.phys.BlockHitResult) rayTrace).getBlockPos();
+ loc = new Vec3(pos.getX() + 0.5D, pos.getY() + 0.5D, pos.getZ() + 0.5D);
+ } else if (rayTrace.getType() == net.minecraft.world.phys.HitResult.Type.ENTITY) {
+ Entity target = ((net.minecraft.world.phys.EntityHitResult) rayTrace).getEntity();
+ loc = new Vec3(target.getX(), target.getY() + (target.getEyeHeight() / 2), target.getZ());
+ } else {
+ org.bukkit.block.Block block = player.getTargetBlock(null, 120);
+ loc = new Vec3(block.getX() + 0.5D, block.getY() + 0.5D, block.getZ() + 0.5D);
+ }
+
+ for (int head : heads) {
+ shoot(head, loc.x(), loc.y(), loc.z(), rider);
+ }
+
+ return true; // handled
+ }
+
+ public void shoot(int head, double x, double y, double z, Player rider) {
+ level().levelEvent(null, 1024, blockPosition(), 0);
+ double headX = getHeadX(head);
+ double headY = getHeadY(head);
+ double headZ = getHeadZ(head);
+ WitherSkull skull = new WitherSkull(level(), this, x - headX, y - headY, z - headZ);
+ skull.setPosRaw(headX, headY, headZ);
+ level().addFreshEntity(skull);
+ }
+ // Purpur end
+
@Override
protected void registerGoals() {
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.goalSelector.addGoal(0, new WitherBoss.WitherDoNothingGoal());
this.goalSelector.addGoal(2, new RangedAttackGoal(this, 1.0D, 40, 20.0F));
this.goalSelector.addGoal(5, new WaterAvoidingRandomFlyingGoal(this, 1.0D));
this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 8.0F));
this.goalSelector.addGoal(7, new RandomLookAroundGoal(this));
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.targetSelector.addGoal(1, new HurtByTargetGoal(this, new Class[0]));
this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, LivingEntity.class, 0, false, false, WitherBoss.LIVING_ENTITY_SELECTOR));
}
@@ -256,6 +370,16 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob
@Override
protected void customServerAiStep() {
+ // Purpur start
+ if (getRider() != null && this.isControllable()) {
+ Vec3 mot = getDeltaMovement();
+ setDeltaMovement(mot.x(), mot.y() + (getVerticalMot() > 0 ? 0.07D : 0.0D), mot.z());
+ }
+ if (shootCooldown > 0) {
+ shootCooldown--;
+ }
+ // Purpur end
+
int i;
if (this.getInvulnerableTicks() > 0) {
@@ -576,11 +700,11 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob
}
public int getAlternativeTarget(int headIndex) {
- return (Integer) this.entityData.get((EntityDataAccessor) WitherBoss.DATA_TARGETS.get(headIndex));
+ return getRider() != null && this.isControllable() ? 0 : this.entityData.get(WitherBoss.DATA_TARGETS.get(headIndex)); // Purpur
}
public void setAlternativeTarget(int headIndex, int id) {
- this.entityData.set((EntityDataAccessor) WitherBoss.DATA_TARGETS.get(headIndex), id);
+ if (getRider() == null || !this.isControllable()) this.entityData.set(WitherBoss.DATA_TARGETS.get(headIndex), id); // Purpur
}
@Override
diff --git a/src/main/java/net/minecraft/world/entity/monster/AbstractSkeleton.java b/src/main/java/net/minecraft/world/entity/monster/AbstractSkeleton.java
index 9ca1e9d95e62929c0015d5ca2c2f9c70e421842e..93443238b77c04f6a9da3c25427d200ccf4699f8 100644
--- a/src/main/java/net/minecraft/world/entity/monster/AbstractSkeleton.java
+++ b/src/main/java/net/minecraft/world/entity/monster/AbstractSkeleton.java
@@ -70,12 +70,14 @@ public abstract class AbstractSkeleton extends Monster implements RangedAttackMo
@Override
protected void registerGoals() {
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.goalSelector.addGoal(2, new RestrictSunGoal(this));
this.goalSelector.addGoal(3, new FleeSunGoal(this, 1.0D));
this.goalSelector.addGoal(3, new AvoidEntityGoal<>(this, Wolf.class, 6.0F, 1.0D, 1.2D));
this.goalSelector.addGoal(5, new WaterAvoidingRandomStrollGoal(this, 1.0D));
this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 8.0F));
this.goalSelector.addGoal(6, new RandomLookAroundGoal(this));
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.targetSelector.addGoal(1, new HurtByTargetGoal(this, new Class[0]));
this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true));
this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, IronGolem.class, true));
diff --git a/src/main/java/net/minecraft/world/entity/monster/Blaze.java b/src/main/java/net/minecraft/world/entity/monster/Blaze.java
index 17aa7676ab624440651850bbe5689f8a6c9dbeed..63049b0b4a4ae09e5925129dc363e43dbae23272 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Blaze.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Blaze.java
@@ -32,6 +32,7 @@ public class Blaze extends Monster {
public Blaze(EntityType<? extends Blaze> type, Level world) {
super(type, world);
+ this.moveControl = new org.purpurmc.purpur.controller.FlyingWithSpacebarMoveControllerWASD(this, 0.3F); // Purpur
this.setPathfindingMalus(BlockPathTypes.WATER, -1.0F);
this.setPathfindingMalus(BlockPathTypes.LAVA, 8.0F);
this.setPathfindingMalus(BlockPathTypes.DANGER_FIRE, 0.0F);
@@ -39,19 +40,55 @@ public class Blaze extends Monster {
this.xpReward = 10;
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.blazeRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.blazeRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.blazeControllable;
+ }
+
+ @Override
+ public double getMaxY() {
+ return level().purpurConfig.blazeMaxY;
+ }
+
+ @Override
+ public void travel(Vec3 vec3) {
+ super.travel(vec3);
+ if (getRider() != null && this.isControllable() && !onGround) {
+ float speed = (float) getAttributeValue(Attributes.FLYING_SPEED);
+ setSpeed(speed);
+ Vec3 mot = getDeltaMovement();
+ move(net.minecraft.world.entity.MoverType.SELF, mot.multiply(speed, 1.0, speed));
+ setDeltaMovement(mot.scale(0.9D));
+ }
+ }
+ // Purpur end
+
@Override
protected void registerGoals() {
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.goalSelector.addGoal(4, new Blaze.BlazeAttackGoal(this));
this.goalSelector.addGoal(5, new MoveTowardsRestrictionGoal(this, 1.0D));
this.goalSelector.addGoal(7, new WaterAvoidingRandomStrollGoal(this, 1.0D, 0.0F));
this.goalSelector.addGoal(8, new LookAtPlayerGoal(this, Player.class, 8.0F));
this.goalSelector.addGoal(8, new RandomLookAroundGoal(this));
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.targetSelector.addGoal(1, (new HurtByTargetGoal(this)).setAlertOthers());
this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true));
}
public static AttributeSupplier.Builder createAttributes() {
- return Monster.createMonsterAttributes().add(Attributes.ATTACK_DAMAGE, 6.0D).add(Attributes.MOVEMENT_SPEED, (double)0.23F).add(Attributes.FOLLOW_RANGE, 48.0D);
+ return Monster.createMonsterAttributes().add(Attributes.ATTACK_DAMAGE, 6.0D).add(Attributes.MOVEMENT_SPEED, (double)0.23F).add(Attributes.FOLLOW_RANGE, 48.0D).add(Attributes.FLYING_SPEED, 0.6D); // Purpur
}
@Override
@@ -106,6 +143,14 @@ public class Blaze extends Monster {
@Override
protected void customServerAiStep() {
+ // Purpur start
+ if (getRider() != null && this.isControllable()) {
+ Vec3 mot = getDeltaMovement();
+ setDeltaMovement(mot.x(), getVerticalMot() > 0 ? 0.07D : -0.07D, mot.z());
+ return;
+ }
+ // Purpur end
+
--this.nextHeightOffsetChangeTick;
if (this.nextHeightOffsetChangeTick <= 0) {
this.nextHeightOffsetChangeTick = 100;
diff --git a/src/main/java/net/minecraft/world/entity/monster/CaveSpider.java b/src/main/java/net/minecraft/world/entity/monster/CaveSpider.java
index da48bd8e15463be8170262ae90a8e95575959bf2..34abd8093015d7cdaf2faca2396d3950ca0a361b 100644
--- a/src/main/java/net/minecraft/world/entity/monster/CaveSpider.java
+++ b/src/main/java/net/minecraft/world/entity/monster/CaveSpider.java
@@ -28,6 +28,23 @@ public class CaveSpider extends Spider {
return Spider.createAttributes().add(Attributes.MAX_HEALTH, 12.0D);
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.caveSpiderRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.caveSpiderRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.caveSpiderControllable;
+ }
+ // Purpur end
+
@Override
public boolean doHurtTarget(Entity target) {
if (super.doHurtTarget(target)) {
diff --git a/src/main/java/net/minecraft/world/entity/monster/Creeper.java b/src/main/java/net/minecraft/world/entity/monster/Creeper.java
index fd1b5a1beea7594fa65decfdcccfa15781fc005b..66b2fdcda53fdfd278994dc16be3e12041be6e99 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Creeper.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Creeper.java
@@ -59,21 +59,98 @@ public class Creeper extends Monster implements PowerableMob {
public int maxSwell = 30;
public int explosionRadius = 3;
private int droppedSkulls;
+ // Purpur start
+ private int spacebarCharge = 0;
+ private int prevSpacebarCharge = 0;
+ private int powerToggleDelay = 0;
+ // Purpur end
public Creeper(EntityType<? extends Creeper> type, Level world) {
super(type, world);
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.creeperRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.creeperRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.creeperControllable;
+ }
+
+ @Override
+ protected void customServerAiStep() {
+ if (powerToggleDelay > 0) {
+ powerToggleDelay--;
+ }
+ if (getRider() != null && this.isControllable()) {
+ if (getRider().getForwardMot() != 0 || getRider().getStrafeMot() != 0) {
+ spacebarCharge = 0;
+ setIgnited(false);
+ setSwellDir(-1);
+ }
+ if (spacebarCharge == prevSpacebarCharge) {
+ spacebarCharge = 0;
+ }
+ prevSpacebarCharge = spacebarCharge;
+ }
+ super.customServerAiStep();
+ }
+
+ @Override
+ public void onMount(Player rider) {
+ super.onMount(rider);
+ setIgnited(false);
+ setSwellDir(-1);
+ }
+
+ @Override
+ public boolean onSpacebar() {
+ if (powerToggleDelay > 0) {
+ return true; // just toggled power, do not jump or ignite
+ }
+ spacebarCharge++;
+ if (spacebarCharge > maxSwell - 2) {
+ spacebarCharge = 0;
+ if (getRider() != null && getRider().getBukkitEntity().hasPermission("allow.powered.creeper")) {
+ powerToggleDelay = 20;
+ setPowered(!isPowered());
+ setIgnited(false);
+ setSwellDir(-1);
+ return true;
+ }
+ }
+ if (!isIgnited()) {
+ if (getRider() != null && getRider().getForwardMot() == 0 && getRider().getStrafeMot() == 0 &&
+ getRider().getBukkitEntity().hasPermission("allow.special.creeper")) {
+ setIgnited(true);
+ setSwellDir(1);
+ return true;
+ }
+ }
+ return getForwardMot() == 0 && getStrafeMot() == 0; // do not jump if standing still
+ }
+ // Purpur end
+
@Override
protected void registerGoals() {
this.goalSelector.addGoal(1, new FloatGoal(this));
this.goalSelector.addGoal(2, new SwellGoal(this));
+ this.goalSelector.addGoal(3, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.goalSelector.addGoal(3, new AvoidEntityGoal<>(this, Ocelot.class, 6.0F, 1.0D, 1.2D));
this.goalSelector.addGoal(3, new AvoidEntityGoal<>(this, Cat.class, 6.0F, 1.0D, 1.2D));
this.goalSelector.addGoal(4, new MeleeAttackGoal(this, 1.0D, false));
this.goalSelector.addGoal(5, new WaterAvoidingRandomStrollGoal(this, 0.8D));
this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 8.0F));
this.goalSelector.addGoal(6, new RandomLookAroundGoal(this));
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, Player.class, true));
this.targetSelector.addGoal(2, new HurtByTargetGoal(this, new Class[0]));
}
@@ -324,6 +401,7 @@ public class Creeper extends Monster implements PowerableMob {
com.destroystokyo.paper.event.entity.CreeperIgniteEvent event = new com.destroystokyo.paper.event.entity.CreeperIgniteEvent((org.bukkit.entity.Creeper) getBukkitEntity(), ignited);
if (event.callEvent()) {
this.entityData.set(Creeper.DATA_IS_IGNITED, event.isIgnited());
+ if (!event.isIgnited()) setSwellDir(-1); // Purpur
}
}
// Paper end
diff --git a/src/main/java/net/minecraft/world/entity/monster/Drowned.java b/src/main/java/net/minecraft/world/entity/monster/Drowned.java
index 991d728db2a3b64316fc2102cf3aee470327a62e..c64c88ddd929d49fcfc6ebc2d0030bc86c8ac337 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Drowned.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Drowned.java
@@ -68,6 +68,23 @@ public class Drowned extends Zombie implements RangedAttackMob {
this.groundNavigation = new GroundPathNavigation(this, world);
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.drownedRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.drownedRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.drownedControllable;
+ }
+ // Purpur end
+
@Override
protected void addBehaviourGoals() {
this.goalSelector.addGoal(1, new Drowned.DrownedGoToWaterGoal(this, 1.0D));
@@ -259,8 +276,7 @@ public class Drowned extends Zombie implements RangedAttackMob {
this.searchingForLand = targetingUnderwater;
}
- private static class DrownedMoveControl extends MoveControl {
-
+ private static class DrownedMoveControl extends org.purpurmc.purpur.controller.MoveControllerWASD { // Purpur
private final Drowned drowned;
public DrownedMoveControl(Drowned drowned) {
@@ -269,7 +285,7 @@ public class Drowned extends Zombie implements RangedAttackMob {
}
@Override
- public void tick() {
+ public void vanillaTick() { // Purpur
LivingEntity entityliving = this.drowned.getTarget();
if (this.drowned.wantsToSwim() && this.drowned.isInWater()) {
@@ -292,7 +308,7 @@ public class Drowned extends Zombie implements RangedAttackMob {
this.drowned.setYRot(this.rotlerp(this.drowned.getYRot(), f, 90.0F));
this.drowned.yBodyRot = this.drowned.getYRot();
- float f1 = (float) (this.speedModifier * this.drowned.getAttributeValue(Attributes.MOVEMENT_SPEED));
+ float f1 = (float) (this.getSpeedModifier() * this.drowned.getAttributeValue(Attributes.MOVEMENT_SPEED)); // Purpur
float f2 = Mth.lerp(0.125F, this.drowned.getSpeed(), f1);
this.drowned.setSpeed(f2);
@@ -302,7 +318,7 @@ public class Drowned extends Zombie implements RangedAttackMob {
this.drowned.setDeltaMovement(this.drowned.getDeltaMovement().add(0.0D, -0.008D, 0.0D));
}
- super.tick();
+ super.vanillaTick(); // Purpur
}
}
diff --git a/src/main/java/net/minecraft/world/entity/monster/ElderGuardian.java b/src/main/java/net/minecraft/world/entity/monster/ElderGuardian.java
index 8f481e11815d7162dd62a2b850b3d2af6d904519..16486ece9fc415d875ff94d9b806b0b5884ebc11 100644
--- a/src/main/java/net/minecraft/world/entity/monster/ElderGuardian.java
+++ b/src/main/java/net/minecraft/world/entity/monster/ElderGuardian.java
@@ -33,6 +33,18 @@ public class ElderGuardian extends Guardian {
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.elderGuardianRidable;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.elderGuardianControllable;
+ }
+ // Purpur end
+
public static AttributeSupplier.Builder createAttributes() {
return Guardian.createAttributes().add(Attributes.MOVEMENT_SPEED, 0.30000001192092896D).add(Attributes.ATTACK_DAMAGE, 8.0D).add(Attributes.MAX_HEALTH, 80.0D);
}
diff --git a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java
index 410f10ad93935d1c078447a4596023f367a8e9b7..dc359bb3a45a0b648933894e18d4ee6b51e3c7bc 100644
--- a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java
+++ b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java
@@ -96,9 +96,27 @@ public class EnderMan extends Monster implements NeutralMob {
this.setPathfindingMalus(BlockPathTypes.WATER, -1.0F);
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.endermanRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.endermanRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.endermanControllable;
+ }
+ // Purpur end
+
@Override
protected void registerGoals() {
this.goalSelector.addGoal(0, new FloatGoal(this));
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.goalSelector.addGoal(1, new EnderMan.EndermanFreezeWhenLookedAt(this));
this.goalSelector.addGoal(2, new MeleeAttackGoal(this, 1.0D, false));
this.goalSelector.addGoal(7, new WaterAvoidingRandomStrollGoal(this, 1.0D, 0.0F));
@@ -106,6 +124,7 @@ public class EnderMan extends Monster implements NeutralMob {
this.goalSelector.addGoal(8, new RandomLookAroundGoal(this));
this.goalSelector.addGoal(10, new EnderMan.EndermanLeaveBlockGoal(this));
this.goalSelector.addGoal(11, new EnderMan.EndermanTakeBlockGoal(this));
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.targetSelector.addGoal(1, new EnderMan.EndermanLookForPlayerGoal(this, this::isAngryAt));
this.targetSelector.addGoal(2, new HurtByTargetGoal(this, new Class[0]));
this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Endermite.class, true, false));
@@ -287,7 +306,7 @@ public class EnderMan extends Monster implements NeutralMob {
@Override
protected void customServerAiStep() {
- if (this.level().isDay() && this.tickCount >= this.targetChangeTime + 600) {
+ if ((getRider() == null || !this.isControllable()) && this.level().isDay() && this.tickCount >= this.targetChangeTime + 600) { // Purpur - no random teleporting
float f = this.getLightLevelDependentMagicValue();
if (f > 0.5F && this.level().canSeeSky(this.blockPosition()) && this.random.nextFloat() * 30.0F < (f - 0.4F) * 2.0F && this.tryEscape(com.destroystokyo.paper.event.entity.EndermanEscapeEvent.Reason.RUNAWAY)) { // Paper
@@ -408,6 +427,7 @@ public class EnderMan extends Monster implements NeutralMob {
public boolean hurt(DamageSource source, float amount) {
if (this.isInvulnerableTo(source)) {
return false;
+ } else if (getRider() != null && this.isControllable()) { return super.hurt(source, amount); // Purpur - no teleporting on damage
} else {
boolean flag = source.getDirectEntity() instanceof ThrownPotion;
boolean flag1;
diff --git a/src/main/java/net/minecraft/world/entity/monster/Endermite.java b/src/main/java/net/minecraft/world/entity/monster/Endermite.java
index 29b8771ec85f07fcd9d9ba041c9a25a212009b16..638665391f7552d054965f7e449b96ec6bceb03d 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Endermite.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Endermite.java
@@ -37,14 +37,33 @@ public class Endermite extends Monster {
this.xpReward = 3;
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.endermiteRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.endermiteRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.endermiteControllable;
+ }
+ // Purpur end
+
@Override
protected void registerGoals() {
this.goalSelector.addGoal(1, new FloatGoal(this));
+ this.goalSelector.addGoal(1, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.goalSelector.addGoal(1, new ClimbOnTopOfPowderSnowGoal(this, this.level()));
this.goalSelector.addGoal(2, new MeleeAttackGoal(this, 1.0D, false));
this.goalSelector.addGoal(3, new WaterAvoidingRandomStrollGoal(this, 1.0D));
this.goalSelector.addGoal(7, new LookAtPlayerGoal(this, Player.class, 8.0F));
this.goalSelector.addGoal(8, new RandomLookAroundGoal(this));
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.targetSelector.addGoal(1, (new HurtByTargetGoal(this)).setAlertOthers());
this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true));
}
diff --git a/src/main/java/net/minecraft/world/entity/monster/Evoker.java b/src/main/java/net/minecraft/world/entity/monster/Evoker.java
index a6dde4e31790eaecff27135a4036627192c85702..13e0fc756c6f572d0d688ae788e784eda6ce6d98 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Evoker.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Evoker.java
@@ -48,10 +48,28 @@ public class Evoker extends SpellcasterIllager {
this.xpReward = 10;
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.evokerRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.evokerRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.evokerControllable;
+ }
+ // Purpur end
+
@Override
protected void registerGoals() {
super.registerGoals();
this.goalSelector.addGoal(0, new FloatGoal(this));
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.goalSelector.addGoal(1, new Evoker.EvokerCastingSpellGoal());
this.goalSelector.addGoal(2, new AvoidEntityGoal<>(this, Player.class, 8.0F, 0.6D, 1.0D));
this.goalSelector.addGoal(4, new Evoker.EvokerSummonSpellGoal());
@@ -60,6 +78,7 @@ public class Evoker extends SpellcasterIllager {
this.goalSelector.addGoal(8, new RandomStrollGoal(this, 0.6D));
this.goalSelector.addGoal(9, new LookAtPlayerGoal(this, Player.class, 3.0F, 1.0F));
this.goalSelector.addGoal(10, new LookAtPlayerGoal(this, Mob.class, 8.0F));
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.targetSelector.addGoal(1, (new HurtByTargetGoal(this, new Class[]{Raider.class})).setAlertOthers());
this.targetSelector.addGoal(2, (new NearestAttackableTargetGoal<>(this, Player.class, true)).setUnseenMemoryTicks(300));
this.targetSelector.addGoal(3, (new NearestAttackableTargetGoal<>(this, AbstractVillager.class, false)).setUnseenMemoryTicks(300));
diff --git a/src/main/java/net/minecraft/world/entity/monster/Ghast.java b/src/main/java/net/minecraft/world/entity/monster/Ghast.java
index 592b0dae251800552a0771ec46b4b8532b63075d..9f09ccd41320423179dd1cf81c2a7094b88a008b 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Ghast.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Ghast.java
@@ -44,11 +44,47 @@ public class Ghast extends FlyingMob implements Enemy {
this.moveControl = new Ghast.GhastMoveControl(this);
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.ghastRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.ghastRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.ghastControllable;
+ }
+
+ @Override
+ public double getMaxY() {
+ return level().purpurConfig.ghastMaxY;
+ }
+
+ @Override
+ public void travel(Vec3 vec3) {
+ super.travel(vec3);
+ if (getRider() != null && this.isControllable() && !onGround) {
+ float speed = (float) getAttributeValue(Attributes.FLYING_SPEED);
+ setSpeed(speed);
+ Vec3 mot = getDeltaMovement();
+ move(net.minecraft.world.entity.MoverType.SELF, mot.multiply(speed, 1.0, speed));
+ setDeltaMovement(mot.scale(0.9D));
+ }
+ }
+ // Purpur end
+
@Override
protected void registerGoals() {
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.goalSelector.addGoal(5, new Ghast.RandomFloatAroundGoal(this));
this.goalSelector.addGoal(7, new Ghast.GhastLookGoal(this));
this.goalSelector.addGoal(7, new Ghast.GhastShootFireballGoal(this));
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, (entityliving) -> {
return Math.abs(entityliving.getY() - this.getY()) <= 4.0D;
}));
@@ -103,7 +139,7 @@ public class Ghast extends FlyingMob implements Enemy {
}
public static AttributeSupplier.Builder createAttributes() {
- return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 10.0D).add(Attributes.FOLLOW_RANGE, 100.0D);
+ return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 10.0D).add(Attributes.FOLLOW_RANGE, 100.0D).add(Attributes.FLYING_SPEED, 0.6D); // Purpur
}
@Override
@@ -160,7 +196,7 @@ public class Ghast extends FlyingMob implements Enemy {
return 2.6F;
}
- private static class GhastMoveControl extends MoveControl {
+ private static class GhastMoveControl extends org.purpurmc.purpur.controller.FlyingMoveControllerWASD { // Purpur
private final Ghast ghast;
private int floatDuration;
@@ -171,7 +207,7 @@ public class Ghast extends FlyingMob implements Enemy {
}
@Override
- public void tick() {
+ public void vanillaTick() { // Purpur
if (this.operation == MoveControl.Operation.MOVE_TO) {
if (this.floatDuration-- <= 0) {
this.floatDuration += this.ghast.getRandom().nextInt(5) + 2;
diff --git a/src/main/java/net/minecraft/world/entity/monster/Giant.java b/src/main/java/net/minecraft/world/entity/monster/Giant.java
index 41004c28edb748e12c4f868aa07b4672891197c1..33d9d90495efc8b80a8f948752becfb203ee8e63 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Giant.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Giant.java
@@ -14,6 +14,29 @@ public class Giant extends Monster {
super(type, world);
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.giantRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.giantRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.giantControllable;
+ }
+
+ @Override
+ protected void registerGoals() {
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this));
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this));
+ }
+ // Purpur end
+
@Override
protected float getStandingEyeHeight(Pose pose, EntityDimensions dimensions) {
return 10.440001F;
diff --git a/src/main/java/net/minecraft/world/entity/monster/Guardian.java b/src/main/java/net/minecraft/world/entity/monster/Guardian.java
index 5e9b9e9f124ec6103b2e832ae94bc5b334a2c533..a893a8cb6f9aa79520d9a7814be459d48e3c6853 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Guardian.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Guardian.java
@@ -69,15 +69,36 @@ public class Guardian extends Monster {
this.xpReward = 10;
this.setPathfindingMalus(BlockPathTypes.WATER, 0.0F);
this.moveControl = new Guardian.GuardianMoveControl(this);
+ // Purpur start
+ this.lookControl = new org.purpurmc.purpur.controller.LookControllerWASD(this) {
+ @Override
+ public void setYawPitch(float yaw, float pitch) {
+ super.setYawPitch(yaw, pitch * 0.35F);
+ }
+ };
+ // Purpur end
this.clientSideTailAnimation = this.random.nextFloat();
this.clientSideTailAnimationO = this.clientSideTailAnimation;
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.guardianRidable;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.guardianControllable;
+ }
+ // Purpur end
+
@Override
protected void registerGoals() {
MoveTowardsRestrictionGoal pathfindergoalmovetowardsrestriction = new MoveTowardsRestrictionGoal(this, 1.0D);
this.randomStrollGoal = new RandomStrollGoal(this, 1.0D, 80);
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.goalSelector.addGoal(4, this.guardianAttackGoal = new Guardian.GuardianAttackGoal(this)); // CraftBukkit - assign field
this.goalSelector.addGoal(5, pathfindergoalmovetowardsrestriction);
this.goalSelector.addGoal(7, this.randomStrollGoal);
@@ -86,6 +107,7 @@ public class Guardian extends Monster {
this.goalSelector.addGoal(9, new RandomLookAroundGoal(this));
this.randomStrollGoal.setFlags(EnumSet.of(Goal.Flag.MOVE, Goal.Flag.LOOK));
pathfindergoalmovetowardsrestriction.setFlags(EnumSet.of(Goal.Flag.MOVE, Goal.Flag.LOOK));
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, LivingEntity.class, 10, true, false, new Guardian.GuardianAttackSelector(this)));
}
@@ -351,7 +373,7 @@ public class Guardian extends Monster {
@Override
public void travel(Vec3 movementInput) {
if (this.isControlledByLocalInstance() && this.isInWater()) {
- this.moveRelative(0.1F, movementInput);
+ this.moveRelative(getRider() != null && this.isControllable() ? getSpeed() : 0.1F, movementInput); // Purpur
this.move(MoverType.SELF, this.getDeltaMovement());
this.setDeltaMovement(this.getDeltaMovement().scale(0.9D));
if (!this.isMoving() && this.getTarget() == null) {
@@ -363,7 +385,7 @@ public class Guardian extends Monster {
}
- private static class GuardianMoveControl extends MoveControl {
+ private static class GuardianMoveControl extends org.purpurmc.purpur.controller.WaterMoveControllerWASD { // Purpur
private final Guardian guardian;
@@ -372,8 +394,17 @@ public class Guardian extends Monster {
this.guardian = guardian;
}
+ // Purpur start
@Override
- public void tick() {
+ public void purpurTick(Player rider) {
+ super.purpurTick(rider);
+ guardian.setDeltaMovement(guardian.getDeltaMovement().add(0.0D, 0.005D, 0.0D));
+ guardian.setMoving(guardian.getForwardMot() > 0.0F); // control tail speed
+ }
+ // Purpur end
+
+ @Override
+ public void vanillaTick() { // Purpur
if (this.operation == MoveControl.Operation.MOVE_TO && !this.guardian.getNavigation().isDone()) {
Vec3 vec3d = new Vec3(this.wantedX - this.guardian.getX(), this.wantedY - this.guardian.getY(), this.wantedZ - this.guardian.getZ());
double d0 = vec3d.length();
@@ -384,7 +415,7 @@ public class Guardian extends Monster {
this.guardian.setYRot(this.rotlerp(this.guardian.getYRot(), f, 90.0F));
this.guardian.yBodyRot = this.guardian.getYRot();
- float f1 = (float) (this.speedModifier * this.guardian.getAttributeValue(Attributes.MOVEMENT_SPEED));
+ float f1 = (float) (this.getSpeedModifier() * this.guardian.getAttributeValue(Attributes.MOVEMENT_SPEED)); // Purpur
float f2 = Mth.lerp(0.125F, this.guardian.getSpeed(), f1);
this.guardian.setSpeed(f2);
diff --git a/src/main/java/net/minecraft/world/entity/monster/Husk.java b/src/main/java/net/minecraft/world/entity/monster/Husk.java
index efe8e6b895da1a81e5de42697fda58443cf15a59..5beed21038f88ebb4826b66013d0ae45a1f1ec34 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Husk.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Husk.java
@@ -22,6 +22,23 @@ public class Husk extends Zombie {
super(type, world);
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.huskRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.huskRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.huskControllable;
+ }
+ // Purpur end
+
public static boolean checkHuskSpawnRules(EntityType<Husk> type, ServerLevelAccessor world, MobSpawnType spawnReason, BlockPos pos, RandomSource random) {
return checkMonsterSpawnRules(type, world, spawnReason, pos, random) && (spawnReason == MobSpawnType.SPAWNER || world.canSeeSky(pos));
}
diff --git a/src/main/java/net/minecraft/world/entity/monster/Illusioner.java b/src/main/java/net/minecraft/world/entity/monster/Illusioner.java
index 63fce7e3d9f59f36e29bc827a46396d73143bb8b..f83f09f7568c44a1165aac73f7127c5d1af0478f 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Illusioner.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Illusioner.java
@@ -59,10 +59,28 @@ public class Illusioner extends SpellcasterIllager implements RangedAttackMob {
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.illusionerRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.illusionerRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.illusionerControllable;
+ }
+ // Purpur end
+
@Override
protected void registerGoals() {
super.registerGoals();
this.goalSelector.addGoal(0, new FloatGoal(this));
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.goalSelector.addGoal(1, new SpellcasterIllager.SpellcasterCastingSpellGoal());
this.goalSelector.addGoal(4, new Illusioner.IllusionerMirrorSpellGoal());
this.goalSelector.addGoal(5, new Illusioner.IllusionerBlindnessSpellGoal());
@@ -70,6 +88,7 @@ public class Illusioner extends SpellcasterIllager implements RangedAttackMob {
this.goalSelector.addGoal(8, new RandomStrollGoal(this, 0.6D));
this.goalSelector.addGoal(9, new LookAtPlayerGoal(this, Player.class, 3.0F, 1.0F));
this.goalSelector.addGoal(10, new LookAtPlayerGoal(this, Mob.class, 8.0F));
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.targetSelector.addGoal(1, (new HurtByTargetGoal(this, new Class[]{Raider.class})).setAlertOthers());
this.targetSelector.addGoal(2, (new NearestAttackableTargetGoal<>(this, Player.class, true)).setUnseenMemoryTicks(300));
this.targetSelector.addGoal(3, (new NearestAttackableTargetGoal<>(this, AbstractVillager.class, false)).setUnseenMemoryTicks(300));
diff --git a/src/main/java/net/minecraft/world/entity/monster/MagmaCube.java b/src/main/java/net/minecraft/world/entity/monster/MagmaCube.java
index 2858ea5562d06c11e5c7337a2b123f9be7a3f62e..aad51022eac584fbc058c2b25e2bf1929fccca6f 100644
--- a/src/main/java/net/minecraft/world/entity/monster/MagmaCube.java
+++ b/src/main/java/net/minecraft/world/entity/monster/MagmaCube.java
@@ -25,6 +25,28 @@ public class MagmaCube extends Slime {
super(type, world);
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.magmaCubeRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.magmaCubeRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.magmaCubeControllable;
+ }
+
+ @Override
+ public float getJumpPower() {
+ return 0.42F * this.getBlockJumpFactor(); // from EntityLiving
+ }
+ // Purpur end
+
public static AttributeSupplier.Builder createAttributes() {
return Monster.createMonsterAttributes().add(Attributes.MOVEMENT_SPEED, (double)0.2F);
}
@@ -70,11 +92,12 @@ public class MagmaCube extends Slime {
}
@Override
- protected void jumpFromGround() {
+ public void jumpFromGround() { // Purpur - protected -> public
Vec3 vec3 = this.getDeltaMovement();
float f = (float)this.getSize() * 0.1F;
this.setDeltaMovement(vec3.x, (double)(this.getJumpPower() + f), vec3.z);
this.hasImpulse = true;
+ this.actualJump = false; // Purpur
}
@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 4a132c3eff6978e927bcd4df56b9ce0306af6d19..8c8ff7d291999cc8ab251a145db000871050236f 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Phantom.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Phantom.java
@@ -60,6 +60,64 @@ public class Phantom extends FlyingMob implements Enemy {
this.lookControl = new Phantom.PhantomLookControl(this);
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.phantomRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.phantomRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.phantomControllable;
+ }
+
+ @Override
+ public double getMaxY() {
+ return level().purpurConfig.phantomMaxY;
+ }
+
+ @Override
+ public void travel(Vec3 vec3) {
+ super.travel(vec3);
+ if (getRider() != null && this.isControllable() && !onGround) {
+ float speed = (float) getAttributeValue(Attributes.FLYING_SPEED);
+ setSpeed(speed);
+ Vec3 mot = getDeltaMovement();
+ move(net.minecraft.world.entity.MoverType.SELF, mot.multiply(speed, speed, speed));
+ setDeltaMovement(mot.scale(0.9D));
+ }
+ }
+
+ public static net.minecraft.world.entity.ai.attributes.AttributeSupplier.Builder createAttributes() {
+ return Monster.createMonsterAttributes().add(Attributes.FLYING_SPEED, 3.0D);
+ }
+
+ @Override
+ public boolean onSpacebar() {
+ if (getRider() != null && getRider().getBukkitEntity().hasPermission("allow.special.phantom")) {
+ shoot();
+ }
+ return false;
+ }
+
+ public boolean shoot() {
+ org.bukkit.Location loc = ((org.bukkit.entity.LivingEntity) getBukkitEntity()).getEyeLocation();
+ loc.setPitch(-loc.getPitch());
+ org.bukkit.util.Vector target = loc.getDirection().normalize().multiply(100).add(loc.toVector());
+
+ org.purpurmc.purpur.entity.PhantomFlames flames = new org.purpurmc.purpur.entity.PhantomFlames(level(), this);
+ flames.canGrief = level().purpurConfig.phantomAllowGriefing;
+ flames.shoot(target.getX() - getX(), target.getY() - getY(), target.getZ() - getZ(), 1.0F, 5.0F);
+ level().addFreshEntity(flames);
+ return true;
+ }
+ // Purpur end
+
@Override
public boolean isFlapping() {
return (this.getUniqueFlapTickOffset() + this.tickCount) % Phantom.TICKS_PER_FLAP == 0;
@@ -72,9 +130,11 @@ 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
this.targetSelector.addGoal(1, new Phantom.PhantomAttackPlayerTargetGoal());
}
@@ -145,6 +205,7 @@ public class Phantom extends FlyingMob implements Enemy {
@Override
public void aiStep() {
if (this.isAlive() && shouldBurnInDay && this.isSunBurnTick()) { // Paper - Configurable Burning
+ if (getRider() == null || !this.isControllable()) // Purpur
this.setSecondsOnFire(8);
}
@@ -269,7 +330,7 @@ public class Phantom extends FlyingMob implements Enemy {
private AttackPhase() {}
}
- private class PhantomMoveControl extends MoveControl {
+ private class PhantomMoveControl extends org.purpurmc.purpur.controller.FlyingMoveControllerWASD { // Purpur
private float speed = 0.1F;
@@ -277,8 +338,19 @@ public class Phantom extends FlyingMob implements Enemy {
super(entity);
}
+ // Purpur start
+ public void purpurTick(Player rider) {
+ if (!Phantom.this.onGround) {
+ // phantom is always in motion when flying
+ // TODO - FIX THIS
+ // rider.setForward(1.0F);
+ }
+ super.purpurTick(rider);
+ }
+ // Purpur end
+
@Override
- public void tick() {
+ public void vanillaTick() { // Purpur
if (Phantom.this.horizontalCollision) {
Phantom.this.setYRot(Phantom.this.getYRot() + 180.0F);
this.speed = 0.1F;
@@ -324,14 +396,20 @@ public class Phantom extends FlyingMob implements Enemy {
}
}
- private class PhantomLookControl extends LookControl {
+ private class PhantomLookControl extends org.purpurmc.purpur.controller.LookControllerWASD { // Purpur
public PhantomLookControl(Mob entity) {
super(entity);
}
+ // Purpur start
+ public void purpurTick(Player rider) {
+ setYawPitch(rider.getYRot(), -rider.xRotO * 0.75F);
+ }
+ // Purpur end
+
@Override
- public void tick() {}
+ public void vanillaTick() {} // Purpur
}
private class PhantomBodyRotationControl extends BodyRotationControl {
diff --git a/src/main/java/net/minecraft/world/entity/monster/Pillager.java b/src/main/java/net/minecraft/world/entity/monster/Pillager.java
index cec545c3baa6599d47b9cf1a4b97de8771062a22..31d204d8d81ccc30371070af3678d82dc721618d 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Pillager.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Pillager.java
@@ -62,15 +62,34 @@ public class Pillager extends AbstractIllager implements CrossbowAttackMob, Inve
super(type, world);
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.pillagerRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.pillagerRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.pillagerControllable;
+ }
+ // Purpur end
+
@Override
protected void registerGoals() {
super.registerGoals();
this.goalSelector.addGoal(0, new FloatGoal(this));
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.goalSelector.addGoal(2, new Raider.HoldGroundAttackGoal(this, 10.0F));
this.goalSelector.addGoal(3, new RangedCrossbowAttackGoal<>(this, 1.0D, 8.0F));
this.goalSelector.addGoal(8, new RandomStrollGoal(this, 0.6D));
this.goalSelector.addGoal(9, new LookAtPlayerGoal(this, Player.class, 15.0F, 1.0F));
this.goalSelector.addGoal(10, new LookAtPlayerGoal(this, Mob.class, 15.0F));
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.targetSelector.addGoal(1, (new HurtByTargetGoal(this, Raider.class)).setAlertOthers());
this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true));
this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, AbstractVillager.class, false));
diff --git a/src/main/java/net/minecraft/world/entity/monster/Ravager.java b/src/main/java/net/minecraft/world/entity/monster/Ravager.java
index e87c6c8ab585d6b0b38bbb8e42c0082e38f03250..8a99c74f1cb6b558421b11e8562ba6082492b2d2 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Ravager.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Ravager.java
@@ -68,14 +68,39 @@ public class Ravager extends Raider {
this.setPathfindingMalus(BlockPathTypes.LEAVES, 0.0F);
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.ravagerRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.ravagerRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.ravagerControllable;
+ }
+
+ @Override
+ public void onMount(Player rider) {
+ super.onMount(rider);
+ getNavigation().stop();
+ }
+ // Purpur end
+
@Override
protected void registerGoals() {
super.registerGoals();
this.goalSelector.addGoal(0, new FloatGoal(this));
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.goalSelector.addGoal(4, new Ravager.RavagerMeleeAttackGoal());
this.goalSelector.addGoal(5, new WaterAvoidingRandomStrollGoal(this, 0.4D));
this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 6.0F));
this.goalSelector.addGoal(10, new LookAtPlayerGoal(this, Mob.class, 8.0F));
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.targetSelector.addGoal(2, (new HurtByTargetGoal(this, new Class[]{Raider.class})).setAlertOthers());
this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Player.class, true));
this.targetSelector.addGoal(4, new NearestAttackableTargetGoal<>(this, AbstractVillager.class, true, (entityliving) -> {
@@ -153,7 +178,7 @@ public class Ravager extends Raider {
@Override
public void aiStep() {
super.aiStep();
- if (this.isAlive()) {
+ if (this.isAlive() && (getRider() == null || !this.isControllable())) { // Purpur
if (this.isImmobile()) {
this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue(0.0D);
} else {
diff --git a/src/main/java/net/minecraft/world/entity/monster/Shulker.java b/src/main/java/net/minecraft/world/entity/monster/Shulker.java
index d2073de07bdad71b20e05046577b16649123967b..3e5548d0489b13b4a1d5c22c9d3bf19ead87928e 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Shulker.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Shulker.java
@@ -98,12 +98,31 @@ public class Shulker extends AbstractGolem implements VariantHolder<Optional<Dye
this.lookControl = new Shulker.ShulkerLookControl(this);
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.shulkerRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.shulkerRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.shulkerControllable;
+ }
+ // Purpur end
+
@Override
protected void registerGoals() {
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.goalSelector.addGoal(1, new LookAtPlayerGoal(this, Player.class, 8.0F, 0.02F, true));
this.goalSelector.addGoal(4, new Shulker.ShulkerAttackGoal());
this.goalSelector.addGoal(7, new Shulker.ShulkerPeekGoal());
this.goalSelector.addGoal(8, new RandomLookAroundGoal(this));
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.targetSelector.addGoal(1, (new HurtByTargetGoal(this, new Class[]{this.getClass()})).setAlertOthers());
this.targetSelector.addGoal(2, new Shulker.ShulkerNearestAttackGoal(this));
this.targetSelector.addGoal(3, new Shulker.ShulkerDefenseAttackGoal(this));
@@ -609,7 +628,7 @@ public class Shulker extends AbstractGolem implements VariantHolder<Optional<Dye
return b0 != 16 && b0 <= 15 ? DyeColor.byId(b0) : null;
}
- private class ShulkerLookControl extends LookControl {
+ private class ShulkerLookControl extends org.purpurmc.purpur.controller.LookControllerWASD { // Purpur
public ShulkerLookControl(Mob entity) {
super(entity);
diff --git a/src/main/java/net/minecraft/world/entity/monster/Silverfish.java b/src/main/java/net/minecraft/world/entity/monster/Silverfish.java
index 8d1f99f95a08eac98c6a03c6e534fc1997f8fe71..3b782334e9654acb6748c65970bb62626481da69 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Silverfish.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Silverfish.java
@@ -45,14 +45,33 @@ public class Silverfish extends Monster {
super(type, world);
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.silverfishRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.silverfishRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.silverfishControllable;
+ }
+ // Purpur end
+
@Override
protected void registerGoals() {
this.friendsGoal = new Silverfish.SilverfishWakeUpFriendsGoal(this);
this.goalSelector.addGoal(1, new FloatGoal(this));
+ this.goalSelector.addGoal(1, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.goalSelector.addGoal(1, new ClimbOnTopOfPowderSnowGoal(this, this.level()));
this.goalSelector.addGoal(3, this.friendsGoal);
this.goalSelector.addGoal(4, new MeleeAttackGoal(this, 1.0D, false));
this.goalSelector.addGoal(5, new Silverfish.SilverfishMergeWithStoneGoal(this));
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.targetSelector.addGoal(1, (new HurtByTargetGoal(this, new Class[0])).setAlertOthers());
this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true));
}
diff --git a/src/main/java/net/minecraft/world/entity/monster/Skeleton.java b/src/main/java/net/minecraft/world/entity/monster/Skeleton.java
index 8b818a7cb835512c4bd2ea9641d4bfd904150332..4db19589d19d1a0e488ddd0b691e81152ac236e4 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Skeleton.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Skeleton.java
@@ -26,6 +26,23 @@ public class Skeleton extends AbstractSkeleton {
super(type, world);
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.skeletonRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.skeletonRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.skeletonControllable;
+ }
+ // Purpur end
+
@Override
protected void defineSynchedData() {
super.defineSynchedData();
diff --git a/src/main/java/net/minecraft/world/entity/monster/Slime.java b/src/main/java/net/minecraft/world/entity/monster/Slime.java
index 343433158507451152e5b2fc5e5fd0b0e6b229db..c74db4080ad3d726c4b5cb374e20e6df2a262d2f 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Slime.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Slime.java
@@ -64,6 +64,7 @@ public class Slime extends Mob implements Enemy {
public float squish;
public float oSquish;
private boolean wasOnGround;
+ protected boolean actualJump; // Purpur
public Slime(EntityType<? extends Slime> type, Level world) {
super(type, world);
@@ -71,12 +72,48 @@ public class Slime extends Mob implements Enemy {
this.moveControl = new Slime.SlimeMoveControl(this);
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.slimeRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.slimeRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.slimeControllable;
+ }
+
+ @Override
+ public float getJumpPower() {
+ float height = super.getJumpPower();
+ return getRider() != null && this.isControllable() && actualJump ? height * 1.5F : height;
+ }
+
+ @Override
+ public boolean onSpacebar() {
+ if (onGround && getRider() != null && this.isControllable()) {
+ actualJump = true;
+ if (getRider().getForwardMot() == 0 || getRider().getStrafeMot() == 0) {
+ jumpFromGround(); // jump() here if not moving
+ }
+ }
+ return true; // do not jump() in wasd controller, let vanilla controller handle
+ }
+ // Purpur end
+
@Override
protected void registerGoals() {
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.goalSelector.addGoal(1, new Slime.SlimeFloatGoal(this));
this.goalSelector.addGoal(2, new Slime.SlimeAttackGoal(this));
this.goalSelector.addGoal(3, new Slime.SlimeRandomDirectionGoal(this));
this.goalSelector.addGoal(5, new Slime.SlimeKeepOnJumpingGoal(this));
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, (entityliving) -> {
return Math.abs(entityliving.getY() - this.getY()) <= 4.0D;
}));
@@ -368,11 +405,12 @@ public class Slime extends Mob implements Enemy {
}
@Override
- protected void jumpFromGround() {
+ public void jumpFromGround() { // Purpur - protected -> public
Vec3 vec3d = this.getDeltaMovement();
this.setDeltaMovement(vec3d.x, (double) this.getJumpPower(), vec3d.z);
this.hasImpulse = true;
+ this.actualJump = false; // Purpur
}
@Nullable
@@ -406,7 +444,7 @@ public class Slime extends Mob implements Enemy {
return super.getDimensions(pose).scale(0.255F * (float) this.getSize());
}
- private static class SlimeMoveControl extends MoveControl {
+ private static class SlimeMoveControl extends org.purpurmc.purpur.controller.MoveControllerWASD { // Purpur
private float yRot;
private int jumpDelay;
@@ -425,21 +463,33 @@ public class Slime extends Mob implements Enemy {
}
public void setWantedMovement(double speed) {
- this.speedModifier = speed;
+ this.setSpeedModifier(speed); // Purpur
this.operation = MoveControl.Operation.MOVE_TO;
}
@Override
public void tick() {
+ // Purpur start
+ if (slime.getRider() != null && slime.isControllable()) {
+ purpurTick(slime.getRider());
+ if (slime.getForwardMot() != 0 || slime.getStrafeMot() != 0) {
+ if (jumpDelay > 10) {
+ jumpDelay = 6;
+ }
+ } else {
+ jumpDelay = 20;
+ }
+ } else {
+ // Purpur end
this.mob.setYRot(this.rotlerp(this.mob.getYRot(), this.yRot, 90.0F));
this.mob.yHeadRot = this.mob.getYRot();
this.mob.yBodyRot = this.mob.getYRot();
- if (this.operation != MoveControl.Operation.MOVE_TO) {
+ } if ((slime.getRider() == null || !slime.isControllable()) && this.operation != MoveControl.Operation.MOVE_TO) { // Purpur
this.mob.setZza(0.0F);
} else {
this.operation = MoveControl.Operation.WAIT;
if (this.mob.onGround()) {
- this.mob.setSpeed((float) (this.speedModifier * this.mob.getAttributeValue(Attributes.MOVEMENT_SPEED)));
+ this.mob.setSpeed((float) (this.getSpeedModifier() * this.mob.getAttributeValue(Attributes.MOVEMENT_SPEED) * (slime.getRider() != null && slime.isControllable() && (slime.getRider().getForwardMot() != 0 || slime.getRider().getStrafeMot() != 0) ? 2.0D : 1.0D))); // Purpur
if (this.jumpDelay-- <= 0) {
this.jumpDelay = this.slime.getJumpDelay();
if (this.isAggressive) {
@@ -456,7 +506,7 @@ public class Slime extends Mob implements Enemy {
this.mob.setSpeed(0.0F);
}
} else {
- this.mob.setSpeed((float) (this.speedModifier * this.mob.getAttributeValue(Attributes.MOVEMENT_SPEED)));
+ this.mob.setSpeed((float) (this.getSpeedModifier() * this.mob.getAttributeValue(Attributes.MOVEMENT_SPEED) * (slime.getRider() != null && slime.isControllable() && (slime.getRider().getForwardMot() != 0 || slime.getRider().getStrafeMot() != 0) ? 2.0D : 1.0D))); // Purpur
}
}
diff --git a/src/main/java/net/minecraft/world/entity/monster/Spider.java b/src/main/java/net/minecraft/world/entity/monster/Spider.java
index 05432184077752b1d0cb764a5e39ed875748b2d6..9f6eaf76a39a9c3e7aed2e0ce91960200b4de664 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Spider.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Spider.java
@@ -51,14 +51,33 @@ public class Spider extends Monster {
super(type, world);
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.spiderRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.spiderRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.spiderControllable;
+ }
+ // Purpur end
+
@Override
protected void registerGoals() {
this.goalSelector.addGoal(1, new FloatGoal(this));
+ this.goalSelector.addGoal(1, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.goalSelector.addGoal(3, new LeapAtTargetGoal(this, 0.4F));
this.goalSelector.addGoal(4, new Spider.SpiderAttackGoal(this));
this.goalSelector.addGoal(5, new WaterAvoidingRandomStrollGoal(this, 0.8D));
this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 8.0F));
this.goalSelector.addGoal(6, new RandomLookAroundGoal(this));
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.targetSelector.addGoal(1, new HurtByTargetGoal(this, new Class[0]));
this.targetSelector.addGoal(2, new Spider.SpiderTargetGoal<>(this, Player.class));
this.targetSelector.addGoal(3, new Spider.SpiderTargetGoal<>(this, IronGolem.class));
diff --git a/src/main/java/net/minecraft/world/entity/monster/Stray.java b/src/main/java/net/minecraft/world/entity/monster/Stray.java
index 118b636a44e4b062e812e433f603b039276337da..137ee6d1aa8ec334009b6fab21b9f30303ace45f 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Stray.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Stray.java
@@ -21,6 +21,23 @@ public class Stray extends AbstractSkeleton {
super(type, world);
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.strayRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.strayRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.strayControllable;
+ }
+ // Purpur end
+
public static boolean checkStraySpawnRules(EntityType<Stray> type, ServerLevelAccessor world, MobSpawnType spawnReason, BlockPos pos, RandomSource random) {
BlockPos blockPos = pos;
diff --git a/src/main/java/net/minecraft/world/entity/monster/Strider.java b/src/main/java/net/minecraft/world/entity/monster/Strider.java
index ebf54d6e36fdee2833250816fae14195ac45eb67..ccc76bf5431af723eef8e4d39c96daa44d1f6b5c 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Strider.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Strider.java
@@ -100,6 +100,23 @@ public class Strider extends Animal implements ItemSteerable, Saddleable {
this.setPathfindingMalus(BlockPathTypes.DAMAGE_FIRE, 0.0F);
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.striderRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.striderRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.striderControllable;
+ }
+ // Purpur end
+
public static boolean checkStriderSpawnRules(EntityType<Strider> type, LevelAccessor world, MobSpawnType spawnReason, BlockPos pos, RandomSource random) {
BlockPos.MutableBlockPos blockposition_mutableblockposition = pos.mutable();
@@ -161,6 +178,7 @@ public class Strider extends Animal implements ItemSteerable, Saddleable {
@Override
protected void registerGoals() {
this.panicGoal = new PanicGoal(this, 1.65D);
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.goalSelector.addGoal(1, this.panicGoal);
this.goalSelector.addGoal(2, new BreedGoal(this, 1.0D));
this.temptGoal = new TemptGoal(this, 1.4D, Strider.TEMPT_ITEMS, false);
@@ -470,7 +488,7 @@ public class Strider extends Animal implements ItemSteerable, Saddleable {
if (!enuminteractionresult.consumesAction()) {
ItemStack itemstack = player.getItemInHand(hand);
- return itemstack.is(Items.SADDLE) ? itemstack.interactLivingEntity(player, this, hand) : InteractionResult.PASS;
+ return itemstack.is(Items.SADDLE) ? itemstack.interactLivingEntity(player, this, hand) : tryRide(player, hand); // Purpur
} else {
if (flag && !this.isSilent()) {
this.level().playSound((Player) null, this.getX(), this.getY(), this.getZ(), SoundEvents.STRIDER_EAT, this.getSoundSource(), 1.0F, 1.0F + (this.random.nextFloat() - this.random.nextFloat()) * 0.2F);
diff --git a/src/main/java/net/minecraft/world/entity/monster/Vex.java b/src/main/java/net/minecraft/world/entity/monster/Vex.java
index 65cb385ab294e362d666a6d03c4496cdc3b64890..40f42c83d6417258123753ba9315ca27c2b43b22 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Vex.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Vex.java
@@ -63,6 +63,50 @@ public class Vex extends Monster implements TraceableEntity {
this.xpReward = 3;
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.vexRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.vexRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.vexControllable;
+ }
+
+ @Override
+ public double getMaxY() {
+ return level().purpurConfig.vexMaxY;
+ }
+
+ @Override
+ public void travel(Vec3 vec3) {
+ super.travel(vec3);
+ if (getRider() != null && this.isControllable()) {
+ float speed;
+ if (onGround) {
+ speed = (float) getAttributeValue(Attributes.MOVEMENT_SPEED) * 0.1F;
+ } else {
+ speed = (float) getAttributeValue(Attributes.FLYING_SPEED);
+ }
+ setSpeed(speed);
+ Vec3 mot = getDeltaMovement();
+ move(MoverType.SELF, mot.multiply(speed, 1.0, speed));
+ setDeltaMovement(mot.scale(0.9D));
+ }
+ }
+
+ @Override
+ public boolean causeFallDamage(float fallDistance, float damageMultiplier, DamageSource damageSource) {
+ return false; // no fall damage please
+ }
+ // Purpur end
+
@Override
protected float getStandingEyeHeight(Pose pose, EntityDimensions dimensions) {
return dimensions.height - 0.28125F;
@@ -81,7 +125,7 @@ public class Vex extends Monster implements TraceableEntity {
@Override
public void tick() {
- this.noPhysics = true;
+ this.noPhysics = getRider() == null || !this.isControllable(); // Purpur
super.tick();
this.noPhysics = false;
this.setNoGravity(true);
@@ -96,17 +140,19 @@ public class Vex extends Monster implements TraceableEntity {
protected void registerGoals() {
super.registerGoals();
this.goalSelector.addGoal(0, new FloatGoal(this));
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.goalSelector.addGoal(4, new Vex.VexChargeAttackGoal());
this.goalSelector.addGoal(8, new Vex.VexRandomMoveGoal());
this.goalSelector.addGoal(9, new LookAtPlayerGoal(this, Player.class, 3.0F, 1.0F));
this.goalSelector.addGoal(10, new LookAtPlayerGoal(this, Mob.class, 8.0F));
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.targetSelector.addGoal(1, (new HurtByTargetGoal(this, new Class[]{Raider.class})).setAlertOthers());
this.targetSelector.addGoal(2, new Vex.VexCopyOwnerTargetGoal(this));
this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Player.class, true));
}
public static AttributeSupplier.Builder createAttributes() {
- return Monster.createMonsterAttributes().add(Attributes.MAX_HEALTH, 14.0D).add(Attributes.ATTACK_DAMAGE, 4.0D);
+ return Monster.createMonsterAttributes().add(Attributes.MAX_HEALTH, 14.0D).add(Attributes.ATTACK_DAMAGE, 4.0D).add(Attributes.FLYING_SPEED, 0.6D); // Purpur;
}
@Override
@@ -235,14 +281,14 @@ public class Vex extends Monster implements TraceableEntity {
return 0.4D;
}
- private class VexMoveControl extends MoveControl {
+ private class VexMoveControl extends org.purpurmc.purpur.controller.FlyingMoveControllerWASD { // Purpur
public VexMoveControl(Vex entityvex) {
super(entityvex);
}
@Override
- public void tick() {
+ public void vanillaTick() { // Purpur
if (this.operation == MoveControl.Operation.MOVE_TO) {
Vec3 vec3d = new Vec3(this.wantedX - Vex.this.getX(), this.wantedY - Vex.this.getY(), this.wantedZ - Vex.this.getZ());
double d0 = vec3d.length();
@@ -251,7 +297,7 @@ public class Vex extends Monster implements TraceableEntity {
this.operation = MoveControl.Operation.WAIT;
Vex.this.setDeltaMovement(Vex.this.getDeltaMovement().scale(0.5D));
} else {
- Vex.this.setDeltaMovement(Vex.this.getDeltaMovement().add(vec3d.scale(this.speedModifier * 0.05D / d0)));
+ Vex.this.setDeltaMovement(Vex.this.getDeltaMovement().add(vec3d.scale(this.getSpeedModifier() * 0.05D / d0))); // Purpur
if (Vex.this.getTarget() == null) {
Vec3 vec3d1 = Vex.this.getDeltaMovement();
diff --git a/src/main/java/net/minecraft/world/entity/monster/Vindicator.java b/src/main/java/net/minecraft/world/entity/monster/Vindicator.java
index 2acc531bd9e948251cac77d979f973678f576394..f8337259af80cede27e9a37c5634ccba9f837076 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Vindicator.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Vindicator.java
@@ -58,14 +58,33 @@ public class Vindicator extends AbstractIllager {
super(type, world);
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.vindicatorRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.vindicatorRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.vindicatorControllable;
+ }
+ // Purpur end
+
@Override
protected void registerGoals() {
super.registerGoals();
this.goalSelector.addGoal(0, new FloatGoal(this));
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.goalSelector.addGoal(1, new Vindicator.VindicatorBreakDoorGoal(this));
this.goalSelector.addGoal(2, new AbstractIllager.RaiderOpenDoorGoal(this));
this.goalSelector.addGoal(3, new Raider.HoldGroundAttackGoal(this, 10.0F));
this.goalSelector.addGoal(4, new Vindicator.VindicatorMeleeAttackGoal(this));
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.targetSelector.addGoal(1, (new HurtByTargetGoal(this, Raider.class)).setAlertOthers());
this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true));
this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, AbstractVillager.class, true));
diff --git a/src/main/java/net/minecraft/world/entity/monster/Witch.java b/src/main/java/net/minecraft/world/entity/monster/Witch.java
index 701f6bf1d558cf0ec4bc1abb9e1f66d96ea5a6c9..c7ceeb28b53879395c697cbb3778563511bc1209 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Witch.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Witch.java
@@ -57,6 +57,23 @@ public class Witch extends Raider implements RangedAttackMob {
super(type, world);
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.witchRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.witchRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.witchControllable;
+ }
+ // Purpur end
+
@Override
protected void registerGoals() {
super.registerGoals();
@@ -65,10 +82,12 @@ public class Witch extends Raider implements RangedAttackMob {
});
this.attackPlayersGoal = new NearestAttackableWitchTargetGoal<>(this, Player.class, 10, true, false, (Predicate) null);
this.goalSelector.addGoal(1, new FloatGoal(this));
+ this.goalSelector.addGoal(1, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.goalSelector.addGoal(2, new RangedAttackGoal(this, 1.0D, 60, 10.0F));
this.goalSelector.addGoal(2, new WaterAvoidingRandomStrollGoal(this, 1.0D));
this.goalSelector.addGoal(3, new LookAtPlayerGoal(this, Player.class, 8.0F));
this.goalSelector.addGoal(3, new RandomLookAroundGoal(this));
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.targetSelector.addGoal(1, new HurtByTargetGoal(this, new Class[]{Raider.class}));
this.targetSelector.addGoal(2, this.healRaidersGoal);
this.targetSelector.addGoal(3, this.attackPlayersGoal);
diff --git a/src/main/java/net/minecraft/world/entity/monster/WitherSkeleton.java b/src/main/java/net/minecraft/world/entity/monster/WitherSkeleton.java
index 9a81cb0c3a5ac40ff50dc7c81f6196a447cd03c6..45c7d673cb15fc002538c2713e36f591fe33de92 100644
--- a/src/main/java/net/minecraft/world/entity/monster/WitherSkeleton.java
+++ b/src/main/java/net/minecraft/world/entity/monster/WitherSkeleton.java
@@ -35,6 +35,23 @@ public class WitherSkeleton extends AbstractSkeleton {
this.setPathfindingMalus(BlockPathTypes.LAVA, 8.0F);
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.witherSkeletonRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.witherSkeletonRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.witherSkeletonControllable;
+ }
+ // Purpur end
+
@Override
protected void registerGoals() {
this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, AbstractPiglin.class, true));
diff --git a/src/main/java/net/minecraft/world/entity/monster/Zoglin.java b/src/main/java/net/minecraft/world/entity/monster/Zoglin.java
index 7815af9b64ead32d6f7bcad6f86b2d21ec4aec22..393dde215894616c073efd28b529c9dac9815adc 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Zoglin.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Zoglin.java
@@ -67,6 +67,23 @@ public class Zoglin extends Monster implements Enemy, HoglinBase {
this.xpReward = 5;
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.zoglinRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.zoglinRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.zoglinControllable;
+ }
+ // Purpur end
+
@Override
protected Brain.Provider<Zoglin> brainProvider() {
return Brain.provider(MEMORY_TYPES, SENSOR_TYPES);
@@ -199,6 +216,7 @@ public class Zoglin extends Monster implements Enemy, HoglinBase {
@Override
protected void customServerAiStep() {
this.level().getProfiler().push("zoglinBrain");
+ if (getRider() == null || !this.isControllable()) // Purpur - only use brain if no rider
this.getBrain().tick((ServerLevel)this.level(), this);
this.level().getProfiler().pop();
this.updateActivity();
diff --git a/src/main/java/net/minecraft/world/entity/monster/Zombie.java b/src/main/java/net/minecraft/world/entity/monster/Zombie.java
index 3f8c1d1d3c408fc4f15c4b5680bc22c86f104a9d..d3f1bcd9daeea282bef549ea18cedf0fc7a9ad8d 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Zombie.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Zombie.java
@@ -106,11 +106,30 @@ public class Zombie extends Monster {
this(EntityType.ZOMBIE, world);
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.zombieRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.zombieRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.zombieControllable;
+ }
+ // Purpur end
+
@Override
protected void registerGoals() {
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
if (this.level().paperConfig().entities.behavior.zombiesTargetTurtleEggs) this.goalSelector.addGoal(4, new Zombie.ZombieAttackTurtleEggGoal(this, 1.0D, 3)); // Paper
this.goalSelector.addGoal(8, new LookAtPlayerGoal(this, Player.class, 8.0F));
this.goalSelector.addGoal(8, new RandomLookAroundGoal(this));
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
this.addBehaviourGoals();
}
diff --git a/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java b/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java
index 25ed5571b24e590bc95056020d84496492b53298..87ae149be7a3eaf14406a7df9f174574d77c63c5 100644
--- a/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java
+++ b/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java
@@ -79,6 +79,23 @@ public class ZombieVillager extends Zombie implements VillagerDataHolder {
});
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.zombieVillagerRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.zombieVillagerRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.zombieVillagerControllable;
+ }
+ // Purpur end
+
@Override
protected void defineSynchedData() {
super.defineSynchedData();
diff --git a/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java b/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java
index aff140cfb2bbdce8b512080cf394c84c5c4ff807..fe9bdc4006b916748e55a976b6c8621070fb73c5 100644
--- a/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java
+++ b/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java
@@ -63,6 +63,23 @@ public class ZombifiedPiglin extends Zombie implements NeutralMob {
this.setPathfindingMalus(BlockPathTypes.LAVA, 8.0F);
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.zombifiedPiglinRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.zombifiedPiglinRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.zombifiedPiglinControllable;
+ }
+ // Purpur end
+
@Override
public void setPersistentAngerTarget(@Nullable UUID angryAt) {
this.persistentAngerTarget = angryAt;
diff --git a/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java b/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java
index d6d61b91096d28eea1e5af69ea1c07890820ee7f..8ba7896a823d60065c1e293d0fcdb2d3ac76da77 100644
--- a/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java
+++ b/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java
@@ -67,6 +67,23 @@ public class Hoglin extends Animal implements Enemy, HoglinBase {
this.xpReward = 5;
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.hoglinRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.hoglinRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.hoglinControllable;
+ }
+ // Purpur end
+
@Override
public boolean canBeLeashed(Player player) {
return !this.isLeashed();
@@ -130,7 +147,7 @@ public class Hoglin extends Animal implements Enemy, HoglinBase {
@Override
protected void customServerAiStep() {
this.level().getProfiler().push("hoglinBrain");
- if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish
+ if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider
this.getBrain().tick((ServerLevel)this.level(), this);
this.level().getProfiler().pop();
HoglinAi.updateActivity(this);
diff --git a/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java b/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java
index e235cc9d1b3ce59ab662ef3cf3ce0267ca78536d..c54f70f608093511f9ef1e7e904d9497a4710bdd 100644
--- a/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java
+++ b/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java
@@ -96,6 +96,23 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento
this.xpReward = 5;
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.piglinRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.piglinRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.piglinControllable;
+ }
+ // Purpur end
+
@Override
public void addAdditionalSaveData(CompoundTag nbt) {
super.addAdditionalSaveData(nbt);
@@ -309,7 +326,7 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento
@Override
protected void customServerAiStep() {
this.level().getProfiler().push("piglinBrain");
- if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish
+ if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider
this.getBrain().tick((ServerLevel) this.level(), this);
this.level().getProfiler().pop();
PiglinAi.updateActivity(this);
diff --git a/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinBrute.java b/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinBrute.java
index e1be4a77fae0b9120781f460079269b85c993930..2d842c0dfce1c7e7229bd42b2a92c024a4162b68 100644
--- a/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinBrute.java
+++ b/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinBrute.java
@@ -41,6 +41,23 @@ public class PiglinBrute extends AbstractPiglin {
this.xpReward = 20;
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.piglinBruteRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.piglinBruteRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.piglinBruteControllable;
+ }
+ // Purpur end
+
public static AttributeSupplier.Builder createAttributes() {
return Monster.createMonsterAttributes().add(Attributes.MAX_HEALTH, 50.0D).add(Attributes.MOVEMENT_SPEED, (double)0.35F).add(Attributes.ATTACK_DAMAGE, 7.0D);
}
@@ -86,6 +103,7 @@ public class PiglinBrute extends AbstractPiglin {
@Override
protected void customServerAiStep() {
this.level().getProfiler().push("piglinBruteBrain");
+ if (getRider() == null || this.isControllable()) // Purpur - only use brain if no rider
this.getBrain().tick((ServerLevel)this.level(), this);
this.level().getProfiler().pop();
PiglinBruteAi.updateActivity(this);
diff --git a/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java b/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java
index 71db8bd6885377d55cfc571fccc21df6d8a57b54..0aa6874022d4ee8e38f2d85c45a2a4201c2a83fe 100644
--- a/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java
+++ b/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java
@@ -121,8 +121,32 @@ public class Warden extends Monster implements VibrationSystem {
this.setPathfindingMalus(BlockPathTypes.LAVA, 8.0F);
this.setPathfindingMalus(BlockPathTypes.DAMAGE_FIRE, 0.0F);
this.setPathfindingMalus(BlockPathTypes.DANGER_FIRE, 0.0F);
+ this.moveControl = new org.purpurmc.purpur.controller.MoveControllerWASD(this, 0.5F); // Purpur
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.wardenRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.wardenRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.wardenControllable;
+ }
+
+ @Override
+ protected void registerGoals() {
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur
+ }
+ // Purpur end
+
@Override
public Packet<ClientGamePacketListener> getAddEntityPacket() {
return new ClientboundAddEntityPacket(this, this.hasPose(Pose.EMERGING) ? 1 : 0);
@@ -396,19 +420,16 @@ public class Warden extends Monster implements VibrationSystem {
@Contract("null->false")
public boolean canTargetEntity(@Nullable Entity entity) {
- boolean flag;
-
+ if (getRider() != null && isControllable()) return false; // Purpur
if (entity instanceof LivingEntity) {
LivingEntity entityliving = (LivingEntity) entity;
if (this.level() == entity.level() && EntitySelector.NO_CREATIVE_OR_SPECTATOR.test(entity) && !this.isAlliedTo(entity) && entityliving.getType() != EntityType.ARMOR_STAND && entityliving.getType() != EntityType.WARDEN && !entityliving.isInvulnerable() && !entityliving.isDeadOrDying() && this.level().getWorldBorder().isWithinBounds(entityliving.getBoundingBox())) {
- flag = true;
- return flag;
+ return true; // Purpur - wtf
}
}
- flag = false;
- return flag;
+ return false; // Purpur - wtf
}
public static void applyDarknessAround(ServerLevel world, Vec3 pos, @Nullable Entity entity, int range) {
diff --git a/src/main/java/net/minecraft/world/entity/npc/Villager.java b/src/main/java/net/minecraft/world/entity/npc/Villager.java
index c4ddf2661bca728d504918171295e10e307b18b4..7b58329067d8debeb34abe801d150038a362ffed 100644
--- a/src/main/java/net/minecraft/world/entity/npc/Villager.java
+++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java
@@ -156,6 +156,28 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
this.setVillagerData(this.getVillagerData().setType(type).setProfession(VillagerProfession.NONE));
}
+ // Purpur start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.villagerRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.villagerRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.villagerControllable;
+ }
+
+ @Override
+ protected void registerGoals() {
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this));
+ }
+ // Purpur end
+
@Override
public Brain<Villager> getBrain() {
return (Brain<Villager>) super.getBrain(); // CraftBukkit - decompile error
@@ -257,7 +279,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
// Paper end
this.level().getProfiler().push("villagerBrain");
// Pufferfish start
- if (!inactive && this.behaviorTick++ % this.activatedPriority == 0) {
+ if (!inactive && (getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) { // Purpur - only use brain if no rider
this.getBrain().tick((ServerLevel) this.level(), this); // Paper
}
// Pufferfish end
@@ -317,7 +339,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
if (!itemstack.is(Items.VILLAGER_SPAWN_EGG) && this.isAlive() && !this.isTrading() && !this.isSleeping()) {
if (this.isBaby()) {
this.setUnhappy();
- return InteractionResult.sidedSuccess(this.level().isClientSide);
+ return tryRide(player, hand, InteractionResult.sidedSuccess(this.level().isClientSide)); // Purpur
} else {
boolean flag = this.getOffers().isEmpty();
@@ -330,8 +352,9 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
}
if (flag) {
- return InteractionResult.sidedSuccess(this.level().isClientSide);
+ return tryRide(player, hand, InteractionResult.sidedSuccess(this.level().isClientSide)); // Purpur
} else {
+ if (level().purpurConfig.villagerRidable && itemstack.isEmpty()) return tryRide(player, hand); // Purpur
if (!this.level().isClientSide && !this.offers.isEmpty()) {
this.startTrading(player);
}
diff --git a/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java b/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java
index 0002f3a2f2e096d4b2015baf707707f6cc1a9582..7e5650a56d87a48df7ee0a862b22bf015bb48b2a 100644
--- a/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java
+++ b/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java
@@ -66,6 +66,23 @@ public class WanderingTrader extends net.minecraft.world.entity.npc.AbstractVill
//this.setDespawnDelay(48000); // CraftBukkit - set default from MobSpawnerTrader // Paper - move back to MobSpawnerTrader - Vanilla behavior is that only traders spawned by it have this value set.
}
+ // Purpur - start
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.wanderingTraderRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.wanderingTraderRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.wanderingTraderControllable;
+ }
+ // Purpur end
+
@Override
protected void registerGoals() {
this.goalSelector.addGoal(0, new FloatGoal(this));
@@ -113,8 +130,9 @@ public class WanderingTrader extends net.minecraft.world.entity.npc.AbstractVill
}
if (this.getOffers().isEmpty()) {
- return InteractionResult.sidedSuccess(this.level().isClientSide);
+ return tryRide(player, hand, InteractionResult.sidedSuccess(this.level().isClientSide)); // Purpur
} else {
+ if (level().purpurConfig.wanderingTraderRidable && itemstack.isEmpty()) return tryRide(player, hand); // Purpur
if (!this.level().isClientSide) {
this.setTradingPlayer(player);
this.openTradingScreen(player, this.getDisplayName(), 1);
diff --git a/src/main/java/net/minecraft/world/entity/player/Player.java b/src/main/java/net/minecraft/world/entity/player/Player.java
index 58152160d609d0e9d105153aeb166a56a7955603..cf008b8c7a2eb0c642fd9c2da376df86dcd588e2 100644
--- a/src/main/java/net/minecraft/world/entity/player/Player.java
+++ b/src/main/java/net/minecraft/world/entity/player/Player.java
@@ -198,6 +198,19 @@ public abstract class Player extends LivingEntity {
}
// CraftBukkit end
+ // Purpur start
+ public abstract void resetLastActionTime();
+
+ @Override
+ public boolean processClick(InteractionHand hand) {
+ Entity vehicle = getRootVehicle();
+ if (vehicle != null && vehicle.getRider() == this) {
+ return vehicle.onClick(hand);
+ }
+ return false;
+ }
+ // Purpur end
+
public Player(Level world, BlockPos pos, float yaw, GameProfile gameProfile) {
super(EntityType.PLAYER, world);
this.lastItemInMainHand = ItemStack.EMPTY;
diff --git a/src/main/java/net/minecraft/world/entity/projectile/LlamaSpit.java b/src/main/java/net/minecraft/world/entity/projectile/LlamaSpit.java
index 0bbe853f7df93f9dcd2b21d762939f8b6be069aa..7db9844083703944f59e112c6dc5e1a5e062d31c 100644
--- a/src/main/java/net/minecraft/world/entity/projectile/LlamaSpit.java
+++ b/src/main/java/net/minecraft/world/entity/projectile/LlamaSpit.java
@@ -26,6 +26,12 @@ public class LlamaSpit extends Projectile {
this.setPos(owner.getX() - (double) (owner.getBbWidth() + 1.0F) * 0.5D * (double) Mth.sin(owner.yBodyRot * 0.017453292F), owner.getEyeY() - 0.10000000149011612D, owner.getZ() + (double) (owner.getBbWidth() + 1.0F) * 0.5D * (double) Mth.cos(owner.yBodyRot * 0.017453292F));
}
+ // Purpur start
+ public void super_tick() {
+ super.tick();
+ }
+ // Purpur end
+
@Override
public void tick() {
super.tick();
diff --git a/src/main/java/net/minecraft/world/entity/projectile/ProjectileUtil.java b/src/main/java/net/minecraft/world/entity/projectile/ProjectileUtil.java
index cc0a3d9794d05b6bc6ab05f4f2ab8d83134b181d..e1f918d0bd2a70db1aba8bda8717149f58766825 100644
--- a/src/main/java/net/minecraft/world/entity/projectile/ProjectileUtil.java
+++ b/src/main/java/net/minecraft/world/entity/projectile/ProjectileUtil.java
@@ -33,7 +33,7 @@ public final class ProjectileUtil {
return getHitResult(vec32, entity, predicate, vec3, level);
}
- private static HitResult getHitResult(Vec3 pos, Entity entity, Predicate<Entity> predicate, Vec3 velocity, Level world) {
+ public static HitResult getHitResult(Vec3 pos, Entity entity, Predicate<Entity> predicate, Vec3 velocity, Level world) { // Purpur - private -> public
Vec3 vec3 = pos.add(velocity);
HitResult hitResult = world.clip(new ClipContext(pos, vec3, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, entity));
if (hitResult.getType() != HitResult.Type.MISS) {
diff --git a/src/main/java/net/minecraft/world/entity/projectile/WitherSkull.java b/src/main/java/net/minecraft/world/entity/projectile/WitherSkull.java
index 06d1bdb9bd124b201c36d284c50d22bf50d3735a..937f57d8af629c4e913d7ccabf6adab15f91d3d0 100644
--- a/src/main/java/net/minecraft/world/entity/projectile/WitherSkull.java
+++ b/src/main/java/net/minecraft/world/entity/projectile/WitherSkull.java
@@ -109,6 +109,12 @@ public class WitherSkull extends AbstractHurtingProjectile {
}
+ @Override
+ public boolean canHitEntity(Entity target) {
+ // do not hit rider
+ return target != this.getRider() && super.canHitEntity(target);
+ }
+
@Override
public boolean isPickable() {
return false;
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
index 2dbe8b870fd39b4d22e9725912f443757ae70761..912d9e4b3168cf89bd263ba63e049a791beefc90 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
@@ -1507,4 +1507,27 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
return this.getHandle().getScoreboardName();
}
// Paper end - entity scoreboard name
+
+ // Purpur start
+ @Override
+ public org.bukkit.entity.Player getRider() {
+ net.minecraft.world.entity.player.Player rider = getHandle().getRider();
+ return rider != null ? (org.bukkit.entity.Player) rider.getBukkitEntity() : null;
+ }
+
+ @Override
+ public boolean hasRider() {
+ return getHandle().getRider() != null;
+ }
+
+ @Override
+ public boolean isRidable() {
+ return getHandle().isRidable();
+ }
+
+ @Override
+ public boolean isRidableInWater() {
+ return !getHandle().dismountsUnderwater();
+ }
+ // Purpur end
}
diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
index 676c44f688c41df66e304db30a05d6cc967bcf99..222fc8368b6bd0ca32811aebfa611dd5037076b5 100644
--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
+++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
@@ -590,6 +590,15 @@ public class CraftEventFactory {
// Paper end
craftServer.getPluginManager().callEvent(event);
+ // Purpur start
+ if (who != null) {
+ switch (action) {
+ case LEFT_CLICK_BLOCK, LEFT_CLICK_AIR -> who.processClick(InteractionHand.MAIN_HAND);
+ case RIGHT_CLICK_BLOCK, RIGHT_CLICK_AIR -> who.processClick(InteractionHand.OFF_HAND);
+ }
+ }
+ // Purpur end
+
return event;
}
@@ -1031,6 +1040,7 @@ public class CraftEventFactory {
damageCause = DamageCause.ENTITY_EXPLOSION;
}
event = new EntityDamageByEntityEvent(damager.getBukkitEntity(), entity.getBukkitEntity(), damageCause, modifiers, modifierFunctions, source.isCritical()); // Paper - add critical damage API
+ damager.processClick(InteractionHand.MAIN_HAND); // Purpur
}
event.setCancelled(cancelled);
@@ -1145,6 +1155,7 @@ public class CraftEventFactory {
} else {
entity.lastDamageCancelled = true; // SPIGOT-5339, SPIGOT-6252, SPIGOT-6777: Keep track if the event was canceled
}
+ damager.getHandle().processClick(InteractionHand.MAIN_HAND); // Purpur
return event;
}
@@ -1208,6 +1219,7 @@ public class CraftEventFactory {
EntityDamageEvent event;
if (damager != null) {
event = new EntityDamageByEntityEvent(damager.getBukkitEntity(), damagee.getBukkitEntity(), cause, modifiers, modifierFunctions, critical); // Paper - add critical damage API
+ damager.processClick(InteractionHand.MAIN_HAND); // Purpur
} else {
event = new EntityDamageEvent(damagee.getBukkitEntity(), cause, modifiers, modifierFunctions);
}
diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java
index 61d193d8ddd87817bf2c560037d42366cff1eca9..8ad548fc059568d37675e017548b171d4dd1d555 100644
--- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java
+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java
@@ -172,4 +172,9 @@ public class PurpurConfig {
}
return builder.build();
}
+
+ public static String cannotRideMob = "<red>You cannot mount that mob";
+ private static void messages() {
+ cannotRideMob = getString("settings.messages.cannot-ride-mob", cannotRideMob);
+ }
}
diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java
index 5f0732c2b8f85185b6dfc1db3119c22e8be7f5da..4c25461a6d75a47425b66e04285792787d7193ee 100644
--- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java
+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java
@@ -89,4 +89,722 @@ public class PurpurWorldConfig {
final Map<String, Object> value = PurpurConfig.getMap("world-settings." + worldName + "." + path, null);
return value.isEmpty() ? fallback : value;
}
+
+ public boolean babiesAreRidable = true;
+ public boolean untamedTamablesAreRidable = true;
+ public boolean useNightVisionWhenRiding = false;
+ public boolean useDismountsUnderwaterTag = true;
+ private void ridableSettings() {
+ babiesAreRidable = getBoolean("ridable-settings.babies-are-ridable", babiesAreRidable);
+ untamedTamablesAreRidable = getBoolean("ridable-settings.untamed-tamables-are-ridable", untamedTamablesAreRidable);
+ useNightVisionWhenRiding = getBoolean("ridable-settings.use-night-vision", useNightVisionWhenRiding);
+ useDismountsUnderwaterTag = getBoolean("ridable-settings.use-dismounts-underwater-tag", useDismountsUnderwaterTag);
+ }
+
+ public boolean allayRidable = false;
+ public boolean allayRidableInWater = true;
+ public boolean allayControllable = true;
+ private void allaySettings() {
+ allayRidable = getBoolean("mobs.allay.ridable", allayRidable);
+ allayRidableInWater = getBoolean("mobs.allay.ridable-in-water", allayRidableInWater);
+ allayControllable = getBoolean("mobs.allay.controllable", allayControllable);
+ }
+
+ public boolean axolotlRidable = false;
+ public boolean axolotlControllable = true;
+ private void axolotlSettings() {
+ axolotlRidable = getBoolean("mobs.axolotl.ridable", axolotlRidable);
+ axolotlControllable = getBoolean("mobs.axolotl.controllable", axolotlControllable);
+ }
+
+ public boolean batRidable = false;
+ public boolean batRidableInWater = true;
+ public boolean batControllable = true;
+ public double batMaxY = 320D;
+ private void batSettings() {
+ batRidable = getBoolean("mobs.bat.ridable", batRidable);
+ batRidableInWater = getBoolean("mobs.bat.ridable-in-water", batRidableInWater);
+ batControllable = getBoolean("mobs.bat.controllable", batControllable);
+ batMaxY = getDouble("mobs.bat.ridable-max-y", batMaxY);
+ }
+
+ public boolean beeRidable = false;
+ public boolean beeRidableInWater = true;
+ public boolean beeControllable = true;
+ public double beeMaxY = 320D;
+ private void beeSettings() {
+ beeRidable = getBoolean("mobs.bee.ridable", beeRidable);
+ beeRidableInWater = getBoolean("mobs.bee.ridable-in-water", beeRidableInWater);
+ beeControllable = getBoolean("mobs.bee.controllable", beeControllable);
+ beeMaxY = getDouble("mobs.bee.ridable-max-y", beeMaxY);
+ }
+
+ public boolean blazeRidable = false;
+ public boolean blazeRidableInWater = true;
+ public boolean blazeControllable = true;
+ public double blazeMaxY = 320D;
+ private void blazeSettings() {
+ blazeRidable = getBoolean("mobs.blaze.ridable", blazeRidable);
+ blazeRidableInWater = getBoolean("mobs.blaze.ridable-in-water", blazeRidableInWater);
+ blazeControllable = getBoolean("mobs.blaze.controllable", blazeControllable);
+ blazeMaxY = getDouble("mobs.blaze.ridable-max-y", blazeMaxY);
+ }
+
+ public boolean camelRidableInWater = false;
+ private void camelSettings() {
+ camelRidableInWater = getBoolean("mobs.camel.ridable-in-water", camelRidableInWater);
+ }
+
+ public boolean catRidable = false;
+ public boolean catRidableInWater = true;
+ public boolean catControllable = true;
+ private void catSettings() {
+ catRidable = getBoolean("mobs.cat.ridable", catRidable);
+ catRidableInWater = getBoolean("mobs.cat.ridable-in-water", catRidableInWater);
+ catControllable = getBoolean("mobs.cat.controllable", catControllable);
+ }
+
+ public boolean caveSpiderRidable = false;
+ public boolean caveSpiderRidableInWater = true;
+ public boolean caveSpiderControllable = true;
+ private void caveSpiderSettings() {
+ caveSpiderRidable = getBoolean("mobs.cave_spider.ridable", caveSpiderRidable);
+ caveSpiderRidableInWater = getBoolean("mobs.cave_spider.ridable-in-water", caveSpiderRidableInWater);
+ caveSpiderControllable = getBoolean("mobs.cave_spider.controllable", caveSpiderControllable);
+ }
+
+ public boolean chickenRidable = false;
+ public boolean chickenRidableInWater = false;
+ public boolean chickenControllable = true;
+ private void chickenSettings() {
+ chickenRidable = getBoolean("mobs.chicken.ridable", chickenRidable);
+ chickenRidableInWater = getBoolean("mobs.chicken.ridable-in-water", chickenRidableInWater);
+ chickenControllable = getBoolean("mobs.chicken.controllable", chickenControllable);
+ }
+
+ public boolean codRidable = false;
+ public boolean codControllable = true;
+ private void codSettings() {
+ codRidable = getBoolean("mobs.cod.ridable", codRidable);
+ codControllable = getBoolean("mobs.cod.controllable", codControllable);
+ }
+
+ public boolean cowRidable = false;
+ public boolean cowRidableInWater = true;
+ public boolean cowControllable = true;
+ private void cowSettings() {
+ cowRidable = getBoolean("mobs.cow.ridable", cowRidable);
+ cowRidableInWater = getBoolean("mobs.cow.ridable-in-water", cowRidableInWater);
+ cowControllable = getBoolean("mobs.cow.controllable", cowControllable);
+ }
+
+ public boolean creeperRidable = false;
+ public boolean creeperRidableInWater = true;
+ public boolean creeperControllable = true;
+ private void creeperSettings() {
+ creeperRidable = getBoolean("mobs.creeper.ridable", creeperRidable);
+ creeperRidableInWater = getBoolean("mobs.creeper.ridable-in-water", creeperRidableInWater);
+ creeperControllable = getBoolean("mobs.creeper.controllable", creeperControllable);
+ }
+
+ public boolean dolphinRidable = false;
+ public boolean dolphinControllable = true;
+ public int dolphinSpitCooldown = 20;
+ public float dolphinSpitSpeed = 1.0F;
+ public float dolphinSpitDamage = 2.0F;
+ private void dolphinSettings() {
+ dolphinRidable = getBoolean("mobs.dolphin.ridable", dolphinRidable);
+ dolphinControllable = getBoolean("mobs.dolphin.controllable", dolphinControllable);
+ dolphinSpitCooldown = getInt("mobs.dolphin.spit.cooldown", dolphinSpitCooldown);
+ dolphinSpitSpeed = (float) getDouble("mobs.dolphin.spit.speed", dolphinSpitSpeed);
+ dolphinSpitDamage = (float) getDouble("mobs.dolphin.spit.damage", dolphinSpitDamage);
+ }
+
+ public boolean donkeyRidableInWater = false;
+ private void donkeySettings() {
+ donkeyRidableInWater = getBoolean("mobs.donkey.ridable-in-water", donkeyRidableInWater);
+ }
+
+ public boolean drownedRidable = false;
+ public boolean drownedRidableInWater = true;
+ public boolean drownedControllable = true;
+ private void drownedSettings() {
+ drownedRidable = getBoolean("mobs.drowned.ridable", drownedRidable);
+ drownedRidableInWater = getBoolean("mobs.drowned.ridable-in-water", drownedRidableInWater);
+ drownedControllable = getBoolean("mobs.drowned.controllable", drownedControllable);
+ }
+
+ public boolean elderGuardianRidable = false;
+ public boolean elderGuardianControllable = true;
+ private void elderGuardianSettings() {
+ elderGuardianRidable = getBoolean("mobs.elder_guardian.ridable", elderGuardianRidable);
+ elderGuardianControllable = getBoolean("mobs.elder_guardian.controllable", elderGuardianControllable);
+ }
+
+ public boolean enderDragonRidable = false;
+ public boolean enderDragonRidableInWater = true;
+ public boolean enderDragonControllable = true;
+ public double enderDragonMaxY = 320D;
+ private void enderDragonSettings() {
+ enderDragonRidable = getBoolean("mobs.ender_dragon.ridable", enderDragonRidable);
+ enderDragonRidableInWater = getBoolean("mobs.ender_dragon.ridable-in-water", enderDragonRidableInWater);
+ enderDragonControllable = getBoolean("mobs.ender_dragon.controllable", enderDragonControllable);
+ enderDragonMaxY = getDouble("mobs.ender_dragon.ridable-max-y", enderDragonMaxY);
+ }
+
+ public boolean endermanRidable = false;
+ public boolean endermanRidableInWater = true;
+ public boolean endermanControllable = true;
+ private void endermanSettings() {
+ endermanRidable = getBoolean("mobs.enderman.ridable", endermanRidable);
+ endermanRidableInWater = getBoolean("mobs.enderman.ridable-in-water", endermanRidableInWater);
+ endermanControllable = getBoolean("mobs.enderman.controllable", endermanControllable);
+ }
+
+ public boolean endermiteRidable = false;
+ public boolean endermiteRidableInWater = true;
+ public boolean endermiteControllable = true;
+ private void endermiteSettings() {
+ endermiteRidable = getBoolean("mobs.endermite.ridable", endermiteRidable);
+ endermiteRidableInWater = getBoolean("mobs.endermite.ridable-in-water", endermiteRidableInWater);
+ endermiteControllable = getBoolean("mobs.endermite.controllable", endermiteControllable);
+ }
+
+ public boolean evokerRidable = false;
+ public boolean evokerRidableInWater = true;
+ public boolean evokerControllable = true;
+ private void evokerSettings() {
+ evokerRidable = getBoolean("mobs.evoker.ridable", evokerRidable);
+ evokerRidableInWater = getBoolean("mobs.evoker.ridable-in-water", evokerRidableInWater);
+ evokerControllable = getBoolean("mobs.evoker.controllable", evokerControllable);
+ }
+
+ public boolean foxRidable = false;
+ public boolean foxRidableInWater = true;
+ public boolean foxControllable = true;
+ private void foxSettings() {
+ foxRidable = getBoolean("mobs.fox.ridable", foxRidable);
+ foxRidableInWater = getBoolean("mobs.fox.ridable-in-water", foxRidableInWater);
+ foxControllable = getBoolean("mobs.fox.controllable", foxControllable);
+ }
+
+ public boolean frogRidable = false;
+ public boolean frogRidableInWater = true;
+ public boolean frogControllable = true;
+ public float frogRidableJumpHeight = 0.65F;
+ private void frogSettings() {
+ frogRidable = getBoolean("mobs.frog.ridable", frogRidable);
+ frogRidableInWater = getBoolean("mobs.frog.ridable-in-water", frogRidableInWater);
+ frogControllable = getBoolean("mobs.frog.controllable", frogControllable);
+ frogRidableJumpHeight = (float) getDouble("mobs.frog.ridable-jump-height", frogRidableJumpHeight);
+ }
+
+ public boolean ghastRidable = false;
+ public boolean ghastRidableInWater = true;
+ public boolean ghastControllable = true;
+ public double ghastMaxY = 320D;
+ private void ghastSettings() {
+ ghastRidable = getBoolean("mobs.ghast.ridable", ghastRidable);
+ ghastRidableInWater = getBoolean("mobs.ghast.ridable-in-water", ghastRidableInWater);
+ ghastControllable = getBoolean("mobs.ghast.controllable", ghastControllable);
+ ghastMaxY = getDouble("mobs.ghast.ridable-max-y", ghastMaxY);
+ }
+
+ public boolean giantRidable = false;
+ public boolean giantRidableInWater = true;
+ public boolean giantControllable = true;
+ private void giantSettings() {
+ giantRidable = getBoolean("mobs.giant.ridable", giantRidable);
+ giantRidableInWater = getBoolean("mobs.giant.ridable-in-water", giantRidableInWater);
+ giantControllable = getBoolean("mobs.giant.controllable", giantControllable);
+ }
+
+ public boolean glowSquidRidable = false;
+ public boolean glowSquidControllable = true;
+ private void glowSquidSettings() {
+ glowSquidRidable = getBoolean("mobs.glow_squid.ridable", glowSquidRidable);
+ glowSquidControllable = getBoolean("mobs.glow_squid.controllable", glowSquidControllable);
+ }
+
+ public boolean goatRidable = false;
+ public boolean goatRidableInWater = true;
+ public boolean goatControllable = true;
+ private void goatSettings() {
+ goatRidable = getBoolean("mobs.goat.ridable", goatRidable);
+ goatRidableInWater = getBoolean("mobs.goat.ridable-in-water", goatRidableInWater);
+ goatControllable = getBoolean("mobs.goat.controllable", goatControllable);
+ }
+
+ public boolean guardianRidable = false;
+ public boolean guardianControllable = true;
+ private void guardianSettings() {
+ guardianRidable = getBoolean("mobs.guardian.ridable", guardianRidable);
+ guardianControllable = getBoolean("mobs.guardian.controllable", guardianControllable);
+ }
+
+ public boolean hoglinRidable = false;
+ public boolean hoglinRidableInWater = true;
+ public boolean hoglinControllable = true;
+ private void hoglinSettings() {
+ hoglinRidable = getBoolean("mobs.hoglin.ridable", hoglinRidable);
+ hoglinRidableInWater = getBoolean("mobs.hoglin.ridable-in-water", hoglinRidableInWater);
+ hoglinControllable = getBoolean("mobs.hoglin.controllable", hoglinControllable);
+ }
+
+ public boolean horseRidableInWater = false;
+ private void horseSettings() {
+ horseRidableInWater = getBoolean("mobs.horse.ridable-in-water", horseRidableInWater);
+ }
+
+ public boolean huskRidable = false;
+ public boolean huskRidableInWater = true;
+ public boolean huskControllable = true;
+ private void huskSettings() {
+ huskRidable = getBoolean("mobs.husk.ridable", huskRidable);
+ huskRidableInWater = getBoolean("mobs.husk.ridable-in-water", huskRidableInWater);
+ huskControllable = getBoolean("mobs.husk.controllable", huskControllable);
+ }
+
+ public boolean illusionerRidable = false;
+ public boolean illusionerRidableInWater = true;
+ public boolean illusionerControllable = true;
+ private void illusionerSettings() {
+ illusionerRidable = getBoolean("mobs.illusioner.ridable", illusionerRidable);
+ illusionerRidableInWater = getBoolean("mobs.illusioner.ridable-in-water", illusionerRidableInWater);
+ illusionerControllable = getBoolean("mobs.illusioner.controllable", illusionerControllable);
+ }
+
+ public boolean ironGolemRidable = false;
+ public boolean ironGolemRidableInWater = true;
+ public boolean ironGolemControllable = true;
+ public boolean ironGolemCanSwim = false;
+ private void ironGolemSettings() {
+ ironGolemRidable = getBoolean("mobs.iron_golem.ridable", ironGolemRidable);
+ ironGolemRidableInWater = getBoolean("mobs.iron_golem.ridable-in-water", ironGolemRidableInWater);
+ ironGolemControllable = getBoolean("mobs.iron_golem.controllable", ironGolemControllable);
+ ironGolemCanSwim = getBoolean("mobs.iron_golem.can-swim", ironGolemCanSwim);
+ }
+
+ public boolean llamaRidable = false;
+ public boolean llamaRidableInWater = false;
+ public boolean llamaControllable = true;
+ private void llamaSettings() {
+ llamaRidable = getBoolean("mobs.llama.ridable", llamaRidable);
+ llamaRidableInWater = getBoolean("mobs.llama.ridable-in-water", llamaRidableInWater);
+ llamaControllable = getBoolean("mobs.llama.controllable", llamaControllable);
+ }
+
+ public boolean magmaCubeRidable = false;
+ public boolean magmaCubeRidableInWater = true;
+ public boolean magmaCubeControllable = true;
+ private void magmaCubeSettings() {
+ magmaCubeRidable = getBoolean("mobs.magma_cube.ridable", magmaCubeRidable);
+ magmaCubeRidableInWater = getBoolean("mobs.magma_cube.ridable-in-water", magmaCubeRidableInWater);
+ magmaCubeControllable = getBoolean("mobs.magma_cube.controllable", magmaCubeControllable);
+ }
+
+ public boolean mooshroomRidable = false;
+ public boolean mooshroomRidableInWater = true;
+ public boolean mooshroomControllable = true;
+ private void mooshroomSettings() {
+ mooshroomRidable = getBoolean("mobs.mooshroom.ridable", mooshroomRidable);
+ mooshroomRidableInWater = getBoolean("mobs.mooshroom.ridable-in-water", mooshroomRidableInWater);
+ mooshroomControllable = getBoolean("mobs.mooshroom.controllable", mooshroomControllable);
+ }
+
+ public boolean muleRidableInWater = false;
+ private void muleSettings() {
+ muleRidableInWater = getBoolean("mobs.mule.ridable-in-water", muleRidableInWater);
+ }
+
+ public boolean ocelotRidable = false;
+ public boolean ocelotRidableInWater = true;
+ public boolean ocelotControllable = true;
+ private void ocelotSettings() {
+ ocelotRidable = getBoolean("mobs.ocelot.ridable", ocelotRidable);
+ ocelotRidableInWater = getBoolean("mobs.ocelot.ridable-in-water", ocelotRidableInWater);
+ ocelotControllable = getBoolean("mobs.ocelot.controllable", ocelotControllable);
+ }
+
+ public boolean pandaRidable = false;
+ public boolean pandaRidableInWater = true;
+ public boolean pandaControllable = true;
+ private void pandaSettings() {
+ pandaRidable = getBoolean("mobs.panda.ridable", pandaRidable);
+ pandaRidableInWater = getBoolean("mobs.panda.ridable-in-water", pandaRidableInWater);
+ pandaControllable = getBoolean("mobs.panda.controllable", pandaControllable);
+ }
+
+ public boolean parrotRidable = false;
+ public boolean parrotRidableInWater = true;
+ public boolean parrotControllable = true;
+ public double parrotMaxY = 320D;
+ private void parrotSettings() {
+ parrotRidable = getBoolean("mobs.parrot.ridable", parrotRidable);
+ parrotRidableInWater = getBoolean("mobs.parrot.ridable-in-water", parrotRidableInWater);
+ parrotControllable = getBoolean("mobs.parrot.controllable", parrotControllable);
+ parrotMaxY = getDouble("mobs.parrot.ridable-max-y", parrotMaxY);
+ }
+
+ public boolean phantomRidable = false;
+ public boolean phantomRidableInWater = true;
+ public boolean phantomControllable = true;
+ public double phantomMaxY = 320D;
+ public float phantomFlameDamage = 1.0F;
+ public int phantomFlameFireTime = 8;
+ public boolean phantomAllowGriefing = false;
+ private void phantomSettings() {
+ phantomRidable = getBoolean("mobs.phantom.ridable", phantomRidable);
+ phantomRidableInWater = getBoolean("mobs.phantom.ridable-in-water", phantomRidableInWater);
+ phantomControllable = getBoolean("mobs.phantom.controllable", phantomControllable);
+ phantomMaxY = getDouble("mobs.phantom.ridable-max-y", phantomMaxY);
+ phantomFlameDamage = (float) getDouble("mobs.phantom.flames.damage", phantomFlameDamage);
+ phantomFlameFireTime = getInt("mobs.phantom.flames.fire-time", phantomFlameFireTime);
+ phantomAllowGriefing = getBoolean("mobs.phantom.allow-griefing", phantomAllowGriefing);
+ }
+
+ public boolean pigRidable = false;
+ public boolean pigRidableInWater = false;
+ public boolean pigControllable = true;
+ private void pigSettings() {
+ pigRidable = getBoolean("mobs.pig.ridable", pigRidable);
+ pigRidableInWater = getBoolean("mobs.pig.ridable-in-water", pigRidableInWater);
+ pigControllable = getBoolean("mobs.pig.controllable", pigControllable);
+ }
+
+ public boolean piglinRidable = false;
+ public boolean piglinRidableInWater = true;
+ public boolean piglinControllable = true;
+ private void piglinSettings() {
+ piglinRidable = getBoolean("mobs.piglin.ridable", piglinRidable);
+ piglinRidableInWater = getBoolean("mobs.piglin.ridable-in-water", piglinRidableInWater);
+ piglinControllable = getBoolean("mobs.piglin.controllable", piglinControllable);
+ }
+
+ public boolean piglinBruteRidable = false;
+ public boolean piglinBruteRidableInWater = true;
+ public boolean piglinBruteControllable = true;
+ private void piglinBruteSettings() {
+ piglinBruteRidable = getBoolean("mobs.piglin_brute.ridable", piglinBruteRidable);
+ piglinBruteRidableInWater = getBoolean("mobs.piglin_brute.ridable-in-water", piglinBruteRidableInWater);
+ piglinBruteControllable = getBoolean("mobs.piglin_brute.controllable", piglinBruteControllable);
+ }
+
+ public boolean pillagerRidable = false;
+ public boolean pillagerRidableInWater = true;
+ public boolean pillagerControllable = true;
+ private void pillagerSettings() {
+ pillagerRidable = getBoolean("mobs.pillager.ridable", pillagerRidable);
+ pillagerRidableInWater = getBoolean("mobs.pillager.ridable-in-water", pillagerRidableInWater);
+ pillagerControllable = getBoolean("mobs.pillager.controllable", pillagerControllable);
+ }
+
+ public boolean polarBearRidable = false;
+ public boolean polarBearRidableInWater = true;
+ public boolean polarBearControllable = true;
+ private void polarBearSettings() {
+ polarBearRidable = getBoolean("mobs.polar_bear.ridable", polarBearRidable);
+ polarBearRidableInWater = getBoolean("mobs.polar_bear.ridable-in-water", polarBearRidableInWater);
+ polarBearControllable = getBoolean("mobs.polar_bear.controllable", polarBearControllable);
+ }
+
+ public boolean pufferfishRidable = false;
+ public boolean pufferfishControllable = true;
+ private void pufferfishSettings() {
+ pufferfishRidable = getBoolean("mobs.pufferfish.ridable", pufferfishRidable);
+ pufferfishControllable = getBoolean("mobs.pufferfish.controllable", pufferfishControllable);
+ }
+
+ public boolean rabbitRidable = false;
+ public boolean rabbitRidableInWater = true;
+ public boolean rabbitControllable = true;
+ private void rabbitSettings() {
+ rabbitRidable = getBoolean("mobs.rabbit.ridable", rabbitRidable);
+ rabbitRidableInWater = getBoolean("mobs.rabbit.ridable-in-water", rabbitRidableInWater);
+ rabbitControllable = getBoolean("mobs.rabbit.controllable", rabbitControllable);
+ }
+
+ public boolean ravagerRidable = false;
+ public boolean ravagerRidableInWater = false;
+ public boolean ravagerControllable = true;
+ private void ravagerSettings() {
+ ravagerRidable = getBoolean("mobs.ravager.ridable", ravagerRidable);
+ ravagerRidableInWater = getBoolean("mobs.ravager.ridable-in-water", ravagerRidableInWater);
+ ravagerControllable = getBoolean("mobs.ravager.controllable", ravagerControllable);
+ }
+
+ public boolean salmonRidable = false;
+ public boolean salmonControllable = true;
+ private void salmonSettings() {
+ salmonRidable = getBoolean("mobs.salmon.ridable", salmonRidable);
+ salmonControllable = getBoolean("mobs.salmon.controllable", salmonControllable);
+ }
+
+ public boolean sheepRidable = false;
+ public boolean sheepRidableInWater = true;
+ public boolean sheepControllable = true;
+ private void sheepSettings() {
+ sheepRidable = getBoolean("mobs.sheep.ridable", sheepRidable);
+ sheepRidableInWater = getBoolean("mobs.sheep.ridable-in-water", sheepRidableInWater);
+ sheepControllable = getBoolean("mobs.sheep.controllable", sheepControllable);
+ }
+
+ public boolean shulkerRidable = false;
+ public boolean shulkerRidableInWater = true;
+ public boolean shulkerControllable = true;
+ private void shulkerSettings() {
+ shulkerRidable = getBoolean("mobs.shulker.ridable", shulkerRidable);
+ shulkerRidableInWater = getBoolean("mobs.shulker.ridable-in-water", shulkerRidableInWater);
+ shulkerControllable = getBoolean("mobs.shulker.controllable", shulkerControllable);
+ }
+
+ public boolean silverfishRidable = false;
+ public boolean silverfishRidableInWater = true;
+ public boolean silverfishControllable = true;
+ private void silverfishSettings() {
+ silverfishRidable = getBoolean("mobs.silverfish.ridable", silverfishRidable);
+ silverfishRidableInWater = getBoolean("mobs.silverfish.ridable-in-water", silverfishRidableInWater);
+ silverfishControllable = getBoolean("mobs.silverfish.controllable", silverfishControllable);
+ }
+
+ public boolean skeletonRidable = false;
+ public boolean skeletonRidableInWater = true;
+ public boolean skeletonControllable = true;
+ private void skeletonSettings() {
+ skeletonRidable = getBoolean("mobs.skeleton.ridable", skeletonRidable);
+ skeletonRidableInWater = getBoolean("mobs.skeleton.ridable-in-water", skeletonRidableInWater);
+ skeletonControllable = getBoolean("mobs.skeleton.controllable", skeletonControllable);
+ }
+
+ public boolean skeletonHorseRidableInWater = true;
+ public boolean skeletonHorseCanSwim = false;
+ private void skeletonHorseSettings() {
+ skeletonHorseRidableInWater = getBoolean("mobs.skeleton_horse.ridable-in-water", skeletonHorseRidableInWater);
+ skeletonHorseCanSwim = getBoolean("mobs.skeleton_horse.can-swim", skeletonHorseCanSwim);
+ }
+
+ public boolean slimeRidable = false;
+ public boolean slimeRidableInWater = true;
+ public boolean slimeControllable = true;
+ private void slimeSettings() {
+ slimeRidable = getBoolean("mobs.slime.ridable", slimeRidable);
+ slimeRidableInWater = getBoolean("mobs.slime.ridable-in-water", slimeRidableInWater);
+ slimeControllable = getBoolean("mobs.slime.controllable", slimeControllable);
+ }
+
+ public boolean snowGolemRidable = false;
+ public boolean snowGolemRidableInWater = true;
+ public boolean snowGolemControllable = true;
+ public boolean snowGolemLeaveTrailWhenRidden = false;
+ private void snowGolemSettings() {
+ snowGolemRidable = getBoolean("mobs.snow_golem.ridable", snowGolemRidable);
+ snowGolemRidableInWater = getBoolean("mobs.snow_golem.ridable-in-water", snowGolemRidableInWater);
+ snowGolemControllable = getBoolean("mobs.snow_golem.controllable", snowGolemControllable);
+ snowGolemLeaveTrailWhenRidden = getBoolean("mobs.snow_golem.leave-trail-when-ridden", snowGolemLeaveTrailWhenRidden);
+ }
+
+ public boolean snifferRidable = false;
+ public boolean snifferRidableInWater = true;
+ public boolean snifferControllable = true;
+ private void snifferSettings() {
+ snifferRidable = getBoolean("mobs.sniffer.ridable", snifferRidable);
+ snifferRidableInWater = getBoolean("mobs.sniffer.ridable-in-water", snifferRidableInWater);
+ snifferControllable = getBoolean("mobs.sniffer.controllable", snifferControllable);
+ }
+
+ public boolean squidRidable = false;
+ public boolean squidControllable = true;
+ private void squidSettings() {
+ squidRidable = getBoolean("mobs.squid.ridable", squidRidable);
+ squidControllable = getBoolean("mobs.squid.controllable", squidControllable);
+ }
+
+ public boolean spiderRidable = false;
+ public boolean spiderRidableInWater = false;
+ public boolean spiderControllable = true;
+ private void spiderSettings() {
+ spiderRidable = getBoolean("mobs.spider.ridable", spiderRidable);
+ spiderRidableInWater = getBoolean("mobs.spider.ridable-in-water", spiderRidableInWater);
+ spiderControllable = getBoolean("mobs.spider.controllable", spiderControllable);
+ }
+
+ public boolean strayRidable = false;
+ public boolean strayRidableInWater = true;
+ public boolean strayControllable = true;
+ private void straySettings() {
+ strayRidable = getBoolean("mobs.stray.ridable", strayRidable);
+ strayRidableInWater = getBoolean("mobs.stray.ridable-in-water", strayRidableInWater);
+ strayControllable = getBoolean("mobs.stray.controllable", strayControllable);
+ }
+
+ public boolean striderRidable = false;
+ public boolean striderRidableInWater = false;
+ public boolean striderControllable = true;
+ private void striderSettings() {
+ striderRidable = getBoolean("mobs.strider.ridable", striderRidable);
+ striderRidableInWater = getBoolean("mobs.strider.ridable-in-water", striderRidableInWater);
+ striderControllable = getBoolean("mobs.strider.controllable", striderControllable);
+ }
+
+ public boolean tadpoleRidable = false;
+ public boolean tadpoleRidableInWater = true;
+ public boolean tadpoleControllable = true;
+ private void tadpoleSettings() {
+ tadpoleRidable = getBoolean("mobs.tadpole.ridable", tadpoleRidable);
+ tadpoleRidableInWater = getBoolean("mobs.tadpole.ridable-in-water", tadpoleRidableInWater);
+ tadpoleControllable = getBoolean("mobs.tadpole.controllable", tadpoleControllable);
+ }
+
+ public boolean traderLlamaRidable = false;
+ public boolean traderLlamaRidableInWater = false;
+ public boolean traderLlamaControllable = true;
+ private void traderLlamaSettings() {
+ traderLlamaRidable = getBoolean("mobs.trader_llama.ridable", traderLlamaRidable);
+ traderLlamaRidableInWater = getBoolean("mobs.trader_llama.ridable-in-water", traderLlamaRidableInWater);
+ traderLlamaControllable = getBoolean("mobs.trader_llama.controllable", traderLlamaControllable);
+ }
+
+ public boolean tropicalFishRidable = false;
+ public boolean tropicalFishControllable = true;
+ private void tropicalFishSettings() {
+ tropicalFishRidable = getBoolean("mobs.tropical_fish.ridable", tropicalFishRidable);
+ tropicalFishControllable = getBoolean("mobs.tropical_fish.controllable", tropicalFishControllable);
+ }
+
+ public boolean turtleRidable = false;
+ public boolean turtleRidableInWater = true;
+ public boolean turtleControllable = true;
+ private void turtleSettings() {
+ turtleRidable = getBoolean("mobs.turtle.ridable", turtleRidable);
+ turtleRidableInWater = getBoolean("mobs.turtle.ridable-in-water", turtleRidableInWater);
+ turtleControllable = getBoolean("mobs.turtle.controllable", turtleControllable);
+ }
+
+ public boolean vexRidable = false;
+ public boolean vexRidableInWater = true;
+ public boolean vexControllable = true;
+ public double vexMaxY = 320D;
+ private void vexSettings() {
+ vexRidable = getBoolean("mobs.vex.ridable", vexRidable);
+ vexRidableInWater = getBoolean("mobs.vex.ridable-in-water", vexRidableInWater);
+ vexControllable = getBoolean("mobs.vex.controllable", vexControllable);
+ vexMaxY = getDouble("mobs.vex.ridable-max-y", vexMaxY);
+ }
+
+ public boolean villagerRidable = false;
+ public boolean villagerRidableInWater = true;
+ public boolean villagerControllable = true;
+ private void villagerSettings() {
+ villagerRidable = getBoolean("mobs.villager.ridable", villagerRidable);
+ villagerRidableInWater = getBoolean("mobs.villager.ridable-in-water", villagerRidableInWater);
+ villagerControllable = getBoolean("mobs.villager.controllable", villagerControllable);
+ }
+
+ public boolean vindicatorRidable = false;
+ public boolean vindicatorRidableInWater = true;
+ public boolean vindicatorControllable = true;
+ private void vindicatorSettings() {
+ vindicatorRidable = getBoolean("mobs.vindicator.ridable", vindicatorRidable);
+ vindicatorRidableInWater = getBoolean("mobs.vindicator.ridable-in-water", vindicatorRidableInWater);
+ vindicatorControllable = getBoolean("mobs.vindicator.controllable", vindicatorControllable);
+ }
+
+ public boolean wanderingTraderRidable = false;
+ public boolean wanderingTraderRidableInWater = true;
+ public boolean wanderingTraderControllable = true;
+ private void wanderingTraderSettings() {
+ wanderingTraderRidable = getBoolean("mobs.wandering_trader.ridable", wanderingTraderRidable);
+ wanderingTraderRidableInWater = getBoolean("mobs.wandering_trader.ridable-in-water", wanderingTraderRidableInWater);
+ wanderingTraderControllable = getBoolean("mobs.wandering_trader.controllable", wanderingTraderControllable);
+ }
+
+ public boolean wardenRidable = false;
+ public boolean wardenRidableInWater = true;
+ public boolean wardenControllable = true;
+ private void wardenSettings() {
+ wardenRidable = getBoolean("mobs.warden.ridable", wardenRidable);
+ wardenRidableInWater = getBoolean("mobs.warden.ridable-in-water", wardenRidableInWater);
+ wardenControllable = getBoolean("mobs.warden.controllable", wardenControllable);
+ }
+
+ public boolean witchRidable = false;
+ public boolean witchRidableInWater = true;
+ public boolean witchControllable = true;
+ private void witchSettings() {
+ witchRidable = getBoolean("mobs.witch.ridable", witchRidable);
+ witchRidableInWater = getBoolean("mobs.witch.ridable-in-water", witchRidableInWater);
+ witchControllable = getBoolean("mobs.witch.controllable", witchControllable);
+ }
+
+ public boolean witherRidable = false;
+ public boolean witherRidableInWater = true;
+ public boolean witherControllable = true;
+ public double witherMaxY = 320D;
+ private void witherSettings() {
+ witherRidable = getBoolean("mobs.wither.ridable", witherRidable);
+ witherRidableInWater = getBoolean("mobs.wither.ridable-in-water", witherRidableInWater);
+ witherControllable = getBoolean("mobs.wither.controllable", witherControllable);
+ witherMaxY = getDouble("mobs.wither.ridable-max-y", witherMaxY);
+ }
+
+ public boolean witherSkeletonRidable = false;
+ public boolean witherSkeletonRidableInWater = true;
+ public boolean witherSkeletonControllable = true;
+ private void witherSkeletonSettings() {
+ witherSkeletonRidable = getBoolean("mobs.wither_skeleton.ridable", witherSkeletonRidable);
+ witherSkeletonRidableInWater = getBoolean("mobs.wither_skeleton.ridable-in-water", witherSkeletonRidableInWater);
+ witherSkeletonControllable = getBoolean("mobs.wither_skeleton.controllable", witherSkeletonControllable);
+ }
+
+ public boolean wolfRidable = false;
+ public boolean wolfRidableInWater = true;
+ public boolean wolfControllable = true;
+ private void wolfSettings() {
+ wolfRidable = getBoolean("mobs.wolf.ridable", wolfRidable);
+ wolfRidableInWater = getBoolean("mobs.wolf.ridable-in-water", wolfRidableInWater);
+ wolfControllable = getBoolean("mobs.wolf.controllable", wolfControllable);
+ }
+
+ public boolean zoglinRidable = false;
+ public boolean zoglinRidableInWater = true;
+ public boolean zoglinControllable = true;
+ private void zoglinSettings() {
+ zoglinRidable = getBoolean("mobs.zoglin.ridable", zoglinRidable);
+ zoglinRidableInWater = getBoolean("mobs.zoglin.ridable-in-water", zoglinRidableInWater);
+ zoglinControllable = getBoolean("mobs.zoglin.controllable", zoglinControllable);
+ }
+
+ public boolean zombieRidable = false;
+ public boolean zombieRidableInWater = true;
+ public boolean zombieControllable = true;
+ private void zombieSettings() {
+ zombieRidable = getBoolean("mobs.zombie.ridable", zombieRidable);
+ zombieRidableInWater = getBoolean("mobs.zombie.ridable-in-water", zombieRidableInWater);
+ zombieControllable = getBoolean("mobs.zombie.controllable", zombieControllable);
+ }
+
+ public boolean zombieHorseRidableInWater = false;
+ public boolean zombieHorseCanSwim = false;
+ private void zombieHorseSettings() {
+ zombieHorseRidableInWater = getBoolean("mobs.zombie_horse.ridable-in-water", zombieHorseRidableInWater);
+ zombieHorseCanSwim = getBoolean("mobs.zombie_horse.can-swim", zombieHorseCanSwim);
+ }
+
+ public boolean zombieVillagerRidable = false;
+ public boolean zombieVillagerRidableInWater = true;
+ public boolean zombieVillagerControllable = true;
+ private void zombieVillagerSettings() {
+ zombieVillagerRidable = getBoolean("mobs.zombie_villager.ridable", zombieVillagerRidable);
+ zombieVillagerRidableInWater = getBoolean("mobs.zombie_villager.ridable-in-water", zombieVillagerRidableInWater);
+ zombieVillagerControllable = getBoolean("mobs.zombie_villager.controllable", zombieVillagerControllable);
+ }
+
+ public boolean zombifiedPiglinRidable = false;
+ public boolean zombifiedPiglinRidableInWater = true;
+ public boolean zombifiedPiglinControllable = true;
+ private void zombifiedPiglinSettings() {
+ zombifiedPiglinRidable = getBoolean("mobs.zombified_piglin.ridable", zombifiedPiglinRidable);
+ zombifiedPiglinRidableInWater = getBoolean("mobs.zombified_piglin.ridable-in-water", zombifiedPiglinRidableInWater);
+ zombifiedPiglinControllable = getBoolean("mobs.zombified_piglin.controllable", zombifiedPiglinControllable);
+ }
}
diff --git a/src/main/java/org/purpurmc/purpur/controller/FlyingMoveControllerWASD.java b/src/main/java/org/purpurmc/purpur/controller/FlyingMoveControllerWASD.java
new file mode 100644
index 0000000000000000000000000000000000000000..ed494e0ad278813a0eb261101447b84cca3ad7aa
--- /dev/null
+++ b/src/main/java/org/purpurmc/purpur/controller/FlyingMoveControllerWASD.java
@@ -0,0 +1,71 @@
+package org.purpurmc.purpur.controller;
+
+import net.minecraft.world.entity.Mob;
+import net.minecraft.world.entity.ai.attributes.Attributes;
+import net.minecraft.world.entity.player.Player;
+
+public class FlyingMoveControllerWASD extends MoveControllerWASD {
+ protected final float groundSpeedModifier;
+ protected final float flyingSpeedModifier;
+ protected int tooHighCooldown = 0;
+ protected boolean setNoGravityFlag;
+
+ public FlyingMoveControllerWASD(Mob entity) {
+ this(entity, 1.0F);
+ }
+
+ public FlyingMoveControllerWASD(Mob entity, float groundSpeedModifier) {
+ this(entity, groundSpeedModifier, 1.0F, true);
+ }
+
+ public FlyingMoveControllerWASD(Mob entity, float groundSpeedModifier, float flyingSpeedModifier) {
+ this(entity, groundSpeedModifier, flyingSpeedModifier, true);
+ }
+
+ public FlyingMoveControllerWASD(Mob entity, float groundSpeedModifier, float flyingSpeedModifier, boolean setNoGravityFlag) {
+ super(entity);
+ this.groundSpeedModifier = groundSpeedModifier;
+ this.flyingSpeedModifier = flyingSpeedModifier;
+ this.setNoGravityFlag = setNoGravityFlag;
+ }
+
+ @Override
+ public void purpurTick(Player rider) {
+ float forward = Math.max(0.0F, rider.getForwardMot());
+ float vertical = forward == 0.0F ? 0.0F : -(rider.xRotO / 45.0F);
+ float strafe = rider.getStrafeMot();
+
+ if (rider.jumping && spacebarEvent(entity)) {
+ entity.onSpacebar();
+ }
+
+ if (entity.getY() >= entity.getMaxY() || --tooHighCooldown > 0) {
+ if (tooHighCooldown <= 0) {
+ tooHighCooldown = 20;
+ }
+ entity.setDeltaMovement(entity.getDeltaMovement().add(0.0D, -0.05D, 0.0D));
+ vertical = 0.0F;
+ }
+
+ setSpeedModifier(entity.getAttributeValue(Attributes.MOVEMENT_SPEED));
+ float speed = (float) getSpeedModifier();
+
+ if (entity.onGround) {
+ speed *= groundSpeedModifier; // TODO = fix this!
+ } else {
+ speed *= flyingSpeedModifier;
+ }
+
+ if (setNoGravityFlag) {
+ entity.setNoGravity(forward > 0);
+ }
+
+ entity.setSpeed(speed);
+ entity.setVerticalMot(vertical);
+ entity.setStrafeMot(strafe);
+ entity.setForwardMot(forward);
+
+ setForward(entity.getForwardMot());
+ setStrafe(entity.getStrafeMot());
+ }
+}
diff --git a/src/main/java/org/purpurmc/purpur/controller/FlyingWithSpacebarMoveControllerWASD.java b/src/main/java/org/purpurmc/purpur/controller/FlyingWithSpacebarMoveControllerWASD.java
new file mode 100644
index 0000000000000000000000000000000000000000..9383c07fa53141127106a1f289366a040960d52e
--- /dev/null
+++ b/src/main/java/org/purpurmc/purpur/controller/FlyingWithSpacebarMoveControllerWASD.java
@@ -0,0 +1,63 @@
+package org.purpurmc.purpur.controller;
+
+import net.minecraft.world.entity.Mob;
+import net.minecraft.world.entity.ai.attributes.Attributes;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.phys.Vec3;
+
+public class FlyingWithSpacebarMoveControllerWASD extends FlyingMoveControllerWASD {
+ public FlyingWithSpacebarMoveControllerWASD(Mob entity) {
+ super(entity);
+ }
+
+ public FlyingWithSpacebarMoveControllerWASD(Mob entity, float groundSpeedModifier) {
+ super(entity, groundSpeedModifier);
+ }
+
+ @Override
+ public void purpurTick(Player rider) {
+ float forward = rider.getForwardMot();
+ float strafe = rider.getStrafeMot() * 0.5F;
+ float vertical = 0;
+
+ if (forward < 0.0F) {
+ forward *= 0.5F;
+ strafe *= 0.5F;
+ }
+
+ float speed = (float) entity.getAttributeValue(Attributes.MOVEMENT_SPEED);
+
+ if (entity.onGround) {
+ speed *= groundSpeedModifier;
+ }
+
+ if (rider.jumping && spacebarEvent(entity) && !entity.onSpacebar()) {
+ entity.setNoGravity(true);
+ vertical = 1.0F;
+ } else {
+ entity.setNoGravity(false);
+ }
+
+ if (entity.getY() >= entity.getMaxY() || --tooHighCooldown > 0) {
+ if (tooHighCooldown <= 0) {
+ tooHighCooldown = 20;
+ }
+ entity.setDeltaMovement(entity.getDeltaMovement().add(0.0D, -0.2D, 0.0D));
+ vertical = 0.0F;
+ }
+
+ setSpeedModifier(speed);
+ entity.setSpeed((float) getSpeedModifier());
+ entity.setVerticalMot(vertical);
+ entity.setStrafeMot(strafe);
+ entity.setForwardMot(forward);
+
+ setForward(entity.getForwardMot());
+ setStrafe(entity.getStrafeMot());
+
+ Vec3 mot = entity.getDeltaMovement();
+ if (mot.y > 0.2D) {
+ entity.setDeltaMovement(mot.x, 0.2D, mot.z);
+ }
+ }
+}
diff --git a/src/main/java/org/purpurmc/purpur/controller/LookControllerWASD.java b/src/main/java/org/purpurmc/purpur/controller/LookControllerWASD.java
new file mode 100644
index 0000000000000000000000000000000000000000..b8c25c96e95dd5ec3ad9fa4c41bd6c08e144832d
--- /dev/null
+++ b/src/main/java/org/purpurmc/purpur/controller/LookControllerWASD.java
@@ -0,0 +1,76 @@
+package org.purpurmc.purpur.controller;
+
+
+import net.minecraft.network.protocol.game.ClientboundMoveEntityPacket;
+import net.minecraft.util.Mth;
+import net.minecraft.world.entity.Mob;
+import net.minecraft.world.entity.ai.control.LookControl;
+import net.minecraft.world.entity.player.Player;
+
+public class LookControllerWASD extends LookControl {
+ protected final Mob entity;
+ private float yOffset = 0;
+ private float xOffset = 0;
+
+ public LookControllerWASD(Mob entity) {
+ super(entity);
+ this.entity = entity;
+ }
+
+ // tick
+ @Override
+ public void tick() {
+ if (entity.getRider() != null && entity.isControllable()) {
+ purpurTick(entity.getRider());
+ } else {
+ vanillaTick();
+ }
+ }
+
+ protected void purpurTick(Player rider) {
+ setYawPitch(rider.getYRot(), rider.getXRot());
+ }
+
+ public void vanillaTick() {
+ super.tick();
+ }
+
+ public void setYawPitch(float yRot, float xRot) {
+ entity.setXRot(normalizePitch(xRot + xOffset));
+ entity.setYRot(normalizeYaw(yRot + yOffset));
+ entity.setYHeadRot(entity.getYRot());
+ entity.xRotO = entity.getXRot();
+ entity.yRotO = entity.getYRot();
+
+ entity.tracker.broadcast(new ClientboundMoveEntityPacket
+ .PosRot(entity.getId(),
+ (short) 0, (short) 0, (short) 0,
+ (byte) Mth.floor(entity.getYRot() * 256.0F / 360.0F),
+ (byte) Mth.floor(entity.getXRot() * 256.0F / 360.0F),
+ entity.onGround));
+ }
+
+ public void setOffsets(float yaw, float pitch) {
+ yOffset = yaw;
+ xOffset = pitch;
+ }
+
+ public float normalizeYaw(float yaw) {
+ yaw %= 360.0f;
+ if (yaw >= 180.0f) {
+ yaw -= 360.0f;
+ } else if (yaw < -180.0f) {
+ yaw += 360.0f;
+ }
+ return yaw;
+ }
+
+ public float normalizePitch(float pitch) {
+ if (pitch > 90.0f) {
+ pitch = 90.0f;
+ } else if (pitch < -90.0f) {
+ pitch = -90.0f;
+ }
+ return pitch;
+ }
+}
diff --git a/src/main/java/org/purpurmc/purpur/controller/MoveControllerWASD.java b/src/main/java/org/purpurmc/purpur/controller/MoveControllerWASD.java
new file mode 100644
index 0000000000000000000000000000000000000000..21fd6ea2a482758a3016e3bc2cdebe2d89267481
--- /dev/null
+++ b/src/main/java/org/purpurmc/purpur/controller/MoveControllerWASD.java
@@ -0,0 +1,89 @@
+package org.purpurmc.purpur.controller;
+
+import net.minecraft.world.entity.Mob;
+import net.minecraft.world.entity.ai.attributes.Attributes;
+import net.minecraft.world.entity.ai.control.MoveControl;
+import net.minecraft.world.entity.player.Player;
+import org.purpurmc.purpur.event.entity.RidableSpacebarEvent;
+
+public class MoveControllerWASD extends MoveControl {
+ protected final Mob entity;
+ private final double speedModifier;
+
+ public MoveControllerWASD(Mob entity) {
+ this(entity, 1.0D);
+ }
+
+ public MoveControllerWASD(Mob entity, double speedModifier) {
+ super(entity);
+ this.entity = entity;
+ this.speedModifier = speedModifier;
+ }
+
+ @Override
+ public boolean hasWanted() {
+ return entity.getRider() != null ? strafeForwards != 0 || strafeRight != 0 : super.hasWanted();
+ }
+
+ @Override
+ public void tick() {
+ if (entity.getRider() != null && entity.isControllable()) {
+ purpurTick(entity.getRider());
+ } else {
+ vanillaTick();
+ }
+ }
+
+ public void vanillaTick() {
+ super.tick();
+ }
+
+ public void purpurTick(Player rider) {
+ float forward = rider.getForwardMot() * 0.5F;
+ float strafe = rider.getStrafeMot() * 0.25F;
+
+ if (forward <= 0.0F) {
+ forward *= 0.5F;
+ }
+
+ float yawOffset = 0;
+ if (strafe != 0) {
+ if (forward == 0) {
+ yawOffset += strafe > 0 ? -90 : 90;
+ forward = Math.abs(strafe * 2);
+ } else {
+ yawOffset += strafe > 0 ? -30 : 30;
+ strafe /= 2;
+ if (forward < 0) {
+ yawOffset += strafe > 0 ? -110 : 110;
+ forward *= -1;
+ }
+ }
+ } else if (forward < 0) {
+ yawOffset -= 180;
+ forward *= -1;
+ }
+
+ ((LookControllerWASD) entity.getLookControl()).setOffsets(yawOffset, 0);
+
+ if (rider.jumping && spacebarEvent(entity) && !entity.onSpacebar() && entity.onGround) {
+ entity.jumpFromGround();
+ }
+
+ setSpeedModifier(entity.getAttributeValue(Attributes.MOVEMENT_SPEED) * speedModifier);
+
+ entity.setSpeed((float) getSpeedModifier());
+ entity.setForwardMot(forward);
+
+ setForward(entity.getForwardMot());
+ setStrafe(entity.getStrafeMot());
+ }
+
+ public static boolean spacebarEvent(Mob entity) {
+ if (RidableSpacebarEvent.getHandlerList().getRegisteredListeners().length > 0) {
+ return new RidableSpacebarEvent(entity.getBukkitEntity()).callEvent();
+ } else {
+ return true;
+ }
+ }
+}
diff --git a/src/main/java/org/purpurmc/purpur/controller/WaterMoveControllerWASD.java b/src/main/java/org/purpurmc/purpur/controller/WaterMoveControllerWASD.java
new file mode 100644
index 0000000000000000000000000000000000000000..ba2a37dad43e238e54632975abea8ee6fafaa9e0
--- /dev/null
+++ b/src/main/java/org/purpurmc/purpur/controller/WaterMoveControllerWASD.java
@@ -0,0 +1,50 @@
+package org.purpurmc.purpur.controller;
+
+import net.minecraft.world.entity.Mob;
+import net.minecraft.world.entity.ai.attributes.Attributes;
+import net.minecraft.world.entity.player.Player;
+
+public class WaterMoveControllerWASD extends MoveControllerWASD {
+ private final double speedModifier;
+
+ public WaterMoveControllerWASD(Mob entity) {
+ this(entity, 1.0D);
+ }
+
+ public WaterMoveControllerWASD(Mob entity, double speedModifier) {
+ super(entity);
+ this.speedModifier = speedModifier;
+ }
+
+ @Override
+ public void purpurTick(Player rider) {
+ float forward = rider.getForwardMot();
+ float strafe = rider.getStrafeMot() * 0.5F; // strafe slower by default
+ float vertical = -(rider.xRotO / 90);
+
+ if (forward == 0.0F) {
+ // strafe slower if not moving forward
+ strafe *= 0.5F;
+ // do not move vertically if not moving forward
+ vertical = 0.0F;
+ } else if (forward < 0.0F) {
+ // water animals can't swim backwards
+ forward = 0.0F;
+ vertical = 0.0F;
+ }
+
+ if (rider.jumping && spacebarEvent(entity)) {
+ entity.onSpacebar();
+ }
+
+ setSpeedModifier(entity.getAttributeValue(Attributes.MOVEMENT_SPEED) * speedModifier);
+ entity.setSpeed((float) getSpeedModifier() * 0.1F);
+
+ entity.setForwardMot(forward * (float) speedModifier);
+ entity.setStrafeMot(strafe * (float) speedModifier);
+ entity.setVerticalMot(vertical * (float) speedModifier);
+
+ setForward(entity.getForwardMot());
+ setStrafe(entity.getStrafeMot());
+ }
+}
diff --git a/src/main/java/org/purpurmc/purpur/entity/DolphinSpit.java b/src/main/java/org/purpurmc/purpur/entity/DolphinSpit.java
new file mode 100644
index 0000000000000000000000000000000000000000..af057c1d7fd74f3dd806c5ce7f8b0ad06cab7b8e
--- /dev/null
+++ b/src/main/java/org/purpurmc/purpur/entity/DolphinSpit.java
@@ -0,0 +1,99 @@
+package org.purpurmc.purpur.entity;
+
+import net.minecraft.core.particles.ParticleTypes;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.util.Mth;
+import net.minecraft.world.damagesource.DamageSource;
+import net.minecraft.world.entity.Entity;
+import net.minecraft.world.entity.EntityType;
+import net.minecraft.world.entity.LivingEntity;
+import net.minecraft.world.entity.animal.Dolphin;
+import net.minecraft.world.entity.projectile.LlamaSpit;
+import net.minecraft.world.entity.projectile.ProjectileUtil;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.level.block.state.BlockState;
+import net.minecraft.world.phys.BlockHitResult;
+import net.minecraft.world.phys.EntityHitResult;
+import net.minecraft.world.phys.HitResult;
+import net.minecraft.world.phys.Vec3;
+
+public class DolphinSpit extends LlamaSpit {
+ public LivingEntity dolphin;
+ public int ticksLived;
+
+ public DolphinSpit(EntityType<? extends LlamaSpit> type, Level world) {
+ super(type, world);
+ }
+
+ public DolphinSpit(Level world, Dolphin dolphin) {
+ this(EntityType.LLAMA_SPIT, world);
+ setOwner(dolphin.getRider() != null ? dolphin.getRider() : dolphin);
+ this.dolphin = dolphin;
+ this.setPos(
+ dolphin.getX() - (double) (dolphin.getBbWidth() + 1.0F) * 0.5D * (double) Mth.sin(dolphin.yBodyRot * 0.017453292F),
+ dolphin.getEyeY() - 0.10000000149011612D,
+ dolphin.getZ() + (double) (dolphin.getBbWidth() + 1.0F) * 0.5D * (double) Mth.cos(dolphin.yBodyRot * 0.017453292F));
+ }
+
+ public void tick() {
+ super_tick();
+
+ Vec3 mot = this.getDeltaMovement();
+ HitResult hitResult = ProjectileUtil.getHitResult(this.position(), this, this::canHitEntity, mot, level());
+
+ this.preOnHit(hitResult);
+
+ double x = this.getX() + mot.x;
+ double y = this.getY() + mot.y;
+ double z = this.getZ() + mot.z;
+
+ this.updateRotation();
+
+ Vec3 motDouble = mot.scale(2.0);
+ for (int i = 0; i < 5; i++) {
+ ((ServerLevel) level()).sendParticles(null, ParticleTypes.BUBBLE,
+ getX() + random.nextFloat() / 2 - 0.25F,
+ getY() + random.nextFloat() / 2 - 0.25F,
+ getZ() + random.nextFloat() / 2 - 0.25F,
+ 0, motDouble.x(), motDouble.y(), motDouble.z(), 0.1D, true);
+ }
+
+ if (++ticksLived > 20) {
+ this.discard();
+ } else {
+ this.setDeltaMovement(mot.scale(0.99D));
+ if (!this.isNoGravity()) {
+ this.setDeltaMovement(this.getDeltaMovement().add(0.0D, -0.06D, 0.0D));
+ }
+
+ this.setPos(x, y, z);
+ }
+ }
+
+ @Override
+ public void shoot(double x, double y, double z, float speed, float inaccuracy) {
+ setDeltaMovement(new Vec3(x, y, z).normalize().add(
+ random.nextGaussian() * (double) 0.0075F * (double) inaccuracy,
+ random.nextGaussian() * (double) 0.0075F * (double) inaccuracy,
+ random.nextGaussian() * (double) 0.0075F * (double) inaccuracy)
+ .scale(speed));
+ }
+
+ @Override
+ protected void onHitEntity(EntityHitResult entityHitResult) {
+ Entity shooter = this.getOwner();
+ if (shooter instanceof LivingEntity) {
+ entityHitResult.getEntity().hurt(entityHitResult.getEntity().damageSources().mobProjectile(this, (LivingEntity) shooter), level().purpurConfig.dolphinSpitDamage);
+ }
+ }
+
+ @Override
+ protected void onHitBlock(BlockHitResult blockHitResult) {
+ if (this.hitCancelled) {
+ return;
+ }
+ BlockState state = this.level().getBlockState(blockHitResult.getBlockPos());
+ state.onProjectileHit(this.level(), state, blockHitResult, this);
+ this.discard();
+ }
+}
diff --git a/src/main/java/org/purpurmc/purpur/entity/PhantomFlames.java b/src/main/java/org/purpurmc/purpur/entity/PhantomFlames.java
new file mode 100644
index 0000000000000000000000000000000000000000..1fd6412e332ea8ee82b19c108e113624e51d764b
--- /dev/null
+++ b/src/main/java/org/purpurmc/purpur/entity/PhantomFlames.java
@@ -0,0 +1,114 @@
+package org.purpurmc.purpur.entity;
+
+import net.minecraft.core.particles.ParticleTypes;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.util.Mth;
+import net.minecraft.world.damagesource.DamageSource;
+import net.minecraft.world.entity.Entity;
+import net.minecraft.world.entity.EntityType;
+import net.minecraft.world.entity.LivingEntity;
+import net.minecraft.world.entity.decoration.ArmorStand;
+import net.minecraft.world.entity.monster.Phantom;
+import net.minecraft.world.entity.projectile.LlamaSpit;
+import net.minecraft.world.entity.projectile.ProjectileUtil;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.level.block.state.BlockBehaviour;
+import net.minecraft.world.level.block.state.BlockState;
+import net.minecraft.world.phys.BlockHitResult;
+import net.minecraft.world.phys.EntityHitResult;
+import net.minecraft.world.phys.HitResult;
+import net.minecraft.world.phys.Vec3;
+
+public class PhantomFlames extends LlamaSpit {
+ public Phantom phantom;
+ public int ticksLived;
+ public boolean canGrief = false;
+
+ public PhantomFlames(EntityType<? extends LlamaSpit> type, Level world) {
+ super(type, world);
+ }
+
+ public PhantomFlames(Level world, Phantom phantom) {
+ this(EntityType.LLAMA_SPIT, world);
+ setOwner(phantom.getRider() != null ? phantom.getRider() : phantom);
+ this.phantom = phantom;
+ this.setPos(
+ phantom.getX() - (double) (phantom.getBbWidth() + 1.0F) * 0.5D * (double) Mth.sin(phantom.yBodyRot * 0.017453292F),
+ phantom.getEyeY() - 0.10000000149011612D,
+ phantom.getZ() + (double) (phantom.getBbWidth() + 1.0F) * 0.5D * (double) Mth.cos(phantom.yBodyRot * 0.017453292F));
+ }
+
+ public void tick() {
+ super_tick();
+
+ Vec3 mot = this.getDeltaMovement();
+ HitResult hitResult = ProjectileUtil.getHitResult(this.position(), this, this::canHitEntity, mot, level());
+
+ this.preOnHit(hitResult);
+
+ double x = this.getX() + mot.x;
+ double y = this.getY() + mot.y;
+ double z = this.getZ() + mot.z;
+
+ this.updateRotation();
+
+ Vec3 motDouble = mot.scale(2.0);
+ for (int i = 0; i < 5; i++) {
+ ((ServerLevel) level()).sendParticles(null, ParticleTypes.FLAME,
+ getX() + random.nextFloat() / 2 - 0.25F,
+ getY() + random.nextFloat() / 2 - 0.25F,
+ getZ() + random.nextFloat() / 2 - 0.25F,
+ 0, motDouble.x(), motDouble.y(), motDouble.z(), 0.1D, true);
+ }
+
+ if (++ticksLived > 20) {
+ this.discard();
+ } else if (this.level().getBlockStates(this.getBoundingBox()).noneMatch(BlockBehaviour.BlockStateBase::isAir)) {
+ this.discard();
+ } else if (this.isInWaterOrBubble()) {
+ this.discard();
+ } else {
+ this.setDeltaMovement(mot.scale(0.99D));
+ if (!this.isNoGravity()) {
+ this.setDeltaMovement(this.getDeltaMovement().add(0.0D, -0.06D, 0.0D));
+ }
+
+ this.setPos(x, y, z);
+ }
+ }
+
+ @Override
+ public void shoot(double x, double y, double z, float speed, float inaccuracy) {
+ setDeltaMovement(new Vec3(x, y, z).normalize().add(
+ random.nextGaussian() * (double) 0.0075F * (double) inaccuracy,
+ random.nextGaussian() * (double) 0.0075F * (double) inaccuracy,
+ random.nextGaussian() * (double) 0.0075F * (double) inaccuracy)
+ .scale(speed));
+ }
+
+ @Override
+ protected void onHitEntity(EntityHitResult entityHitResult) {
+ Entity shooter = this.getOwner();
+ if (shooter instanceof LivingEntity) {
+ Entity target = entityHitResult.getEntity();
+ if (canGrief || (target instanceof LivingEntity && !(target instanceof ArmorStand))) {
+ boolean hurt = target.hurt(target.damageSources().mobProjectile(this, (LivingEntity) shooter), level().purpurConfig.phantomFlameDamage);
+ if (hurt && level().purpurConfig.phantomFlameFireTime > 0) {
+ target.setSecondsOnFire(level().purpurConfig.phantomFlameFireTime);
+ }
+ }
+ }
+ }
+
+ @Override
+ protected void onHitBlock(BlockHitResult blockHitResult) {
+ if (this.hitCancelled) {
+ return;
+ }
+ if (this.canGrief) {
+ BlockState state = this.level().getBlockState(blockHitResult.getBlockPos());
+ state.onProjectileHit(this.level(), state, blockHitResult, this);
+ }
+ this.discard();
+ }
+}
diff --git a/src/main/java/org/purpurmc/purpur/entity/ai/HasRider.java b/src/main/java/org/purpurmc/purpur/entity/ai/HasRider.java
new file mode 100644
index 0000000000000000000000000000000000000000..8babdaddd8b33278aea0369dbbeeb445abe45016
--- /dev/null
+++ b/src/main/java/org/purpurmc/purpur/entity/ai/HasRider.java
@@ -0,0 +1,20 @@
+package org.purpurmc.purpur.entity.ai;
+
+import net.minecraft.world.entity.Mob;
+import net.minecraft.world.entity.ai.goal.Goal;
+
+import java.util.EnumSet;
+
+public class HasRider extends Goal {
+ public final Mob entity;
+
+ public HasRider(Mob entity) {
+ this.entity = entity;
+ setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK, Flag.TARGET, Flag.UNKNOWN_BEHAVIOR));
+ }
+
+ @Override
+ public boolean canUse() {
+ return entity.getRider() != null && entity.isControllable();
+ }
+}
diff --git a/src/main/java/org/purpurmc/purpur/entity/ai/HorseHasRider.java b/src/main/java/org/purpurmc/purpur/entity/ai/HorseHasRider.java
new file mode 100644
index 0000000000000000000000000000000000000000..432f4f3d82af2f19820890b68d33189a9f2c69f9
--- /dev/null
+++ b/src/main/java/org/purpurmc/purpur/entity/ai/HorseHasRider.java
@@ -0,0 +1,17 @@
+package org.purpurmc.purpur.entity.ai;
+
+import net.minecraft.world.entity.animal.horse.AbstractHorse;
+
+public class HorseHasRider extends HasRider {
+ public final AbstractHorse horse;
+
+ public HorseHasRider(AbstractHorse entity) {
+ super(entity);
+ this.horse = entity;
+ }
+
+ @Override
+ public boolean canUse() {
+ return super.canUse() && horse.isSaddled();
+ }
+}
diff --git a/src/main/java/org/purpurmc/purpur/entity/ai/LlamaHasRider.java b/src/main/java/org/purpurmc/purpur/entity/ai/LlamaHasRider.java
new file mode 100644
index 0000000000000000000000000000000000000000..18a95e043cbffa65eeaaf65ff7695e5dc939820c
--- /dev/null
+++ b/src/main/java/org/purpurmc/purpur/entity/ai/LlamaHasRider.java
@@ -0,0 +1,17 @@
+package org.purpurmc.purpur.entity.ai;
+
+import net.minecraft.world.entity.animal.horse.Llama;
+
+public class LlamaHasRider extends HasRider {
+ public final Llama llama;
+
+ public LlamaHasRider(Llama entity) {
+ super(entity);
+ this.llama = entity;
+ }
+
+ @Override
+ public boolean canUse() {
+ return super.canUse() && llama.isSaddled() && llama.isControllable();
+ }
+}
diff --git a/src/test/java/com/destroystokyo/paper/entity/ai/VanillaMobGoalTest.java b/src/test/java/com/destroystokyo/paper/entity/ai/VanillaMobGoalTest.java
index b2d510459bcf90a3611f3d91dae4ccc3d29b4079..7a052f6deaa30f8a177a2aaf172f9da6c308a22b 100644
--- a/src/test/java/com/destroystokyo/paper/entity/ai/VanillaMobGoalTest.java
+++ b/src/test/java/com/destroystokyo/paper/entity/ai/VanillaMobGoalTest.java
@@ -37,7 +37,7 @@ public class VanillaMobGoalTest {
}
List<Class<?>> classes;
- try (ScanResult scanResult = new ClassGraph().enableAllInfo().whitelistPackages("net.minecraft").scan()) {
+ try (ScanResult scanResult = new ClassGraph().enableAllInfo().whitelistPackages("net.minecraft", "org.purpurmc.purpur.entity.ai").scan()) { // Purpur
classes = scanResult.getSubclasses(net.minecraft.world.entity.ai.goal.Goal.class.getName()).loadClasses();
}