package com.r3944realms.leashedplayer.modInterface; import com.r3944realms.leashedplayer.content.commands.LeashCommand; import com.r3944realms.leashedplayer.content.criteriaTriggers.ModCriteriaTriggers; import com.r3944realms.leashedplayer.utils.Util; import net.minecraft.core.BlockPos; import net.minecraft.network.protocol.game.ClientboundSetEntityLinkPacket; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Leashable; import net.minecraft.world.entity.decoration.LeashFenceKnotEntity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; import org.jetbrains.annotations.NotNull; import javax.annotation.Nullable; import java.util.Optional; import java.util.UUID; public interface PlayerLeashable extends Leashable { /** * 获取拴绳的持有者实体 可能不存在 */ @Nullable Entity getLeashHolder(); /** * 获取拴绳的持有者数据 (从EntityData中获取 */ LeashData getLeashDataFromEntityData(); /** * 是否立即可被拴住 */ boolean canBeLeashedInstantly(Player player); /** * 设置栓绳数据 * @param pLeashHolder 拴绳持有者 * @param pBroadcastPacket 是否广播包 */ default void setLeashedTo(@NotNull Entity pLeashHolder, boolean pBroadcastPacket) { setLeashedTo((Entity & Leashable)this, pLeashHolder, pBroadcastPacket); if(this instanceof ServerPlayer) { ModCriteriaTriggers.LEASH_PLAYER_TRIGGER.get().trigger((ServerPlayer) this, pLeashHolder); } } static void setLeashedTo(E pEntity, Entity pLeashHolder, boolean pBroadcastPacket) { LeashData leashable$leashdata = pEntity.getLeashData(); if (leashable$leashdata == null) { leashable$leashdata = new LeashData(pLeashHolder); pEntity.setLeashData(leashable$leashdata); } else { leashable$leashdata.setLeashHolder(pLeashHolder); } if (pBroadcastPacket && pEntity.level() instanceof ServerLevel serverlevel) { serverlevel.getChunkSource().broadcast(pEntity, new ClientboundSetEntityLinkPacket(pEntity, pLeashHolder)); } //这边覆写去掉了乘坐相关的逻辑,即乘坐状态下也可以正常被栓住,不影响其乘坐状态 } static void legacyElasticRangeLeashBehaviour(Entity holderEntity, Entity restrainedEntity) { float leashLength = LeashCommand.MIN_VALUE; if (restrainedEntity instanceof ILivingEntityExtension iEntity) { // 获取绳长 float leashLengthFormValue = iEntity.getLeashLength(); leashLength = leashLengthFormValue > LeashCommand.MIN_VALUE ? leashLengthFormValue : LeashCommand.MIN_VALUE; } // 两者距离 float distance = holderEntity.distanceTo(restrainedEntity); Entity applyMovementEntity = restrainedEntity.isPassenger() ? restrainedEntity.getVehicle() : restrainedEntity; // 仅当距离大于绳长时施加拉力 if (applyMovementEntity != null && distance > leashLength) { MethodA(holderEntity, applyMovementEntity, distance, leashLength); // 控制速度不要过快,避免偏激移动 Util.MovementBrake(applyMovementEntity, entity -> { Vec3 deltaMovement = entity.getDeltaMovement(); entity.setDeltaMovement( Math.min(Math.abs(deltaMovement.x), 1.0) * Math.signum(deltaMovement.x), Math.min(Math.abs(deltaMovement.y), 1.0) * Math.signum(deltaMovement.y), Math.min(Math.abs(deltaMovement.z), 1.0) * Math.signum(deltaMovement.z) ); entity.hurtMarked = true; }); } } private static void MethodA(Entity holderEntity, Entity applyMovementEntity, float distance, float leashLength) { // 计算朝向持有者的拉力方向 double dX = (holderEntity.getX() - applyMovementEntity.getX()) / (double) distance; double dY = (holderEntity.getY() - applyMovementEntity.getY()) / (double) distance; double dZ = (holderEntity.getZ() - applyMovementEntity.getZ()) / (double) distance; // 拉力大小,距离越远拉力越强,但施加一个最大限制 double pullStrength = Math.min((distance - leashLength) * 0.1, 1.0); // 限制最大拉力 applyMovementEntity.setDeltaMovement( applyMovementEntity.getDeltaMovement().add( dX * pullStrength, dY * pullStrength, dZ * pullStrength ) ); } private static void MethodB(Entity holderEntity, Entity applyMovementEntity, float distance, float leashLength) { // 计算朝向持有者的拉力方向 double dX = (holderEntity.getX() - applyMovementEntity.getX()) / (double) distance; double dZ = (holderEntity.getZ() - applyMovementEntity.getZ()) / (double) distance; // 稳定点的目标高度 double targetY = holderEntity.getY() + 1.0; // 根据需要调整目标高度 double currentY = applyMovementEntity.getY(); double heightDifference = targetY - currentY; // 控制垂直方向的拉力 double dY = heightDifference > 0.5 ? 0.1 : (heightDifference < -0.5 ? -0.1 : 0); // 拉力大小,距离越远拉力越强,但施加一个最大限制 double pullStrength = Math.min((distance - leashLength) * 0.1, 1.0); // 限制最大拉力 applyMovementEntity.setDeltaMovement( applyMovementEntity.getDeltaMovement().add( dX * pullStrength, dY, dZ * pullStrength ) ); } @Nullable static Entity getLeashDataEntity(@NotNull ServerPlayer serverPlayer , @NotNull ServerLevel serverLevel) { LeashData leashDataFromEntityData = ((PlayerLeashable) serverPlayer).getLeashDataFromEntityData(); if (leashDataFromEntityData != null) { return getLeashDataEntity(leashDataFromEntityData, serverLevel); } else return null; } static Entity getLeashDataEntityOrThrown(@NotNull ServerPlayer serverPlayer ,@NotNull ServerLevel serverLevel) throws Exception { Entity leashedEntity = getLeashDataEntity(serverPlayer, serverLevel); if(leashedEntity == null) throw new Exception("invalid"); else return leashedEntity; } @Nullable static Entity getLeashDataEntity(@NotNull Leashable.LeashData leashDataFromEntityData, @NotNull ServerLevel level) { if(leashDataFromEntityData.delayedLeashInfo != null) { Optional UUID = leashDataFromEntityData.delayedLeashInfo.left(); Optional BlockPos = leashDataFromEntityData.delayedLeashInfo.right(); if (UUID.isPresent()) { return level.getEntity(UUID.get()); } else return BlockPos.map(pos -> LeashFenceKnotEntity.getOrCreateKnot(level, pos)).orElse(null); } else if(leashDataFromEntityData.leashHolder != null) { return leashDataFromEntityData.leashHolder; } else if(leashDataFromEntityData.delayedLeashHolderId != 0) { return level.getEntity(leashDataFromEntityData.delayedLeashHolderId); } else return null; } static Entity getLeashDataEntityOrThrown(@NotNull Leashable.LeashData leashDataFromEntityData, @NotNull ServerLevel level) throws Exception { if(leashDataFromEntityData.delayedLeashInfo != null) { Optional UUID = leashDataFromEntityData.delayedLeashInfo.left(); Optional BlockPos = leashDataFromEntityData.delayedLeashInfo.right(); if (UUID.isPresent()) { return level.getEntity(UUID.get()); } else if(BlockPos.isPresent()) { return LeashFenceKnotEntity.getOrCreateKnot(level, BlockPos.get()); } else { throw new Exception("invalid delayedLeashInfo"); } } else if(leashDataFromEntityData.leashHolder != null) { return leashDataFromEntityData.leashHolder; } else if(leashDataFromEntityData.delayedLeashHolderId != 0) { Entity entity = level.getEntity(leashDataFromEntityData.delayedLeashHolderId); if(entity == null) { throw new Exception("Not found Entity. maybe the pId is invalid"); } return entity; } else { throw new Exception("invalid leash data"); } } static boolean isLeashFenceKnotEntityExisted(ServerLevel pLevel, BlockPos pPos) { int i = pPos.getX(); int j = pPos.getY(); int k = pPos.getZ(); for (LeashFenceKnotEntity leashfenceknotentity : pLevel.getEntitiesOfClass( LeashFenceKnotEntity.class, new AABB((double)i - 1.0, (double)j - 1.0, (double)k - 1.0, (double)i + 1.0, (double)j + 1.0, (double)k + 1.0) )) { if (leashfenceknotentity.getPos().equals(pPos)) { return true; } } return false; } @Nullable static Entity getLeashFenceKnotEntity(ServerLevel pLevel, BlockPos pPos) { int i = pPos.getX(); int j = pPos.getY(); int k = pPos.getZ(); for (LeashFenceKnotEntity leashfenceknotentity : pLevel.getEntitiesOfClass( LeashFenceKnotEntity.class, new AABB((double)i - 1.0, (double)j - 1.0, (double)k - 1.0, (double)i + 1.0, (double)j + 1.0, (double)k + 1.0) )) { if (leashfenceknotentity.getPos().equals(pPos)) { return leashfenceknotentity; } } return null; } static Entity createLeashKnotFence(ServerLevel pLevel, BlockPos pPos) { LeashFenceKnotEntity leashfenceknotentity = new LeashFenceKnotEntity(pLevel, pPos); pLevel.addFreshEntity(leashfenceknotentity); return leashfenceknotentity; } }