From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: William Blake Galbreath 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 99f1c3d82e632e7328366dc8b02e8f26500f80ce..bc2be2db242d4cc5ad857ad97f32e42072f09f09 100644 --- a/net/minecraft/server/MinecraftServer.java +++ b/net/minecraft/server/MinecraftServer.java @@ -1865,6 +1865,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop 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 net.minecraft.world.level.block.entity.HopperBlockEntity.skipHopperEvents = level.paperConfig().hopper.disableMoveEvent || org.bukkit.event.inventory.InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0; // Paper - Perf: Optimize Hoppers profiler.push(() -> level + " " + level.dimension().identifier()); diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java index 72ce2b03561af86db2059dfa05f381278af56f64..ca39cddc5d7bccbea4ce8ed95354c7529dd93072 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java @@ -239,6 +239,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet private static final org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry DATA_TYPE_REGISTRY = new org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry(); public final org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer persistentDataContainer = new org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer(DATA_TYPE_REGISTRY); 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 9529e56d83964589a1e55b64e666f66b4148c315..c7e81ed584a3da2521fad58049ee44c18947ca23 100644 --- a/net/minecraft/server/level/ServerPlayer.java +++ b/net/minecraft/server/level/ServerPlayer.java @@ -775,6 +775,15 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc 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 c975f82d59c19d1bc8d1bce776af59ea5271e019..df58df768c938def010d2d215c61c906eda77429 100644 --- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java @@ -2983,6 +2983,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 4ae58d6d8b6bd9510b6caf1e6618da4e9bed28bf..a4b74837096b4653b8504dbf446ad42277975e7e 100644 --- a/net/minecraft/world/entity/Entity.java +++ b/net/minecraft/world/entity/Entity.java @@ -3463,6 +3463,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 @@ -3503,6 +3510,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 { @@ -5490,4 +5505,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 8ec2032ed8e7586b14c3184ba03f9dfea9afc765..e9d53d0708cd1c49d1ed1257d10d22f7201873a0 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; protected 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 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) { @@ -3141,6 +3142,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)); @@ -3869,8 +3884,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()); @@ -3880,6 +3897,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 481d882c8ba5ffd046b98709497f37d89f41e19e..4e1fadf8ea105d1d5ceaef5b5f9ce4f3f356608d 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); @@ -608,6 +608,7 @@ public abstract class Mob extends LivingEntity implements Targeting, EquipmentUs } } } else { + if (getRider() == null || !this.isControllable()) // Purpur - Ridables this.igniteForSeconds(8.0F); } } @@ -1398,7 +1399,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) { @@ -1747,4 +1748,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 attributesToSync = new ObjectOpenHashSet<>(); private final Set 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 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) { 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 2c9b7ef8ed87b42807f5adc9545d692105c91c6b..d2faf838dbf8160afa83919db707fa2ff352a413 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 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 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 69d8a10f524dd4db683a00f194e8d1a26c996f43..c6ed1f00fd931ecff13dfb3309470afab2f0852d 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 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 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 c8c50683597a2314a3c0e72888d5d75dc73fb94a..0b98a358c03c72b89e6b644bf11fc361c25f517f 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 extends SmoothSwimmingMoveControl { + private final org.purpurmc.purpur.controller.WaterMoveControllerWASD 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 c33e18d572463f5d457516bc4bd64826cf7c3acc..ee732f4462cc69070beffdbc7a3a310d3c369a10 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 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 { 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 c646ab45fbfb4a5fd29b8ad4914cf115d2f674b5..b0191f605e6f5e7be78a44868bef1805fc2d005c 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 43ad961d0921c07562d13d4cdb86e63cd9b564ea..242957c6f31aa1e050ae2c2e8c25652b0bab1d89 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 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 type, final Level level) { super(type, level); - this.moveControl = new SmoothSwimmingMoveControl<>(this, 85, 10, 0.02F, 0.1F, true); + // Purpur start - Ridables + class DolphinMoveControl extends SmoothSwimmingMoveControl { + private final org.purpurmc.purpur.controller.WaterMoveControllerWASD 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 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 extends MoveControl { + private static class FishMoveControl extends org.purpurmc.purpur.controller.WaterMoveControllerWASD { // 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 a8e09c33d3aafd18ad2b6b14a5ba0ac4d9f7f125..9edd4af22e009c8f131f42f0fcad56ef1e56903c 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, @@ -1120,15 +1160,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 } } @@ -1164,15 +1204,15 @@ public class Fox extends Animal { } } - private static class FoxMoveControl extends MoveControl { + private static class FoxMoveControl extends org.purpurmc.purpur.controller.MoveControllerWASD { // 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 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 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 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 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 84dbcd50c605a44b55ece4a6f548ba7aa4381638..7953cac5741a5d3672e6b4bf624e1fc1c123a13a 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 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 6ea5197affd9bdfc5a5abcd9ef94a244640e8554..8ec69bb91822bff6e7fe338f97d5bb8dbc2345f7 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 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 4ed8dc9b97fe6319502f651a01d32e6eafa62718..1fee38fdb97678cbf4128962d69ffe57abe76d52 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()); } @@ -615,7 +643,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()) { @@ -659,7 +687,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 } } @@ -966,15 +994,15 @@ public class Panda extends Animal { } } - private static class PandaMoveControl extends MoveControl { + private static class PandaMoveControl extends org.purpurmc.purpur.controller.MoveControllerWASD { // 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 0cff09ae7ca8b5dfd48bb8781081b96e0ba8360d..1beb532330ea892127faf88be85f94056aa2037e 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 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 extends FlyingMoveControl { + 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 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 353866566a00143f138a0a460613af381f507fcf..2d1fd11121d3e6293f35e4e7838cbe59a8b27dc4 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 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 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(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 extends MoveControl { + private static class RabbitMoveControl extends org.purpurmc.purpur.controller.MoveControllerWASD { // 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 b3345d2e0f991f578c5a8d2871bee8f936a733c8..cf20e9a64fee6512c14f1b757f435fd4fc3394fc 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..9166e9f3606c6a96cbf0ddddabb57e2d91d2aa18 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.isJumping()) { + 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 7ee9d2f25178ac97b74d0f2c3619ddfaf2004bea..d08d2d275b1268abe2548352e15890b04536a3ed 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)); @@ -488,9 +506,11 @@ public class Turtle extends Animal { } } - private static class TurtleMoveControl extends MoveControl { + private static class TurtleMoveControl extends org.purpurmc.purpur.controller.MoveControllerWASD { // 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() { @@ -509,7 +529,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(); @@ -523,7 +543,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 dc332c72a6684cf65441343395d99fea129ce2ab..60c02127a504a5130a202a3b532b63bef4c47a31 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 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 73d1a839c541d089e51e547c29bbb239a1a5ef9b..ab1c7889e7742c27589c72d1f73960eda9091078 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 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); @@ -586,11 +710,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 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 5b56fa1f7dadc63e7edbf54576327bbcb7f5f2a2..81031650d914c5d973d5bd89547ce58e92b1acc6 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 { private 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 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)); } @@ -313,6 +390,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 extends MoveControl { + public static class GhastMoveControl extends org.purpurmc.purpur.controller.FlyingMoveControllerWASD { // 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 d4d295b0fe663a7747a7c7b44a08ab1e192f26b1..e6b3bc45f36b438a7164c4be2e97ffa22f39979d 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 extends MoveControl { + private static class GuardianMoveControl extends org.purpurmc.purpur.controller.WaterMoveControllerWASD { // 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 extends MoveControl { + private class PhantomMoveControl extends org.purpurmc.purpur.controller.FlyingMoveControllerWASD { // 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 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 3c3cef88f503db93b2c097096c505577b945fa76..a5fe9509e3e3ee56ef0cd222e8fe92ce8316769f 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 extends MoveControl { + private class VexMoveControl extends org.purpurmc.purpur.controller.FlyingMoveControllerWASD { // 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 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 extends MoveControl { + private static class CreakingMoveControl extends org.purpurmc.purpur.controller.MoveControllerWASD { // 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 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 extends MoveControl { + protected static class CubeMobMoveControl extends org.purpurmc.purpur.controller.MoveControllerWASD { // 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 1856b055cfeafb88303957b570812974b33a03ae..e5fd7eefb749394ee8613339bf5802315c0bb4bf 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 2fb3b7f3928a459d089bd8680673aa589118f464..bc1b7ffce2de2b0835e69c93aad3f00fef025c89 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 dbfe791f45ddb77be7487d16325f56eb3a5d094d..dc8e8379ab43e2f1ef26a8a0cea9a17f796434d4 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); @@ -289,6 +306,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 faff25abf54e00f38b10bdb47e37ab2d100f32fb..6bb58edc4317e33da1d6053b1c66a9ac10fb6ad9 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 d9cb4a5b989ce684bbaf4eee3f642d29d6bf9d45..d92db02786ce6989ca5d8a2d7647e4f60a66ce82 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 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 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 extends MoveControl { + private static class DrownedMoveControl extends org.purpurmc.purpur.controller.MoveControllerWASD { // 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 4a74692e1dc3f6e94f046e0c5941a59893174c32..cbea2f30f20859f03f3b4667c22b380c8d45e7ab 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 2942cf895a145c8d0bd962f765fc8e21079d0cc6..f62603ef4b76d2966d8e70a7ab9bdc2592cf41fb 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 getBrain() { return (Brain)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 49709c2cb9c8804f13bf992e771254e0d7d83cf5..c12d88851552a5ac0b4c2e9c19d049e0a52dc578 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);