581 lines
23 KiB
Java
581 lines
23 KiB
Java
|
package net.minecraft.world.entity.projectile;
|
||
|
|
||
|
import com.mojang.logging.LogUtils;
|
||
|
import java.util.Collections;
|
||
|
import java.util.List;
|
||
|
import javax.annotation.Nullable;
|
||
|
import net.minecraft.advancements.CriteriaTriggers;
|
||
|
import net.minecraft.core.BlockPos;
|
||
|
import net.minecraft.core.particles.ParticleTypes;
|
||
|
import net.minecraft.nbt.CompoundTag;
|
||
|
import net.minecraft.network.protocol.Packet;
|
||
|
import net.minecraft.network.protocol.game.ClientGamePacketListener;
|
||
|
import net.minecraft.network.protocol.game.ClientboundAddEntityPacket;
|
||
|
import net.minecraft.network.syncher.EntityDataAccessor;
|
||
|
import net.minecraft.network.syncher.EntityDataSerializers;
|
||
|
import net.minecraft.network.syncher.SynchedEntityData;
|
||
|
import net.minecraft.server.level.ServerEntity;
|
||
|
import net.minecraft.server.level.ServerLevel;
|
||
|
import net.minecraft.server.level.ServerPlayer;
|
||
|
import net.minecraft.sounds.SoundEvents;
|
||
|
import net.minecraft.stats.Stats;
|
||
|
import net.minecraft.tags.FluidTags;
|
||
|
import net.minecraft.tags.ItemTags;
|
||
|
import net.minecraft.util.Mth;
|
||
|
import net.minecraft.util.RandomSource;
|
||
|
import net.minecraft.world.entity.Entity;
|
||
|
import net.minecraft.world.entity.EntityType;
|
||
|
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.material.FluidState;
|
||
|
import net.minecraft.world.level.storage.loot.BuiltInLootTables;
|
||
|
import net.minecraft.world.level.storage.loot.LootParams;
|
||
|
import net.minecraft.world.level.storage.loot.LootTable;
|
||
|
import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets;
|
||
|
import net.minecraft.world.level.storage.loot.parameters.LootContextParams;
|
||
|
import net.minecraft.world.phys.BlockHitResult;
|
||
|
import net.minecraft.world.phys.EntityHitResult;
|
||
|
import net.minecraft.world.phys.HitResult;
|
||
|
import net.minecraft.world.phys.Vec3;
|
||
|
import org.slf4j.Logger;
|
||
|
|
||
|
public class FishingHook extends Projectile {
|
||
|
private static final Logger LOGGER = LogUtils.getLogger();
|
||
|
private final RandomSource syncronizedRandom = RandomSource.create();
|
||
|
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);
|
||
|
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;
|
||
|
@Nullable
|
||
|
private Entity hookedIn;
|
||
|
private FishingHook.FishHookState currentState = FishingHook.FishHookState.FLYING;
|
||
|
private final int luck;
|
||
|
private final int lureSpeed;
|
||
|
|
||
|
private FishingHook(EntityType<? extends FishingHook> p_150141_, Level p_150142_, int p_150143_, int p_150144_) {
|
||
|
super(p_150141_, p_150142_);
|
||
|
this.luck = Math.max(0, p_150143_);
|
||
|
this.lureSpeed = Math.max(0, p_150144_);
|
||
|
}
|
||
|
|
||
|
public FishingHook(EntityType<? extends FishingHook> p_150138_, Level p_150139_) {
|
||
|
this(p_150138_, p_150139_, 0, 0);
|
||
|
}
|
||
|
|
||
|
public FishingHook(Player p_37106_, Level p_37107_, int p_37108_, int p_37109_) {
|
||
|
this(EntityType.FISHING_BOBBER, p_37107_, p_37108_, p_37109_);
|
||
|
this.setOwner(p_37106_);
|
||
|
float f = p_37106_.getXRot();
|
||
|
float f1 = p_37106_.getYRot();
|
||
|
float f2 = Mth.cos(-f1 * (float) (Math.PI / 180.0) - (float) Math.PI);
|
||
|
float f3 = Mth.sin(-f1 * (float) (Math.PI / 180.0) - (float) Math.PI);
|
||
|
float f4 = -Mth.cos(-f * (float) (Math.PI / 180.0));
|
||
|
float f5 = Mth.sin(-f * (float) (Math.PI / 180.0));
|
||
|
double d0 = p_37106_.getX() - f3 * 0.3;
|
||
|
double d1 = p_37106_.getEyeY();
|
||
|
double d2 = p_37106_.getZ() - f2 * 0.3;
|
||
|
this.snapTo(d0, d1, d2, f1, f);
|
||
|
Vec3 vec3 = new Vec3(-f3, Mth.clamp(-(f5 / f4), -5.0F, 5.0F), -f2);
|
||
|
double d3 = vec3.length();
|
||
|
vec3 = vec3.multiply(
|
||
|
0.6 / d3 + this.random.triangle(0.5, 0.0103365),
|
||
|
0.6 / d3 + this.random.triangle(0.5, 0.0103365),
|
||
|
0.6 / d3 + 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));
|
||
|
this.yRotO = this.getYRot();
|
||
|
this.xRotO = this.getXRot();
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
protected void defineSynchedData(SynchedEntityData.Builder p_330190_) {
|
||
|
p_330190_.define(DATA_HOOKED_ENTITY, 0);
|
||
|
p_330190_.define(DATA_BITING, false);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
protected boolean shouldBounceOnWorldBorder() {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void onSyncedDataUpdated(EntityDataAccessor<?> p_37153_) {
|
||
|
if (DATA_HOOKED_ENTITY.equals(p_37153_)) {
|
||
|
int i = this.getEntityData().get(DATA_HOOKED_ENTITY);
|
||
|
this.hookedIn = i > 0 ? this.level().getEntity(i - 1) : null;
|
||
|
}
|
||
|
|
||
|
if (DATA_BITING.equals(p_37153_)) {
|
||
|
this.biting = this.getEntityData().get(DATA_BITING);
|
||
|
if (this.biting) {
|
||
|
this.setDeltaMovement(this.getDeltaMovement().x, -0.4F * Mth.nextFloat(this.syncronizedRandom, 0.6F, 1.0F), this.getDeltaMovement().z);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
super.onSyncedDataUpdated(p_37153_);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public boolean shouldRenderAtSqrDistance(double p_37125_) {
|
||
|
double d0 = 64.0;
|
||
|
return p_37125_ < 4096.0;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void tick() {
|
||
|
this.syncronizedRandom.setSeed(this.getUUID().getLeastSignificantBits() ^ this.level().getGameTime());
|
||
|
super.tick();
|
||
|
Player player = this.getPlayerOwner();
|
||
|
if (player == null) {
|
||
|
this.discard();
|
||
|
} else if (this.level().isClientSide || !this.shouldStopFishing(player)) {
|
||
|
if (this.onGround()) {
|
||
|
this.life++;
|
||
|
if (this.life >= 1200) {
|
||
|
this.discard();
|
||
|
return;
|
||
|
}
|
||
|
} else {
|
||
|
this.life = 0;
|
||
|
}
|
||
|
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
boolean flag = f > 0.0F;
|
||
|
if (this.currentState == FishingHook.FishHookState.FLYING) {
|
||
|
if (this.hookedIn != null) {
|
||
|
this.setDeltaMovement(Vec3.ZERO);
|
||
|
this.currentState = FishingHook.FishHookState.HOOKED_IN_ENTITY;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (flag) {
|
||
|
this.setDeltaMovement(this.getDeltaMovement().multiply(0.3, 0.2, 0.3));
|
||
|
this.currentState = FishingHook.FishHookState.BOBBING;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
this.checkCollision();
|
||
|
} else {
|
||
|
if (this.currentState == FishingHook.FishHookState.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());
|
||
|
} else {
|
||
|
this.setHookedEntity(null);
|
||
|
this.currentState = FishingHook.FishHookState.FLYING;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (this.currentState == FishingHook.FishHookState.BOBBING) {
|
||
|
Vec3 vec3 = this.getDeltaMovement();
|
||
|
double d0 = this.getY() + vec3.y - blockpos.getY() - f;
|
||
|
if (Math.abs(d0) < 0.01) {
|
||
|
d0 += Math.signum(d0) * 0.1;
|
||
|
}
|
||
|
|
||
|
this.setDeltaMovement(vec3.x * 0.9, vec3.y - d0 * this.random.nextFloat() * 0.2, vec3.z * 0.9);
|
||
|
if (this.nibble <= 0 && this.timeUntilHooked <= 0) {
|
||
|
this.openWater = true;
|
||
|
} else {
|
||
|
this.openWater = this.openWater && this.outOfWaterTime < 10 && this.calculateOpenWater(blockpos);
|
||
|
}
|
||
|
|
||
|
if (flag) {
|
||
|
this.outOfWaterTime = Math.max(0, this.outOfWaterTime - 1);
|
||
|
if (this.biting) {
|
||
|
this.setDeltaMovement(this.getDeltaMovement().add(0.0, -0.1 * this.syncronizedRandom.nextFloat() * this.syncronizedRandom.nextFloat(), 0.0));
|
||
|
}
|
||
|
|
||
|
if (!this.level().isClientSide) {
|
||
|
this.catchingFish(blockpos);
|
||
|
}
|
||
|
} else {
|
||
|
this.outOfWaterTime = Math.min(10, this.outOfWaterTime + 1);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!fluidstate.is(FluidTags.WATER)) {
|
||
|
this.setDeltaMovement(this.getDeltaMovement().add(0.0, -0.03, 0.0));
|
||
|
}
|
||
|
|
||
|
this.move(MoverType.SELF, this.getDeltaMovement());
|
||
|
this.applyEffectsFromBlocks();
|
||
|
this.updateRotation();
|
||
|
if (this.currentState == FishingHook.FishHookState.FLYING && (this.onGround() || this.horizontalCollision)) {
|
||
|
this.setDeltaMovement(Vec3.ZERO);
|
||
|
}
|
||
|
|
||
|
double d1 = 0.92;
|
||
|
this.setDeltaMovement(this.getDeltaMovement().scale(0.92));
|
||
|
this.reapplyPosition();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private boolean shouldStopFishing(Player p_37137_) {
|
||
|
ItemStack itemstack = p_37137_.getMainHandItem();
|
||
|
ItemStack itemstack1 = p_37137_.getOffhandItem();
|
||
|
boolean flag = itemstack.is(Items.FISHING_ROD);
|
||
|
boolean flag1 = itemstack1.is(Items.FISHING_ROD);
|
||
|
if (!p_37137_.isRemoved() && p_37137_.isAlive() && (flag || flag1) && !(this.distanceToSqr(p_37137_) > 1024.0)) {
|
||
|
return false;
|
||
|
} else {
|
||
|
this.discard();
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void checkCollision() {
|
||
|
HitResult hitresult = ProjectileUtil.getHitResultOnMoveVector(this, this::canHitEntity);
|
||
|
this.hitTargetOrDeflectSelf(hitresult);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
protected boolean canHitEntity(Entity p_37135_) {
|
||
|
return super.canHitEntity(p_37135_) || p_37135_.isAlive() && p_37135_ instanceof ItemEntity;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
protected void onHitEntity(EntityHitResult p_37144_) {
|
||
|
super.onHitEntity(p_37144_);
|
||
|
if (!this.level().isClientSide) {
|
||
|
this.setHookedEntity(p_37144_.getEntity());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
protected void onHitBlock(BlockHitResult p_37142_) {
|
||
|
super.onHitBlock(p_37142_);
|
||
|
this.setDeltaMovement(this.getDeltaMovement().normalize().scale(p_37142_.distanceTo(this)));
|
||
|
}
|
||
|
|
||
|
private void setHookedEntity(@Nullable Entity p_150158_) {
|
||
|
this.hookedIn = p_150158_;
|
||
|
this.getEntityData().set(DATA_HOOKED_ENTITY, p_150158_ == null ? 0 : p_150158_.getId() + 1);
|
||
|
}
|
||
|
|
||
|
private void catchingFish(BlockPos p_37146_) {
|
||
|
ServerLevel serverlevel = (ServerLevel)this.level();
|
||
|
int i = 1;
|
||
|
BlockPos blockpos = p_37146_.above();
|
||
|
if (this.random.nextFloat() < 0.25F && this.level().isRainingAt(blockpos)) {
|
||
|
i++;
|
||
|
}
|
||
|
|
||
|
if (this.random.nextFloat() < 0.5F && !this.level().canSeeSky(blockpos)) {
|
||
|
i--;
|
||
|
}
|
||
|
|
||
|
if (this.nibble > 0) {
|
||
|
this.nibble--;
|
||
|
if (this.nibble <= 0) {
|
||
|
this.timeUntilLured = 0;
|
||
|
this.timeUntilHooked = 0;
|
||
|
this.getEntityData().set(DATA_BITING, false);
|
||
|
}
|
||
|
} else if (this.timeUntilHooked > 0) {
|
||
|
this.timeUntilHooked -= i;
|
||
|
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 f1 = Mth.sin(f);
|
||
|
float f2 = Mth.cos(f);
|
||
|
double d0 = this.getX() + f1 * this.timeUntilHooked * 0.1F;
|
||
|
double d1 = Mth.floor(this.getY()) + 1.0F;
|
||
|
double d2 = this.getZ() + f2 * this.timeUntilHooked * 0.1F;
|
||
|
BlockState blockstate = serverlevel.getBlockState(BlockPos.containing(d0, d1 - 1.0, d2));
|
||
|
if (blockstate.is(Blocks.WATER)) {
|
||
|
if (this.random.nextFloat() < 0.15F) {
|
||
|
serverlevel.sendParticles(ParticleTypes.BUBBLE, d0, d1 - 0.1F, d2, 1, f1, 0.1, f2, 0.0);
|
||
|
}
|
||
|
|
||
|
float f3 = f1 * 0.04F;
|
||
|
float f4 = f2 * 0.04F;
|
||
|
serverlevel.sendParticles(ParticleTypes.FISHING, d0, d1, d2, 0, f4, 0.01, -f3, 1.0);
|
||
|
serverlevel.sendParticles(ParticleTypes.FISHING, d0, d1, d2, 0, -f4, 0.01, f3, 1.0);
|
||
|
}
|
||
|
} 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),
|
||
|
this.getBbWidth(),
|
||
|
0.0,
|
||
|
this.getBbWidth(),
|
||
|
0.2F
|
||
|
);
|
||
|
serverlevel.sendParticles(
|
||
|
ParticleTypes.FISHING,
|
||
|
this.getX(),
|
||
|
d3,
|
||
|
this.getZ(),
|
||
|
(int)(1.0F + this.getBbWidth() * 20.0F),
|
||
|
this.getBbWidth(),
|
||
|
0.0,
|
||
|
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 f5 = 0.15F;
|
||
|
if (this.timeUntilLured < 20) {
|
||
|
f5 += (20 - this.timeUntilLured) * 0.05F;
|
||
|
} else if (this.timeUntilLured < 40) {
|
||
|
f5 += (40 - this.timeUntilLured) * 0.02F;
|
||
|
} else if (this.timeUntilLured < 60) {
|
||
|
f5 += (60 - this.timeUntilLured) * 0.01F;
|
||
|
}
|
||
|
|
||
|
if (this.random.nextFloat() < f5) {
|
||
|
float f6 = Mth.nextFloat(this.random, 0.0F, 360.0F) * (float) (Math.PI / 180.0);
|
||
|
float f7 = Mth.nextFloat(this.random, 25.0F, 60.0F);
|
||
|
double d4 = this.getX() + Mth.sin(f6) * f7 * 0.1;
|
||
|
double d5 = Mth.floor(this.getY()) + 1.0F;
|
||
|
double d6 = this.getZ() + Mth.cos(f6) * f7 * 0.1;
|
||
|
BlockState blockstate1 = serverlevel.getBlockState(BlockPos.containing(d4, d5 - 1.0, d6));
|
||
|
if (blockstate1.is(Blocks.WATER)) {
|
||
|
serverlevel.sendParticles(ParticleTypes.SPLASH, d4, d5, d6, 2 + this.random.nextInt(2), 0.1F, 0.0, 0.1F, 0.0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (this.timeUntilLured <= 0) {
|
||
|
this.fishAngle = Mth.nextFloat(this.random, 0.0F, 360.0F);
|
||
|
this.timeUntilHooked = Mth.nextInt(this.random, 20, 80);
|
||
|
}
|
||
|
} else {
|
||
|
this.timeUntilLured = Mth.nextInt(this.random, 100, 600);
|
||
|
this.timeUntilLured = this.timeUntilLured - this.lureSpeed;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private boolean calculateOpenWater(BlockPos p_37159_) {
|
||
|
FishingHook.OpenWaterType fishinghook$openwatertype = FishingHook.OpenWaterType.INVALID;
|
||
|
|
||
|
for (int i = -1; i <= 2; i++) {
|
||
|
FishingHook.OpenWaterType fishinghook$openwatertype1 = this.getOpenWaterTypeForArea(p_37159_.offset(-2, i, -2), p_37159_.offset(2, i, 2));
|
||
|
switch (fishinghook$openwatertype1) {
|
||
|
case ABOVE_WATER:
|
||
|
if (fishinghook$openwatertype == FishingHook.OpenWaterType.INVALID) {
|
||
|
return false;
|
||
|
}
|
||
|
break;
|
||
|
case INSIDE_WATER:
|
||
|
if (fishinghook$openwatertype == FishingHook.OpenWaterType.ABOVE_WATER) {
|
||
|
return false;
|
||
|
}
|
||
|
break;
|
||
|
case INVALID:
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
fishinghook$openwatertype = fishinghook$openwatertype1;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
private FishingHook.OpenWaterType getOpenWaterTypeForArea(BlockPos p_37148_, BlockPos p_37149_) {
|
||
|
return BlockPos.betweenClosedStream(p_37148_, p_37149_)
|
||
|
.map(this::getOpenWaterTypeForBlock)
|
||
|
.reduce((p_37139_, p_37140_) -> p_37139_ == p_37140_ ? p_37139_ : FishingHook.OpenWaterType.INVALID)
|
||
|
.orElse(FishingHook.OpenWaterType.INVALID);
|
||
|
}
|
||
|
|
||
|
private FishingHook.OpenWaterType getOpenWaterTypeForBlock(BlockPos p_37164_) {
|
||
|
BlockState blockstate = this.level().getBlockState(p_37164_);
|
||
|
if (!blockstate.isAir() && !blockstate.is(Blocks.LILY_PAD)) {
|
||
|
FluidState fluidstate = blockstate.getFluidState();
|
||
|
return fluidstate.is(FluidTags.WATER) && fluidstate.isSource() && blockstate.getCollisionShape(this.level(), p_37164_).isEmpty()
|
||
|
? FishingHook.OpenWaterType.INSIDE_WATER
|
||
|
: FishingHook.OpenWaterType.INVALID;
|
||
|
} else {
|
||
|
return FishingHook.OpenWaterType.ABOVE_WATER;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public boolean isOpenWaterFishing() {
|
||
|
return this.openWater;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void addAdditionalSaveData(CompoundTag p_37161_) {
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void readAdditionalSaveData(CompoundTag p_37151_) {
|
||
|
}
|
||
|
|
||
|
public int retrieve(ItemStack p_37157_) {
|
||
|
Player player = this.getPlayerOwner();
|
||
|
if (!this.level().isClientSide && player != null && !this.shouldStopFishing(player)) {
|
||
|
int i = 0;
|
||
|
if (this.hookedIn != null) {
|
||
|
this.pullEntity(this.hookedIn);
|
||
|
CriteriaTriggers.FISHING_ROD_HOOKED.trigger((ServerPlayer)player, p_37157_, 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, p_37157_)
|
||
|
.withParameter(LootContextParams.THIS_ENTITY, this)
|
||
|
.withLuck(this.luck + player.getLuck())
|
||
|
.create(LootContextParamSets.FISHING);
|
||
|
LootTable loottable = this.level().getServer().reloadableRegistries().getLootTable(BuiltInLootTables.FISHING);
|
||
|
List<ItemStack> list = loottable.getRandomItems(lootparams);
|
||
|
CriteriaTriggers.FISHING_ROD_HOOKED.trigger((ServerPlayer)player, p_37157_, this, list);
|
||
|
|
||
|
for (ItemStack itemstack : list) {
|
||
|
ItemEntity itementity = new ItemEntity(this.level(), this.getX(), this.getY(), this.getZ(), itemstack);
|
||
|
double d0 = player.getX() - this.getX();
|
||
|
double d1 = player.getY() - this.getY();
|
||
|
double d2 = player.getZ() - this.getZ();
|
||
|
double d3 = 0.1;
|
||
|
itementity.setDeltaMovement(d0 * 0.1, d1 * 0.1 + Math.sqrt(Math.sqrt(d0 * d0 + d1 * d1 + d2 * d2)) * 0.08, d2 * 0.1);
|
||
|
this.level().addFreshEntity(itementity);
|
||
|
player.level()
|
||
|
.addFreshEntity(
|
||
|
new ExperienceOrb(
|
||
|
player.level(), player.getX(), player.getY() + 0.5, player.getZ() + 0.5, this.random.nextInt(6) + 1
|
||
|
)
|
||
|
);
|
||
|
if (itemstack.is(ItemTags.FISHES)) {
|
||
|
player.awardStat(Stats.FISH_CAUGHT, 1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
i = 1;
|
||
|
}
|
||
|
|
||
|
if (this.onGround()) {
|
||
|
i = 2;
|
||
|
}
|
||
|
|
||
|
this.discard();
|
||
|
return i;
|
||
|
} else {
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void handleEntityEvent(byte p_37123_) {
|
||
|
if (p_37123_ == 31 && this.level().isClientSide && this.hookedIn instanceof Player player && player.isLocalPlayer()) {
|
||
|
this.pullEntity(this.hookedIn);
|
||
|
}
|
||
|
|
||
|
super.handleEntityEvent(p_37123_);
|
||
|
}
|
||
|
|
||
|
protected void pullEntity(Entity p_150156_) {
|
||
|
Entity entity = this.getOwner();
|
||
|
if (entity != null) {
|
||
|
Vec3 vec3 = new Vec3(entity.getX() - this.getX(), entity.getY() - this.getY(), entity.getZ() - this.getZ()).scale(0.1);
|
||
|
p_150156_.setDeltaMovement(p_150156_.getDeltaMovement().add(vec3));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
protected Entity.MovementEmission getMovementEmission() {
|
||
|
return Entity.MovementEmission.NONE;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void remove(Entity.RemovalReason p_150146_) {
|
||
|
this.updateOwnerInfo(null);
|
||
|
super.remove(p_150146_);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void onClientRemoval() {
|
||
|
this.updateOwnerInfo(null);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void setOwner(@Nullable Entity p_150154_) {
|
||
|
super.setOwner(p_150154_);
|
||
|
this.updateOwnerInfo(this);
|
||
|
}
|
||
|
|
||
|
private void updateOwnerInfo(@Nullable FishingHook p_150148_) {
|
||
|
Player player = this.getPlayerOwner();
|
||
|
if (player != null) {
|
||
|
player.fishing = p_150148_;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Nullable
|
||
|
public Player getPlayerOwner() {
|
||
|
return this.getOwner() instanceof Player player ? player : null;
|
||
|
}
|
||
|
|
||
|
@Nullable
|
||
|
public Entity getHookedIn() {
|
||
|
return this.hookedIn;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public boolean canUsePortal(boolean p_344610_) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public Packet<ClientGamePacketListener> getAddEntityPacket(ServerEntity p_343294_) {
|
||
|
Entity entity = this.getOwner();
|
||
|
return new ClientboundAddEntityPacket(this, p_343294_, entity == null ? this.getId() : entity.getId());
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void recreateFromPacket(ClientboundAddEntityPacket p_150150_) {
|
||
|
super.recreateFromPacket(p_150150_);
|
||
|
if (this.getPlayerOwner() == null) {
|
||
|
int i = p_150150_.getData();
|
||
|
LOGGER.error("Failed to recreate fishing hook on client. {} (id: {}) is not a valid owner.", this.level().getEntity(i), i);
|
||
|
this.discard();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static enum FishHookState {
|
||
|
FLYING,
|
||
|
HOOKED_IN_ENTITY,
|
||
|
BOBBING;
|
||
|
}
|
||
|
|
||
|
static enum OpenWaterType {
|
||
|
ABOVE_WATER,
|
||
|
INSIDE_WATER,
|
||
|
INVALID;
|
||
|
}
|
||
|
}
|