aboutsummaryrefslogtreecommitdiffhomepage
path: root/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/FishingHook.java.patch
diff options
context:
space:
mode:
Diffstat (limited to 'patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/FishingHook.java.patch')
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/FishingHook.java.patch854
1 files changed, 854 insertions, 0 deletions
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/FishingHook.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/FishingHook.java.patch
new file mode 100644
index 0000000000..80a95da104
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/FishingHook.java.patch
@@ -0,0 +1,854 @@
+--- a/net/minecraft/world/entity/projectile/FishingHook.java
++++ b/net/minecraft/world/entity/projectile/FishingHook.java
+@@ -2,6 +2,7 @@
+
+ import com.mojang.logging.LogUtils;
+ import java.util.Collections;
++import java.util.Iterator;
+ import java.util.List;
+ import javax.annotation.Nullable;
+ import net.minecraft.advancements.CriteriaTriggers;
+@@ -24,15 +25,14 @@
+ import net.minecraft.util.RandomSource;
+ import net.minecraft.world.entity.Entity;
+ import net.minecraft.world.entity.EntityType;
++import net.minecraft.world.entity.EnumMoveType;
+ import net.minecraft.world.entity.ExperienceOrb;
+-import net.minecraft.world.entity.MoverType;
+ import net.minecraft.world.entity.item.ItemEntity;
+-import net.minecraft.world.entity.player.Player;
+ import net.minecraft.world.item.ItemStack;
+ import net.minecraft.world.item.Items;
+ import net.minecraft.world.level.Level;
+ import net.minecraft.world.level.block.Blocks;
+-import net.minecraft.world.level.block.state.BlockState;
++import net.minecraft.world.level.block.state.IBlockData;
+ import net.minecraft.world.level.material.FluidState;
+ import net.minecraft.world.level.storage.loot.BuiltInLootTables;
+ import net.minecraft.world.level.storage.loot.LootParams;
+@@ -45,28 +45,50 @@
+ import net.minecraft.world.phys.Vec3;
+ import org.slf4j.Logger;
+
++// CraftBukkit start
++import org.bukkit.entity.Player;
++import org.bukkit.entity.FishHook;
++import org.bukkit.event.player.PlayerFishEvent;
++// CraftBukkit end
++
+ public class FishingHook extends Projectile {
++
+ private static final Logger LOGGER = LogUtils.getLogger();
+- private final RandomSource syncronizedRandom = RandomSource.create();
++ private final RandomSource syncronizedRandom;
+ private boolean biting;
+ private int outOfWaterTime;
+ private static final int MAX_OUT_OF_WATER_TIME = 10;
+- private static final EntityDataAccessor<Integer> DATA_HOOKED_ENTITY = SynchedEntityData.defineId(FishingHook.class, EntityDataSerializers.INT);
++ public static final EntityDataAccessor<Integer> DATA_HOOKED_ENTITY = SynchedEntityData.defineId(FishingHook.class, EntityDataSerializers.INT);
+ private static final EntityDataAccessor<Boolean> DATA_BITING = SynchedEntityData.defineId(FishingHook.class, EntityDataSerializers.BOOLEAN);
+ private int life;
+ private int nibble;
+ private int timeUntilLured;
+ private int timeUntilHooked;
+ private float fishAngle;
+- private boolean openWater = true;
++ private boolean openWater;
+ @Nullable
+- private Entity hookedIn;
+- private FishingHook.FishHookState currentState = FishingHook.FishHookState.FLYING;
++ public Entity hookedIn;
++ public FishingHook.HookState currentState;
+ private final int luck;
+ private final int lureSpeed;
+
++ // CraftBukkit start - Extra variables to enable modification of fishing wait time, values are minecraft defaults
++ public int minWaitTime = 100;
++ public int maxWaitTime = 600;
++ public int minLureTime = 20;
++ public int maxLureTime = 80;
++ public float minLureAngle = 0.0F;
++ public float maxLureAngle = 360.0F;
++ public boolean applyLure = true;
++ public boolean rainInfluenced = true;
++ public boolean skyInfluenced = true;
++ // CraftBukkit end
++
+ private FishingHook(EntityType<? extends FishingHook> entityType, Level level, int luck, int lureSpeed) {
+ super(entityType, level);
++ this.syncronizedRandom = RandomSource.create();
++ this.openWater = true;
++ this.currentState = FishingHook.HookState.FLYING;
+ this.noCulling = true;
+ this.luck = Math.max(0, luck);
+ this.lureSpeed = Math.max(0, lureSpeed);
+@@ -76,50 +98,49 @@
+ this(entityType, level, 0, 0);
+ }
+
+- public FishingHook(Player player, Level level, int luck, int lureSpeed) {
++ public FishingHook(net.minecraft.world.entity.player.Player player, Level level, int luck, int lureSpeed) {
+ this(EntityType.FISHING_BOBBER, level, luck, lureSpeed);
+ this.setOwner(player);
+- float xRot = player.getXRot();
+- float yRot = player.getYRot();
+- float cos = Mth.cos(-yRot * (float) (Math.PI / 180.0) - (float) Math.PI);
+- float sin = Mth.sin(-yRot * (float) (Math.PI / 180.0) - (float) Math.PI);
+- float f = -Mth.cos(-xRot * (float) (Math.PI / 180.0));
+- float sin1 = Mth.sin(-xRot * (float) (Math.PI / 180.0));
+- double d = player.getX() - (double)sin * 0.3;
+- double eyeY = player.getEyeY();
+- double d1 = player.getZ() - (double)cos * 0.3;
+- this.moveTo(d, eyeY, d1, yRot, xRot);
+- Vec3 vec3 = new Vec3((double)(-sin), (double)Mth.clamp(-(sin1 / f), -5.0F, 5.0F), (double)(-cos));
+- double len = vec3.length();
+- vec3 = vec3.multiply(
+- 0.6 / len + this.random.triangle(0.5, 0.0103365),
+- 0.6 / len + this.random.triangle(0.5, 0.0103365),
+- 0.6 / len + this.random.triangle(0.5, 0.0103365)
+- );
+- this.setDeltaMovement(vec3);
+- this.setYRot((float)(Mth.atan2(vec3.x, vec3.z) * 180.0F / (float)Math.PI));
+- this.setXRot((float)(Mth.atan2(vec3.y, vec3.horizontalDistance()) * 180.0F / (float)Math.PI));
++ float f = player.getXRot();
++ float f1 = player.getYRot();
++ float f2 = Mth.cos(-f1 * 0.017453292F - 3.1415927F);
++ float f3 = Mth.sin(-f1 * 0.017453292F - 3.1415927F);
++ float f4 = -Mth.cos(-f * 0.017453292F);
++ float f5 = Mth.sin(-f * 0.017453292F);
++ double d0 = player.getX() - (double) f3 * 0.3D;
++ double d1 = player.getEyeY();
++ double d2 = player.getZ() - (double) f2 * 0.3D;
++
++ this.moveTo(d0, d1, d2, f1, f);
++ Vec3 vec3d = new Vec3((double) (-f3), (double) Mth.clamp(-(f5 / f4), -5.0F, 5.0F), (double) (-f2));
++ double d3 = vec3d.length();
++
++ vec3d = vec3d.multiply(0.6D / d3 + this.random.triangle(0.5D, 0.0103365D), 0.6D / d3 + this.random.triangle(0.5D, 0.0103365D), 0.6D / d3 + this.random.triangle(0.5D, 0.0103365D));
++ this.setDeltaMovement(vec3d);
++ this.setYRot((float) (Mth.atan2(vec3d.x, vec3d.z) * 57.2957763671875D));
++ this.setXRot((float) (Mth.atan2(vec3d.y, vec3d.horizontalDistance()) * 57.2957763671875D));
+ this.yRotO = this.getYRot();
+ this.xRotO = this.getXRot();
+ }
+
+ @Override
+ protected void defineSynchedData() {
+- this.getEntityData().define(DATA_HOOKED_ENTITY, 0);
+- this.getEntityData().define(DATA_BITING, false);
++ this.getEntityData().define(FishingHook.DATA_HOOKED_ENTITY, 0);
++ this.getEntityData().define(FishingHook.DATA_BITING, false);
+ }
+
+ @Override
+ public void onSyncedDataUpdated(EntityDataAccessor<?> key) {
+- if (DATA_HOOKED_ENTITY.equals(key)) {
+- int i = this.getEntityData().get(DATA_HOOKED_ENTITY);
++ if (FishingHook.DATA_HOOKED_ENTITY.equals(key)) {
++ int i = (Integer) this.getEntityData().get(FishingHook.DATA_HOOKED_ENTITY);
++
+ this.hookedIn = i > 0 ? this.level().getEntity(i - 1) : null;
+ }
+
+- if (DATA_BITING.equals(key)) {
+- this.biting = this.getEntityData().get(DATA_BITING);
++ if (FishingHook.DATA_BITING.equals(key)) {
++ this.biting = (Boolean) this.getEntityData().get(FishingHook.DATA_BITING);
+ if (this.biting) {
+- this.setDeltaMovement(this.getDeltaMovement().x, (double)(-0.4F * Mth.nextFloat(this.syncronizedRandom, 0.6F, 1.0F)), this.getDeltaMovement().z);
++ this.setDeltaMovement(this.getDeltaMovement().x, (double) (-0.4F * Mth.nextFloat(this.syncronizedRandom, 0.6F, 1.0F)), this.getDeltaMovement().z);
+ }
+ }
+
+@@ -128,24 +149,25 @@
+
+ @Override
+ public boolean shouldRenderAtSqrDistance(double distance) {
+- double d = 64.0;
+- return distance < 4096.0;
++ double d1 = 64.0D;
++
++ return distance < 4096.0D;
+ }
+
+ @Override
+- public void lerpTo(double d, double d1, double d2, float f, float f1, int i) {
+- }
++ public void lerpTo(double d0, double d1, double d2, float f, float f1, int i) {}
+
+ @Override
+ public void tick() {
+ this.syncronizedRandom.setSeed(this.getUUID().getLeastSignificantBits() ^ this.level().getGameTime());
+ super.tick();
+- Player playerOwner = this.getPlayerOwner();
+- if (playerOwner == null) {
++ net.minecraft.world.entity.player.Player entityhuman = this.getPlayerOwner();
++
++ if (entityhuman == null) {
+ this.discard();
+- } else if (this.level().isClientSide || !this.shouldStopFishing(playerOwner)) {
++ } else if (this.level().isClientSide || !this.shouldStopFishing(entityhuman)) {
+ if (this.onGround()) {
+- this.life++;
++ ++this.life;
+ if (this.life >= 1200) {
+ this.discard();
+ return;
+@@ -155,66 +177,66 @@
+ }
+
+ float f = 0.0F;
+- BlockPos blockPos = this.blockPosition();
+- FluidState fluidState = this.level().getFluidState(blockPos);
+- if (fluidState.is(FluidTags.WATER)) {
+- f = fluidState.getHeight(this.level(), blockPos);
++ BlockPos blockposition = this.blockPosition();
++ FluidState fluid = this.level().getFluidState(blockposition);
++
++ if (fluid.is(FluidTags.WATER)) {
++ f = fluid.getHeight(this.level(), blockposition);
+ }
+
+ boolean flag = f > 0.0F;
+- if (this.currentState == FishingHook.FishHookState.FLYING) {
++
++ if (this.currentState == FishingHook.HookState.FLYING) {
+ if (this.hookedIn != null) {
+ this.setDeltaMovement(Vec3.ZERO);
+- this.currentState = FishingHook.FishHookState.HOOKED_IN_ENTITY;
++ this.currentState = FishingHook.HookState.HOOKED_IN_ENTITY;
+ return;
+ }
+
+ if (flag) {
+- this.setDeltaMovement(this.getDeltaMovement().multiply(0.3, 0.2, 0.3));
+- this.currentState = FishingHook.FishHookState.BOBBING;
++ this.setDeltaMovement(this.getDeltaMovement().multiply(0.3D, 0.2D, 0.3D));
++ this.currentState = FishingHook.HookState.BOBBING;
+ return;
+ }
+
+ this.checkCollision();
+ } else {
+- if (this.currentState == FishingHook.FishHookState.HOOKED_IN_ENTITY) {
++ if (this.currentState == FishingHook.HookState.HOOKED_IN_ENTITY) {
+ if (this.hookedIn != null) {
+ if (!this.hookedIn.isRemoved() && this.hookedIn.level().dimension() == this.level().dimension()) {
+- this.setPos(this.hookedIn.getX(), this.hookedIn.getY(0.8), this.hookedIn.getZ());
++ this.setPos(this.hookedIn.getX(), this.hookedIn.getY(0.8D), this.hookedIn.getZ());
+ } else {
+- this.setHookedEntity(null);
+- this.currentState = FishingHook.FishHookState.FLYING;
++ this.setHookedEntity((Entity) null);
++ this.currentState = FishingHook.HookState.FLYING;
+ }
+ }
+
+ return;
+ }
+
+- if (this.currentState == FishingHook.FishHookState.BOBBING) {
+- Vec3 deltaMovement = this.getDeltaMovement();
+- double d = this.getY() + deltaMovement.y - (double)blockPos.getY() - (double)f;
+- if (Math.abs(d) < 0.01) {
+- d += Math.signum(d) * 0.1;
++ if (this.currentState == FishingHook.HookState.BOBBING) {
++ Vec3 vec3d = this.getDeltaMovement();
++ double d0 = this.getY() + vec3d.y - (double) blockposition.getY() - (double) f;
++
++ if (Math.abs(d0) < 0.01D) {
++ d0 += Math.signum(d0) * 0.1D;
+ }
+
+- this.setDeltaMovement(deltaMovement.x * 0.9, deltaMovement.y - d * (double)this.random.nextFloat() * 0.2, deltaMovement.z * 0.9);
++ this.setDeltaMovement(vec3d.x * 0.9D, vec3d.y - d0 * (double) this.random.nextFloat() * 0.2D, vec3d.z * 0.9D);
+ if (this.nibble <= 0 && this.timeUntilHooked <= 0) {
+ this.openWater = true;
+ } else {
+- this.openWater = this.openWater && this.outOfWaterTime < 10 && this.calculateOpenWater(blockPos);
++ this.openWater = this.openWater && this.outOfWaterTime < 10 && this.calculateOpenWater(blockposition);
+ }
+
+ if (flag) {
+ this.outOfWaterTime = Math.max(0, this.outOfWaterTime - 1);
+ if (this.biting) {
+- this.setDeltaMovement(
+- this.getDeltaMovement()
+- .add(0.0, -0.1 * (double)this.syncronizedRandom.nextFloat() * (double)this.syncronizedRandom.nextFloat(), 0.0)
+- );
++ this.setDeltaMovement(this.getDeltaMovement().add(0.0D, -0.1D * (double) this.syncronizedRandom.nextFloat() * (double) this.syncronizedRandom.nextFloat(), 0.0D));
+ }
+
+ if (!this.level().isClientSide) {
+- this.catchingFish(blockPos);
++ this.catchingFish(blockposition);
+ }
+ } else {
+ this.outOfWaterTime = Math.min(10, this.outOfWaterTime + 1);
+@@ -222,28 +244,30 @@
+ }
+ }
+
+- if (!fluidState.is(FluidTags.WATER)) {
+- this.setDeltaMovement(this.getDeltaMovement().add(0.0, -0.03, 0.0));
++ if (!fluid.is(FluidTags.WATER)) {
++ this.setDeltaMovement(this.getDeltaMovement().add(0.0D, -0.03D, 0.0D));
+ }
+
+- this.move(MoverType.SELF, this.getDeltaMovement());
++ this.move(EnumMoveType.SELF, this.getDeltaMovement());
+ this.updateRotation();
+- if (this.currentState == FishingHook.FishHookState.FLYING && (this.onGround() || this.horizontalCollision)) {
++ if (this.currentState == FishingHook.HookState.FLYING && (this.onGround() || this.horizontalCollision)) {
+ this.setDeltaMovement(Vec3.ZERO);
+ }
+
+- double d1 = 0.92;
+- this.setDeltaMovement(this.getDeltaMovement().scale(0.92));
++ double d1 = 0.92D;
++
++ this.setDeltaMovement(this.getDeltaMovement().scale(0.92D));
+ this.reapplyPosition();
+ }
+ }
+
+- private boolean shouldStopFishing(Player player) {
+- ItemStack mainHandItem = player.getMainHandItem();
+- ItemStack offhandItem = player.getOffhandItem();
+- boolean isFishingRod = mainHandItem.is(Items.FISHING_ROD);
+- boolean isFishingRod1 = offhandItem.is(Items.FISHING_ROD);
+- if (!player.isRemoved() && player.isAlive() && (isFishingRod || isFishingRod1) && !(this.distanceToSqr(player) > 1024.0)) {
++ private boolean shouldStopFishing(net.minecraft.world.entity.player.Player player) {
++ ItemStack itemstack = player.getMainHandItem();
++ ItemStack itemstack1 = player.getOffhandItem();
++ boolean flag = itemstack.is(Items.FISHING_ROD);
++ boolean flag1 = itemstack1.is(Items.FISHING_ROD);
++
++ if (!player.isRemoved() && player.isAlive() && (flag || flag1) && this.distanceToSqr((Entity) player) <= 1024.0D) {
+ return false;
+ } else {
+ this.discard();
+@@ -252,8 +276,9 @@
+ }
+
+ private void checkCollision() {
+- HitResult hitResultOnMoveVector = ProjectileUtil.getHitResultOnMoveVector(this, this::canHitEntity);
+- this.onHit(hitResultOnMoveVector);
++ HitResult movingobjectposition = ProjectileUtil.getHitResultOnMoveVector(this, this::canHitEntity);
++
++ this.preOnHit(movingobjectposition); // CraftBukkit - projectile hit event
+ }
+
+ @Override
+@@ -267,6 +292,7 @@
+ if (!this.level().isClientSide) {
+ this.setHookedEntity(result.getEntity());
+ }
++
+ }
+
+ @Override
+@@ -275,153 +301,162 @@
+ this.setDeltaMovement(this.getDeltaMovement().normalize().scale(result.distanceTo(this)));
+ }
+
+- private void setHookedEntity(@Nullable Entity hookedEntity) {
++ public void setHookedEntity(@Nullable Entity hookedEntity) {
+ this.hookedIn = hookedEntity;
+- this.getEntityData().set(DATA_HOOKED_ENTITY, hookedEntity == null ? 0 : hookedEntity.getId() + 1);
++ this.getEntityData().set(FishingHook.DATA_HOOKED_ENTITY, hookedEntity == null ? 0 : hookedEntity.getId() + 1);
+ }
+
+ private void catchingFish(BlockPos pos) {
+- ServerLevel serverLevel = (ServerLevel)this.level();
++ ServerLevel worldserver = (ServerLevel) this.level();
+ int i = 1;
+- BlockPos blockPos = pos.above();
+- if (this.random.nextFloat() < 0.25F && this.level().isRainingAt(blockPos)) {
+- i++;
++ BlockPos blockposition1 = pos.above();
++
++ if (this.rainInfluenced && this.random.nextFloat() < 0.25F && this.level().isRainingAt(blockposition1)) { // CraftBukkit
++ ++i;
+ }
+
+- if (this.random.nextFloat() < 0.5F && !this.level().canSeeSky(blockPos)) {
+- i--;
++ if (this.skyInfluenced && this.random.nextFloat() < 0.5F && !this.level().canSeeSky(blockposition1)) { // CraftBukkit
++ --i;
+ }
+
+ if (this.nibble > 0) {
+- this.nibble--;
++ --this.nibble;
+ if (this.nibble <= 0) {
+ this.timeUntilLured = 0;
+ this.timeUntilHooked = 0;
+- this.getEntityData().set(DATA_BITING, false);
++ this.getEntityData().set(FishingHook.DATA_BITING, false);
++ // CraftBukkit start
++ PlayerFishEvent playerFishEvent = new PlayerFishEvent((Player) this.getPlayerOwner().getBukkitEntity(), null, (FishHook) this.getBukkitEntity(), PlayerFishEvent.State.FAILED_ATTEMPT);
++ this.level().getCraftServer().getPluginManager().callEvent(playerFishEvent);
++ // CraftBukkit end
+ }
+- } else if (this.timeUntilHooked > 0) {
+- this.timeUntilHooked -= i;
++ } else {
++ float f;
++ float f1;
++ float f2;
++ double d0;
++ double d1;
++ double d2;
++ IBlockData iblockdata;
++
+ if (this.timeUntilHooked > 0) {
+- this.fishAngle = this.fishAngle + (float)this.random.triangle(0.0, 9.188);
+- float f = this.fishAngle * (float) (Math.PI / 180.0);
+- float sin = Mth.sin(f);
+- float cos = Mth.cos(f);
+- double d = this.getX() + (double)(sin * (float)this.timeUntilHooked * 0.1F);
+- double d1 = (double)((float)Mth.floor(this.getY()) + 1.0F);
+- double d2 = this.getZ() + (double)(cos * (float)this.timeUntilHooked * 0.1F);
+- BlockState blockState = serverLevel.getBlockState(BlockPos.containing(d, d1 - 1.0, d2));
+- if (blockState.is(Blocks.WATER)) {
+- if (this.random.nextFloat() < 0.15F) {
+- serverLevel.sendParticles(ParticleTypes.BUBBLE, d, d1 - 0.1F, d2, 1, (double)sin, 0.1, (double)cos, 0.0);
++ this.timeUntilHooked -= i;
++ if (this.timeUntilHooked > 0) {
++ this.fishAngle += (float) this.random.triangle(0.0D, 9.188D);
++ f = this.fishAngle * 0.017453292F;
++ f1 = Mth.sin(f);
++ f2 = Mth.cos(f);
++ d0 = this.getX() + (double) (f1 * (float) this.timeUntilHooked * 0.1F);
++ d1 = (double) ((float) Mth.floor(this.getY()) + 1.0F);
++ d2 = this.getZ() + (double) (f2 * (float) this.timeUntilHooked * 0.1F);
++ iblockdata = worldserver.getBlockState(BlockPos.containing(d0, d1 - 1.0D, d2));
++ if (iblockdata.is(Blocks.WATER)) {
++ if (this.random.nextFloat() < 0.15F) {
++ worldserver.sendParticles(ParticleTypes.BUBBLE, d0, d1 - 0.10000000149011612D, d2, 1, (double) f1, 0.1D, (double) f2, 0.0D);
++ }
++
++ float f3 = f1 * 0.04F;
++ float f4 = f2 * 0.04F;
++
++ worldserver.sendParticles(ParticleTypes.FISHING, d0, d1, d2, 0, (double) f4, 0.01D, (double) (-f3), 1.0D);
++ worldserver.sendParticles(ParticleTypes.FISHING, d0, d1, d2, 0, (double) (-f4), 0.01D, (double) f3, 1.0D);
+ }
++ } else {
++ // CraftBukkit start
++ PlayerFishEvent playerFishEvent = new PlayerFishEvent((Player) this.getPlayerOwner().getBukkitEntity(), null, (FishHook) this.getBukkitEntity(), PlayerFishEvent.State.BITE);
++ this.level().getCraftServer().getPluginManager().callEvent(playerFishEvent);
++ if (playerFishEvent.isCancelled()) {
++ return;
++ }
++ // CraftBukkit end
++ this.playSound(SoundEvents.FISHING_BOBBER_SPLASH, 0.25F, 1.0F + (this.random.nextFloat() - this.random.nextFloat()) * 0.4F);
++ double d3 = this.getY() + 0.5D;
+
+- float f1 = sin * 0.04F;
+- float f2 = cos * 0.04F;
+- serverLevel.sendParticles(ParticleTypes.FISHING, d, d1, d2, 0, (double)f2, 0.01, (double)(-f1), 1.0);
+- serverLevel.sendParticles(ParticleTypes.FISHING, d, d1, d2, 0, (double)(-f2), 0.01, (double)f1, 1.0);
++ worldserver.sendParticles(ParticleTypes.BUBBLE, this.getX(), d3, this.getZ(), (int) (1.0F + this.getBbWidth() * 20.0F), (double) this.getBbWidth(), 0.0D, (double) this.getBbWidth(), 0.20000000298023224D);
++ worldserver.sendParticles(ParticleTypes.FISHING, this.getX(), d3, this.getZ(), (int) (1.0F + this.getBbWidth() * 20.0F), (double) this.getBbWidth(), 0.0D, (double) this.getBbWidth(), 0.20000000298023224D);
++ this.nibble = Mth.nextInt(this.random, 20, 40);
++ this.getEntityData().set(FishingHook.DATA_BITING, true);
+ }
+- } else {
+- this.playSound(SoundEvents.FISHING_BOBBER_SPLASH, 0.25F, 1.0F + (this.random.nextFloat() - this.random.nextFloat()) * 0.4F);
+- double d3 = this.getY() + 0.5;
+- serverLevel.sendParticles(
+- ParticleTypes.BUBBLE,
+- this.getX(),
+- d3,
+- this.getZ(),
+- (int)(1.0F + this.getBbWidth() * 20.0F),
+- (double)this.getBbWidth(),
+- 0.0,
+- (double)this.getBbWidth(),
+- 0.2F
+- );
+- serverLevel.sendParticles(
+- ParticleTypes.FISHING,
+- this.getX(),
+- d3,
+- this.getZ(),
+- (int)(1.0F + this.getBbWidth() * 20.0F),
+- (double)this.getBbWidth(),
+- 0.0,
+- (double)this.getBbWidth(),
+- 0.2F
+- );
+- this.nibble = Mth.nextInt(this.random, 20, 40);
+- this.getEntityData().set(DATA_BITING, true);
+- }
+- } else if (this.timeUntilLured > 0) {
+- this.timeUntilLured -= i;
+- float f = 0.15F;
+- if (this.timeUntilLured < 20) {
+- f += (float)(20 - this.timeUntilLured) * 0.05F;
+- } else if (this.timeUntilLured < 40) {
+- f += (float)(40 - this.timeUntilLured) * 0.02F;
+- } else if (this.timeUntilLured < 60) {
+- f += (float)(60 - this.timeUntilLured) * 0.01F;
+- }
++ } else if (this.timeUntilLured > 0) {
++ this.timeUntilLured -= i;
++ f = 0.15F;
++ if (this.timeUntilLured < 20) {
++ f += (float) (20 - this.timeUntilLured) * 0.05F;
++ } else if (this.timeUntilLured < 40) {
++ f += (float) (40 - this.timeUntilLured) * 0.02F;
++ } else if (this.timeUntilLured < 60) {
++ f += (float) (60 - this.timeUntilLured) * 0.01F;
++ }
+
+- if (this.random.nextFloat() < f) {
+- float sin = Mth.nextFloat(this.random, 0.0F, 360.0F) * (float) (Math.PI / 180.0);
+- float cos = Mth.nextFloat(this.random, 25.0F, 60.0F);
+- double d = this.getX() + (double)(Mth.sin(sin) * cos) * 0.1;
+- double d1 = (double)((float)Mth.floor(this.getY()) + 1.0F);
+- double d2 = this.getZ() + (double)(Mth.cos(sin) * cos) * 0.1;
+- BlockState blockState = serverLevel.getBlockState(BlockPos.containing(d, d1 - 1.0, d2));
+- if (blockState.is(Blocks.WATER)) {
+- serverLevel.sendParticles(ParticleTypes.SPLASH, d, d1, d2, 2 + this.random.nextInt(2), 0.1F, 0.0, 0.1F, 0.0);
++ if (this.random.nextFloat() < f) {
++ f1 = Mth.nextFloat(this.random, 0.0F, 360.0F) * 0.017453292F;
++ f2 = Mth.nextFloat(this.random, 25.0F, 60.0F);
++ d0 = this.getX() + (double) (Mth.sin(f1) * f2) * 0.1D;
++ d1 = (double) ((float) Mth.floor(this.getY()) + 1.0F);
++ d2 = this.getZ() + (double) (Mth.cos(f1) * f2) * 0.1D;
++ iblockdata = worldserver.getBlockState(BlockPos.containing(d0, d1 - 1.0D, d2));
++ if (iblockdata.is(Blocks.WATER)) {
++ worldserver.sendParticles(ParticleTypes.SPLASH, d0, d1, d2, 2 + this.random.nextInt(2), 0.10000000149011612D, 0.0D, 0.10000000149011612D, 0.0D);
++ }
+ }
+- }
+
+- if (this.timeUntilLured <= 0) {
+- this.fishAngle = Mth.nextFloat(this.random, 0.0F, 360.0F);
+- this.timeUntilHooked = Mth.nextInt(this.random, 20, 80);
++ if (this.timeUntilLured <= 0) {
++ // CraftBukkit start - logic to modify fishing wait time, lure time, and lure angle
++ this.fishAngle = Mth.nextFloat(this.random, this.minLureAngle, this.maxLureAngle);
++ this.timeUntilHooked = Mth.nextInt(this.random, this.minLureTime, this.maxLureTime);
++ // CraftBukkit end
++ }
++ } else {
++ // CraftBukkit start - logic to modify fishing wait time
++ this.timeUntilLured = Mth.nextInt(this.random, this.minWaitTime, this.maxWaitTime);
++ this.timeUntilLured -= (this.applyLure) ? this.lureSpeed * 20 * 5 : 0;
++ // CraftBukkit end
+ }
+- } else {
+- this.timeUntilLured = Mth.nextInt(this.random, 100, 600);
+- this.timeUntilLured = this.timeUntilLured - this.lureSpeed * 20 * 5;
+ }
++
+ }
+
+ private boolean calculateOpenWater(BlockPos pos) {
+- FishingHook.OpenWaterType openWaterType = FishingHook.OpenWaterType.INVALID;
++ FishingHook.WaterPosition entityfishinghook_waterposition = FishingHook.WaterPosition.INVALID;
+
+- for (int i = -1; i <= 2; i++) {
+- FishingHook.OpenWaterType openWaterTypeForArea = this.getOpenWaterTypeForArea(pos.offset(-2, i, -2), pos.offset(2, i, 2));
+- switch (openWaterTypeForArea) {
++ for (int i = -1; i <= 2; ++i) {
++ FishingHook.WaterPosition entityfishinghook_waterposition1 = this.getOpenWaterTypeForArea(pos.offset(-2, i, -2), pos.offset(2, i, 2));
++
++ switch (entityfishinghook_waterposition1) {
+ case INVALID:
+ return false;
+ case ABOVE_WATER:
+- if (openWaterType == FishingHook.OpenWaterType.INVALID) {
++ if (entityfishinghook_waterposition == FishingHook.WaterPosition.INVALID) {
+ return false;
+ }
+ break;
+ case INSIDE_WATER:
+- if (openWaterType == FishingHook.OpenWaterType.ABOVE_WATER) {
++ if (entityfishinghook_waterposition == FishingHook.WaterPosition.ABOVE_WATER) {
+ return false;
+ }
+ }
+
+- openWaterType = openWaterTypeForArea;
++ entityfishinghook_waterposition = entityfishinghook_waterposition1;
+ }
+
+ return true;
+ }
+
+- private FishingHook.OpenWaterType getOpenWaterTypeForArea(BlockPos firstPos, BlockPos secondPos) {
+- return BlockPos.betweenClosedStream(firstPos, secondPos)
+- .map(this::getOpenWaterTypeForBlock)
+- .reduce((firstType, secondType) -> firstType == secondType ? firstType : FishingHook.OpenWaterType.INVALID)
+- .orElse(FishingHook.OpenWaterType.INVALID);
++ private FishingHook.WaterPosition getOpenWaterTypeForArea(BlockPos firstPos, BlockPos secondPos) {
++ return (FishingHook.WaterPosition) BlockPos.betweenClosedStream(firstPos, secondPos).map(this::getOpenWaterTypeForBlock).reduce((entityfishinghook_waterposition, entityfishinghook_waterposition1) -> {
++ return entityfishinghook_waterposition == entityfishinghook_waterposition1 ? entityfishinghook_waterposition : FishingHook.WaterPosition.INVALID;
++ }).orElse(FishingHook.WaterPosition.INVALID);
+ }
+
+- private FishingHook.OpenWaterType getOpenWaterTypeForBlock(BlockPos pos) {
+- BlockState blockState = this.level().getBlockState(pos);
+- if (!blockState.isAir() && !blockState.is(Blocks.LILY_PAD)) {
+- FluidState fluidState = blockState.getFluidState();
+- return fluidState.is(FluidTags.WATER) && fluidState.isSource() && blockState.getCollisionShape(this.level(), pos).isEmpty()
+- ? FishingHook.OpenWaterType.INSIDE_WATER
+- : FishingHook.OpenWaterType.INVALID;
++ private FishingHook.WaterPosition getOpenWaterTypeForBlock(BlockPos pos) {
++ IBlockData iblockdata = this.level().getBlockState(pos);
++
++ if (!iblockdata.isAir() && !iblockdata.is(Blocks.LILY_PAD)) {
++ FluidState fluid = iblockdata.getFluidState();
++
++ return fluid.is(FluidTags.WATER) && fluid.isSource() && iblockdata.getCollisionShape(this.level(), pos).isEmpty() ? FishingHook.WaterPosition.INSIDE_WATER : FishingHook.WaterPosition.INVALID;
+ } else {
+- return FishingHook.OpenWaterType.ABOVE_WATER;
++ return FishingHook.WaterPosition.ABOVE_WATER;
+ }
+ }
+
+@@ -430,58 +465,90 @@
+ }
+
+ @Override
+- public void addAdditionalSaveData(CompoundTag compound) {
+- }
++ public void addAdditionalSaveData(CompoundTag compound) {}
+
+ @Override
+- public void readAdditionalSaveData(CompoundTag compound) {
+- }
++ public void readAdditionalSaveData(CompoundTag compound) {}
+
+ public int retrieve(ItemStack stack) {
+- Player playerOwner = this.getPlayerOwner();
+- if (!this.level().isClientSide && playerOwner != null && !this.shouldStopFishing(playerOwner)) {
++ net.minecraft.world.entity.player.Player entityhuman = this.getPlayerOwner();
++
++ if (!this.level().isClientSide && entityhuman != null && !this.shouldStopFishing(entityhuman)) {
+ int i = 0;
++
+ if (this.hookedIn != null) {
++ // CraftBukkit start
++ PlayerFishEvent playerFishEvent = new PlayerFishEvent((Player) entityhuman.getBukkitEntity(), this.hookedIn.getBukkitEntity(), (FishHook) this.getBukkitEntity(), PlayerFishEvent.State.CAUGHT_ENTITY);
++ this.level().getCraftServer().getPluginManager().callEvent(playerFishEvent);
++
++ if (playerFishEvent.isCancelled()) {
++ return 0;
++ }
++ // CraftBukkit end
+ this.pullEntity(this.hookedIn);
+- CriteriaTriggers.FISHING_ROD_HOOKED.trigger((ServerPlayer)playerOwner, stack, this, Collections.emptyList());
+- this.level().broadcastEntityEvent(this, (byte)31);
++ CriteriaTriggers.FISHING_ROD_HOOKED.trigger((ServerPlayer) entityhuman, stack, this, Collections.emptyList());
++ this.level().broadcastEntityEvent(this, (byte) 31);
+ i = this.hookedIn instanceof ItemEntity ? 3 : 5;
+ } else if (this.nibble > 0) {
+- LootParams lootParams = new LootParams.Builder((ServerLevel)this.level())
+- .withParameter(LootContextParams.ORIGIN, this.position())
+- .withParameter(LootContextParams.TOOL, stack)
+- .withParameter(LootContextParams.THIS_ENTITY, this)
+- .withLuck((float)this.luck + playerOwner.getLuck())
+- .create(LootContextParamSets.FISHING);
+- LootTable lootTable = this.level().getServer().getLootData().getLootTable(BuiltInLootTables.FISHING);
+- List<ItemStack> randomItems = lootTable.getRandomItems(lootParams);
+- CriteriaTriggers.FISHING_ROD_HOOKED.trigger((ServerPlayer)playerOwner, stack, this, randomItems);
++ LootParams lootparams = (new LootParams.Builder((ServerLevel) this.level())).withParameter(LootContextParams.ORIGIN, this.position()).withParameter(LootContextParams.TOOL, stack).withParameter(LootContextParams.THIS_ENTITY, this).withLuck((float) this.luck + entityhuman.getLuck()).create(LootContextParamSets.FISHING);
++ LootTable loottable = this.level().getServer().getLootData().getLootTable(BuiltInLootTables.FISHING);
++ List<ItemStack> list = loottable.getRandomItems(lootparams);
+
+- for (ItemStack itemStack : randomItems) {
+- ItemEntity itemEntity = new ItemEntity(this.level(), this.getX(), this.getY(), this.getZ(), itemStack);
+- double d = playerOwner.getX() - this.getX();
+- double d1 = playerOwner.getY() - this.getY();
+- double d2 = playerOwner.getZ() - this.getZ();
+- double d3 = 0.1;
+- itemEntity.setDeltaMovement(d * 0.1, d1 * 0.1 + Math.sqrt(Math.sqrt(d * d + d1 * d1 + d2 * d2)) * 0.08, d2 * 0.1);
+- this.level().addFreshEntity(itemEntity);
+- playerOwner.level()
+- .addFreshEntity(
+- new ExperienceOrb(
+- playerOwner.level(), playerOwner.getX(), playerOwner.getY() + 0.5, playerOwner.getZ() + 0.5, this.random.nextInt(6) + 1
+- )
+- );
+- if (itemStack.is(ItemTags.FISHES)) {
+- playerOwner.awardStat(Stats.FISH_CAUGHT, 1);
++ CriteriaTriggers.FISHING_ROD_HOOKED.trigger((ServerPlayer) entityhuman, stack, this, list);
++ Iterator iterator = list.iterator();
++
++ while (iterator.hasNext()) {
++ ItemStack itemstack1 = (ItemStack) iterator.next();
++ ItemEntity entityitem = new ItemEntity(this.level(), this.getX(), this.getY(), this.getZ(), itemstack1);
++ // CraftBukkit start
++ PlayerFishEvent playerFishEvent = new PlayerFishEvent((Player) entityhuman.getBukkitEntity(), entityitem.getBukkitEntity(), (FishHook) this.getBukkitEntity(), PlayerFishEvent.State.CAUGHT_FISH);
++ playerFishEvent.setExpToDrop(this.random.nextInt(6) + 1);
++ this.level().getCraftServer().getPluginManager().callEvent(playerFishEvent);
++
++ if (playerFishEvent.isCancelled()) {
++ return 0;
+ }
++ // CraftBukkit end
++ double d0 = entityhuman.getX() - this.getX();
++ double d1 = entityhuman.getY() - this.getY();
++ double d2 = entityhuman.getZ() - this.getZ();
++ double d3 = 0.1D;
++
++ entityitem.setDeltaMovement(d0 * 0.1D, d1 * 0.1D + Math.sqrt(Math.sqrt(d0 * d0 + d1 * d1 + d2 * d2)) * 0.08D, d2 * 0.1D);
++ this.level().addFreshEntity(entityitem);
++ // CraftBukkit start - this.random.nextInt(6) + 1 -> playerFishEvent.getExpToDrop()
++ if (playerFishEvent.getExpToDrop() > 0) {
++ entityhuman.level().addFreshEntity(new ExperienceOrb(entityhuman.level(), entityhuman.getX(), entityhuman.getY() + 0.5D, entityhuman.getZ() + 0.5D, playerFishEvent.getExpToDrop()));
++ }
++ // CraftBukkit end
++ if (itemstack1.is(ItemTags.FISHES)) {
++ entityhuman.awardStat(Stats.FISH_CAUGHT, 1);
++ }
+ }
+
+ i = 1;
+ }
+
+ if (this.onGround()) {
++ // CraftBukkit start
++ PlayerFishEvent playerFishEvent = new PlayerFishEvent((Player) entityhuman.getBukkitEntity(), null, (FishHook) this.getBukkitEntity(), PlayerFishEvent.State.IN_GROUND);
++ this.level().getCraftServer().getPluginManager().callEvent(playerFishEvent);
++
++ if (playerFishEvent.isCancelled()) {
++ return 0;
++ }
++ // CraftBukkit end
+ i = 2;
+ }
++ // CraftBukkit start
++ if (i == 0) {
++ PlayerFishEvent playerFishEvent = new PlayerFishEvent((Player) entityhuman.getBukkitEntity(), null, (FishHook) this.getBukkitEntity(), PlayerFishEvent.State.REEL_IN);
++ this.level().getCraftServer().getPluginManager().callEvent(playerFishEvent);
++ if (playerFishEvent.isCancelled()) {
++ return 0;
++ }
++ }
++ // CraftBukkit end
+
+ this.discard();
+ return i;
+@@ -492,18 +559,20 @@
+
+ @Override
+ public void handleEntityEvent(byte id) {
+- if (id == 31 && this.level().isClientSide && this.hookedIn instanceof Player && ((Player)this.hookedIn).isLocalPlayer()) {
++ if (id == 31 && this.level().isClientSide && this.hookedIn instanceof net.minecraft.world.entity.player.Player && ((net.minecraft.world.entity.player.Player) this.hookedIn).isLocalPlayer()) {
+ this.pullEntity(this.hookedIn);
+ }
+
+ super.handleEntityEvent(id);
+ }
+
+- protected void pullEntity(Entity entity) {
+- Entity owner = this.getOwner();
+- if (owner != null) {
+- Vec3 vec3 = new Vec3(owner.getX() - this.getX(), owner.getY() - this.getY(), owner.getZ() - this.getZ()).scale(0.1);
+- entity.setDeltaMovement(entity.getDeltaMovement().add(vec3));
++ public void pullEntity(Entity entity) {
++ Entity entity1 = this.getOwner();
++
++ if (entity1 != null) {
++ Vec3 vec3d = (new Vec3(entity1.getX() - this.getX(), entity1.getY() - this.getY(), entity1.getZ() - this.getZ())).scale(0.1D);
++
++ entity.setDeltaMovement(entity.getDeltaMovement().add(vec3d));
+ }
+ }
+
+@@ -514,13 +583,13 @@
+
+ @Override
+ public void remove(Entity.RemovalReason reason) {
+- this.updateOwnerInfo(null);
++ this.updateOwnerInfo((FishingHook) null);
+ super.remove(reason);
+ }
+
+ @Override
+ public void onClientRemoval() {
+- this.updateOwnerInfo(null);
++ this.updateOwnerInfo((FishingHook) null);
+ }
+
+ @Override
+@@ -530,16 +599,19 @@
+ }
+
+ private void updateOwnerInfo(@Nullable FishingHook fishingHook) {
+- Player playerOwner = this.getPlayerOwner();
+- if (playerOwner != null) {
+- playerOwner.fishing = fishingHook;
++ net.minecraft.world.entity.player.Player entityhuman = this.getPlayerOwner();
++
++ if (entityhuman != null) {
++ entityhuman.fishing = fishingHook;
+ }
++
+ }
+
+ @Nullable
+- public Player getPlayerOwner() {
+- Entity owner = this.getOwner();
+- return owner instanceof Player ? (Player)owner : null;
++ public net.minecraft.world.entity.player.Player getPlayerOwner() {
++ Entity entity = this.getOwner();
++
++ return entity instanceof net.minecraft.world.entity.player.Player ? (net.minecraft.world.entity.player.Player) entity : null;
+ }
+
+ @Nullable
+@@ -554,29 +626,34 @@
+
+ @Override
+ public Packet<ClientGamePacketListener> getAddEntityPacket() {
+- Entity owner = this.getOwner();
+- return new ClientboundAddEntityPacket(this, owner == null ? this.getId() : owner.getId());
++ Entity entity = this.getOwner();
++
++ return new ClientboundAddEntityPacket(this, entity == null ? this.getId() : entity.getId());
+ }
+
+ @Override
+ public void recreateFromPacket(ClientboundAddEntityPacket packet) {
+ super.recreateFromPacket(packet);
+ if (this.getPlayerOwner() == null) {
+- int data = packet.getData();
+- LOGGER.error("Failed to recreate fishing hook on client. {} (id: {}) is not a valid owner.", this.level().getEntity(data), data);
++ int i = packet.getData();
++
++ FishingHook.LOGGER.error("Failed to recreate fishing hook on client. {} (id: {}) is not a valid owner.", this.level().getEntity(i), i);
+ this.kill();
+ }
++
+ }
+
+- static enum FishHookState {
+- FLYING,
+- HOOKED_IN_ENTITY,
+- BOBBING;
++ public static enum HookState {
++
++ FLYING, HOOKED_IN_ENTITY, BOBBING;
++
++ private HookState() {}
+ }
+
+- static enum OpenWaterType {
+- ABOVE_WATER,
+- INSIDE_WATER,
+- INVALID;
++ private static enum WaterPosition {
++
++ ABOVE_WATER, INSIDE_WATER, INVALID;
++
++ private WaterPosition() {}
+ }
+ }