Files
Purpur/purpur-server/minecraft-patches/features/0001-Ridables.patch
granny c94a2c9b86 Updated Upstream (Paper)
Upstream has released updates that appear to apply and compile correctly

Paper Changes:
PaperMC/Paper@70f253f3 Update ItemType
PaperMC/Paper@774a32e8 sulfur cube archetype registry
PaperMC/Paper@f2ebae60 update legacy ItemType generic type
PaperMC/Paper@93feb440 deprecate PigZapEvent
PaperMC/Paper@73725c6b update to pre-5
PaperMC/Paper@5357d03b fix serialization error when creating empty flat world
PaperMC/Paper@620da256 expand CreeperIgniteEvent to cover minecart tnt and sulfur cube
2026-06-08 12:17:33 -07:00

5255 lines
244 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/net/minecraft/gametest/framework/GameTestHelper.java b/net/minecraft/gametest/framework/GameTestHelper.java
index ec6dd9de7b82841b1403b1bb851392132be5275b..146c404ac0471ed7df6d3740859663aa962e7a60 100644
--- a/net/minecraft/gametest/framework/GameTestHelper.java
+++ b/net/minecraft/gametest/framework/GameTestHelper.java
@@ -363,6 +363,8 @@ public class GameTestHelper {
public void setAfk(final boolean afk) {} // Purpur - AFK API
+ public void resetLastActionTime() {} // Purpur - Ridables
+
@Override
public boolean isClientAuthoritative() {
return false;
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index b145362e0c12e77eb582f7339363971a904622e7..fcade65785db82fc64eeb9b337900a405f05d196 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -1746,6 +1746,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
for (ServerLevel level : this.getAllLevels()) {
level.hasPhysicsEvent = org.bukkit.event.block.BlockPhysicsEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - BlockPhysicsEvent
level.hasEntityMoveEvent = io.papermc.paper.event.entity.EntityMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - Add EntityMoveEvent
+ level.hasRidableMoveEvent = org.purpurmc.purpur.event.entity.RidableMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Purpur - Ridables
level.updateLagCompensationTick(); // Paper - lag compensation
profiler.push(() -> level + " " + level.dimension().identifier());
profiler.push("tick");
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index f220993206a4ea36d2fdf4f053af2b34ccc3beba..3d328c1871993b5e55eb2892364d66b79db22338 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -237,6 +237,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
public boolean hasPhysicsEvent = true; // Paper - BlockPhysicsEvent
public boolean hasEntityMoveEvent; // Paper - Add EntityMoveEvent
private final alternate.current.wire.WireHandler wireHandler = new alternate.current.wire.WireHandler(this); // Paper - optimize redstone (Alternate Current)
+ public boolean hasRidableMoveEvent = false; // Purpur - Ridables
@Override
public @Nullable LevelChunk getChunkIfLoaded(int x, int z) {
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
index cd3221273750b8d824e49cd472e8f05dffc5f457..119530367e7bd7f99160d8a8b8b6c1f757c3bd01 100644
--- a/net/minecraft/server/level/ServerPlayer.java
+++ b/net/minecraft/server/level/ServerPlayer.java
@@ -749,6 +749,15 @@ public class ServerPlayer extends Player {
this.trackEnteredOrExitedLavaOnVehicle();
this.updatePlayerAttributes();
this.advancements.flushDirty(this, true);
+
+ // Purpur start - Ridables
+ 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 - Ridables
}
private void updatePlayerAttributes() {
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index e9364611059653bd4c37cb75571bb53225cf5d94..5a814aa9df95fbbbff5d3c3bda4b76d04440e624 100644
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -2940,6 +2940,8 @@ public class ServerGamePacketListenerImpl
ServerGamePacketListenerImpl.this.cserver.getPluginManager().callEvent(event);
final boolean resendData = event.isCancelled() || !ServerGamePacketListenerImpl.this.player.getItemInHand(hand).is(itemType);
+ player.processClick(hand); // Purpur - Ridables
+
// Entity in bucket - SPIGOT-4048 and SPIGOT-6859
if (itemType == Items.WATER_BUCKET && target instanceof net.minecraft.world.entity.Bucketable && target instanceof LivingEntity && resendData) {
target.resendPossiblyDesyncedEntityData(ServerGamePacketListenerImpl.this.player); // Paper - The entire mob gets deleted, so resend it
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
index 3b558c80ee8ebc03fb68d63c86f4371617057d7f..6b51feb4e41f10dae9e5fcd303d260d5f511ae91 100644
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
@@ -3223,6 +3223,13 @@ public abstract class Entity
this.passengers = ImmutableList.copyOf(newPassengers);
}
+
+ // Purpur start - Ridables
+ if (isRidable() && this.passengers.get(0) == passenger && passenger instanceof Player player) {
+ onMount(player);
+ this.rider = player;
+ }
+ // Purpur end - Ridables
}
// Paper start - Force entity dismount during teleportation
@@ -3263,6 +3270,14 @@ public abstract class Entity
return false;
}
// CraftBukkit end
+
+ // Purpur start - Ridables
+ if (this.rider != null && this.passengers.get(0) == this.rider) {
+ onDismount(this.rider);
+ this.rider = null;
+ }
+ // Purpur end - Ridables
+
if (this.passengers.size() == 1 && this.passengers.get(0) == passenger) {
this.passengers = ImmutableList.of();
} else {
@@ -5227,4 +5242,44 @@ public abstract class Entity
return ((ServerLevel) this.level()).isPositionEntityTicking(this.blockPosition());
}
// Paper end
+ // Purpur start - Ridables
+ @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);
+ ((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 - Ridables
}
diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java
index 225cf2d48b15f4787e493d0c2f332cfdb313978f..2d856ba2ce8c6febdc1d5dd2df2718fbe4e1312d 100644
--- a/net/minecraft/world/entity/LivingEntity.java
+++ b/net/minecraft/world/entity/LivingEntity.java
@@ -258,9 +258,9 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
protected int noActionTime;
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 final InterpolationHandler interpolation = new InterpolationHandler(this);
protected double lerpYHeadRot;
protected int lerpHeadSteps;
@@ -309,7 +309,7 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
protected LivingEntity(final EntityType<? extends LivingEntity> type, final Level level) {
super(type, level);
- this.attributes = new AttributeMap(DefaultAttributes.getSupplier(type));
+ this.attributes = new AttributeMap(DefaultAttributes.getSupplier(type), this); // Purpur - Ridables
this.craftAttributes = new org.bukkit.craftbukkit.attribute.CraftAttributeMap(this.attributes); // CraftBukkit
// CraftBukkit - this.setHealth(this.getMaxHealth()) inlined and simplified to skip the instanceof check for Player, as getBukkitEntity() is not initialized in constructor
this.entityData.set(LivingEntity.DATA_HEALTH_ID, this.getMaxHealth());
@@ -384,6 +384,7 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
.add(Attributes.NAME_TAG_DISTANCE)
.add(Attributes.BELOW_NAME_DISTANCE);
}
+ public boolean shouldSendAttribute(Attribute attribute) { return true; } // Purpur - Ridables
@Override
protected void checkFallDamage(final double ya, final boolean onGround, final BlockState onState, final BlockPos pos) {
@@ -3202,6 +3203,20 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
this.move(MoverType.SELF, this.getDeltaMovement());
this.setDeltaMovement(this.getDeltaMovement().scale(0.5));
} else {
+ // Purpur start - Ridables
+ if (this.getRider() != null && this.isControllable()) {
+ float friction = 0.91F;
+ if (this.onGround()) {
+ friction = this.level().getBlockState(this.getBlockPosBelowThatAffectsMyMovement()).getBlock().getFriction() * 0.91F;
+ }
+
+ float frictionCompensation = 0.16277137F / (friction * friction * friction);
+ this.moveRelative(this.onGround() ? 0.1F * frictionCompensation : 0.02F, input);
+ this.move(MoverType.SELF, this.getDeltaMovement());
+ this.setDeltaMovement(this.getDeltaMovement().scale(friction));
+ return;
+ }
+ // Purpur end - Ridables
this.moveRelative(airSpeed, input);
this.move(MoverType.SELF, this.getDeltaMovement());
this.setDeltaMovement(this.getDeltaMovement().scale(0.91F));
@@ -3930,8 +3945,10 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
this.pushEntities();
profiler.pop();
// Paper start - Add EntityMoveEvent
- if (((ServerLevel) this.level()).hasEntityMoveEvent && !(this instanceof Player)) {
- if (this.xo != this.getX() || this.yo != this.getY() || this.zo != this.getZ() || this.yRotO != this.getYRot() || this.xRotO != this.getXRot()) {
+ // Purpur start - Ridables
+ 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 Player)) {
+ // Purpur end - Ridables
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());
@@ -3941,6 +3958,21 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
this.absSnapTo(event.getTo().getX(), event.getTo().getY(), event.getTo().getZ(), event.getTo().getYaw(), event.getTo().getPitch());
}
}
+ // Purpur start - Ridables
+ 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(), (org.bukkit.entity.Player) getRider().getBukkitEntity(), from, to.clone());
+ if (!event.callEvent()) {
+ this.absSnapTo(from.getX(), from.getY(), from.getZ(), from.getYaw(), from.getPitch());
+ } else if (!to.equals(event.getTo())) {
+ this.absSnapTo(event.getTo().getX(), event.getTo().getY(), event.getTo().getZ(), event.getTo().getYaw(), event.getTo().getPitch());
+ }
+ }
+ }
+ // Purpur end - Ridables
}
// Paper end - Add EntityMoveEvent
if (this.level() instanceof ServerLevel serverLevel && this.isSensitiveToWater() && this.isInWaterOrRain()) {
diff --git a/net/minecraft/world/entity/Mob.java b/net/minecraft/world/entity/Mob.java
index 85cb2e946f3f2e51b7697af78b9f9939bc5f99c8..c707a18175920b12b4d554d8041c7afe10a0efd1 100644
--- a/net/minecraft/world/entity/Mob.java
+++ b/net/minecraft/world/entity/Mob.java
@@ -161,8 +161,8 @@ public abstract class Mob extends LivingEntity implements Targeting, EquipmentUs
super(type, level);
this.goalSelector = new GoalSelector();
this.targetSelector = new GoalSelector();
- this.lookControl = new LookControl(this);
- this.moveControl = new MoveControl<>(this);
+ this.lookControl = new org.purpurmc.purpur.controller.LookControllerWASD(this); // Purpur - Ridables
+ this.moveControl = new org.purpurmc.purpur.controller.MoveControllerWASD<>(this); // Purpur - Ridables
this.jumpControl = new JumpControl(this);
this.bodyRotationControl = this.createBodyControl();
this.navigation = this.createNavigation(level);
@@ -614,6 +614,7 @@ public abstract class Mob extends LivingEntity implements Targeting, EquipmentUs
}
}
} else {
+ if (getRider() == null || !this.isControllable()) // Purpur - Ridables
this.igniteForSeconds(8.0F);
}
}
@@ -1400,7 +1401,7 @@ public abstract class Mob extends LivingEntity implements Targeting, EquipmentUs
}
protected InteractionResult mobInteract(final Player player, final InteractionHand hand) {
- return InteractionResult.PASS;
+ return tryRide(player, hand); // Purpur - Ridables
}
protected void usePlayerItem(final Player player, final InteractionHand hand, final ItemStack itemStack) {
@@ -1749,4 +1750,58 @@ public abstract class Mob extends LivingEntity implements Targeting, EquipmentUs
public float chargeSpeedModifier() {
return 1.0F;
}
+
+ // Purpur start - Ridables
+ 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())) {
+ if (player instanceof net.minecraft.server.level.ServerPlayer serverPlayer) {
+ serverPlayer.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 - Ridables
}
diff --git a/net/minecraft/world/entity/ai/attributes/AttributeMap.java b/net/minecraft/world/entity/ai/attributes/AttributeMap.java
index 68bad6752bd313ca3ebde7fd4e3b2c46c5dee3ef..fe5f4f79e5a1b091d15fa3bfd876b831e493655c 100644
--- a/net/minecraft/world/entity/ai/attributes/AttributeMap.java
+++ b/net/minecraft/world/entity/ai/attributes/AttributeMap.java
@@ -18,14 +18,21 @@ public class AttributeMap {
private final Set<AttributeInstance> attributesToSync = new ObjectOpenHashSet<>();
private final Set<AttributeInstance> attributesToUpdate = new ObjectOpenHashSet<>();
private final AttributeSupplier supplier;
+ private final net.minecraft.world.entity.LivingEntity entity; // Purpur - Ridables
public AttributeMap(final AttributeSupplier supplier) {
- this.supplier = supplier;
+ // Purpur start - Ridables
+ this(supplier, null);
+ }
+ public AttributeMap(AttributeSupplier defaultAttributes, net.minecraft.world.entity.LivingEntity entity) {
+ this.entity = entity;
+ // Purpur end - Ridables
+ this.supplier = defaultAttributes;
}
private void onAttributeModified(final AttributeInstance attributeInstance) {
this.attributesToUpdate.add(attributeInstance);
- if (attributeInstance.getAttribute().value().isClientSyncable()) {
+ if (attributeInstance.getAttribute().value().isClientSyncable() && (entity == null || entity.shouldSendAttribute(attributeInstance.getAttribute().value()))) { // Purpur - Ridables
this.attributesToSync.add(attributeInstance);
}
}
@@ -39,7 +46,7 @@ public class AttributeMap {
}
public Collection<AttributeInstance> getSyncableAttributes() {
- return this.attributes.values().stream().filter(instance -> instance.getAttribute().value().isClientSyncable()).collect(Collectors.toList());
+ return this.attributes.values().stream().filter(instance -> instance.getAttribute().value().isClientSyncable() && (entity == null || entity.shouldSendAttribute(instance.getAttribute().value()))).collect(Collectors.toList()); // Purpur - Ridables
}
public @Nullable AttributeInstance getInstance(final Holder<Attribute> attribute) {
diff --git a/net/minecraft/world/entity/ai/attributes/DefaultAttributes.java b/net/minecraft/world/entity/ai/attributes/DefaultAttributes.java
index 0dac24960ce1e04a1962def9dc49315998b79e25..b828332b3e5db2722ca2193721da996fb84ce55e 100644
--- a/net/minecraft/world/entity/ai/attributes/DefaultAttributes.java
+++ b/net/minecraft/world/entity/ai/attributes/DefaultAttributes.java
@@ -145,7 +145,7 @@ public class DefaultAttributes {
.put(EntityTypes.PANDA, Panda.createAttributes().build())
.put(EntityTypes.PARCHED, Parched.createAttributes().build())
.put(EntityTypes.PARROT, Parrot.createAttributes().build())
- .put(EntityTypes.PHANTOM, Monster.createMonsterAttributes().build())
+ .put(EntityTypes.PHANTOM, net.minecraft.world.entity.monster.Phantom.createAttributes().build()) // Purpur - Ridables
.put(EntityTypes.PIG, Pig.createAttributes().build())
.put(EntityTypes.PIGLIN, Piglin.createAttributes().build())
.put(EntityTypes.PIGLIN_BRUTE, PiglinBrute.createAttributes().build())
diff --git a/net/minecraft/world/entity/ai/control/MoveControl.java b/net/minecraft/world/entity/ai/control/MoveControl.java
index ca8932589bb59d8d12cc7838a026255fc2914715..f179455b90140a4663c46f19dcb39abfaf3bf4f4 100644
--- a/net/minecraft/world/entity/ai/control/MoveControl.java
+++ b/net/minecraft/world/entity/ai/control/MoveControl.java
@@ -29,6 +29,20 @@ public class MoveControl<T extends Mob> implements Control {
this.mob = mob;
}
+ // Purpur start - Ridables
+ 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 - Ridables
+
public boolean hasWanted() {
return this.operation == MoveControl.Operation.MOVE_TO;
}
diff --git a/net/minecraft/world/entity/ai/control/SmoothSwimmingLookControl.java b/net/minecraft/world/entity/ai/control/SmoothSwimmingLookControl.java
index 99493ad5ca4222a2817ee061be01fb888666b3a4..5829ea72b321b2b63f60407aabd83e1b2bd9681c 100644
--- a/net/minecraft/world/entity/ai/control/SmoothSwimmingLookControl.java
+++ b/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 - Ridables
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 - Ridables
if (this.lookAtCooldown > 0) {
this.lookAtCooldown--;
this.getYRotD().ifPresent(yRotD -> this.mob.yHeadRot = this.rotateTowards(this.mob.yHeadRot, yRotD + 20.0F, this.yMaxRotSpeed));
diff --git a/net/minecraft/world/entity/ambient/Bat.java b/net/minecraft/world/entity/ambient/Bat.java
index 5dd0c83e44eb68d90cda5743bc24d97044a02f6e..9861837c1d10e5ffe5dada9f4a75fb9adaccc161 100644
--- a/net/minecraft/world/entity/ambient/Bat.java
+++ b/net/minecraft/world/entity/ambient/Bat.java
@@ -42,11 +42,58 @@ public class Bat extends AmbientCreature {
public Bat(final EntityType<? extends Bat> type, final Level level) {
super(type, level);
+ this.moveControl = new org.purpurmc.purpur.controller.FlyingWithSpacebarMoveControllerWASD(this, 0.075F); // Purpur - Ridables
if (!level.isClientSide()) {
this.setResting(true);
}
}
+ // Purpur start - Ridables
+ @Override
+ public boolean shouldSendAttribute(net.minecraft.world.entity.ai.attributes.Attribute attribute) { return attribute != Attributes.FLYING_SPEED.value(); } // 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(net.minecraft.world.entity.player.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(net.minecraft.world.entity.MoverType.SELF, mot.multiply(speed, 0.25, speed));
+ setDeltaMovement(mot.scale(0.9D));
+ }
+ }
+ // Purpur end - Ridables
+
@Override
public boolean isFlapping() {
return !this.isResting() && this.tickCount % 10.0F == 0.0F;
@@ -97,7 +144,7 @@ public class Bat extends AmbientCreature {
}
public static AttributeSupplier.Builder createAttributes() {
- return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 6.0);
+ return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 6.0).add(Attributes.FLYING_SPEED, 0.6D); // Purpur - Ridables
}
public boolean isResting() {
@@ -128,6 +175,13 @@ public class Bat extends AmbientCreature {
@Override
protected void customServerAiStep(final ServerLevel level) {
+ // Purpur start - Ridables
+ if (getRider() != null && this.isControllable()) {
+ Vec3 mot = getDeltaMovement();
+ setDeltaMovement(mot.x(), mot.y() + (getVerticalMot() > 0 ? 0.07D : 0.0D), mot.z());
+ return;
+ }
+ // Purpur end - Ridables
super.customServerAiStep(level);
BlockPos pos = this.blockPosition();
BlockPos above = pos.above();
diff --git a/net/minecraft/world/entity/animal/allay/Allay.java b/net/minecraft/world/entity/animal/allay/Allay.java
index 242628d88736ac1401e76ecf94fb3f97837fa67c..ca1f1d18b765db8d197f70977e0e175744b07ee4 100644
--- a/net/minecraft/world/entity/animal/allay/Allay.java
+++ b/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 - Ridables
public Allay(final EntityType<? extends Allay> type, final Level level) {
super(type, level);
- this.moveControl = new FlyingMoveControl<>(this, 20, true);
+ // Purpur start - Ridables
+ 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 - Ridables
this.setCanPickUpLoot(this.canPickUpLoot());
this.vibrationUser = new Allay.VibrationUser();
this.vibrationData = new VibrationSystem.Data();
@@ -120,6 +133,28 @@ public class Allay extends PathfinderMob implements InventoryCarrier, VibrationS
}
// CraftBukkit end
+ // Purpur start - Ridables
+ @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 - Ridables
+ }
+ // Purpur end - Ridables
+
@Override
protected Brain<Allay> makeBrain(final Brain.Packed packedBrain) {
return BRAIN_PROVIDER.makeBrain(this, packedBrain);
@@ -210,6 +245,7 @@ public class Allay extends PathfinderMob implements InventoryCarrier, VibrationS
protected void customServerAiStep(final ServerLevel level) {
ProfilerFiller profiler = Profiler.get();
profiler.push("allayBrain");
+ if (getRider() == null || !this.isControllable()) // Purpur - only use brain if no rider
this.getBrain().tick(level, this);
profiler.pop();
profiler.push("allayActivityUpdate");
diff --git a/net/minecraft/world/entity/animal/armadillo/Armadillo.java b/net/minecraft/world/entity/animal/armadillo/Armadillo.java
index fbf3feec55deeeafa29c296722cf085ead1617db..e417fa354f22e3532d3bfda9c9ebfccfd4906bc6 100644
--- a/net/minecraft/world/entity/animal/armadillo/Armadillo.java
+++ b/net/minecraft/world/entity/animal/armadillo/Armadillo.java
@@ -93,6 +93,23 @@ public class Armadillo extends Animal {
return Animal.createAnimalAttributes().add(Attributes.MAX_HEALTH, 12.0).add(Attributes.MOVEMENT_SPEED, 0.14);
}
+ // Purpur start - Ridables
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.armadilloRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.armadilloRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.armadilloControllable;
+ }
+ // Purpur end - Ridables
+
@Override
protected void defineSynchedData(final SynchedEntityData.Builder entityData) {
super.defineSynchedData(entityData);
diff --git a/net/minecraft/world/entity/animal/axolotl/Axolotl.java b/net/minecraft/world/entity/animal/axolotl/Axolotl.java
index fa774ac8f3eea281c9b684c76dd12d4e66d3ee24..ed0d11c8048c3c6b25a003633684b89376b76be9 100644
--- a/net/minecraft/world/entity/animal/axolotl/Axolotl.java
+++ b/net/minecraft/world/entity/animal/axolotl/Axolotl.java
@@ -122,6 +122,23 @@ public class Axolotl extends Animal implements Bucketable {
this.lookControl = new Axolotl.AxolotlLookControl(this, 20);
}
+ // Purpur start - Ridables
+ @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 - Ridables
+ }
+ // Purpur end - Ridables
+
@Override
public float getWalkTargetValue(final BlockPos pos, final LevelReader level) {
return 0.0F;
@@ -370,6 +387,7 @@ public class Axolotl extends Animal implements Bucketable {
protected void customServerAiStep(final ServerLevel level) {
ProfilerFiller profiler = Profiler.get();
profiler.push("axolotlBrain");
+ if (getRider() == null || !this.isControllable()) // Purpur - only use brain if no rider
this.getBrain().tick(level, this);
profiler.pop();
profiler.push("axolotlActivityUpdate");
@@ -608,20 +626,28 @@ public class Axolotl extends Animal implements Bucketable {
}
@Override
- public void tick() {
+ public void vanillaTick() { // Purpur - Ridables
if (!Axolotl.this.isPlayingDead()) {
- super.tick();
+ super.vanillaTick(); // Purpur - Ridables
}
}
}
private static class AxolotlMoveControl<T extends Axolotl> extends SmoothSwimmingMoveControl<T> {
+ private final org.purpurmc.purpur.controller.WaterMoveControllerWASD<T> waterController; // Purpur - Ridables
public AxolotlMoveControl(final T axolotl) {
super(axolotl, 85, 10, 0.1F, 0.5F, false);
+ this.waterController = new org.purpurmc.purpur.controller.WaterMoveControllerWASD<>(axolotl, 0.5D); // Purpur - Ridables
}
@Override
public void tick() {
+ // Purpur start - Ridables
+ if (this.mob.getRider() != null && this.mob.isControllable()) {
+ this.waterController.purpurTick(this.mob.getRider());
+ return;
+ }
+ // Purpur end - Ridables
if (!this.mob.isPlayingDead()) {
super.tick();
}
diff --git a/net/minecraft/world/entity/animal/bee/Bee.java b/net/minecraft/world/entity/animal/bee/Bee.java
index cadd762e86868adcaafe1ad138b221495c906389..ec110b0dcef5ae0923cdda8aa43720f10b9f826e 100644
--- a/net/minecraft/world/entity/animal/bee/Bee.java
+++ b/net/minecraft/world/entity/animal/bee/Bee.java
@@ -151,6 +151,7 @@ public class Bee extends Animal implements NeutralMob {
public Bee(final EntityType<? extends Bee> type, final Level level) {
super(type, level);
+ final org.purpurmc.purpur.controller.FlyingMoveControllerWASD flyingController = new org.purpurmc.purpur.controller.FlyingMoveControllerWASD(this, 0.25F, 1.0F, false); // Purpur - Ridables
// Paper start - Fix MC-167279
class BeeFlyingMoveControl extends FlyingMoveControl<Mob> {
public BeeFlyingMoveControl(final Mob mob, final int maxTurn, final boolean hoversInPlace) {
@@ -159,11 +160,24 @@ public class Bee extends Animal implements NeutralMob {
@Override
public void tick() {
+ // Purpur start - Ridables
+ if (mob.getRider() != null && mob.isControllable()) {
+ flyingController.purpurTick(mob.getRider());
+ return;
+ }
+ // Purpur end - Ridables
if (this.mob.getY() <= Bee.this.level().getMinY()) {
this.mob.setNoGravity(false);
}
super.tick();
}
+
+ // Purpur start - Ridables
+ @Override
+ public boolean hasWanted() {
+ return mob.getRider() != null || !mob.isControllable() || super.hasWanted();
+ }
+ // Purpur end - Ridables
}
this.moveControl = new BeeFlyingMoveControl(this, 20, true);
// Paper end - Fix MC-167279
@@ -175,6 +189,40 @@ public class Bee extends Animal implements NeutralMob {
this.setPathfindingMalus(PathType.FENCE, -1.0F);
}
+ // Purpur start - Ridables
+ @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(net.minecraft.world.entity.MoverType.SELF, mot.multiply(speed, speed, speed));
+ setDeltaMovement(mot.scale(0.9D));
+ }
+ }
+ // Purpur end - Ridables
+
@Override
protected void defineSynchedData(final SynchedEntityData.Builder entityData) {
super.defineSynchedData(entityData);
@@ -189,6 +237,7 @@ public class Bee extends Animal implements NeutralMob {
@Override
protected void registerGoals() {
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables
this.goalSelector.addGoal(0, new Bee.BeeAttackGoal(this, 1.4F, true));
this.goalSelector.addGoal(1, new Bee.BeeEnterHiveGoal());
this.goalSelector.addGoal(2, new BreedGoal(this, 1.0));
@@ -206,6 +255,7 @@ public class Bee extends Animal implements NeutralMob {
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 - Ridables
this.targetSelector.addGoal(1, new Bee.BeeHurtByOtherGoal(this).setAlertOthers());
this.targetSelector.addGoal(2, new Bee.BeeBecomeAngryTargetGoal(this));
this.targetSelector.addGoal(3, new ResetUniversalAngerTargetGoal<>(this, true));
@@ -1083,15 +1133,15 @@ public class Bee extends Animal implements NeutralMob {
}
}
- private class BeeLookControl extends LookControl {
+ private class BeeLookControl extends org.purpurmc.purpur.controller.LookControllerWASD { // Purpur - Ridables
public BeeLookControl(final Mob mob) {
super(mob);
}
@Override
- public void tick() {
+ public void vanillaTick() { // Purpur - Ridables
if (!Bee.this.isAngry()) {
- super.tick();
+ super.vanillaTick(); // Purpur - Ridables
}
}
diff --git a/net/minecraft/world/entity/animal/camel/Camel.java b/net/minecraft/world/entity/animal/camel/Camel.java
index 57127335a3c68fbf9ac3d64a8504c6e271a5d532..8296a5a5935a77b01f4d11b102f3159d97fc8a30 100644
--- a/net/minecraft/world/entity/animal/camel/Camel.java
+++ b/net/minecraft/world/entity/animal/camel/Camel.java
@@ -100,6 +100,13 @@ public class Camel extends AbstractHorse {
navigation.setCanWalkOverFences(true);
}
+ // Purpur start - Ridables
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.camelRidableInWater;
+ }
+ // Purpur end - Ridables
+
@Override
protected void addAdditionalSaveData(final ValueOutput output) {
super.addAdditionalSaveData(output);
diff --git a/net/minecraft/world/entity/animal/chicken/Chicken.java b/net/minecraft/world/entity/animal/chicken/Chicken.java
index 36a34232cd48fa8f382d8f9b99308d2661d754b5..4a3d3ab6c9f52fd06107131f2564b196e7b42957 100644
--- a/net/minecraft/world/entity/animal/chicken/Chicken.java
+++ b/net/minecraft/world/entity/animal/chicken/Chicken.java
@@ -81,9 +81,27 @@ public class Chicken extends Animal {
this.setPathfindingMalus(PathType.WATER, 0.0F);
}
+ // Purpur start - Ridables
+ @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 - Ridables
+
@Override
protected void registerGoals() {
this.goalSelector.addGoal(0, new FloatGoal(this));
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables
this.goalSelector.addGoal(1, new PanicGoal(this, 1.4));
this.goalSelector.addGoal(2, new BreedGoal(this, 1.0));
this.goalSelector.addGoal(3, new TemptGoal(this, 1.0, i -> i.is(ItemTags.CHICKEN_FOOD), false));
diff --git a/net/minecraft/world/entity/animal/cow/AbstractCow.java b/net/minecraft/world/entity/animal/cow/AbstractCow.java
index ff0ddd53fc7ebd870c0522a3c9e967c6be619aea..5325932b1e60dd40014d9043328404266c02e3bf 100644
--- a/net/minecraft/world/entity/animal/cow/AbstractCow.java
+++ b/net/minecraft/world/entity/animal/cow/AbstractCow.java
@@ -39,6 +39,7 @@ public abstract class AbstractCow 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 - Ridables
this.goalSelector.addGoal(1, new PanicGoal(this, 2.0));
this.goalSelector.addGoal(2, new BreedGoal(this, 1.0));
this.goalSelector.addGoal(3, new TemptGoal(this, 1.25, i -> level().purpurConfig.cowFeedMushrooms > 0 && (i.is(net.minecraft.world.level.block.Blocks.RED_MUSHROOM.asItem()) || i.is(net.minecraft.world.level.block.Blocks.BROWN_MUSHROOM.asItem())) || i.is(ItemTags.COW_FOOD), false)); // Purpur - Cows eat mushrooms
@@ -88,13 +89,14 @@ public abstract class AbstractCow extends Animal {
@Override
public InteractionResult mobInteract(final Player player, final InteractionHand hand) {
+ if (getRider() != null) return InteractionResult.PASS; // Purpur - Ridables
ItemStack itemStack = player.getItemInHand(hand);
if (itemStack.is(Items.BUCKET) && !this.isBaby()) {
// CraftBukkit start - Got milk?
org.bukkit.event.player.PlayerBucketFillEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerBucketFillEvent(player.level(), player, this.blockPosition(), this.blockPosition(), null, itemStack, Items.MILK_BUCKET, hand);
if (event.isCancelled()) {
player.containerMenu.sendAllDataToRemote(); // Paper - Fix inventory desync
- return InteractionResult.PASS;
+ return tryRide(player, hand); // Purpur - Ridables
}
// CraftBukkit end
player.playSound(SoundEvents.COW_MILK, 1.0F, 1.0F);
diff --git a/net/minecraft/world/entity/animal/cow/Cow.java b/net/minecraft/world/entity/animal/cow/Cow.java
index f6d3a006218fee82134a40b7a41953b5738a382e..4d408217239cf11ff7c8925ca48c278fc8cfdfce 100644
--- a/net/minecraft/world/entity/animal/cow/Cow.java
+++ b/net/minecraft/world/entity/animal/cow/Cow.java
@@ -42,6 +42,23 @@ public class Cow extends AbstractCow {
super(type, level);
}
+ // Purpur start - Ridables
+ @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 - Ridables
+
@Override
protected void defineSynchedData(final SynchedEntityData.Builder entityData) {
super.defineSynchedData(entityData);
diff --git a/net/minecraft/world/entity/animal/cow/MushroomCow.java b/net/minecraft/world/entity/animal/cow/MushroomCow.java
index e5dbbd6ae9de60a8759c791edf60ca5e12b384f4..1e4747e800e1b987862fcb1b7c9dfff9fec0f9a2 100644
--- a/net/minecraft/world/entity/animal/cow/MushroomCow.java
+++ b/net/minecraft/world/entity/animal/cow/MushroomCow.java
@@ -69,6 +69,23 @@ public class MushroomCow extends AbstractCow implements Shearable {
super(type, level);
}
+ // Purpur start - Ridables
+ @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 - Ridables
+
@Override
public float getWalkTargetValue(final BlockPos pos, final LevelReader level) {
return level.getBlockState(pos.below()).is(Blocks.MYCELIUM) ? 10.0F : level.getPathfindingCostFromLightLevels(pos);
@@ -127,7 +144,7 @@ public class MushroomCow extends AbstractCow implements Shearable {
// Paper start - call PlayerShearEntityEvent
java.util.List<ItemStack> drops = this.generateDefaultDrops(level, itemStack);
org.bukkit.event.player.PlayerShearEntityEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.handlePlayerShearEntityEvent(player, this, itemStack, hand, drops);
- if (event.isCancelled()) return InteractionResult.PASS;
+ if (event.isCancelled()) return tryRide(player, hand); // Purpur - Ridables
drops = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getDrops());
this.shear(level, SoundSource.PLAYERS, itemStack, drops);
// Paper end - call PlayerShearEntityEvent
diff --git a/net/minecraft/world/entity/animal/dolphin/Dolphin.java b/net/minecraft/world/entity/animal/dolphin/Dolphin.java
index f66c8eb6412bc32f7d035f5575135263c2ae8b24..00ec3c3764ec9a16fe67a584fcbf2d80fc59f3e1 100644
--- a/net/minecraft/world/entity/animal/dolphin/Dolphin.java
+++ b/net/minecraft/world/entity/animal/dolphin/Dolphin.java
@@ -87,14 +87,80 @@ public class Dolphin extends AgeableWaterCreature {
private static final boolean DEFAULT_GOT_FISH = false;
public @Nullable BlockPos treasurePos;
private boolean isNaturallyAggressiveToPlayers; // Purpur - Dolphins naturally aggressive to players chance
+ private int spitCooldown; // Purpur - Ridables
public Dolphin(final EntityType<? extends Dolphin> type, final Level level) {
super(type, level);
- this.moveControl = new SmoothSwimmingMoveControl<>(this, 85, 10, 0.02F, 0.1F, true);
+ // Purpur start - Ridables
+ class DolphinMoveControl<T extends Mob> extends SmoothSwimmingMoveControl<T> {
+ private final org.purpurmc.purpur.controller.WaterMoveControllerWASD<T> waterMoveControllerWASD;
+
+ public DolphinMoveControl(T dolphin, int pitchChange, int yawChange, float speedInWater, float speedInAir, boolean buoyant) {
+ super(dolphin, pitchChange, yawChange, speedInWater, speedInAir, buoyant);
+ this.waterMoveControllerWASD = new org.purpurmc.purpur.controller.WaterMoveControllerWASD<>(dolphin);
+ }
+
+ @Override
+ public void tick() {
+ if (this.mob.getRider() != null && this.mob.isControllable()) {
+ purpurTick(this.mob.getRider());
+ } else {
+ super.tick();
+ }
+ }
+
+ public void purpurTick(Player rider) {
+ if (this.mob.getAirSupply() < 150) {
+ // if drowning override player WASD controls to find air
+ super.tick();
+ } else {
+ waterMoveControllerWASD.purpurTick(rider);
+ this.mob.setDeltaMovement(this.mob.getDeltaMovement().add(0.0D, 0.005D, 0.0D));
+ }
+ }
+ };
+ this.moveControl = new DolphinMoveControl<>(this, 85, 10, 0.02F, 0.1F, true);
+ // Purpur end - Ridables
this.lookControl = new SmoothSwimmingLookControl(this, 10);
this.setCanPickUpLoot(true);
}
+ // Purpur start - Ridables
+ @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.projectile.DolphinSpit spit = new org.purpurmc.purpur.entity.projectile.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 - Ridables
+
@Override
public @Nullable SpawnGroupData finalizeSpawn(
final ServerLevelAccessor level, final DifficultyInstance difficulty, final EntitySpawnReason spawnReason, final @Nullable SpawnGroupData groupData
@@ -169,6 +235,7 @@ public class Dolphin extends AgeableWaterCreature {
this.goalSelector.addGoal(0, new BreathAirGoal(this));
this.goalSelector.addGoal(0, new TryFindWaterGoal(this));
this.goalSelector.addGoal(1, new MeleeAttackGoal(this, 1.2000000476837158D, true)); // Purpur - Dolphins naturally aggressive to players chance
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables
this.goalSelector.addGoal(1, new Dolphin.DolphinSwimToTreasureGoal(this));
this.goalSelector.addGoal(2, new Dolphin.DolphinSwimWithPlayerGoal(this, 4.0));
this.goalSelector.addGoal(4, new RandomSwimmingGoal(this, 1.0, 10));
@@ -180,6 +247,7 @@ public class Dolphin extends AgeableWaterCreature {
this.goalSelector.addGoal(8, new FollowPlayerRiddenEntityGoal(this, AbstractBoat.class));
this.goalSelector.addGoal(8, new FollowPlayerRiddenEntityGoal(this, AbstractNautilus.class));
this.goalSelector.addGoal(9, new AvoidEntityGoal<>(this, Guardian.class, 8.0F, 1.0, 1.0));
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables
this.targetSelector.addGoal(1, new HurtByTargetGoal(this, Guardian.class).setAlertOthers());
this.targetSelector.addGoal(2, new net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, (ignored, ignored2) -> isNaturallyAggressiveToPlayers)); // Purpur - Dolphins naturally aggressive to players chance
}
@@ -249,6 +317,11 @@ public class Dolphin extends AgeableWaterCreature {
@Override
public void tick() {
super.tick();
+ // Purpur start - Ridables
+ if (spitCooldown > 0) {
+ spitCooldown--;
+ }
+ // Purpur end - Ridables
if (this.isNoAi()) {
this.setAirSupply(this.getMaxAirSupply());
} else {
diff --git a/net/minecraft/world/entity/animal/equine/AbstractHorse.java b/net/minecraft/world/entity/animal/equine/AbstractHorse.java
index 847fcecc4046d9fe880aaa7eb2a3c55c5bc61faf..3e3786a2803eacd3cdbcdc0039a7957256bf5fe9 100644
--- a/net/minecraft/world/entity/animal/equine/AbstractHorse.java
+++ b/net/minecraft/world/entity/animal/equine/AbstractHorse.java
@@ -128,11 +128,21 @@ public abstract class AbstractHorse extends Animal implements PlayerRideableJump
protected AbstractHorse(final EntityType<? extends AbstractHorse> type, final Level level) {
super(type, level);
+ 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.createInventory();
}
+ // Purpur start - Ridables
+ @Override
+ public boolean isRidable() {
+ return false; // vanilla handles
+ }
+ // Purpur end - Ridables
+
@Override
protected void registerGoals() {
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HorseHasRider(this)); // Purpur - Ridables
this.goalSelector.addGoal(1, new RunAroundLikeCrazyGoal(this, 1.2));
this.goalSelector.addGoal(2, new BreedGoal(this, 1.0, AbstractHorse.class));
this.goalSelector.addGoal(4, new FollowParentGoal(this, 1.0));
@@ -142,6 +152,7 @@ public abstract class AbstractHorse extends Animal implements PlayerRideableJump
if (this.canPerformRearing()) {
this.goalSelector.addGoal(9, new RandomStandGoal(this));
}
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HorseHasRider(this)); // Purpur - Ridables
this.addBehaviourGoals();
}
diff --git a/net/minecraft/world/entity/animal/equine/Donkey.java b/net/minecraft/world/entity/animal/equine/Donkey.java
index 1403a4f893d32a51c9c8421e869214a9813d1e79..31cd10ab4c499c6b5ccb4be67782340c44b9758c 100644
--- a/net/minecraft/world/entity/animal/equine/Donkey.java
+++ b/net/minecraft/world/entity/animal/equine/Donkey.java
@@ -17,6 +17,13 @@ public class Donkey extends AbstractChestedHorse {
super(type, level);
}
+ // Purpur start - Ridables
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.donkeyRidableInWater;
+ }
+ // Purpur end - Ridables
+
@Override
public SoundEvent getAmbientSound() {
return SoundEvents.DONKEY_AMBIENT;
diff --git a/net/minecraft/world/entity/animal/equine/Horse.java b/net/minecraft/world/entity/animal/equine/Horse.java
index 013ca7a41ac31005f3b60af4ab89efae37144573..663c6aea26a6a619f1d7af7af022abae20c930f9 100644
--- a/net/minecraft/world/entity/animal/equine/Horse.java
+++ b/net/minecraft/world/entity/animal/equine/Horse.java
@@ -49,6 +49,13 @@ public class Horse extends AbstractHorse {
super(type, level);
}
+ // Purpur start - Ridables
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.horseRidableInWater;
+ }
+ // Purpur end - Ridables
+
@Override
protected void randomizeAttributes(final RandomSource random) {
this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(generateMaxHealth(random::nextInt));
diff --git a/net/minecraft/world/entity/animal/equine/Llama.java b/net/minecraft/world/entity/animal/equine/Llama.java
index 26143c77ca33da032de237817d8953647e3bdff4..ccb2486027263bb45f250c19a4b870c3494af1b9 100644
--- a/net/minecraft/world/entity/animal/equine/Llama.java
+++ b/net/minecraft/world/entity/animal/equine/Llama.java
@@ -82,7 +82,58 @@ public class Llama extends AbstractChestedHorse implements RangedAttackMob {
super(type, level);
this.getNavigation().setRequiredPathLength(40.0F);
this.maxDomestication = 30; // Paper - Missing entity API; configure max temper instead of a hardcoded value
+ // Purpur start - Ridables
+ 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 - Ridables
+ }
+
+ // Purpur start - Ridables
+ @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.isWearingBodyArmor() || this.isTamed();
+ }
+
+ @Nullable
+ @Override
+ public LivingEntity getControllingPassenger() {
+ Entity firstPassenger = this.getFirstPassenger();
+ return !this.isNoAi() && firstPassenger instanceof net.minecraft.world.entity.Mob mob && firstPassenger.canControlVehicle() ? mob : null;
}
+ // Purpur end - Ridables
public boolean isTraderLlama() {
return false;
@@ -120,6 +171,7 @@ public class Llama extends AbstractChestedHorse implements RangedAttackMob {
@Override
protected void registerGoals() {
this.goalSelector.addGoal(0, new FloatGoal(this));
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.LlamaHasRider(this)); // Purpur - Ridables
this.goalSelector.addGoal(1, new RunAroundLikeCrazyGoal(this, 1.2));
this.goalSelector.addGoal(2, new LlamaFollowCaravanGoal(this, 2.1F));
this.goalSelector.addGoal(3, new RangedAttackGoal(this, 1.25, 40, 20.0F));
@@ -130,6 +182,7 @@ public class Llama extends AbstractChestedHorse implements RangedAttackMob {
this.goalSelector.addGoal(7, new WaterAvoidingRandomStrollGoal(this, 0.7));
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 - Ridables
this.targetSelector.addGoal(1, new Llama.LlamaHurtByTargetGoal(this));
this.targetSelector.addGoal(2, new Llama.LlamaAttackWolfGoal(this));
}
diff --git a/net/minecraft/world/entity/animal/equine/Mule.java b/net/minecraft/world/entity/animal/equine/Mule.java
index b565ca64bfe28a8d636dc3829fed44516a6e1af7..4a82af3b1f5d22fc5d4060f56cdab0ec51ef83e3 100644
--- a/net/minecraft/world/entity/animal/equine/Mule.java
+++ b/net/minecraft/world/entity/animal/equine/Mule.java
@@ -16,6 +16,13 @@ public class Mule extends AbstractChestedHorse {
super(type, level);
}
+ // Purpur start - Ridables
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.muleRidableInWater;
+ }
+ // Purpur end - Ridables
+
@Override
public SoundEvent getAmbientSound() {
return SoundEvents.MULE_AMBIENT;
diff --git a/net/minecraft/world/entity/animal/equine/SkeletonHorse.java b/net/minecraft/world/entity/animal/equine/SkeletonHorse.java
index 71da9124581c7f5a7a9cdd35f9629397fba75668..53ddd31255fab4f5f8472dd48407012ea7363264 100644
--- a/net/minecraft/world/entity/animal/equine/SkeletonHorse.java
+++ b/net/minecraft/world/entity/animal/equine/SkeletonHorse.java
@@ -44,6 +44,13 @@ public class SkeletonHorse extends AbstractHorse {
super(type, level);
}
+ // Purpur start - Ridables
+ @Override
+ public boolean isTamed() {
+ return super.isTamed() || this.level().purpurConfig.skeletonHorseRidable;
+ }
+ // Purpur end - Ridables
+
public static AttributeSupplier.Builder createAttributes() {
return createBaseHorseAttributes().add(Attributes.MAX_HEALTH, 15.0).add(Attributes.MOVEMENT_SPEED, 0.2F);
}
@@ -63,6 +70,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)); // Purpur - Ridables
}
@Override
diff --git a/net/minecraft/world/entity/animal/equine/TraderLlama.java b/net/minecraft/world/entity/animal/equine/TraderLlama.java
index 7c92c9335997cee76ff360c51f704ee41a180796..ca89823a7951dba970c6bd82a7610eb7ed119a8b 100644
--- a/net/minecraft/world/entity/animal/equine/TraderLlama.java
+++ b/net/minecraft/world/entity/animal/equine/TraderLlama.java
@@ -32,6 +32,28 @@ public class TraderLlama extends Llama {
super(type, level);
}
+ // Purpur start - Ridables
+ @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 - Ridables
+
@Override
public boolean isTraderLlama() {
return true;
diff --git a/net/minecraft/world/entity/animal/equine/ZombieHorse.java b/net/minecraft/world/entity/animal/equine/ZombieHorse.java
index 09c0ec915408ea35b4644d9c7ab20efd90a705c8..fdc590ca470ffbbac481eb684c105218fb3603e2 100644
--- a/net/minecraft/world/entity/animal/equine/ZombieHorse.java
+++ b/net/minecraft/world/entity/animal/equine/ZombieHorse.java
@@ -50,6 +50,18 @@ public class ZombieHorse extends AbstractHorse {
super(type, level);
}
+ // Purpur start - Ridables
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.zombieHorseRidableInWater;
+ }
+
+ @Override
+ public boolean isTamed() {
+ return super.isTamed() || this.level().purpurConfig.zombieHorseRidable;
+ }
+ // Purpur end - Ridables
+
public static AttributeSupplier.Builder createAttributes() {
return createBaseHorseAttributes().add(Attributes.MAX_HEALTH, 25.0);
}
diff --git a/net/minecraft/world/entity/animal/feline/Cat.java b/net/minecraft/world/entity/animal/feline/Cat.java
index 7a6b32188f042d846c4ef7bce46b63fc31ede4d3..5f6cb6f3d0bf8b5df66e16155df91982a5d2eb47 100644
--- a/net/minecraft/world/entity/animal/feline/Cat.java
+++ b/net/minecraft/world/entity/animal/feline/Cat.java
@@ -102,10 +102,36 @@ public class Cat extends TamableAnimal {
this.reassessTameGoals();
}
+ // Purpur start - Ridables
+ @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 - Ridables
+
@Override
protected void registerGoals() {
this.temptGoal = new Cat.CatTemptGoal(this, 0.6, i -> i.is(ItemTags.CAT_FOOD), true);
this.goalSelector.addGoal(1, new FloatGoal(this));
+ this.goalSelector.addGoal(1, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables
this.goalSelector.addGoal(1, new TamableAnimal.TamableAnimalPanicGoal(1.5));
this.goalSelector.addGoal(2, new SitWhenOrderedToGoal(this));
this.goalSelector.addGoal(3, new Cat.CatRelaxOnOwnerGoal(this));
@@ -118,6 +144,7 @@ public class Cat extends TamableAnimal {
this.goalSelector.addGoal(10, new BreedGoal(this, 0.8));
this.goalSelector.addGoal(11, new WaterAvoidingRandomStrollGoal(this, 0.8, 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 - Ridables
this.targetSelector.addGoal(1, new NonTameRandomTargetGoal<>(this, Rabbit.class, false, null));
this.targetSelector.addGoal(1, new NonTameRandomTargetGoal<>(this, Turtle.class, false, Turtle.BABY_ON_LAND_SELECTOR));
}
@@ -414,6 +441,7 @@ public class Cat extends TamableAnimal {
@Override
public InteractionResult mobInteract(final Player player, final InteractionHand hand) {
+ if (getRider() != null) return InteractionResult.PASS; // Purpur - Ridables
ItemStack itemStack = player.getItemInHand(hand);
if (this.isTame()) {
if (this.isOwnedBy(player)) {
diff --git a/net/minecraft/world/entity/animal/feline/Ocelot.java b/net/minecraft/world/entity/animal/feline/Ocelot.java
index 024266211d2e860f04775266db39e1fd69c01952..086ca07d2a6cba84226367d2410a4e04e1f7a4e8 100644
--- a/net/minecraft/world/entity/animal/feline/Ocelot.java
+++ b/net/minecraft/world/entity/animal/feline/Ocelot.java
@@ -73,6 +73,23 @@ public class Ocelot extends Animal {
this.reassessTrustingGoals();
}
+ // Purpur start - Ridables
+ @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 - Ridables
+
public boolean isTrusting() {
return this.entityData.get(DATA_TRUSTING);
}
@@ -104,12 +121,14 @@ public class Ocelot extends Animal {
protected void registerGoals() {
this.temptGoal = new Ocelot.OcelotTemptGoal(this, 0.6, i -> i.is(ItemTags.OCELOT_FOOD), true);
this.goalSelector.addGoal(1, new FloatGoal(this));
+ this.goalSelector.addGoal(1, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables
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.8));
this.goalSelector.addGoal(10, new WaterAvoidingRandomStrollGoal(this, 0.8, 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 - Ridables
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/net/minecraft/world/entity/animal/fish/AbstractFish.java b/net/minecraft/world/entity/animal/fish/AbstractFish.java
index 16f8d170699b8c1c7fd37a4db91bb89e88903fbf..9a486a9a572916b86631d4ed21abfdcb5ef307af 100644
--- a/net/minecraft/world/entity/animal/fish/AbstractFish.java
+++ b/net/minecraft/world/entity/animal/fish/AbstractFish.java
@@ -91,6 +91,7 @@ public abstract class AbstractFish extends WaterAnimal implements Bucketable {
@Override
protected void registerGoals() {
super.registerGoals();
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables
this.goalSelector.addGoal(0, new PanicGoal(this, 1.25));
this.goalSelector.addGoal(2, new AvoidEntityGoal<>(this, Player.class, 8.0F, 1.6, 1.4, EntitySelector.NO_SPECTATORS));
this.goalSelector.addGoal(4, new AbstractFish.FishSwimGoal(this));
@@ -103,7 +104,7 @@ public abstract class AbstractFish extends WaterAnimal implements Bucketable {
@Override
protected void travelInWater(final Vec3 input, final double baseGravity, final boolean isFalling, final double oldY) {
- this.moveRelative(0.01F, input);
+ this.moveRelative(getRider() != null ? getSpeed() : 0.01F, input); // Purpur - Ridables
this.move(MoverType.SELF, this.getDeltaMovement());
this.setDeltaMovement(this.getDeltaMovement().scale(0.9));
if (this.getTarget() == null) {
@@ -160,19 +161,27 @@ public abstract class AbstractFish extends WaterAnimal implements Bucketable {
protected void playStepSound(final BlockPos pos, final BlockState blockState) {
}
- private static class FishMoveControl<T extends AbstractFish> extends MoveControl<T> {
+ private static class FishMoveControl<T extends AbstractFish> extends org.purpurmc.purpur.controller.WaterMoveControllerWASD<T> { // Purpur - Ridables
public FishMoveControl(final T fish) {
super(fish);
}
+ // Purpur start - Ridables
@Override
- public void tick() {
+ public void purpurTick(Player rider) {
+ super.purpurTick(rider);
+ this.mob.setDeltaMovement(this.mob.getDeltaMovement().add(0.0D, 0.005, 0.0D));
+ }
+ // Purpur end - Ridables
+
+ @Override
+ public void vanillaTick() { // Purpur - Ridables
if (this.mob.isEyeInFluid(FluidTags.WATER)) {
this.mob.setDeltaMovement(this.mob.getDeltaMovement().add(0.0, 0.005, 0.0));
}
if (this.operation == MoveControl.Operation.MOVE_TO && !this.mob.getNavigation().isDone()) {
- float targetSpeed = (float)(this.speedModifier * this.mob.getAttributeValue(Attributes.MOVEMENT_SPEED));
+ float targetSpeed = (float)(this.getSpeedModifier() * this.mob.getAttributeValue(Attributes.MOVEMENT_SPEED)); // Purpur - Ridables
this.mob.setSpeed(Mth.lerp(0.125F, this.mob.getSpeed(), targetSpeed));
double xd = this.wantedX - this.mob.getX();
double yd = this.wantedY - this.mob.getY();
diff --git a/net/minecraft/world/entity/animal/fish/Cod.java b/net/minecraft/world/entity/animal/fish/Cod.java
index 1909fefa4af14a9aca43ccb8b93317b12ee4a092..becb5c214adacd36372bbe2b21ab15e341640f7d 100644
--- a/net/minecraft/world/entity/animal/fish/Cod.java
+++ b/net/minecraft/world/entity/animal/fish/Cod.java
@@ -13,6 +13,18 @@ public class Cod extends AbstractSchoolingFish {
super(type, level);
}
+ // Purpur start - Ridables
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.codRidable;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.codControllable;
+ }
+ // Purpur end - Ridables
+
@Override
public ItemStack getBucketItemStack() {
return new ItemStack(Items.COD_BUCKET);
diff --git a/net/minecraft/world/entity/animal/fish/Pufferfish.java b/net/minecraft/world/entity/animal/fish/Pufferfish.java
index ad7bdb7f21fe80dbf85160ef8b40417d567bfdd6..baca62c789359f02b3ef22cbf8f72ba6008fb51c 100644
--- a/net/minecraft/world/entity/animal/fish/Pufferfish.java
+++ b/net/minecraft/world/entity/animal/fish/Pufferfish.java
@@ -47,6 +47,18 @@ public class Pufferfish extends AbstractFish {
this.refreshDimensions();
}
+ // Purpur start - Ridables
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.pufferfishRidable;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.pufferfishControllable;
+ }
+ // Purpur end - Ridables
+
@Override
protected void defineSynchedData(final SynchedEntityData.Builder entityData) {
super.defineSynchedData(entityData);
diff --git a/net/minecraft/world/entity/animal/fish/Salmon.java b/net/minecraft/world/entity/animal/fish/Salmon.java
index ab27233be7b3d541139aecc090428be6910d1fc0..0b89e3b80ec06345377133c2f954cc65db7e3b9c 100644
--- a/net/minecraft/world/entity/animal/fish/Salmon.java
+++ b/net/minecraft/world/entity/animal/fish/Salmon.java
@@ -41,6 +41,18 @@ public class Salmon extends AbstractSchoolingFish {
this.refreshDimensions();
}
+ // Purpur start - Ridables
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.salmonRidable;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.salmonControllable;
+ }
+ // Purpur end - Ridables
+
@Override
public int getMaxSchoolSize() {
return 5;
diff --git a/net/minecraft/world/entity/animal/fish/TropicalFish.java b/net/minecraft/world/entity/animal/fish/TropicalFish.java
index f846e5c736baea99fe5e411e36cf56a074145752..d48f8dc3e806ffe767565158bcfa17631839a61c 100644
--- a/net/minecraft/world/entity/animal/fish/TropicalFish.java
+++ b/net/minecraft/world/entity/animal/fish/TropicalFish.java
@@ -77,6 +77,18 @@ public class TropicalFish extends AbstractSchoolingFish {
super(type, level);
}
+ // Purpur start - Ridables
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.tropicalFishRidable;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.tropicalFishControllable;
+ }
+ // Purpur end - Ridables
+
public static String getPredefinedName(final int index) {
return "entity.minecraft.tropical_fish.predefined." + index;
}
diff --git a/net/minecraft/world/entity/animal/fox/Fox.java b/net/minecraft/world/entity/animal/fox/Fox.java
index 788b32b81fca696ed01d86789846f5043c2d56d4..aecbd2bddf4eeb365ccad8a85799f7fcd8b9f319 100644
--- a/net/minecraft/world/entity/animal/fox/Fox.java
+++ b/net/minecraft/world/entity/animal/fox/Fox.java
@@ -161,6 +161,44 @@ public class Fox extends Animal {
this.getNavigation().setRequiredPathLength(32.0F);
}
+ // Purpur start - Ridables
+ @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 - Ridables
+
@Override
protected void defineSynchedData(final SynchedEntityData.Builder entityData) {
super.defineSynchedData(entityData);
@@ -180,6 +218,7 @@ public class Fox extends Animal {
this, AbstractFish.class, 20, false, false, (target, level) -> target instanceof AbstractSchoolingFish
);
this.goalSelector.addGoal(0, new Fox.FoxFloatGoal());
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables
this.goalSelector.addGoal(0, new ClimbOnTopOfPowderSnowGoal(this, this.level()));
this.goalSelector.addGoal(1, new Fox.FaceplantGoal());
this.goalSelector.addGoal(2, new Fox.FoxPanicGoal(2.2));
@@ -204,6 +243,7 @@ public class Fox extends Animal {
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 - Ridables
this.targetSelector
.addGoal(
3,
@@ -1132,15 +1172,15 @@ public class Fox extends Animal {
}
}
- public class FoxLookControl extends LookControl {
+ public class FoxLookControl extends org.purpurmc.purpur.controller.LookControllerWASD { // Purpur - Ridables
public FoxLookControl() {
super(Fox.this);
}
@Override
- public void tick() {
+ public void vanillaTick() { // Purpur - Ridables
if (!Fox.this.isSleeping()) {
- super.tick();
+ super.vanillaTick(); // Purpur - Ridables
}
}
@@ -1176,15 +1216,15 @@ public class Fox extends Animal {
}
}
- private static class FoxMoveControl<T extends Fox> extends MoveControl<T> {
+ private static class FoxMoveControl<T extends Fox> extends org.purpurmc.purpur.controller.MoveControllerWASD<T> { // Purpur - Ridables
public FoxMoveControl(final T fox) {
super(fox);
}
@Override
- public void tick() {
+ public void vanillaTick() { // Purpur - Ridables
if (this.mob.canMove()) {
- super.tick();
+ super.vanillaTick(); // Purpur - Ridables
}
}
}
diff --git a/net/minecraft/world/entity/animal/frog/Frog.java b/net/minecraft/world/entity/animal/frog/Frog.java
index d7d8216524e8e4c66c9e5a8dd4e682f46f335ca1..1d9689489d67098148960d49540db77532d3adeb 100644
--- a/net/minecraft/world/entity/animal/frog/Frog.java
+++ b/net/minecraft/world/entity/animal/frog/Frog.java
@@ -83,14 +83,63 @@ public class Frog extends Animal {
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 - Ridables
+ private org.purpurmc.purpur.controller.WaterMoveControllerWASD purpurWaterController; // Purpur - Ridables
public Frog(final EntityType<? extends Animal> type, final Level level) {
super(type, level);
this.lookControl = new Frog.FrogLookControl(this);
this.setPathfindingMalus(PathType.WATER, 4.0F);
this.setPathfindingMalus(PathType.TRAPDOOR, -1.0F);
- this.moveControl = new SmoothSwimmingMoveControl<>(this, 85, 10, 0.02F, 0.1F, true);
+ // Purpur start - Ridables
+ 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 - Ridables
+ }
+
+ // Purpur start - Ridables
+ @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 - Ridables
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables
+ }
+
+ @Override
+ public float getJumpPower() {
+ return (getRider() != null && isControllable()) ? level().purpurConfig.frogRidableJumpHeight * this.getBlockJumpFactor() : super.getJumpPower();
}
+ // Purpur end - Ridables
@Override
protected Brain<Frog> makeBrain(final Brain.Packed packedBrain) {
@@ -177,6 +226,7 @@ public class Frog extends Animal {
protected void customServerAiStep(final ServerLevel level) {
ProfilerFiller profiler = Profiler.get();
profiler.push("frogBrain");
+ if (getRider() == null || !this.isControllable()) // Purpur - only use brain if no rider
this.getBrain().tick(level, this);
profiler.pop();
profiler.push("frogActivityUpdate");
@@ -342,7 +392,7 @@ public class Frog extends Animal {
return level.getBlockState(pos.below()).is(BlockTags.FROGS_SPAWNABLE_ON) && isBrightEnoughToSpawn(level, pos);
}
- private class FrogLookControl extends LookControl {
+ private class FrogLookControl extends org.purpurmc.purpur.controller.LookControllerWASD { // Purpur - Ridables
public FrogLookControl(final Mob mob) {
super(mob);
}
diff --git a/net/minecraft/world/entity/animal/frog/Tadpole.java b/net/minecraft/world/entity/animal/frog/Tadpole.java
index 296cae1d5f5a3a054949ee03e2a2552bfeae4682..30b538a10497d7808217cdcc161161a0feea505e 100644
--- a/net/minecraft/world/entity/animal/frog/Tadpole.java
+++ b/net/minecraft/world/entity/animal/frog/Tadpole.java
@@ -55,13 +55,50 @@ public class Tadpole extends AbstractFish {
List.of(SensorType.NEAREST_LIVING_ENTITIES, SensorType.NEAREST_PLAYERS, SensorType.HURT_BY, SensorType.FROG_TEMPTATIONS),
var0 -> TadpoleAi.getActivities()
);
+ private org.purpurmc.purpur.controller.WaterMoveControllerWASD purpurController; // Purpur - Ridables
public Tadpole(final EntityType<? extends AbstractFish> type, final Level level) {
super(type, level);
- this.moveControl = new SmoothSwimmingMoveControl<>(this, 85, 10, 0.02F, 0.1F, true);
+ // Purpur start - Ridables
+ 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 - Ridables
this.lookControl = new SmoothSwimmingLookControl(this, 10);
}
+ // Purpur start - Ridables
+ @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 - Ridables
+ }
+ // Purpur end - Ridables
+
@Override
protected PathNavigation createNavigation(final Level level) {
return new WaterBoundPathNavigation(this, level);
@@ -86,6 +123,7 @@ public class Tadpole extends AbstractFish {
protected void customServerAiStep(final ServerLevel level) {
ProfilerFiller profiler = Profiler.get();
profiler.push("tadpoleBrain");
+ if (getRider() == null || !this.isControllable()) // Purpur - only use brain if no rider
this.getBrain().tick(level, this);
profiler.pop();
profiler.push("tadpoleActivityUpdate");
diff --git a/net/minecraft/world/entity/animal/goat/Goat.java b/net/minecraft/world/entity/animal/goat/Goat.java
index 0691d05d6ac63cf2dd6d6410ed7dc585bc02fa4b..6d81790bf42f7a580b95644f0f19c3ddfc7d1764 100644
--- a/net/minecraft/world/entity/animal/goat/Goat.java
+++ b/net/minecraft/world/entity/animal/goat/Goat.java
@@ -106,6 +106,23 @@ public class Goat extends Animal {
.orElseGet(() -> new ItemStack(Items.GOAT_HORN));
}
+ // Purpur start - Ridables
+ @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 - Ridables
+
@Override
protected Brain<Goat> makeBrain(final Brain.Packed packedBrain) {
return BRAIN_PROVIDER.makeBrain(this, packedBrain);
@@ -176,6 +193,7 @@ public class Goat extends Animal {
protected void customServerAiStep(final ServerLevel level) {
ProfilerFiller profiler = Profiler.get();
profiler.push("goatBrain");
+ if (getRider() == null || !this.isControllable()) // Purpur - only use brain if no rider
this.getBrain().tick(level, this);
profiler.pop();
profiler.push("goatActivityUpdate");
diff --git a/net/minecraft/world/entity/animal/golem/CopperGolem.java b/net/minecraft/world/entity/animal/golem/CopperGolem.java
index 3feb7ed930e1d65e8fdfa761d3ede9104c158f2e..c27319adafa8089f098efde14989425a4715b260 100644
--- a/net/minecraft/world/entity/animal/golem/CopperGolem.java
+++ b/net/minecraft/world/entity/animal/golem/CopperGolem.java
@@ -112,6 +112,28 @@ public class CopperGolem extends AbstractGolem implements ContainerUser, Shearab
}
// Purpur end - Summoner API
+ // Purpur start - Ridables
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.copperGolemRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.copperGolemRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.copperGolemControllable;
+ }
+
+ @Override
+ protected void registerGoals() {
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this));
+ }
+ // Purpur end - Ridables
+
public static AttributeSupplier.Builder createAttributes() {
return Mob.createMobAttributes().add(Attributes.MOVEMENT_SPEED, 0.2F).add(Attributes.STEP_HEIGHT, 1.0).add(Attributes.MAX_HEALTH, 12.0);
}
@@ -197,6 +219,7 @@ public class CopperGolem extends AbstractGolem implements ContainerUser, Shearab
protected void customServerAiStep(final ServerLevel level) {
ProfilerFiller profiler = Profiler.get();
profiler.push("copperGolemBrain");
+ if (getRider() == null || !this.isControllable()) // Purpur - only use brain if no rider
this.getBrain().tick(level, this);
profiler.pop();
profiler.push("copperGolemActivityUpdate");
@@ -235,7 +258,7 @@ public class CopperGolem extends AbstractGolem implements ContainerUser, Shearab
// Paper start - call PlayerShearEntityEvent
java.util.List<ItemStack> drops = this.generateDefaultDrops(serverLevel, itemStack);
org.bukkit.event.player.PlayerShearEntityEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.handlePlayerShearEntityEvent(player, this, itemStack, hand, drops);
- if (event.isCancelled()) return InteractionResult.PASS;
+ if (event.isCancelled()) return tryRide(player, hand); // Purpur - Ridables
drops = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getDrops());
this.shear(serverLevel, SoundSource.PLAYERS, itemStack, drops);
// Paper end - call PlayerShearEntityEvent
@@ -276,6 +299,8 @@ public class CopperGolem extends AbstractGolem implements ContainerUser, Shearab
}
}
+ if (level().purpurConfig.villagerRidable && itemStack.isEmpty()) return tryRide(player, hand); // Purpur - Ridables
+
return super.mobInteract(player, hand);
}
}
diff --git a/net/minecraft/world/entity/animal/golem/IronGolem.java b/net/minecraft/world/entity/animal/golem/IronGolem.java
index ea29222d152ec528a4c649712bf506e9c5176b0f..8c674f2f3a068e92b3975df8137912f4b8327cf2 100644
--- a/net/minecraft/world/entity/animal/golem/IronGolem.java
+++ b/net/minecraft/world/entity/animal/golem/IronGolem.java
@@ -75,9 +75,28 @@ public class IronGolem extends AbstractGolem implements NeutralMob {
}
// Purpur end - Summoner API
+ // Purpur start - Ridables
+ @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 - Ridables
+
@Override
protected void registerGoals() {
if (this.level().purpurConfig.ironGolemPoppyCalm) this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.ReceiveFlower(this)); // Purpur - Iron golem calm anger options
+ if (level().purpurConfig.ironGolemCanSwim) this.goalSelector.addGoal(0, new net.minecraft.world.entity.ai.goal.FloatGoal(this)); // Purpur - Ridables
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables
this.goalSelector.addGoal(1, new MeleeAttackGoal(this, 1.0, true));
this.goalSelector.addGoal(2, new MoveTowardsTargetGoal(this, 0.9, 32.0F));
this.goalSelector.addGoal(2, new MoveBackToVillageGoal(this, 0.6, false));
@@ -85,6 +104,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 - Ridables
this.targetSelector.addGoal(1, new DefendVillageTargetGoal(this));
this.targetSelector.addGoal(2, new HurtByTargetGoal(this));
this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, this::isAngryAt));
@@ -272,13 +292,13 @@ public class IronGolem extends AbstractGolem implements NeutralMob {
protected InteractionResult mobInteract(final Player player, final InteractionHand hand) {
ItemStack itemStack = player.getItemInHand(hand);
if (!itemStack.is(Items.IRON_INGOT)) {
- return InteractionResult.PASS;
+ return tryRide(player, hand); // Purpur - Ridables
}
float healthBefore = this.getHealth();
this.heal(25.0F);
if (this.getHealth() == healthBefore) {
- return InteractionResult.PASS;
+ return tryRide(player, hand); // Purpur - Ridables
}
float pitch = 1.0F + (this.random.nextFloat() - this.random.nextFloat()) * 0.2F;
diff --git a/net/minecraft/world/entity/animal/golem/SnowGolem.java b/net/minecraft/world/entity/animal/golem/SnowGolem.java
index 1f6507dd9a815ac8b8f57138c96fb4ccba0b64b3..acb61a212f9240ccfa414770868c0fb3835c538a 100644
--- a/net/minecraft/world/entity/animal/golem/SnowGolem.java
+++ b/net/minecraft/world/entity/animal/golem/SnowGolem.java
@@ -62,12 +62,31 @@ public class SnowGolem extends AbstractGolem implements RangedAttackMob, Shearab
}
// Purpur end - Summoner API
+ // Purpur start - Ridables
+ @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 - Ridables
+
@Override
protected void registerGoals() {
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables
this.goalSelector.addGoal(1, new RangedAttackGoal(this, level().purpurConfig.snowGolemAttackDistance, level().purpurConfig.snowGolemSnowBallMin, level().purpurConfig.snowGolemSnowBallMax, level().purpurConfig.snowGolemSnowBallModifier)); // Purpur - Snow Golem rate of fire config
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 - Ridables
this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, Mob.class, 10, true, false, (target, level) -> target instanceof Enemy));
}
@@ -112,6 +131,7 @@ public class SnowGolem extends AbstractGolem implements RangedAttackMob, Shearab
return;
}
+ if (getRider() != null && this.isControllable() && !level().purpurConfig.snowGolemLeaveTrailWhenRidden) return; // Purpur - don't leave snow trail when being ridden
BlockState snow = Blocks.SNOW.defaultBlockState();
for (int i = 0; i < 4; i++) {
@@ -155,7 +175,7 @@ public class SnowGolem extends AbstractGolem implements RangedAttackMob, Shearab
java.util.List<ItemStack> drops = this.generateDefaultDrops(level, itemStack);
org.bukkit.event.player.PlayerShearEntityEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.handlePlayerShearEntityEvent(player, this, itemStack, hand, drops);
if (event.isCancelled()) {
- return InteractionResult.PASS;
+ return tryRide(player, hand); // Purpur - Ridables
}
drops = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getDrops());
this.shear(level, SoundSource.PLAYERS, itemStack, drops);
@@ -174,7 +194,7 @@ public class SnowGolem extends AbstractGolem implements RangedAttackMob, Shearab
return InteractionResult.SUCCESS;
// Purpur end - Snowman drop and put back pumpkin
} else {
- return InteractionResult.PASS;
+ return tryRide(player, hand); // Purpur - Ridables
}
}
diff --git a/net/minecraft/world/entity/animal/happyghast/HappyGhast.java b/net/minecraft/world/entity/animal/happyghast/HappyGhast.java
index ee400d1bfd24cc738ca02c1ec50332d7cfc9c266..60c67b49d236fe1ad152b668083b61407f31ea6e 100644
--- a/net/minecraft/world/entity/animal/happyghast/HappyGhast.java
+++ b/net/minecraft/world/entity/animal/happyghast/HappyGhast.java
@@ -137,6 +137,13 @@ public class HappyGhast extends Animal {
this.removeAllGoals(goal -> true);
}
+ // Purpur start - Ridables
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.happyGhastRidableInWater;
+ }
+ // Purpur end - Ridables
+
@Override
protected void ageBoundaryReached() {
if (this.isBaby()) {
diff --git a/net/minecraft/world/entity/animal/panda/Panda.java b/net/minecraft/world/entity/animal/panda/Panda.java
index 037beb367fd445d18461b84dca790ca579c00c95..f0b23d5f443a8f29ce98e16f0254425147599d8d 100644
--- a/net/minecraft/world/entity/animal/panda/Panda.java
+++ b/net/minecraft/world/entity/animal/panda/Panda.java
@@ -112,6 +112,32 @@ public class Panda extends Animal {
}
}
+ // Purpur start - Ridables
+ @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 - Ridables
+
@Override
protected boolean canDispenserEquipIntoSlot(final EquipmentSlot slot) {
return slot == EquipmentSlot.MAINHAND && this.canPickUpLoot();
@@ -264,6 +290,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 - Ridables
this.goalSelector.addGoal(2, new Panda.PandaPanicGoal(this, 2.0));
this.goalSelector.addGoal(2, new Panda.PandaBreedGoal(this, 1.0));
this.goalSelector.addGoal(3, new Panda.PandaAttackGoal(this, 1.2F, true));
@@ -279,6 +306,7 @@ public class Panda extends Animal {
this.goalSelector.addGoal(12, new Panda.PandaRollGoal(this));
this.goalSelector.addGoal(13, new FollowParentGoal(this, 1.25));
this.goalSelector.addGoal(14, new WaterAvoidingRandomStrollGoal(this, 1.0));
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables
this.targetSelector.addGoal(1, new Panda.PandaHurtByTargetGoal(this).setAlertOthers());
}
@@ -617,7 +645,7 @@ public class Panda extends Animal {
public InteractionResult mobInteract(final Player player, final InteractionHand hand) {
ItemStack interactionItemStack = player.getItemInHand(hand);
if (this.isScared()) {
- return InteractionResult.PASS;
+ return tryRide(player, hand); // Purpur - Ridables
}
if (this.isOnBack()) {
@@ -663,7 +691,7 @@ public class Panda extends Animal {
return InteractionResult.SUCCESS_SERVER;
} else {
- return this.isBaby() && player.isHolding(Items.GOLDEN_DANDELION) ? super.mobInteract(player, hand) : InteractionResult.PASS;
+ return tryRide(player, hand, this.isBaby() && player.isHolding(Items.GOLDEN_DANDELION) ? super.mobInteract(player, hand) : InteractionResult.PASS); // Purpur - Ridables
}
}
@@ -970,15 +998,15 @@ public class Panda extends Animal {
}
}
- private static class PandaMoveControl<T extends Panda> extends MoveControl<T> {
+ private static class PandaMoveControl<T extends Panda> extends org.purpurmc.purpur.controller.MoveControllerWASD<T> { // Purpur - Ridables
public PandaMoveControl(final T mob) {
super(mob);
}
@Override
- public void tick() {
+ public void vanillaTick() { // Purpur - Ridables
if (this.mob.canPerformAction()) {
- super.tick();
+ super.vanillaTick(); // Purpur - Ridables
}
}
}
diff --git a/net/minecraft/world/entity/animal/parrot/Parrot.java b/net/minecraft/world/entity/animal/parrot/Parrot.java
index c93170c007dc3e06b8324d5fa6a1710099c3e26c..ace47c04cb0bdb8b44fa80bfad7b01e670839f91 100644
--- a/net/minecraft/world/entity/animal/parrot/Parrot.java
+++ b/net/minecraft/world/entity/animal/parrot/Parrot.java
@@ -137,12 +137,68 @@ public class Parrot extends ShoulderRidingEntity {
public Parrot(final EntityType<? extends Parrot> type, final Level level) {
super(type, level);
- this.moveControl = new FlyingMoveControl<>(this, 10, false);
+ // Purpur start - Ridables
+ final org.purpurmc.purpur.controller.FlyingWithSpacebarMoveControllerWASD flyingController = new org.purpurmc.purpur.controller.FlyingWithSpacebarMoveControllerWASD<>(this, 0.3F);
+ class ParrotMoveControl<T extends Mob> extends FlyingMoveControl<T> {
+ public ParrotMoveControl(T 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 - Ridables
this.setPathfindingMalus(PathType.FIRE_IN_NEIGHBOR, -1.0F);
this.setPathfindingMalus(PathType.FIRE, -1.0F);
this.setPathfindingMalus(PathType.COCOA, -1.0F);
}
+ // Purpur start - Ridables
+ @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 - Ridables
+
@Override
public @Nullable SpawnGroupData finalizeSpawn(
final ServerLevelAccessor level, final DifficultyInstance difficulty, final EntitySpawnReason spawnReason, @Nullable SpawnGroupData groupData
@@ -162,9 +218,11 @@ public class Parrot extends ShoulderRidingEntity {
@Override
protected void registerGoals() {
- this.goalSelector.addGoal(0, new TamableAnimal.TamableAnimalPanicGoal(1.25));
+ //this.goalSelector.addGoal(0, new TamableAnimal.TamableAnimalPanicGoal(1.25)); // Purpur - move down
this.goalSelector.addGoal(0, new FloatGoal(this));
if (this.level().purpurConfig.parrotBreedable) this.goalSelector.addGoal(1, new net.minecraft.world.entity.ai.goal.BreedGoal(this, 1.0D)); // Purpur - Breedable parrots
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables
+ this.goalSelector.addGoal(1, new TamableAnimal.TamableAnimalPanicGoal(1.25D)); // Purpur - Ridables
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.0, 5.0F, 1.0F));
diff --git a/net/minecraft/world/entity/animal/pig/Pig.java b/net/minecraft/world/entity/animal/pig/Pig.java
index 79914b2f63b560338a9747dc75d5c86b27a1275d..b3a24a2e48c0ada66cde1ee7710c2d89d4f221d9 100644
--- a/net/minecraft/world/entity/animal/pig/Pig.java
+++ b/net/minecraft/world/entity/animal/pig/Pig.java
@@ -76,9 +76,27 @@ public class Pig extends Animal implements ItemSteerable {
super(type, level);
}
+ // Purpur start - Ridables
+ @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 - Ridables
+
@Override
protected void registerGoals() {
this.goalSelector.addGoal(0, new FloatGoal(this));
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables
this.goalSelector.addGoal(1, new PanicGoal(this, 1.25));
this.goalSelector.addGoal(3, new BreedGoal(this, 1.0));
this.goalSelector.addGoal(4, new TemptGoal(this, 1.2, i -> i.is(Items.CARROT_ON_A_STICK), false));
diff --git a/net/minecraft/world/entity/animal/polarbear/PolarBear.java b/net/minecraft/world/entity/animal/polarbear/PolarBear.java
index 5de97090f68c5a371f0b196c211941d7a5e503b1..250401cef7c93375235ef275165f1e0d9684d42a 100644
--- a/net/minecraft/world/entity/animal/polarbear/PolarBear.java
+++ b/net/minecraft/world/entity/animal/polarbear/PolarBear.java
@@ -67,6 +67,7 @@ public class PolarBear extends Animal implements NeutralMob {
private static final EntityDimensions BABY_DIMENSIONS = EntityDimensions.scalable(0.7F, 0.7F)
.withEyeHeight(0.34375F)
.withAttachments(EntityAttachments.builder().attach(EntityAttachment.PASSENGER, 0.0F, 0.625F, 0.0F));
+ private int standTimer = 0; // Purpur - Ridables
public PolarBear(final EntityType<? extends PolarBear> type, final Level level) {
super(type, level);
@@ -95,6 +96,34 @@ public class PolarBear extends Animal implements NeutralMob {
}
// Purpur end - Breedable Polar Bears
+ // Purpur start - Ridables
+ @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 - Ridables
+
@Override
public @Nullable AgeableMob getBreedOffspring(final ServerLevel level, final AgeableMob partner) {
return EntityTypes.POLAR_BEAR.create(level, EntitySpawnReason.BREEDING);
@@ -109,6 +138,7 @@ 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 - Ridables
this.goalSelector.addGoal(1, new PolarBear.PolarBearMeleeAttackGoal());
this.goalSelector.addGoal(1, new PanicGoal(this, 2.0, bear -> bear.isBaby() ? DamageTypeTags.PANIC_CAUSES : DamageTypeTags.PANIC_ENVIRONMENTAL_CAUSES));
// Purpur start - Breedable Polar Bears
@@ -121,6 +151,7 @@ public class PolarBear extends Animal implements NeutralMob {
this.goalSelector.addGoal(5, new RandomStrollGoal(this, 1.0));
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 - Ridables
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));
@@ -238,6 +269,12 @@ public class PolarBear extends Animal implements NeutralMob {
if (!this.level().isClientSide()) {
this.updatePersistentAnger((ServerLevel)this.level(), true);
}
+
+ // Purpur start - Ridables
+ if (isStanding() && --standTimer <= 0) {
+ setStanding(false);
+ }
+ // Purpur end - Ridables
}
@Override
@@ -258,6 +295,7 @@ public class PolarBear extends Animal implements NeutralMob {
public void setStanding(final boolean value) {
this.entityData.set(DATA_STANDING_ID, value);
+ standTimer = value ? 20 : -1; // Purpur - Ridables
}
public float getStandingAnimationScale(final float a) {
diff --git a/net/minecraft/world/entity/animal/rabbit/Rabbit.java b/net/minecraft/world/entity/animal/rabbit/Rabbit.java
index 3c43ae68e2510505773e36bf7b051835986e3df0..2d2078ca3adeb78b6ec2633c35d3c622184cc3b2 100644
--- a/net/minecraft/world/entity/animal/rabbit/Rabbit.java
+++ b/net/minecraft/world/entity/animal/rabbit/Rabbit.java
@@ -109,6 +109,7 @@ public class Rabbit extends Animal {
private boolean wasOnGround;
private int jumpDelayTicks;
public int moreCarrotTicks = 0;
+ private boolean actualJump; // Purpur - Ridables
public Rabbit(final EntityType<? extends Rabbit> type, final Level level) {
super(type, level);
@@ -117,9 +118,55 @@ public class Rabbit extends Animal {
// this.setSpeedModifier(0.0); // CraftBukkit
}
+ // Purpur start - Ridables
+ @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 - Ridables
+
@Override
public void registerGoals() {
this.goalSelector.addGoal(1, new FloatGoal(this));
+ this.goalSelector.addGoal(1, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables
this.goalSelector.addGoal(1, new ClimbOnTopOfPowderSnowGoal(this, this.level()));
this.goalSelector.addGoal(1, new Rabbit.RabbitPanicGoal(this, 2.2));
this.goalSelector.addGoal(2, new BreedGoal(this, 0.8));
@@ -139,6 +186,14 @@ public class Rabbit extends Animal {
@Override
protected float getJumpPower() {
+ // Purpur start - Ridables
+ if (getRider() != null && this.isControllable()) {
+ if (getForwardMot() < 0) {
+ setSpeed(getForwardMot() * 2F);
+ }
+ return actualJump ? 0.5F : 0.3F;
+ }
+ // Purpur end - Ridables
float baseJumpPower = 0.3F;
if (this.moveControl.getSpeedModifier() <= 0.6) {
baseJumpPower = 0.2F;
@@ -206,6 +261,12 @@ public class Rabbit extends Animal {
@Override
public void customServerAiStep(final ServerLevel level) {
+ // Purpur start - Ridables
+ if (getRider() != null && this.isControllable()) {
+ handleJumping();
+ return;
+ }
+ // Purpur end - Ridables
if (this.jumpDelayTicks > 0) {
this.jumpDelayTicks--;
}
@@ -488,7 +549,7 @@ public class Rabbit extends Animal {
}
private boolean shouldPlayIdleAnimation() {
- return this.idleAnimationTimeout <= 0 && (this.getLeashData() == null || this.getLeashData().leashHolder == null) && !this.isNoAi();
+ return getRider() == null && this.idleAnimationTimeout <= 0 && (this.getLeashData() == null || this.getLeashData().leashHolder == null) && !this.isNoAi(); // Purpur - Ridables
}
@Override
@@ -560,7 +621,7 @@ public class Rabbit extends Animal {
}
}
- private static class RabbitMoveControl<T extends Rabbit> extends MoveControl<T> {
+ private static class RabbitMoveControl<T extends Rabbit> extends org.purpurmc.purpur.controller.MoveControllerWASD<T> { // Purpur - Ridables
private double nextJumpSpeed;
public RabbitMoveControl(final T rabbit) {
@@ -568,14 +629,14 @@ public class Rabbit extends Animal {
}
@Override
- public void tick() {
+ public void vanillaTick() { // Purpur - Ridables
if (this.mob.onGround() && !this.mob.jumping && !((Rabbit.RabbitJumpControl)this.mob.jumpControl).wantJump()) {
this.mob.setSpeedModifier(0.0);
} else if (this.hasWanted() || this.operation == MoveControl.Operation.JUMPING) {
this.mob.setSpeedModifier(this.nextJumpSpeed);
}
- super.tick();
+ super.vanillaTick(); // Purpur - Ridables
}
@Override
diff --git a/net/minecraft/world/entity/animal/sheep/Sheep.java b/net/minecraft/world/entity/animal/sheep/Sheep.java
index 044e31764d4bb5f4bf2b0657d184a62628430b95..ef69bac6181d5f130008629ddb2763f56232345b 100644
--- a/net/minecraft/world/entity/animal/sheep/Sheep.java
+++ b/net/minecraft/world/entity/animal/sheep/Sheep.java
@@ -71,10 +71,28 @@ public class Sheep extends Animal implements Shearable {
super(type, level);
}
+ // Purpur start - Ridables
+ @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 - Ridables
+
@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 - Ridables
this.goalSelector.addGoal(1, new PanicGoal(this, 1.25));
this.goalSelector.addGoal(2, new BreedGoal(this, 1.0));
this.goalSelector.addGoal(3, new TemptGoal(this, 1.1, i -> i.is(ItemTags.SHEEP_FOOD), false));
diff --git a/net/minecraft/world/entity/animal/sniffer/Sniffer.java b/net/minecraft/world/entity/animal/sniffer/Sniffer.java
index d5394ae7ce56555ad21aeafb2d78c291c62e2988..1e844ec746aa200ed8e70af5dd4389f3bd671112 100644
--- a/net/minecraft/world/entity/animal/sniffer/Sniffer.java
+++ b/net/minecraft/world/entity/animal/sniffer/Sniffer.java
@@ -95,6 +95,23 @@ public class Sniffer extends Animal {
this.setPathfindingMalus(PathType.DAMAGE_CAUTIOUS, -1.0F);
}
+ // Purpur start - Ridables
+ @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 - Ridables
+
@Override
protected void defineSynchedData(final SynchedEntityData.Builder entityData) {
super.defineSynchedData(entityData);
@@ -468,6 +485,7 @@ public class Sniffer extends Animal {
protected void customServerAiStep(final ServerLevel level) {
ProfilerFiller profiler = Profiler.get();
profiler.push("snifferBrain");
+ if (getRider() == null || !this.isControllable()) // Purpur - only use brain if no rider
this.getBrain().tick(level, this);
profiler.popPush("snifferActivityUpdate");
SnifferAi.updateActivity(this);
diff --git a/net/minecraft/world/entity/animal/squid/GlowSquid.java b/net/minecraft/world/entity/animal/squid/GlowSquid.java
index ecc3fb073a3e40c590527f0aec8b23a7aee04a71..bde43e42ba7eec863fd0e2cd9b8068e8cefaaa1c 100644
--- a/net/minecraft/world/entity/animal/squid/GlowSquid.java
+++ b/net/minecraft/world/entity/animal/squid/GlowSquid.java
@@ -38,6 +38,19 @@ public class GlowSquid extends Squid {
}
// Purpur end - Flying squids! Oh my!
+ // Purpur start - Ridables
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.glowSquidRidable;
+ }
+
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.glowSquidControllable;
+ }
+ // Purpur end - Ridables
+
@Override
protected ParticleOptions getInkParticle() {
return ParticleTypes.GLOW_SQUID_INK;
diff --git a/net/minecraft/world/entity/animal/squid/Squid.java b/net/minecraft/world/entity/animal/squid/Squid.java
index e0db02e7f2166e894137190252f28a3f02386207..e2273ce904e2c95491caec67d36d37b4b5e0fe67 100644
--- a/net/minecraft/world/entity/animal/squid/Squid.java
+++ b/net/minecraft/world/entity/animal/squid/Squid.java
@@ -74,9 +74,32 @@ public class Squid extends AgeableWaterCreature {
}
// Purpur end - Flying squids! Oh my!
+ // Purpur start - Ridables
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.squidRidable;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.squidControllable;
+ }
+
+ protected static 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 - Ridables
+
@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 - Ridables
this.goalSelector.addGoal(1, new Squid.SquidFleeGoal());
}
@@ -333,6 +356,37 @@ public class Squid extends AgeableWaterCreature {
@Override
public void tick() {
+ // Purpur start - Ridables
+ net.minecraft.world.entity.player.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.movementVector = new Vec3((float) dir.getX(), (float) dir.getY(), (float) dir.getZ());
+ } else {
+ squid.movementVector = Vec3.ZERO;
+ }
+ return;
+ }
+ // Purpur end - Ridables
int noActionTime = this.squid.getNoActionTime();
if (noActionTime > 100) {
this.squid.movementVector = Vec3.ZERO;
diff --git a/net/minecraft/world/entity/animal/turtle/Turtle.java b/net/minecraft/world/entity/animal/turtle/Turtle.java
index 3fec7cfb25ae8798921bca35c076ecb792dccb14..2e147b2276dd8604dc6adb75c7ac90ef42e63955 100644
--- a/net/minecraft/world/entity/animal/turtle/Turtle.java
+++ b/net/minecraft/world/entity/animal/turtle/Turtle.java
@@ -88,6 +88,23 @@ public class Turtle extends Animal {
this.moveControl = new Turtle.TurtleMoveControl<>(this);
}
+ // Purpur start - Ridables
+ @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 - Ridables
+
public void setHomePos(final BlockPos pos) {
this.homePos = pos;
}
@@ -150,6 +167,7 @@ public class Turtle extends Animal {
@Override
protected void registerGoals() {
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables
this.goalSelector.addGoal(0, new Turtle.TurtlePanicGoal(this, 1.2));
this.goalSelector.addGoal(1, new Turtle.TurtleBreedGoal(this, 1.0));
this.goalSelector.addGoal(1, new Turtle.TurtleLayEggGoal(this, 1.0));
@@ -490,9 +508,11 @@ public class Turtle extends Animal {
}
}
- private static class TurtleMoveControl<T extends Turtle> extends MoveControl<T> {
+ private static class TurtleMoveControl<T extends Turtle> extends org.purpurmc.purpur.controller.MoveControllerWASD<T> { // Purpur - Ridables
+ private final org.purpurmc.purpur.controller.WaterMoveControllerWASD waterController; // Purpur - Ridables
public TurtleMoveControl(final T turtle) {
super(turtle);
+ waterController = new org.purpurmc.purpur.controller.WaterMoveControllerWASD<>(turtle, 0.25D); // Purpur - Ridables
}
private void updateSpeed() {
@@ -511,7 +531,7 @@ public class Turtle extends Animal {
}
@Override
- public void tick() {
+ public void vanillaTick() { // Purpur - Ridables
this.updateSpeed();
if (this.operation == MoveControl.Operation.MOVE_TO && !this.mob.getNavigation().isDone()) {
double xd = this.wantedX - this.mob.getX();
@@ -525,7 +545,7 @@ public class Turtle extends Animal {
float yRotD = (float)(Mth.atan2(zd, xd) * 180.0F / (float)Math.PI) - 90.0F;
this.mob.setYRot(this.rotlerp(this.mob.getYRot(), yRotD, 90.0F));
this.mob.yBodyRot = this.mob.getYRot();
- float targetSpeed = (float)(this.speedModifier * this.mob.getAttributeValue(Attributes.MOVEMENT_SPEED));
+ float targetSpeed = (float)(this.getSpeedModifier() * this.mob.getAttributeValue(Attributes.MOVEMENT_SPEED)); // Purpur - Ridables
this.mob.setSpeed(Mth.lerp(0.125F, this.mob.getSpeed(), targetSpeed));
this.mob.setDeltaMovement(this.mob.getDeltaMovement().add(0.0, this.mob.getSpeed() * yd * 0.1, 0.0));
}
diff --git a/net/minecraft/world/entity/animal/wolf/Wolf.java b/net/minecraft/world/entity/animal/wolf/Wolf.java
index 5d1e0d44163b9f29dc824b46b7b983456a273419..f54a14a1e434cbfedf709d3b1495f2ae9ae4f11f 100644
--- a/net/minecraft/world/entity/animal/wolf/Wolf.java
+++ b/net/minecraft/world/entity/animal/wolf/Wolf.java
@@ -190,9 +190,32 @@ public class Wolf extends TamableAnimal implements NeutralMob {
}
// Purpur end - Configurable default collar color
+ // Purpur start - Ridables
+ @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 - Ridables
+
@Override
protected void registerGoals() {
this.goalSelector.addGoal(1, new FloatGoal(this));
+ this.goalSelector.addGoal(1, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables
this.goalSelector.addGoal(1, new TamableAnimal.TamableAnimalPanicGoal(1.5, DamageTypeTags.PANIC_ENVIRONMENTAL_CAUSES));
this.goalSelector.addGoal(2, new SitWhenOrderedToGoal(this));
this.goalSelector.addGoal(3, new Wolf.WolfAvoidEntityGoal<>(this, Llama.class, 24.0F, 1.5, 1.5));
@@ -205,6 +228,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 - Ridables
this.targetSelector.addGoal(1, new OwnerHurtByTargetGoal(this));
this.targetSelector.addGoal(2, new OwnerHurtTargetGoal(this));
this.targetSelector.addGoal(3, new HurtByTargetGoal(this).setAlertOthers());
diff --git a/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java b/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java
index f960bb265bc5a4b350d24c9d1caafb3a06068df0..c0ffda7c18461494aee68aac2d99a660e3b21dcf 100644
--- a/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java
+++ b/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java
@@ -92,6 +92,7 @@ public class EnderDragon extends Mob implements Enemy {
private final net.minecraft.world.level.Explosion explosionSource; // Paper - reusable source for CraftTNTPrimed.getSource()
private @Nullable BlockPos podium;
// Paper end
+ private boolean hadRider; // Purpur - Ridables
public EnderDragon(final EntityType<? extends EnderDragon> type, final Level level) {
super(EntityTypes.ENDER_DRAGON, level);
@@ -108,6 +109,37 @@ public class EnderDragon extends Mob implements Enemy {
this.noPhysics = true;
this.phaseManager = new EnderDragonPhaseManager(this);
this.explosionSource = new net.minecraft.world.level.ServerExplosion(level.getMinecraftWorld(), this, null, null, new Vec3(Double.NaN, Double.NaN, Double.NaN), Float.NaN, true, net.minecraft.world.level.Explosion.BlockInteraction.DESTROY); // Paper
+
+ // Purpur start - Ridables
+ 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 - Ridables
+ }
+
+ // Purpur start - Ridables
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.enderDragonRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.enderDragonRidableInWater;
}
public void setDragonFight(final EnderDragonFight fight) {
@@ -122,6 +154,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 - Ridables
+
public static AttributeSupplier.Builder createAttributes() {
return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 200.0).add(Attributes.CAMERA_DISTANCE, 16.0);
}
@@ -171,6 +214,37 @@ public class EnderDragon extends Mob implements Enemy {
@Override
public void aiStep() {
+ // Purpur start - Ridables
+ 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 - Ridables
+
this.processFlappingMovement();
if (this.level().isClientSide()) {
this.setHealth(this.getHealth());
@@ -199,6 +273,7 @@ public class EnderDragon extends Mob implements Enemy {
this.oFlapTime = this.flapTime;
if (this.isDeadOrDying()) {
+ if (hasRider) ejectPassengers(); // Purpur - Ridables
float xo = (this.random.nextFloat() - 0.5F) * 8.0F;
float yo = (this.random.nextFloat() - 0.5F) * 4.0F;
float zo = (this.random.nextFloat() - 0.5F) * 8.0F;
@@ -208,9 +283,9 @@ public class EnderDragon extends Mob implements Enemy {
Vec3 movement = this.getDeltaMovement();
float flapSpeed = 0.2F / ((float)movement.horizontalDistance() * 10.0F + 1.0F);
flapSpeed *= (float)Math.pow(2.0, movement.y);
- if (this.phaseManager.getCurrentPhase().isSitting()) {
+ if (!hasRider && this.phaseManager.getCurrentPhase().isSitting()) { // Purpur - Ridables
this.flapTime += 0.1F;
- } else if (this.inWall) {
+ } else if (!hasRider && this.inWall) { // Purpur - Ridables
this.flapTime += flapSpeed * 0.5F;
} else {
this.flapTime += flapSpeed;
@@ -221,7 +296,7 @@ public class EnderDragon extends Mob implements Enemy {
this.flapTime = 0.5F;
} else {
this.flightHistory.record(this.getY(), this.getYRot());
- if (this.level() instanceof ServerLevel level) {
+ if (this.level() instanceof ServerLevel level && !hasRider) { // Purpur - Ridables
DragonPhaseInstance currentPhase = this.phaseManager.getCurrentPhase();
currentPhase.doServerTick(level);
if (this.phaseManager.getCurrentPhase() != currentPhase) {
@@ -292,7 +367,7 @@ public class EnderDragon extends Mob implements Enemy {
this.tickPart(this.body, ss1 * 0.5F, 0.0, -cc1 * 0.5F);
this.tickPart(this.wing1, cc1 * 4.5F, 2.0, ss1 * 4.5F);
this.tickPart(this.wing2, cc1 * -4.5F, 2.0, ss1 * -4.5F);
- if (this.level() instanceof ServerLevel serverLevel && this.hurtTime == 0) {
+ if (this.level() instanceof ServerLevel serverLevel && this.hurtTime == 0 && !hasRider) { // Purpur - Ridables
this.knockBack(
serverLevel,
serverLevel.getEntities(
@@ -340,9 +415,9 @@ public class EnderDragon extends Mob implements Enemy {
}
if (this.level() instanceof ServerLevel level) {
- this.inWall = this.checkWalls(level, this.head.getBoundingBox())
+ this.inWall = !hasRider && this.checkWalls(level, this.head.getBoundingBox())
| this.checkWalls(level, this.neck.getBoundingBox())
- | this.checkWalls(level, this.body.getBoundingBox());
+ | this.checkWalls(level, this.body.getBoundingBox()); // Purpur - Ridables
if (this.dragonFight != null) {
this.dragonFight.updateDragon(this);
}
diff --git a/net/minecraft/world/entity/boss/enderdragon/EnderDragonPart.java b/net/minecraft/world/entity/boss/enderdragon/EnderDragonPart.java
index a8e3df21c27b9c86f9f03ea436ef4099a0805b71..a677aed54e16f3150ea1eabe22935a3d0d1507c4 100644
--- a/net/minecraft/world/entity/boss/enderdragon/EnderDragonPart.java
+++ b/net/minecraft/world/entity/boss/enderdragon/EnderDragonPart.java
@@ -27,6 +27,13 @@ public class EnderDragonPart extends Entity {
this.name = name;
}
+ // Purpur start - Ridables
+ @Override
+ public net.minecraft.world.InteractionResult interact(final net.minecraft.world.entity.player.Player player, final net.minecraft.world.InteractionHand hand, final net.minecraft.world.phys.Vec3 location) {
+ return parentMob.isAlive() ? parentMob.tryRide(player, hand) : net.minecraft.world.InteractionResult.PASS;
+ }
+ // Purpur end - Ridables
+
@Override
protected void defineSynchedData(final SynchedEntityData.Builder entityData) {
}
diff --git a/net/minecraft/world/entity/boss/wither/WitherBoss.java b/net/minecraft/world/entity/boss/wither/WitherBoss.java
index 6669dd5ea935cce9d0df89986237aa068b1899ff..1f32d0d87727009a8ca671415159566c10f79f7e 100644
--- a/net/minecraft/world/entity/boss/wither/WitherBoss.java
+++ b/net/minecraft/world/entity/boss/wither/WitherBoss.java
@@ -73,6 +73,7 @@ public class WitherBoss extends Monster implements RangedAttackMob {
private final int[] nextHeadUpdate = new int[2];
private final int[] idleHeadUpdates = new int[2];
private int destroyBlocksTick;
+ private int shootCooldown = 0; // Purpur - Ridables
private boolean canPortal = false; // Paper
public final ServerBossEvent bossEvent = Util.make(
new ServerBossEvent(Mth.createInsecureUUID(this.random), this.getDisplayName(), BossEvent.BossBarColor.PURPLE, BossEvent.BossBarOverlay.PROGRESS),
@@ -82,10 +83,23 @@ public class WitherBoss extends Monster implements RangedAttackMob {
&& target.attackable();
private static final TargetingConditions TARGETING_CONDITIONS = TargetingConditions.forCombat().range(20.0).selector(LIVING_ENTITY_SELECTOR);
private java.util.@Nullable UUID summoner; // Purpur - Summoner API
+ private org.purpurmc.purpur.controller.FlyingWithSpacebarMoveControllerWASD purpurController; // Purpur - Ridables
public WitherBoss(final EntityType<? extends WitherBoss> type, final Level level) {
super(type, level);
- this.moveControl = new FlyingMoveControl<>(this, 10, false);
+ // Purpur start - Ridables
+ 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 - Ridables
this.setHealth(this.getMaxHealth());
this.xpReward = 50;
}
@@ -100,6 +114,105 @@ public class WitherBoss extends Monster implements RangedAttackMob {
}
// Purpur end - Summoner API
+ // Purpur start - Ridables
+ @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);
+ Vec3 vec3d = new Vec3(x - headX, y - headY, z - headZ);
+ WitherSkull skull = new WitherSkull(level(), this, vec3d.normalize());
+ skull.setPosRaw(headX, headY, headZ);
+ level().addFreshEntity(skull);
+ }
+ // Purpur end - Ridables
+
@Override
protected PathNavigation createNavigation(final Level level) {
FlyingPathNavigation flyingPathNavigation = new FlyingPathNavigation(this, level);
@@ -110,11 +223,13 @@ public class WitherBoss extends Monster implements RangedAttackMob {
@Override
protected void registerGoals() {
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables
this.goalSelector.addGoal(0, new WitherBoss.WitherDoNothingGoal());
this.goalSelector.addGoal(2, new RangedAttackGoal(this, 1.0, 40, 20.0F));
this.goalSelector.addGoal(5, new WaterAvoidingRandomFlyingGoal(this, 1.0));
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 - Ridables
this.targetSelector.addGoal(1, new HurtByTargetGoal(this));
this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, LivingEntity.class, 0, false, false, LIVING_ENTITY_SELECTOR));
}
@@ -274,6 +389,15 @@ public class WitherBoss extends Monster implements RangedAttackMob {
@Override
protected void customServerAiStep(final ServerLevel level) {
+ // Purpur start - Ridables
+ 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 - Ridables
if (this.getInvulnerableTicks() > 0) {
int newCount = this.getInvulnerableTicks() - 1;
this.bossEvent.setProgress(1.0F - newCount / 220.0F);
@@ -581,11 +705,11 @@ public class WitherBoss extends Monster implements RangedAttackMob {
}
public int getAlternativeTarget(final int headIndex) {
- return this.entityData.get(DATA_TARGETS.get(headIndex));
+ return getRider() != null && this.isControllable() ? 0 : this.entityData.get(DATA_TARGETS.get(headIndex)); // Purpur - Ridables
}
public void setAlternativeTarget(final int headIndex, final int entityId) {
- this.entityData.set(DATA_TARGETS.get(headIndex), entityId);
+ if (getRider() == null || !this.isControllable()) this.entityData.set(DATA_TARGETS.get(headIndex), entityId); // Purpur - Ridables
}
public boolean isPowered() {
diff --git a/net/minecraft/world/entity/monster/Blaze.java b/net/minecraft/world/entity/monster/Blaze.java
index d48e066e8c43494aa74158452da369e5617d6a7e..c1acc0a648493ec33ef4fc4a48b52fe0b3dc04b2 100644
--- a/net/minecraft/world/entity/monster/Blaze.java
+++ b/net/minecraft/world/entity/monster/Blaze.java
@@ -34,6 +34,7 @@ public class Blaze extends Monster {
public Blaze(final EntityType<? extends Blaze> blaze, final Level level) {
super(blaze, level);
+ this.moveControl = new org.purpurmc.purpur.controller.FlyingWithSpacebarMoveControllerWASD(this, 0.3F); // Purpur - Ridables
this.setPathfindingMalus(PathType.WATER, -1.0F);
this.setPathfindingMalus(PathType.LAVA, 8.0F);
this.setPathfindingMalus(PathType.FIRE_IN_NEIGHBOR, 0.0F);
@@ -41,19 +42,55 @@ public class Blaze extends Monster {
this.xpReward = 10;
}
+ // Purpur start - Ridables
+ @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 - Ridables
+
@Override
protected void registerGoals() {
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables
this.goalSelector.addGoal(4, new Blaze.BlazeAttackGoal(this));
this.goalSelector.addGoal(5, new MoveTowardsRestrictionGoal(this, 1.0));
this.goalSelector.addGoal(7, new WaterAvoidingRandomStrollGoal(this, 1.0, 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 - Ridables
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.0).add(Attributes.MOVEMENT_SPEED, 0.23F).add(Attributes.FOLLOW_RANGE, 48.0);
+ return Monster.createMonsterAttributes().add(Attributes.ATTACK_DAMAGE, 6.0).add(Attributes.MOVEMENT_SPEED, 0.23F).add(Attributes.FOLLOW_RANGE, 48.0).add(Attributes.FLYING_SPEED, 0.6D); // Purpur - Ridables
}
@Override
@@ -118,6 +155,13 @@ public class Blaze extends Monster {
@Override
protected void customServerAiStep(final ServerLevel level) {
+ // Purpur start - Ridables
+ if (getRider() != null && this.isControllable()) {
+ Vec3 mot = getDeltaMovement();
+ setDeltaMovement(mot.x(), getVerticalMot() > 0 ? 0.07D : -0.07D, mot.z());
+ return;
+ }
+ // Purpur end - Ridables
this.nextHeightOffsetChangeTick--;
if (this.nextHeightOffsetChangeTick <= 0) {
this.nextHeightOffsetChangeTick = 100;
diff --git a/net/minecraft/world/entity/monster/Creeper.java b/net/minecraft/world/entity/monster/Creeper.java
index 4811c0607acbdf0e8f74327542560b96c84f728b..e5f7d276c083cdb78d2f420c03be1b08115d5597 100644
--- a/net/minecraft/world/entity/monster/Creeper.java
+++ b/net/minecraft/world/entity/monster/Creeper.java
@@ -57,21 +57,98 @@ public class Creeper extends Monster {
public boolean droppedSkulls;
public @Nullable Entity entityIgniter; // CraftBukkit
private boolean exploding = false; // Purpur - Config to make Creepers explode on death
+ // Purpur start - Ridables
+ private int spacebarCharge = 0;
+ private int prevSpacebarCharge = 0;
+ private int powerToggleDelay = 0;
+ // Purpur end - Ridables
public Creeper(final EntityType<? extends Creeper> type, final Level level) {
super(type, level);
}
+ // Purpur start - Ridables
+ @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(ServerLevel world) {
+ 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(world);
+ }
+
+ @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 - Ridables
+
@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 - Ridables
this.goalSelector.addGoal(3, new AvoidEntityGoal<>(this, Ocelot.class, 6.0F, 1.0, 1.2));
this.goalSelector.addGoal(3, new AvoidEntityGoal<>(this, Cat.class, 6.0F, 1.0, 1.2));
this.goalSelector.addGoal(4, new MeleeAttackGoal(this, 1.0, false));
this.goalSelector.addGoal(5, new WaterAvoidingRandomStrollGoal(this, 0.8));
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 - Ridables
this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, Player.class, true));
this.targetSelector.addGoal(2, new HurtByTargetGoal(this));
}
@@ -314,6 +391,7 @@ public class Creeper extends Monster {
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(DATA_IS_IGNITED, event.isIgnited());
+ if (!event.isIgnited()) setSwellDir(-1); // Purpur - Ridables
}
}
}
diff --git a/net/minecraft/world/entity/monster/ElderGuardian.java b/net/minecraft/world/entity/monster/ElderGuardian.java
index b3d0dc020027c626b6e9661a8d54095385c2f8b6..f27035be70e05dddb17944f0b461de8b506490d6 100644
--- a/net/minecraft/world/entity/monster/ElderGuardian.java
+++ b/net/minecraft/world/entity/monster/ElderGuardian.java
@@ -32,6 +32,18 @@ public class ElderGuardian extends Guardian {
}
}
+ // Purpur start - Ridables
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.elderGuardianRidable;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.elderGuardianControllable;
+ }
+ // Purpur end - Ridables
+
public static AttributeSupplier.Builder createAttributes() {
return Guardian.createAttributes().add(Attributes.MOVEMENT_SPEED, 0.3F).add(Attributes.ATTACK_DAMAGE, 8.0).add(Attributes.MAX_HEALTH, 80.0);
}
diff --git a/net/minecraft/world/entity/monster/EnderMan.java b/net/minecraft/world/entity/monster/EnderMan.java
index 5b9733f1227f049ca3e00dc437fc3bc455271229..8b958dbdec8e68690525d345bd2f1038b1466ef9 100644
--- a/net/minecraft/world/entity/monster/EnderMan.java
+++ b/net/minecraft/world/entity/monster/EnderMan.java
@@ -89,9 +89,27 @@ public class EnderMan extends Monster implements NeutralMob {
this.setPathfindingMalus(PathType.WATER, -1.0F);
}
+ // Purpur start - Ridables
+ @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 - Ridables
+
@Override
protected void registerGoals() {
this.goalSelector.addGoal(0, new FloatGoal(this));
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables
this.goalSelector.addGoal(1, new EnderMan.EndermanFreezeWhenLookedAt(this));
this.goalSelector.addGoal(2, new MeleeAttackGoal(this, 1.0, false));
this.goalSelector.addGoal(7, new WaterAvoidingRandomStrollGoal(this, 1.0, 0.0F));
@@ -99,6 +117,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 - Ridables
this.targetSelector.addGoal(1, new EnderMan.EndermanLookForPlayerGoal(this, this::isAngryAt));
this.targetSelector.addGoal(2, new HurtByTargetGoal(this));
this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Endermite.class, 10, true, false, (entityliving, ignored) -> entityliving.level().purpurConfig.endermanAggroEndermites && entityliving instanceof Endermite endermite && (!entityliving.level().purpurConfig.endermanAggroEndermitesOnlyIfPlayerSpawned || endermite.isPlayerSpawned()))); // Purpur
@@ -263,7 +282,7 @@ public class EnderMan extends Monster implements NeutralMob {
@Override
protected void customServerAiStep(final ServerLevel level) {
- if (level.isBrightOutside() && this.tickCount >= this.targetChangeTime + 600) {
+ if ((getRider() == null || !this.isControllable()) && level.isBrightOutside() && this.tickCount >= this.targetChangeTime + 600) { // Purpur - Ridables - no random teleporting
float br = this.getLightLevelDependentMagicValue();
if (br > 0.5F && level.canSeeSky(this.blockPosition()) && this.random.nextFloat() * 30.0F < (br - 0.4F) * 2.0F && this.tryEscape(com.destroystokyo.paper.event.entity.EndermanEscapeEvent.Reason.RUNAWAY)) { // Paper - EndermanEscapeEvent
this.setTarget(null);
@@ -373,6 +392,7 @@ public class EnderMan extends Monster implements NeutralMob {
public boolean hurtServer(final ServerLevel level, final DamageSource source, final float damage) {
if (this.isInvulnerableTo(level, source)) {
return false;
+ } else if (getRider() != null && this.isControllable()) { return super.hurtServer(level, source, damage); // Purpur - no teleporting on damage
} else if (org.purpurmc.purpur.PurpurConfig.endermanShortHeight && source.is(net.minecraft.world.damagesource.DamageTypes.IN_WALL)) { return false; } // Purpur - no suffocation damage if short height - Short enderman height
AbstractThrownPotion thrownPotion = source.getDirectEntity() instanceof AbstractThrownPotion potion ? potion : null;
diff --git a/net/minecraft/world/entity/monster/Endermite.java b/net/minecraft/world/entity/monster/Endermite.java
index c5957be3e49710ac7d03cf5f1362d8745889fc16..8405358d2221d76bc0949047fcc6f17fdfc99dfc 100644
--- a/net/minecraft/world/entity/monster/Endermite.java
+++ b/net/minecraft/world/entity/monster/Endermite.java
@@ -47,14 +47,33 @@ public class Endermite extends Monster {
}
// Purpur end - Add back player spawned endermite API
+ // Purpur start - Ridables
+ @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 - Ridables
+
@Override
protected void registerGoals() {
this.goalSelector.addGoal(1, new FloatGoal(this));
+ this.goalSelector.addGoal(1, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables
this.goalSelector.addGoal(1, new ClimbOnTopOfPowderSnowGoal(this, this.level()));
this.goalSelector.addGoal(2, new MeleeAttackGoal(this, 1.0, false));
this.goalSelector.addGoal(3, new WaterAvoidingRandomStrollGoal(this, 1.0));
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 - Ridables
this.targetSelector.addGoal(1, new HurtByTargetGoal(this).setAlertOthers());
this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true));
}
diff --git a/net/minecraft/world/entity/monster/Ghast.java b/net/minecraft/world/entity/monster/Ghast.java
index f526dcb981a929b8917cda926b1d6c8c96f4681a..7e843a144900d8d5c95848ce32904432502a18e6 100644
--- a/net/minecraft/world/entity/monster/Ghast.java
+++ b/net/minecraft/world/entity/monster/Ghast.java
@@ -53,11 +53,35 @@ public class Ghast extends Mob implements Enemy {
this.moveControl = new Ghast.GhastMoveControl<>(this, false, () -> false);
}
+ // Purpur start - Ridables
+ @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;
+ }
+ // Purpur end - Ridables
+
@Override
protected void registerGoals() {
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables
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 - Ridables
this.targetSelector
.addGoal(1, new NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, (target, level) -> Math.abs(target.getY() - this.getY()) <= 4.0));
}
@@ -102,6 +126,15 @@ public class Ghast extends Mob implements Enemy {
@Override
public void travel(final Vec3 input) {
this.travelFlying(input, 0.02F);
+ // Purpur start - Ridables
+ 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 - Ridables
}
@Override
@@ -237,7 +270,7 @@ public class Ghast extends Mob implements Enemy {
}
}
- public static class GhastMoveControl<T extends Mob> extends MoveControl<T> {
+ public static class GhastMoveControl<T extends Mob> extends org.purpurmc.purpur.controller.FlyingMoveControllerWASD<T> { // Purpur - Ridables
private int floatDuration;
private final boolean careful;
private final BooleanSupplier shouldBeStopped;
@@ -249,7 +282,7 @@ public class Ghast extends Mob implements Enemy {
}
@Override
- public void tick() {
+ public void vanillaTick() { // Purpur - Ridables
if (this.shouldBeStopped.getAsBoolean()) {
this.operation = MoveControl.Operation.WAIT;
this.mob.stopInPlace();
diff --git a/net/minecraft/world/entity/monster/Giant.java b/net/minecraft/world/entity/monster/Giant.java
index 5f8074c93d2ad3d163bb416b16711d82edd7dacc..1b7a75a2be45928093382fa2f2da079ffc5d190d 100644
--- a/net/minecraft/world/entity/monster/Giant.java
+++ b/net/minecraft/world/entity/monster/Giant.java
@@ -12,6 +12,29 @@ public class Giant extends Monster {
super(type, level);
}
+ // Purpur start - Ridables
+ @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 - Ridables
+
public static AttributeSupplier.Builder createAttributes() {
return Monster.createMonsterAttributes()
.add(Attributes.MAX_HEALTH, 100.0)
diff --git a/net/minecraft/world/entity/monster/Guardian.java b/net/minecraft/world/entity/monster/Guardian.java
index a22a5b20b4ceb666e09aea74882362cc9c5af758..af865e3a6549f008826ed7469b2ecb4c92598863 100644
--- a/net/minecraft/world/entity/monster/Guardian.java
+++ b/net/minecraft/world/entity/monster/Guardian.java
@@ -65,14 +65,35 @@ public class Guardian extends Monster {
this.xpReward = 10;
this.setPathfindingMalus(PathType.WATER, 0.0F);
this.moveControl = new Guardian.GuardianMoveControl<>(this);
+ // Purpur start - Ridables
+ 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 - Ridables
this.clientSideTailAnimation = this.random.nextFloat();
this.clientSideTailAnimationO = this.clientSideTailAnimation;
}
+ // Purpur start - Ridables
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.guardianRidable;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.guardianControllable;
+ }
+ // Purpur end - Ridables
+
@Override
protected void registerGoals() {
MoveTowardsRestrictionGoal goal = new MoveTowardsRestrictionGoal(this, 1.0);
this.randomStrollGoal = new RandomStrollGoal(this, 1.0, 80);
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables
this.goalSelector.addGoal(4, this.guardianAttackGoal = new Guardian.GuardianAttackGoal(this)); // CraftBukkit - assign field
this.goalSelector.addGoal(5, goal);
this.goalSelector.addGoal(7, this.randomStrollGoal);
@@ -81,6 +102,7 @@ public class Guardian extends Monster {
this.goalSelector.addGoal(9, new RandomLookAroundGoal(this));
this.randomStrollGoal.setFlags(EnumSet.of(Goal.Flag.MOVE, Goal.Flag.LOOK));
goal.setFlags(EnumSet.of(Goal.Flag.MOVE, Goal.Flag.LOOK));
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables
this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, LivingEntity.class, 10, true, false, new Guardian.GuardianAttackSelector(this)));
}
@@ -341,7 +363,7 @@ public class Guardian extends Monster {
@Override
protected void travelInWater(final Vec3 input, final double baseGravity, final boolean isFalling, final double oldY) {
- this.moveRelative(0.1F, input);
+ this.moveRelative(getRider() != null && this.isControllable() ? getSpeed() : 0.1F, input); // Purpur - Ridables
this.move(MoverType.SELF, this.getDeltaMovement());
this.setDeltaMovement(this.getDeltaMovement().scale(0.9));
if (!this.isMoving() && this.getTarget() == null) {
@@ -446,13 +468,22 @@ public class Guardian extends Monster {
}
}
- private static class GuardianMoveControl<T extends Guardian> extends MoveControl<T> {
+ private static class GuardianMoveControl<T extends Guardian> extends org.purpurmc.purpur.controller.WaterMoveControllerWASD<T> { // Purpur - Ridables
public GuardianMoveControl(final T guardian) {
super(guardian);
}
+ // Purpur start - Ridables
@Override
- public void tick() {
+ public void purpurTick(Player rider) {
+ super.purpurTick(rider);
+ this.mob.setDeltaMovement(this.mob.getDeltaMovement().add(0.0D, 0.005D, 0.0D));
+ this.mob.setMoving(this.mob.getForwardMot() > 0.0F); // control tail speed
+ }
+ // Purpur end - Ridables
+
+ @Override
+ public void vanillaTick() { // Purpur - Ridables
if (this.operation == MoveControl.Operation.MOVE_TO && !this.mob.getNavigation().isDone()) {
Vec3 delta = new Vec3(this.wantedX - this.mob.getX(), this.wantedY - this.mob.getY(), this.wantedZ - this.mob.getZ());
double length = delta.length();
@@ -462,7 +493,7 @@ public class Guardian extends Monster {
float yRotD = (float)(Mth.atan2(delta.z, delta.x) * 180.0F / (float)Math.PI) - 90.0F;
this.mob.setYRot(this.rotlerp(this.mob.getYRot(), yRotD, 90.0F));
this.mob.yBodyRot = this.mob.getYRot();
- float targetSpeed = (float)(this.speedModifier * this.mob.getAttributeValue(Attributes.MOVEMENT_SPEED));
+ float targetSpeed = (float)(this.getSpeedModifier() * this.mob.getAttributeValue(Attributes.MOVEMENT_SPEED)); // Purpur - Ridables
float newSpeed = Mth.lerp(0.125F, this.mob.getSpeed(), targetSpeed);
this.mob.setSpeed(newSpeed);
double push = Math.sin((this.mob.tickCount + this.mob.getId()) * 0.5) * 0.05;
diff --git a/net/minecraft/world/entity/monster/Phantom.java b/net/minecraft/world/entity/monster/Phantom.java
index 38a2c84a9f7c3e36b48c0992d00fac244600e654..6ad191adcbb9af4610fc86902cacf20977f31d6a 100644
--- a/net/minecraft/world/entity/monster/Phantom.java
+++ b/net/minecraft/world/entity/monster/Phantom.java
@@ -61,6 +61,52 @@ public class Phantom extends Mob implements Enemy {
this.lookControl = new Phantom.PhantomLookControl(this);
}
+ // Purpur start - Ridables
+ @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;
+ }
+
+ 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.projectile.PhantomFlames flames = new org.purpurmc.purpur.entity.projectile.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 - Ridables
+
@Override
public boolean isFlapping() {
return (this.getUniqueFlapTickOffset() + this.tickCount) % TICKS_PER_FLAP == 0;
@@ -73,9 +119,11 @@ public class Phantom extends Mob implements Enemy {
@Override
protected void registerGoals() {
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables
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 - Ridables
this.targetSelector.addGoal(1, new Phantom.PhantomAttackPlayerTargetGoal());
}
@@ -91,6 +139,7 @@ public class Phantom extends Mob implements Enemy {
private void updatePhantomSizeInfo() {
this.refreshDimensions();
+ if (level().purpurConfig.phantomFlamesOnSwoop && attackPhase == AttackPhase.SWOOP) shoot(); // Purpur - Ridables - Phantom flames on swoop
this.getAttribute(Attributes.ATTACK_DAMAGE).setBaseValue(6 + this.getPhantomSize());
}
@@ -159,6 +208,15 @@ public class Phantom extends Mob implements Enemy {
@Override
public void travel(final Vec3 input) {
this.travelFlying(input, 0.2F);
+ // Purpur start - Ridables
+ if (this.getRider() != null && this.isControllable() && !this.onGround) {
+ float speed = (float) this.getAttributeValue(Attributes.FLYING_SPEED);
+ this.setSpeed(speed);
+ Vec3 mot = this.getDeltaMovement();
+ this.move(net.minecraft.world.entity.MoverType.SELF, mot.multiply(speed, speed, speed));
+ this.setDeltaMovement(mot.scale(0.9D));
+ }
+ // Purpur end - Ridables
}
@Override
@@ -400,25 +458,42 @@ public class Phantom extends Mob implements Enemy {
}
}
- private static class PhantomLookControl extends LookControl {
+ private static class PhantomLookControl extends org.purpurmc.purpur.controller.LookControllerWASD { // Purpur - Ridables
public PhantomLookControl(final Mob mob) {
super(mob);
}
+ // Purpur start - Ridables
+ public void purpurTick(Player rider) {
+ setYawPitch(rider.getYRot(), -rider.xRotO * 0.75F);
+ }
+ // Purpur end - Ridables
+
@Override
- public void tick() {
+ public void vanillaTick() { // Purpur - Ridables
}
}
- private class PhantomMoveControl<T extends Mob> extends MoveControl<T> {
+ private class PhantomMoveControl<T extends Mob> extends org.purpurmc.purpur.controller.FlyingMoveControllerWASD<T> { // Purpur - Ridables
private float speed = 0.1F;
public PhantomMoveControl(final T mob) {
super(mob);
}
+ // Purpur start - Ridables
+ 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 - Ridables
+
@Override
- public void tick() {
+ public void vanillaTick() { // Purpur - Ridables
if (Phantom.this.horizontalCollision) {
Phantom.this.setYRot(Phantom.this.getYRot() + 180.0F);
this.speed = 0.1F;
diff --git a/net/minecraft/world/entity/monster/Ravager.java b/net/minecraft/world/entity/monster/Ravager.java
index 8230efa5a42fedf977f065fd4e0b8503edb04a46..99490398f72e91c0d3dc9d2a3dd82fb6f49734d5 100644
--- a/net/minecraft/world/entity/monster/Ravager.java
+++ b/net/minecraft/world/entity/monster/Ravager.java
@@ -71,15 +71,40 @@ public class Ravager extends Raider {
this.setPathfindingMalus(PathType.LEAVES, 0.0F);
}
+ // Purpur start - Ridables
+ @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 - Ridables
+
@Override
protected void registerGoals() {
super.registerGoals();
this.goalSelector.addGoal(0, new FloatGoal(this));
if (level().purpurConfig.ravagerAvoidRabbits) this.goalSelector.addGoal(3, new net.minecraft.world.entity.ai.goal.AvoidEntityGoal<>(this, net.minecraft.world.entity.animal.rabbit.Rabbit.class, 6.0F, 1.0D, 1.2D)); // Purpur - option to make ravagers afraid of rabbits
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables
this.goalSelector.addGoal(4, new MeleeAttackGoal(this, 1.0, true));
this.goalSelector.addGoal(5, new WaterAvoidingRandomStrollGoal(this, 0.4));
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 - Ridables
this.targetSelector.addGoal(2, new HurtByTargetGoal(this, Raider.class).setAlertOthers());
this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Player.class, true));
this.targetSelector.addGoal(4, new NearestAttackableTargetGoal<>(this, AbstractVillager.class, true, (target, level) -> !target.isBaby()));
@@ -136,7 +161,7 @@ public class Ravager extends Raider {
@Override
public void aiStep() {
super.aiStep();
- if (this.isAlive()) {
+ if (this.isAlive() && (getRider() == null || !this.isControllable())) { // Purpur - Ridables
if (this.isImmobile()) {
this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue(0.0);
} else {
diff --git a/net/minecraft/world/entity/monster/Shulker.java b/net/minecraft/world/entity/monster/Shulker.java
index abbf4c63e57604e7f1428182c2e48e9aa917bfb0..8ef97a74c5c2d4caa033c93cf553ba885605d052 100644
--- a/net/minecraft/world/entity/monster/Shulker.java
+++ b/net/minecraft/world/entity/monster/Shulker.java
@@ -109,12 +109,31 @@ public class Shulker extends AbstractGolem implements Enemy {
}
// Purpur end - Shulker change color with dye
+ // Purpur start - Ridables
+ @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 - Ridables
+
@Override
protected void registerGoals() {
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables
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 - Ridables
this.targetSelector.addGoal(1, new HurtByTargetGoal(this, this.getClass()).setAlertOthers());
this.targetSelector.addGoal(2, new Shulker.ShulkerNearestAttackGoal(this));
this.targetSelector.addGoal(3, new Shulker.ShulkerDefenseAttackGoal(this));
@@ -721,7 +740,7 @@ public class Shulker extends AbstractGolem implements Enemy {
}
}
- private class ShulkerLookControl extends LookControl {
+ private class ShulkerLookControl extends org.purpurmc.purpur.controller.LookControllerWASD { // Purpur - Ridables
public ShulkerLookControl(final Mob mob) {
super(mob);
}
diff --git a/net/minecraft/world/entity/monster/Silverfish.java b/net/minecraft/world/entity/monster/Silverfish.java
index 773b4b6f0fcf68295bdb63ef84156fe7ae3a105b..6aa67e7857cf205d436b451387083af75e9ece20 100644
--- a/net/minecraft/world/entity/monster/Silverfish.java
+++ b/net/minecraft/world/entity/monster/Silverfish.java
@@ -38,14 +38,33 @@ public class Silverfish extends Monster {
super(type, level);
}
+ // Purpur start - Ridables
+ @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 - Ridables
+
@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 - Ridables
this.goalSelector.addGoal(1, new ClimbOnTopOfPowderSnowGoal(this, this.level()));
this.goalSelector.addGoal(3, this.friendsGoal);
this.goalSelector.addGoal(4, new MeleeAttackGoal(this, 1.0, false));
this.goalSelector.addGoal(5, new Silverfish.SilverfishMergeWithStoneGoal(this));
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables
this.targetSelector.addGoal(1, new HurtByTargetGoal(this).setAlertOthers());
this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true));
}
diff --git a/net/minecraft/world/entity/monster/Strider.java b/net/minecraft/world/entity/monster/Strider.java
index ea4a025b6054fc43d9b5272cac6055c475875d3e..cd35797eff1ec2881e72b0fc44bd9a098d795a74 100644
--- a/net/minecraft/world/entity/monster/Strider.java
+++ b/net/minecraft/world/entity/monster/Strider.java
@@ -99,6 +99,23 @@ public class Strider extends Animal implements ItemSteerable {
this.setPathfindingMalus(PathType.FIRE, 0.0F);
}
+ // Purpur start - Ridables
+ @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 - Ridables
+
public static boolean checkStriderSpawnRules(
final EntityType<Strider> ignoredType,
final LevelAccessor level,
@@ -149,6 +166,7 @@ public class Strider extends Animal implements ItemSteerable {
@Override
protected void registerGoals() {
this.goalSelector.addGoal(1, new PanicGoal(this, 1.65));
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables
this.goalSelector.addGoal(2, new BreedGoal(this, 1.0));
this.temptGoal = new TemptGoal(this, 1.4, i -> i.is(ItemTags.STRIDER_TEMPT_ITEMS), false);
this.goalSelector.addGoal(3, this.temptGoal);
@@ -433,7 +451,7 @@ public class Strider extends Animal implements ItemSteerable {
InteractionResult interactionResult = super.mobInteract(player, hand);
if (!interactionResult.consumesAction()) {
ItemStack itemStack = player.getItemInHand(hand);
- return this.isEquippableInSlot(itemStack, EquipmentSlot.SADDLE) ? itemStack.interactLivingEntity(player, this, hand) : InteractionResult.PASS;
+ return this.isEquippableInSlot(itemStack, EquipmentSlot.SADDLE) ? itemStack.interactLivingEntity(player, this, hand) : tryRide(player, hand); // Purpur - Ridables
}
if (hasFood && !this.isSilent()) {
diff --git a/net/minecraft/world/entity/monster/Vex.java b/net/minecraft/world/entity/monster/Vex.java
index a03067833e27fb5b97b8c77a1ed4386338c16a53..3ddf4ba1d809e62e4a1f7cc54f44738b1c129145 100644
--- a/net/minecraft/world/entity/monster/Vex.java
+++ b/net/minecraft/world/entity/monster/Vex.java
@@ -60,6 +60,50 @@ public class Vex extends Monster implements TraceableEntity, OwnableEntity {
this.xpReward = 3;
}
+ // Purpur start - Ridables
+ @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(net.minecraft.world.entity.MoverType.SELF, mot.multiply(speed, 1.0, speed));
+ setDeltaMovement(mot.scale(0.9D));
+ }
+ }
+
+ @Override
+ public boolean causeFallDamage(double fallDistance, float damageMultiplier, DamageSource damageSource) {
+ return false; // no fall damage please
+ }
+ // Purpur end - Ridables
+
@Override
public boolean isFlapping() {
return this.tickCount % TICKS_PER_FLAP == 0;
@@ -72,7 +116,7 @@ public class Vex extends Monster implements TraceableEntity, OwnableEntity {
@Override
public void tick() {
- this.noPhysics = true;
+ this.noPhysics = getRider() == null || !this.isControllable(); // Purpur - Ridables
super.tick();
this.noPhysics = false;
this.setNoGravity(true);
@@ -86,17 +130,19 @@ public class Vex extends Monster implements TraceableEntity, OwnableEntity {
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 - Ridables
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 - Ridables
this.targetSelector.addGoal(1, new HurtByTargetGoal(this, 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.0).add(Attributes.ATTACK_DAMAGE, 4.0);
+ return Monster.createMonsterAttributes().add(Attributes.MAX_HEALTH, 14.0).add(Attributes.ATTACK_DAMAGE, 4.0).add(Attributes.FLYING_SPEED, 0.6D); // Purpur;
}
@Override
@@ -298,13 +344,13 @@ public class Vex extends Monster implements TraceableEntity, OwnableEntity {
}
}
- private class VexMoveControl<T extends Mob> extends MoveControl<T> {
+ private class VexMoveControl<T extends Mob> extends org.purpurmc.purpur.controller.FlyingMoveControllerWASD<T> { // Purpur - Ridables
public VexMoveControl(final T vex) {
super(vex);
}
@Override
- public void tick() {
+ public void vanillaTick() { // Purpur - Ridables
if (this.operation == MoveControl.Operation.MOVE_TO) {
Vec3 delta = new Vec3(this.wantedX - Vex.this.getX(), this.wantedY - Vex.this.getY(), this.wantedZ - Vex.this.getZ());
double deltaLength = delta.length();
@@ -312,7 +358,7 @@ public class Vex extends Monster implements TraceableEntity, OwnableEntity {
this.operation = MoveControl.Operation.WAIT;
Vex.this.setDeltaMovement(Vex.this.getDeltaMovement().scale(0.5));
} else {
- Vex.this.setDeltaMovement(Vex.this.getDeltaMovement().add(delta.scale(this.speedModifier * 0.05 / deltaLength)));
+ Vex.this.setDeltaMovement(Vex.this.getDeltaMovement().add(delta.scale(this.getSpeedModifier() * 0.05 / deltaLength))); // Purpur - Ridables
if (Vex.this.getTarget() == null) {
Vec3 movement = Vex.this.getDeltaMovement();
Vex.this.setYRot(-((float)Mth.atan2(movement.x, movement.z)) * Mth.RAD_TO_DEG);
diff --git a/net/minecraft/world/entity/monster/Witch.java b/net/minecraft/world/entity/monster/Witch.java
index b259b84556f0759dff1e1587b09995cbf80480e3..0d08901296fa2c7acf0b9ad2e0ef217c4b0ab874 100644
--- a/net/minecraft/world/entity/monster/Witch.java
+++ b/net/minecraft/world/entity/monster/Witch.java
@@ -58,6 +58,23 @@ public class Witch extends Raider implements RangedAttackMob {
super(type, level);
}
+ // Purpur start - Ridables
+ @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 - Ridables
+
@Override
protected void registerGoals() {
super.registerGoals();
@@ -66,10 +83,12 @@ public class Witch extends Raider implements RangedAttackMob {
);
this.attackPlayersGoal = new NearestAttackableWitchTargetGoal<>(this, Player.class, 10, true, false, null);
this.goalSelector.addGoal(1, new FloatGoal(this));
+ this.goalSelector.addGoal(1, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables
this.goalSelector.addGoal(2, new RangedAttackGoal(this, 1.0, 60, 10.0F));
this.goalSelector.addGoal(2, new WaterAvoidingRandomStrollGoal(this, 1.0));
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 - Ridables
this.targetSelector.addGoal(1, new HurtByTargetGoal(this, Raider.class));
this.targetSelector.addGoal(2, this.healRaidersGoal);
this.targetSelector.addGoal(3, this.attackPlayersGoal);
diff --git a/net/minecraft/world/entity/monster/Zoglin.java b/net/minecraft/world/entity/monster/Zoglin.java
index c9091f9aed31ce23371539cb44695732b2ae4e9b..41cd8cef18309f1dcfdb5c45f6733bad10587303 100644
--- a/net/minecraft/world/entity/monster/Zoglin.java
+++ b/net/minecraft/world/entity/monster/Zoglin.java
@@ -84,6 +84,23 @@ public class Zoglin extends Monster implements HoglinBase {
this.xpReward = 5;
}
+ // Purpur start - Ridables
+ @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 - Ridables
+
@Override
protected Brain<Zoglin> makeBrain(final Brain.Packed packedBrain) {
return BRAIN_PROVIDER.makeBrain(this, packedBrain);
@@ -240,6 +257,7 @@ public class Zoglin extends Monster implements HoglinBase {
protected void customServerAiStep(final ServerLevel level) {
ProfilerFiller profiler = Profiler.get();
profiler.push("zoglinBrain");
+ if (getRider() == null || !this.isControllable()) // Purpur - only use brain if no rider
this.getBrain().tick(level, this);
profiler.pop();
this.updateActivity();
diff --git a/net/minecraft/world/entity/monster/breeze/Breeze.java b/net/minecraft/world/entity/monster/breeze/Breeze.java
index eaa7eb69bdc819228bbab05b053844f1578294ba..dd1a4c7ee396572269aa3dfec12f6b01018a66c3 100644
--- a/net/minecraft/world/entity/monster/breeze/Breeze.java
+++ b/net/minecraft/world/entity/monster/breeze/Breeze.java
@@ -234,6 +234,7 @@ public class Breeze extends Monster {
protected void customServerAiStep(final ServerLevel level) {
ProfilerFiller profiler = Profiler.get();
profiler.push("breezeBrain");
+ if (getRider() == null || !this.isControllable()) // Purpur - only use brain if no rider
this.getBrain().tick(level, this);
profiler.popPush("breezeActivityUpdate");
BreezeAi.updateActivity(this);
diff --git a/net/minecraft/world/entity/monster/creaking/Creaking.java b/net/minecraft/world/entity/monster/creaking/Creaking.java
index 22ea080d8c793c8c1bb9163808271c28d48dd302..a63b919b08b2fba048a338fe24ae2a1b90c869bd 100644
--- a/net/minecraft/world/entity/monster/creaking/Creaking.java
+++ b/net/minecraft/world/entity/monster/creaking/Creaking.java
@@ -106,6 +106,29 @@ public class Creaking extends Monster {
return this.getHomePos() != null;
}
+ // Purpur start - Ridables
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.creakingRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.creakingRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.creakingControllable;
+ }
+
+ @Override
+ protected void registerGoals() {
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables
+ }
+ // Purpur end - Ridables
+
@Override
protected BodyRotationControl createBodyControl() {
return new Creaking.CreakingBodyRotationControl(this);
@@ -548,28 +571,28 @@ public class Creaking extends Monster {
}
}
- private class CreakingLookControl extends LookControl {
+ private class CreakingLookControl extends org.purpurmc.purpur.controller.LookControllerWASD { // Purpur - Ridables {
public CreakingLookControl(final Creaking creaking) {
super(creaking);
}
@Override
- public void tick() {
+ public void vanillaTick() { // Purpur - Ridables
if (Creaking.this.canMove()) {
- super.tick();
+ super.vanillaTick(); // Purpur - Ridables
}
}
}
- private static class CreakingMoveControl<T extends Creaking> extends MoveControl<T> {
+ private static class CreakingMoveControl<T extends Creaking> extends org.purpurmc.purpur.controller.MoveControllerWASD<T> { // Purpur - Ridables
public CreakingMoveControl(final T creaking) {
super(creaking);
}
@Override
- public void tick() {
+ public void vanillaTick() { // Purpur - Ridables
if (this.mob.canMove()) {
- super.tick();
+ super.vanillaTick(); // Purpur - Ridables
}
}
}
diff --git a/net/minecraft/world/entity/monster/cubemob/AbstractCubeMob.java b/net/minecraft/world/entity/monster/cubemob/AbstractCubeMob.java
index 0b9bf94d6192235d916b8c335cafed3280286afe..d01d020a562493ba0c429d2c647bf13fe729dbe0 100644
--- a/net/minecraft/world/entity/monster/cubemob/AbstractCubeMob.java
+++ b/net/minecraft/world/entity/monster/cubemob/AbstractCubeMob.java
@@ -50,6 +50,7 @@ public abstract class AbstractCubeMob extends AgeableMob {
public float oSquish;
private boolean wasOnGround = false;
private boolean canWander = true; // Paper - Slime pathfinder events
+ protected boolean actualJump; // Purpur - Ridables
protected AbstractCubeMob(final EntityType<? extends AbstractCubeMob> type, final Level level) {
super(type, level);
@@ -59,6 +60,10 @@ public abstract class AbstractCubeMob extends AgeableMob {
@Override
protected void registerGoals() {
+ // Purpur start - Ridables
+ 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 - Ridables
this.goalSelector.addGoal(1, new AbstractCubeMob.CubeMobFloatGoal(this));
this.goalSelector.addGoal(4, new AbstractCubeMob.CubeMobRandomDirectionGoal(this));
this.goalSelector.addGoal(5, new AbstractCubeMob.CubeMobKeepOnJumpingGoal(this));
@@ -486,7 +491,7 @@ public abstract class AbstractCubeMob extends AgeableMob {
}
}
- protected static class CubeMobMoveControl<T extends AbstractCubeMob> extends MoveControl<T> {
+ protected static class CubeMobMoveControl<T extends AbstractCubeMob> extends org.purpurmc.purpur.controller.MoveControllerWASD<T> { // Purpur - Ridables
private float yRot;
private int jumpDelay;
private boolean isAggressive;
@@ -502,21 +507,33 @@ public abstract class AbstractCubeMob extends AgeableMob {
}
public void setWantedMovement(final double speedModifier) {
- this.speedModifier = speedModifier;
+ this.setSpeedModifier(speedModifier); // Purpur - Ridables
this.operation = MoveControl.Operation.MOVE_TO;
}
@Override
public void tick() {
+ // Purpur start - Ridables
+ if (this.mob.getRider() != null && this.mob.isControllable()) {
+ purpurTick(this.mob.getRider());
+ if (this.mob.getForwardMot() != 0 || this.mob.getStrafeMot() != 0) {
+ if (jumpDelay > 10) {
+ jumpDelay = 6;
+ }
+ } else {
+ jumpDelay = 20;
+ }
+ } else {
+ // Purpur end - Ridables
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 ((this.mob.getRider() == null || !this.mob.isControllable()) && this.operation != MoveControl.Operation.MOVE_TO) { // Purpur - Ridables
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) * (this.mob.getRider() != null && this.mob.isControllable() && (this.mob.getRider().getForwardMot() != 0 || this.mob.getRider().getStrafeMot() != 0) ? 2.0D : 1.0D))); // Purpur - Ridables
if (this.jumpDelay-- <= 0) {
this.jumpDelay = this.mob.getJumpDelay();
if (this.isAggressive) {
@@ -533,7 +550,7 @@ public abstract class AbstractCubeMob extends AgeableMob {
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) * (this.mob.getRider() != null && this.mob.isControllable() && (this.mob.getRider().getForwardMot() != 0 || this.mob.getRider().getStrafeMot() != 0) ? 2.0D : 1.0D))); // Purpur - Ridables
}
}
}
diff --git a/net/minecraft/world/entity/monster/cubemob/MagmaCube.java b/net/minecraft/world/entity/monster/cubemob/MagmaCube.java
index 547d9304d4c85efefbf7984d0d519307af727edd..bb36ce235aac8d2cf9e4b5dc1328991f65353d20 100644
--- a/net/minecraft/world/entity/monster/cubemob/MagmaCube.java
+++ b/net/minecraft/world/entity/monster/cubemob/MagmaCube.java
@@ -46,6 +46,28 @@ public class MagmaCube extends AbstractCubeMob implements Enemy {
this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, IronGolem.class, true));
}
+ // Purpur start - Ridables
+ @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 - Ridables
+
public static AttributeSupplier.Builder createAttributes() {
return Monster.createMonsterAttributes().add(Attributes.MOVEMENT_SPEED, 0.2F);
}
@@ -101,6 +123,7 @@ public class MagmaCube extends AbstractCubeMob implements Enemy {
float sizeJumpBoostPower = this.getSize() * 0.1F;
this.setDeltaMovement(movement.x, this.getJumpPower() + sizeJumpBoostPower, movement.z);
this.needsSync = true;
+ this.actualJump = false; // Purpur - Ridables
}
@Override
diff --git a/net/minecraft/world/entity/monster/cubemob/Slime.java b/net/minecraft/world/entity/monster/cubemob/Slime.java
index 96776d23370736da3408e6a86ed89b7f3d686732..0ac7a442a0ea79f190aceb60b771d616b377e613 100644
--- a/net/minecraft/world/entity/monster/cubemob/Slime.java
+++ b/net/minecraft/world/entity/monster/cubemob/Slime.java
@@ -45,6 +45,40 @@ public class Slime extends AbstractCubeMob implements Enemy {
this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, IronGolem.class, true));
}
+ // Purpur start - Ridables
+ @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 - Ridables
+
@Override
public SoundEvent getHurtSound(final DamageSource source) {
return this.isTiny() ? SoundEvents.SLIME_HURT_SMALL : SoundEvents.SLIME_HURT;
diff --git a/net/minecraft/world/entity/monster/cubemob/SulfurCube.java b/net/minecraft/world/entity/monster/cubemob/SulfurCube.java
index 7f79c8e4e5874d2ce3a1a58b96df698c3f774011..7b3ab0515bae1963cdd2319f097bfffb4d12f4e5 100644
--- a/net/minecraft/world/entity/monster/cubemob/SulfurCube.java
+++ b/net/minecraft/world/entity/monster/cubemob/SulfurCube.java
@@ -129,6 +129,28 @@ public class SulfurCube extends AbstractCubeMob implements Bucketable, Shearable
this.goalSelector.addGoal(3, new SulfurCube.SulfurCubeSearchForItemsGoal(this));
}
+ // Purpur start - Ridables
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.sulfurCubeRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.sulfurCubeRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.sulfurCubeControllable;
+ }
+
+ @Override
+ public float getJumpPower() {
+ return 0.42F * this.getBlockJumpFactor(); // from EntityLiving
+ }
+ // Purpur end - Ridables
+
@Override
public boolean fromBucket() {
return this.entityData.get(FROM_BUCKET);
diff --git a/net/minecraft/world/entity/monster/hoglin/Hoglin.java b/net/minecraft/world/entity/monster/hoglin/Hoglin.java
index a18738fb6fd94759b41c58db343ed3a0ec105340..0f2b6f8d17fe80cb07b0cc18d3501ae700427eda 100644
--- a/net/minecraft/world/entity/monster/hoglin/Hoglin.java
+++ b/net/minecraft/world/entity/monster/hoglin/Hoglin.java
@@ -81,6 +81,23 @@ public class Hoglin extends Animal implements Enemy, HoglinBase {
this.xpReward = 5;
}
+ // Purpur start - Ridables
+ @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 - Ridables
+
@VisibleForTesting
public void setTimeInOverworld(final int timeInOverworld) {
this.timeInOverworld = timeInOverworld;
@@ -144,6 +161,7 @@ public class Hoglin extends Animal implements Enemy, HoglinBase {
protected void customServerAiStep(final ServerLevel level) {
ProfilerFiller profiler = Profiler.get();
profiler.push("hoglinBrain");
+ if (getRider() == null || !this.isControllable()) // Purpur - only use brain if no rider
this.getBrain().tick(level, this);
profiler.pop();
HoglinAi.updateActivity(this);
diff --git a/net/minecraft/world/entity/monster/illager/Evoker.java b/net/minecraft/world/entity/monster/illager/Evoker.java
index 44a3f5a2f5fef29c542e7820acb2b7d36cfc63d4..ba8d47480a1927a28b13025bff6c785121dd13eb 100644
--- a/net/minecraft/world/entity/monster/illager/Evoker.java
+++ b/net/minecraft/world/entity/monster/illager/Evoker.java
@@ -50,10 +50,28 @@ public class Evoker extends SpellcasterIllager {
this.xpReward = 10;
}
+ // Purpur start - Ridables
+ @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 - Ridables
+
@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 - Ridables
this.goalSelector.addGoal(1, new Evoker.EvokerCastingSpellGoal());
this.goalSelector.addGoal(2, new AvoidEntityGoal<>(this, Player.class, 8.0F, 0.6, 1.0));
this.goalSelector.addGoal(3, new AvoidEntityGoal<>(this, Creaking.class, 8.0F, 0.6, 1.0));
@@ -63,6 +81,7 @@ public class Evoker extends SpellcasterIllager {
this.goalSelector.addGoal(8, new RandomStrollGoal(this, 0.6));
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 - Ridables
this.targetSelector.addGoal(1, new HurtByTargetGoal(this, 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/net/minecraft/world/entity/monster/illager/Illusioner.java b/net/minecraft/world/entity/monster/illager/Illusioner.java
index f4b93c3dd12d762f280c7b52af5d3b5f58cb9cef..9ea4745eb0d72c25ac5b89a6bfa5ba2846740f07 100644
--- a/net/minecraft/world/entity/monster/illager/Illusioner.java
+++ b/net/minecraft/world/entity/monster/illager/Illusioner.java
@@ -59,10 +59,28 @@ public class Illusioner extends SpellcasterIllager implements RangedAttackMob {
}
}
+ // Purpur start - Ridables
+ @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 - Ridables
+
@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 - Ridables
this.goalSelector.addGoal(1, new SpellcasterIllager.SpellcasterCastingSpellGoal());
this.goalSelector.addGoal(3, new AvoidEntityGoal<>(this, Creaking.class, 8.0F, 1.0, 1.2));
this.goalSelector.addGoal(4, new Illusioner.IllusionerMirrorSpellGoal());
@@ -71,6 +89,7 @@ public class Illusioner extends SpellcasterIllager implements RangedAttackMob {
this.goalSelector.addGoal(8, new RandomStrollGoal(this, 0.6));
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 - Ridables
this.targetSelector.addGoal(1, new HurtByTargetGoal(this, 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/net/minecraft/world/entity/monster/illager/Pillager.java b/net/minecraft/world/entity/monster/illager/Pillager.java
index 26e179d146a3b363d0c96a6d113c613bbaf263b2..a0e926bedb5000a8e3d8e37207916cc06ad01090 100644
--- a/net/minecraft/world/entity/monster/illager/Pillager.java
+++ b/net/minecraft/world/entity/monster/illager/Pillager.java
@@ -65,16 +65,35 @@ public class Pillager extends AbstractIllager implements CrossbowAttackMob, Inve
super(type, level);
}
+ // Purpur start - Ridables
+ @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 - Ridables
+
@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 - Ridables
this.goalSelector.addGoal(1, new AvoidEntityGoal<>(this, Creaking.class, 8.0F, 1.0, 1.2));
this.goalSelector.addGoal(2, new Raider.HoldGroundAttackGoal(this, 10.0F));
this.goalSelector.addGoal(3, new RangedCrossbowAttackGoal<>(this, 1.0, 8.0F));
this.goalSelector.addGoal(8, new RandomStrollGoal(this, 0.6));
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 - Ridables
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/net/minecraft/world/entity/monster/illager/Vindicator.java b/net/minecraft/world/entity/monster/illager/Vindicator.java
index 061d08fd2451a7bbd020edbd4035b1e793f552ba..7caebb032e03ce54463c1e1050dc2bfe14cb16ee 100644
--- a/net/minecraft/world/entity/monster/illager/Vindicator.java
+++ b/net/minecraft/world/entity/monster/illager/Vindicator.java
@@ -57,15 +57,34 @@ public class Vindicator extends AbstractIllager {
super(type, level);
}
+ // Purpur start - Ridables
+ @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 - Ridables
+
@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 - Ridables
this.goalSelector.addGoal(1, new AvoidEntityGoal<>(this, Creaking.class, 8.0F, 1.0, 1.2));
this.goalSelector.addGoal(2, new Vindicator.VindicatorBreakDoorGoal(this));
this.goalSelector.addGoal(3, new AbstractIllager.RaiderOpenDoorGoal(this));
this.goalSelector.addGoal(4, new Raider.HoldGroundAttackGoal(this, 10.0F));
this.goalSelector.addGoal(5, new MeleeAttackGoal(this, 1.0, false));
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables
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/net/minecraft/world/entity/monster/piglin/Piglin.java b/net/minecraft/world/entity/monster/piglin/Piglin.java
index f1c2639637906a8214df967741c84bbe170049c2..f74df436798220d83f62383c523566d0acc2c87a 100644
--- a/net/minecraft/world/entity/monster/piglin/Piglin.java
+++ b/net/minecraft/world/entity/monster/piglin/Piglin.java
@@ -109,6 +109,23 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento
this.xpReward = 5;
}
+ // Purpur start - Ridables
+ @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 - Ridables
+
@Override
protected void addAdditionalSaveData(final ValueOutput output) {
super.addAdditionalSaveData(output);
@@ -288,6 +305,7 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento
protected void customServerAiStep(final ServerLevel level) {
ProfilerFiller profiler = Profiler.get();
profiler.push("piglinBrain");
+ if (getRider() == null || !this.isControllable()) // Purpur - only use brain if no rider
this.getBrain().tick(level, this);
profiler.pop();
PiglinAi.updateActivity(this);
diff --git a/net/minecraft/world/entity/monster/piglin/PiglinBrute.java b/net/minecraft/world/entity/monster/piglin/PiglinBrute.java
index 8bba57ee432ab41b47b6dd89a21bf17c4b462d1f..3b8a2fbd8f403c72fe092f8f2a95f95c9f7f930c 100644
--- a/net/minecraft/world/entity/monster/piglin/PiglinBrute.java
+++ b/net/minecraft/world/entity/monster/piglin/PiglinBrute.java
@@ -50,6 +50,23 @@ public class PiglinBrute extends AbstractPiglin {
this.xpReward = 20;
}
+ // Purpur start - Ridables
+ @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 - Ridables
+
public static AttributeSupplier.Builder createAttributes() {
return Monster.createMonsterAttributes()
.add(Attributes.MAX_HEALTH, 50.0)
@@ -96,6 +113,7 @@ public class PiglinBrute extends AbstractPiglin {
protected void customServerAiStep(final ServerLevel level) {
ProfilerFiller profiler = Profiler.get();
profiler.push("piglinBruteBrain");
+ if (getRider() == null || this.isControllable()) // Purpur - only use brain if no rider
this.getBrain().tick(level, this);
profiler.pop();
PiglinBruteAi.updateActivity(this);
diff --git a/net/minecraft/world/entity/monster/skeleton/AbstractSkeleton.java b/net/minecraft/world/entity/monster/skeleton/AbstractSkeleton.java
index 5676e8c076954f3109b263aeedadd21bb960db00..3294aca7cb32d5cf9bbc7c2c99a538eb00fc3dcc 100644
--- a/net/minecraft/world/entity/monster/skeleton/AbstractSkeleton.java
+++ b/net/minecraft/world/entity/monster/skeleton/AbstractSkeleton.java
@@ -75,12 +75,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 - Ridables
this.goalSelector.addGoal(2, new RestrictSunGoal(this));
this.goalSelector.addGoal(3, new FleeSunGoal(this, 1.0));
this.goalSelector.addGoal(3, new AvoidEntityGoal<>(this, Wolf.class, 6.0F, 1.0, 1.2));
this.goalSelector.addGoal(5, new WaterAvoidingRandomStrollGoal(this, 1.0));
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 - Ridables
this.targetSelector.addGoal(1, new HurtByTargetGoal(this));
this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true));
this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, IronGolem.class, true));
diff --git a/net/minecraft/world/entity/monster/skeleton/Bogged.java b/net/minecraft/world/entity/monster/skeleton/Bogged.java
index 8c8e2d8c7d3cdf83053c291bcd05ecc6f840f3ce..faa10efb42f00c8ff0726afcb689501e9c9ae50c 100644
--- a/net/minecraft/world/entity/monster/skeleton/Bogged.java
+++ b/net/minecraft/world/entity/monster/skeleton/Bogged.java
@@ -41,6 +41,23 @@ public class Bogged extends AbstractSkeleton implements Shearable {
super(type, level);
}
+ // Purpur start - Ridables
+ @Override
+ public boolean isRidable() {
+ return level().purpurConfig.boggedRidable;
+ }
+
+ @Override
+ public boolean dismountsUnderwater() {
+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.boggedRidableInWater;
+ }
+
+ @Override
+ public boolean isControllable() {
+ return level().purpurConfig.boggedControllable;
+ }
+ // Purpur end - Ridables
+
@Override
protected void defineSynchedData(final SynchedEntityData.Builder entityData) {
super.defineSynchedData(entityData);
diff --git a/net/minecraft/world/entity/monster/skeleton/Skeleton.java b/net/minecraft/world/entity/monster/skeleton/Skeleton.java
index c920ea3116795a047225506649d10deec5a964a4..ff352802190974eb466213c464b1f74541886c9b 100644
--- a/net/minecraft/world/entity/monster/skeleton/Skeleton.java
+++ b/net/minecraft/world/entity/monster/skeleton/Skeleton.java
@@ -27,6 +27,23 @@ public class Skeleton extends AbstractSkeleton {
super(type, level);
}
+ // Purpur start - Ridables
+ @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 - Ridables
+
@Override
protected void defineSynchedData(final SynchedEntityData.Builder entityData) {
super.defineSynchedData(entityData);
diff --git a/net/minecraft/world/entity/monster/skeleton/Stray.java b/net/minecraft/world/entity/monster/skeleton/Stray.java
index d45556b27a55789bd91c1cb612d7f939fed9aebf..ce8d70886fd84f10400a1bfee857434fe5e4857b 100644
--- a/net/minecraft/world/entity/monster/skeleton/Stray.java
+++ b/net/minecraft/world/entity/monster/skeleton/Stray.java
@@ -23,6 +23,23 @@ public class Stray extends AbstractSkeleton {
super(type, level);
}
+ // Purpur start - Ridables
+ @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 - Ridables
+
public static boolean checkStraySpawnRules(
final EntityType<Stray> type, final ServerLevelAccessor level, final EntitySpawnReason spawnReason, final BlockPos pos, final RandomSource random
) {
diff --git a/net/minecraft/world/entity/monster/skeleton/WitherSkeleton.java b/net/minecraft/world/entity/monster/skeleton/WitherSkeleton.java
index a69cda3d70e1698e1750142efd194f98a0805a47..2a094760c7a60a5620c12527b2d69821f8849137 100644
--- a/net/minecraft/world/entity/monster/skeleton/WitherSkeleton.java
+++ b/net/minecraft/world/entity/monster/skeleton/WitherSkeleton.java
@@ -34,6 +34,23 @@ public class WitherSkeleton extends AbstractSkeleton {
this.setPathfindingMalus(PathType.LAVA, 8.0F);
}
+ // Purpur start - Ridables
+ @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 - Ridables
+
@Override
protected void registerGoals() {
this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, AbstractPiglin.class, true));
diff --git a/net/minecraft/world/entity/monster/spider/CaveSpider.java b/net/minecraft/world/entity/monster/spider/CaveSpider.java
index 5e1f910303895a6ce5332894491a7203769321e2..0ded67c38295e216b8359e58cd6c15025ea1b362 100644
--- a/net/minecraft/world/entity/monster/spider/CaveSpider.java
+++ b/net/minecraft/world/entity/monster/spider/CaveSpider.java
@@ -26,6 +26,23 @@ public class CaveSpider extends Spider {
return Spider.createAttributes().add(Attributes.MAX_HEALTH, 12.0);
}
+ // Purpur start - Ridables
+ @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 - Ridables
+
@Override
public boolean doHurtTarget(final ServerLevel level, final Entity target) {
if (super.doHurtTarget(level, target)) {
diff --git a/net/minecraft/world/entity/monster/spider/Spider.java b/net/minecraft/world/entity/monster/spider/Spider.java
index 820a257e3cb43cbda930b5512b63eecfc6e37fa8..76505ffb0e308f866657a0a3af1bea120c26f097 100644
--- a/net/minecraft/world/entity/monster/spider/Spider.java
+++ b/net/minecraft/world/entity/monster/spider/Spider.java
@@ -53,15 +53,34 @@ public class Spider extends Monster {
super(type, level);
}
+ // Purpur start - Ridables
+ @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 - Ridables
+
@Override
protected void registerGoals() {
this.goalSelector.addGoal(1, new FloatGoal(this));
+ this.goalSelector.addGoal(1, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables
this.goalSelector.addGoal(2, new AvoidEntityGoal<>(this, Armadillo.class, 6.0F, 1.0, 1.2, entity -> !((Armadillo)entity).isScared()));
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.8));
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 - Ridables
this.targetSelector.addGoal(1, new HurtByTargetGoal(this));
this.targetSelector.addGoal(2, new Spider.SpiderTargetGoal<>(this, Player.class));
this.targetSelector.addGoal(3, new Spider.SpiderTargetGoal<>(this, IronGolem.class));
diff --git a/net/minecraft/world/entity/monster/warden/Warden.java b/net/minecraft/world/entity/monster/warden/Warden.java
index 9eb917bf61a890be013a035d041a728e2514d137..4354b55e4aaca68a3f5be71cbf811255c1d5fc65 100644
--- a/net/minecraft/world/entity/monster/warden/Warden.java
+++ b/net/minecraft/world/entity/monster/warden/Warden.java
@@ -133,8 +133,32 @@ public class Warden extends Monster implements VibrationSystem {
this.setPathfindingMalus(PathType.LAVA, 8.0F);
this.setPathfindingMalus(PathType.FIRE, 0.0F);
this.setPathfindingMalus(PathType.FIRE_IN_NEIGHBOR, 0.0F);
+ this.moveControl = new org.purpurmc.purpur.controller.MoveControllerWASD(this, 0.5F); // Purpur - Ridables
}
+ // Purpur start - Ridables
+ @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 - Ridables
+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables
+ }
+ // Purpur end - Ridables
+
@Override
public Packet<ClientGamePacketListener> getAddEntityPacket(final ServerEntity serverEntity) {
return new ClientboundAddEntityPacket(this, serverEntity, this.hasPose(Pose.EMERGING) ? 1 : 0);
@@ -290,6 +314,7 @@ public class Warden extends Monster implements VibrationSystem {
protected void customServerAiStep(final ServerLevel level) {
ProfilerFiller profiler = Profiler.get();
profiler.push("wardenBrain");
+ if (getRider() == null || !this.isControllable()) // Purpur - only use brain if no rider
this.getBrain().tick(level, this);
profiler.pop();
super.customServerAiStep(level);
@@ -397,6 +422,7 @@ public class Warden extends Monster implements VibrationSystem {
@Contract("null->false")
public boolean canTargetEntity(final @Nullable Entity entity) {
+ if (getRider() != null && isControllable()) return false; // Purpur - Ridables
return entity instanceof LivingEntity livingEntity
&& this.level() == entity.level()
&& EntitySelector.NO_CREATIVE_OR_SPECTATOR.test(entity)
diff --git a/net/minecraft/world/entity/monster/zombie/Drowned.java b/net/minecraft/world/entity/monster/zombie/Drowned.java
index a94a023731bf61e0de7abfb7803a292d1583eb06..9c725e29cb56a2d03722a988bab79b6729bd898e 100644
--- a/net/minecraft/world/entity/monster/zombie/Drowned.java
+++ b/net/minecraft/world/entity/monster/zombie/Drowned.java
@@ -82,6 +82,23 @@ public class Drowned extends Zombie implements RangedAttackMob {
return Zombie.createAttributes().add(Attributes.STEP_HEIGHT, 1.0);
}
+ // Purpur start - Ridables
+ @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 - Ridables
+
@Override
protected PathNavigation createNavigation(final Level level) {
return new AmphibiousPathNavigation(this, level);
@@ -452,13 +469,13 @@ public class Drowned extends Zombie implements RangedAttackMob {
}
}
- private static class DrownedMoveControl<T extends Drowned> extends MoveControl<T> {
+ private static class DrownedMoveControl<T extends Drowned> extends org.purpurmc.purpur.controller.MoveControllerWASD<T> { // Purpur - Ridables
public DrownedMoveControl(final T drowned) {
super(drowned);
}
@Override
- public void tick() {
+ public void vanillaTick() { // Purpur - Ridables
LivingEntity target = this.mob.getTarget();
if (this.mob.wantsToSwim() && this.mob.isInWater()) {
if (target != null && target.getY() > this.mob.getY() || this.mob.isSearchingForLand()) {
@@ -478,7 +495,7 @@ public class Drowned extends Zombie implements RangedAttackMob {
float yRotD = (float)(Mth.atan2(zd, xd) * 180.0F / (float)Math.PI) - 90.0F;
this.mob.setYRot(this.rotlerp(this.mob.getYRot(), yRotD, 90.0F));
this.mob.yBodyRot = this.mob.getYRot();
- float targetSpeed = (float)(this.speedModifier * this.mob.getAttributeValue(Attributes.MOVEMENT_SPEED));
+ float targetSpeed = (float)(this.getSpeedModifier() * this.mob.getAttributeValue(Attributes.MOVEMENT_SPEED)); // Purpur - Ridables
float newSpeed = Mth.lerp(0.125F, this.mob.getSpeed(), targetSpeed);
this.mob.setSpeed(newSpeed);
this.mob.setDeltaMovement(this.mob.getDeltaMovement().add(newSpeed * xd * 0.005, newSpeed * yd * 0.1, newSpeed * zd * 0.005));
@@ -487,7 +504,7 @@ public class Drowned extends Zombie implements RangedAttackMob {
this.mob.setDeltaMovement(this.mob.getDeltaMovement().add(0.0, -0.008, 0.0));
}
- super.tick();
+ super.vanillaTick(); // Purpur - Ridables
}
}
}
diff --git a/net/minecraft/world/entity/monster/zombie/Husk.java b/net/minecraft/world/entity/monster/zombie/Husk.java
index b3ba75be06325db01b97a35b0e91bbcb61faa41c..2dc09198f7bc86f5837c30e7e85eeb4c85b8d114 100644
--- a/net/minecraft/world/entity/monster/zombie/Husk.java
+++ b/net/minecraft/world/entity/monster/zombie/Husk.java
@@ -38,6 +38,23 @@ public class Husk extends Zombie {
super(type, level);
}
+ // Purpur start - Ridables
+ @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 - Ridables
+
@Override
public boolean isSunSensitive() {
return false;
diff --git a/net/minecraft/world/entity/monster/zombie/Zombie.java b/net/minecraft/world/entity/monster/zombie/Zombie.java
index 2b58954cabda006adc0023831af520f4d5eb7275..d231dbf446c4e28df3512918ae50c1fc5e4db402 100644
--- a/net/minecraft/world/entity/monster/zombie/Zombie.java
+++ b/net/minecraft/world/entity/monster/zombie/Zombie.java
@@ -113,11 +113,30 @@ public class Zombie extends Monster {
this(EntityTypes.ZOMBIE, level);
}
+ // Purpur start - Ridables
+ @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 - Ridables
+
@Override
protected void registerGoals() {
+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables
if (this.level().paperConfig().entities.behavior.zombiesTargetTurtleEggs) this.goalSelector.addGoal(4, new Zombie.ZombieAttackTurtleEggGoal(this, 1.0, 3)); // Paper - Add zombie targets turtle egg config
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 - Ridables
this.addBehaviourGoals();
}
diff --git a/net/minecraft/world/entity/monster/zombie/ZombieVillager.java b/net/minecraft/world/entity/monster/zombie/ZombieVillager.java
index 32cf40d4254bc7be406b3cfea728b01259dbd83f..67031d93d9f59cf616c7ed1a3fcb18182148c6e6 100644
--- a/net/minecraft/world/entity/monster/zombie/ZombieVillager.java
+++ b/net/minecraft/world/entity/monster/zombie/ZombieVillager.java
@@ -87,6 +87,23 @@ public class ZombieVillager extends Zombie implements VillagerDataHolder {
super(type, level);
}
+ // Purpur start - Ridables
+ @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 - Ridables
+
@Override
protected void defineSynchedData(final SynchedEntityData.Builder entityData) {
super.defineSynchedData(entityData);
diff --git a/net/minecraft/world/entity/monster/zombie/ZombifiedPiglin.java b/net/minecraft/world/entity/monster/zombie/ZombifiedPiglin.java
index 6002fab2ca888fb1a73f09ce552adc9646c00d32..a9829f453a6ff8d8b844beb75b653799c763fbc1 100644
--- a/net/minecraft/world/entity/monster/zombie/ZombifiedPiglin.java
+++ b/net/minecraft/world/entity/monster/zombie/ZombifiedPiglin.java
@@ -68,6 +68,23 @@ public class ZombifiedPiglin extends Zombie implements NeutralMob {
this.setPathfindingMalus(PathType.LAVA, 8.0F);
}
+ // Purpur start - Ridables
+ @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 - Ridables
+
@Override
protected void addBehaviourGoals() {
this.goalSelector.addGoal(1, new SpearUseGoal<>(this, 1.0, 1.0, 10.0F, 2.0F));
diff --git a/net/minecraft/world/entity/npc/villager/Villager.java b/net/minecraft/world/entity/npc/villager/Villager.java
index 804cbda3085797cfd58ffb550988f65d6650660b..0aca0978653b3b1cf8ca51596a80fdc91225a9a9 100644
--- a/net/minecraft/world/entity/npc/villager/Villager.java
+++ b/net/minecraft/world/entity/npc/villager/Villager.java
@@ -243,6 +243,28 @@ public class Villager extends AbstractVillager implements VillagerDataHolder, Re
}
// Purpur end - Lobotomize stuck villagers
+ // Purpur start - Ridables
+ @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 - Ridables
+
@Override
public Brain<Villager> getBrain() {
return (Brain<Villager>)super.getBrain();
@@ -323,7 +345,7 @@ public class Villager extends AbstractVillager implements VillagerDataHolder, Re
} else {
this.isLobotomized = false;
}
- if (!inactive) {
+ if (!inactive && (getRider() == null || !this.isControllable())) { // Purpur - Ridables
this.getBrain().tick(level, this); // Paper - EAR 2
}
else if (this.isLobotomized && shouldRestock(level)) restock();
@@ -385,7 +407,7 @@ public class Villager extends AbstractVillager implements VillagerDataHolder, Re
if (this.isBaby()) {
this.setUnhappy();
- return InteractionResult.SUCCESS;
+ return tryRide(player, hand, InteractionResult.SUCCESS); // Purpur - Ridables
}
if (!this.level().isClientSide()) {
@@ -399,9 +421,11 @@ public class Villager extends AbstractVillager implements VillagerDataHolder, Re
}
if (noOffers) {
- return InteractionResult.CONSUME;
+ return tryRide(player, hand, InteractionResult.CONSUME); // Purpur - Ridables
}
+ if (level().purpurConfig.villagerRidable && itemStack.isEmpty()) return tryRide(player, hand); // Purpur - Ridables
+
if (this.level().purpurConfig.villagerAllowTrading) // Purpur - Add config for villager trading
this.startTrading(player);
}
diff --git a/net/minecraft/world/entity/npc/wanderingtrader/WanderingTrader.java b/net/minecraft/world/entity/npc/wanderingtrader/WanderingTrader.java
index 0b668baf3fca348d9e3755c114bd109fe7736a80..45279d3473f42dd0f75315961bd178c17c8dac5d 100644
--- a/net/minecraft/world/entity/npc/wanderingtrader/WanderingTrader.java
+++ b/net/minecraft/world/entity/npc/wanderingtrader/WanderingTrader.java
@@ -67,6 +67,23 @@ public class WanderingTrader extends AbstractVillager implements Consumable.Over
}
// Purpur end - Allow leashing villagers
+ // Purpur - start - Ridables
+ @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 - Ridables
+
@Override
protected void registerGoals() {
this.goalSelector.addGoal(0, new FloatGoal(this));
@@ -124,8 +141,9 @@ public class WanderingTrader extends AbstractVillager implements Consumable.Over
if (!this.level().isClientSide()) {
if (this.getOffers().isEmpty()) {
- return InteractionResult.CONSUME;
+ return tryRide(player, hand, InteractionResult.CONSUME); // Purpur - Ridables
}
+ if (level().purpurConfig.wanderingTraderRidable && itemStack.isEmpty()) return tryRide(player, hand); // Purpur - Ridables
if (this.level().purpurConfig.wanderingTraderAllowTrading) { // Purpur - Add config for villager trading
this.setTradingPlayer(player);
diff --git a/net/minecraft/world/entity/player/Player.java b/net/minecraft/world/entity/player/Player.java
index 1cb7700824d608380301e5ff524d17fd7420ec16..def90b5105edb0303eecc4424ff7d6e2cb050791 100644
--- a/net/minecraft/world/entity/player/Player.java
+++ b/net/minecraft/world/entity/player/Player.java
@@ -194,6 +194,19 @@ public abstract class Player extends Avatar implements ContainerUser {
}
// CraftBukkit end
+ // Purpur start - Ridables
+ 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 - Ridables
+
public Player(final Level level, final GameProfile gameProfile) {
super(EntityTypes.PLAYER, level);
this.setUUID(gameProfile.id());
diff --git a/net/minecraft/world/entity/projectile/LlamaSpit.java b/net/minecraft/world/entity/projectile/LlamaSpit.java
index 904d52c45c4f337c8c9130139df1d89edcae0e78..a226360ffbb51f5c5059df9c47e05088cbfe5685 100644
--- a/net/minecraft/world/entity/projectile/LlamaSpit.java
+++ b/net/minecraft/world/entity/projectile/LlamaSpit.java
@@ -34,6 +34,12 @@ public class LlamaSpit extends Projectile {
);
}
+ // Purpur start - Ridables
+ public void projectileTick() {
+ super.tick();
+ }
+ // Purpur end - Ridables
+
@Override
protected double getDefaultGravity() {
return 0.06;
diff --git a/net/minecraft/world/entity/projectile/hurtingprojectile/WitherSkull.java b/net/minecraft/world/entity/projectile/hurtingprojectile/WitherSkull.java
index d09f7e6f87318be566d38b5e8569ddaf7223e651..75b896b43a4865970bd68d677d5b9de700bc99d8 100644
--- a/net/minecraft/world/entity/projectile/hurtingprojectile/WitherSkull.java
+++ b/net/minecraft/world/entity/projectile/hurtingprojectile/WitherSkull.java
@@ -111,6 +111,14 @@ public class WitherSkull extends AbstractHurtingProjectile {
}
// Purpur end - Add canSaveToDisk to Entity
+ // Purpur start - Ridables
+ @Override
+ public boolean canHitEntity(Entity target) {
+ // do not hit rider
+ return target != this.getRider() && super.canHitEntity(target);
+ }
+ // Purpur end - Ridables
+
@Override
protected void defineSynchedData(final SynchedEntityData.Builder entityData) {
entityData.define(DATA_DANGEROUS, false);