feat:优化逻辑与修复之前的BUG
This commit is contained in:
parent
7e68e31888
commit
1137416bb5
41
build.gradle
41
build.gradle
|
|
@ -42,6 +42,37 @@ repositories {
|
|||
maven { url = "https://libraries.minecraft.net/" }
|
||||
maven { url = "https://neoforged.forgecdn.net/releases" }
|
||||
maven { url = "https://neoforged.forgecdn.net/mojang-meta" }
|
||||
maven {
|
||||
url "https://cursemaven.com"
|
||||
content {
|
||||
includeGroup "curse.maven"
|
||||
}
|
||||
}
|
||||
maven {
|
||||
// location of the maven that hosts JEI files before January 2023
|
||||
name = "Progwml6's maven"
|
||||
url = "https://dvs1.progwml6.com/files/maven/"
|
||||
}
|
||||
maven {
|
||||
// location of the maven that hosts JEI files since January 2023
|
||||
name = "Jared's maven"
|
||||
url = "https://maven.blamejared.com/"
|
||||
}
|
||||
maven {
|
||||
// location of a maven mirror for JEI files, as a fallback
|
||||
name = "ModMaven"
|
||||
url = "https://modmaven.dev"
|
||||
}
|
||||
maven {
|
||||
name 'luck-repo'
|
||||
url 'https://repo.lucko.me/'
|
||||
content {
|
||||
includeModule 'me.lucko', 'spark-api'
|
||||
}
|
||||
}
|
||||
flatDir {
|
||||
dir "libs"
|
||||
}
|
||||
}
|
||||
|
||||
legacyForge {
|
||||
|
|
@ -101,7 +132,15 @@ configurations {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
annotationProcessor 'org.spongepowered:mixin:0.8.5:processor'
|
||||
annotationProcessor ('org.spongepowered:mixin:0.8.5:processor')
|
||||
modRuntimeOnly("curse.maven:debug-utils-forge-783008:5337491")
|
||||
modRuntimeOnly("blank:curtain-1.20.1:1.3.2")
|
||||
modCompileOnly("mezz.jei:jei-${minecraft_version}-forge-api:${jei_version}")
|
||||
modRuntimeOnly("mezz.jei:jei-${minecraft_version}-forge:${jei_version}")
|
||||
modRuntimeOnly("curse.maven:spark-361579:4738952")
|
||||
compileOnly ('me.lucko:spark-api:0.1-SNAPSHOT')
|
||||
|
||||
|
||||
}
|
||||
|
||||
// ========== 编译配置 ==========
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ neoForge.parchment.mappingsVersion=2023.09.03
|
|||
|
||||
# enable ProGuard
|
||||
enableProguard=false
|
||||
# Jei Version
|
||||
jei_version=15.20.0.112
|
||||
## Environment Properties
|
||||
|
||||
# The Minecraft version must agree with the Forge version to get a valid artifact
|
||||
|
|
|
|||
BIN
libs/curtain-1.20.1-1.3.2.jar
Normal file
BIN
libs/curtain-1.20.1-1.3.2.jar
Normal file
Binary file not shown.
|
|
@ -16,17 +16,28 @@
|
|||
package top.r3944realms.superleadrope;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.Pose;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.entity.vehicle.Boat;
|
||||
import net.minecraft.world.entity.vehicle.Minecart;
|
||||
import net.minecraft.world.item.CreativeModeTabs;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import net.minecraftforge.common.capabilities.RegisterCapabilitiesEvent;
|
||||
import net.minecraftforge.event.AttachCapabilitiesEvent;
|
||||
import net.minecraftforge.event.BuildCreativeModeTabContentsEvent;
|
||||
import net.minecraftforge.event.TickEvent;
|
||||
import net.minecraftforge.event.entity.EntityJoinLevelEvent;
|
||||
import net.minecraftforge.event.entity.EntityLeaveLevelEvent;
|
||||
import net.minecraftforge.event.entity.EntityTeleportEvent;
|
||||
import net.minecraftforge.event.entity.player.PlayerEvent;
|
||||
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
|
|
@ -34,11 +45,16 @@ import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
|
|||
import top.r3944realms.superleadrope.content.capability.CapabilityHandler;
|
||||
import top.r3944realms.superleadrope.content.capability.CapabilityRemainder;
|
||||
import top.r3944realms.superleadrope.content.capability.LeashDataImpl;
|
||||
import top.r3944realms.superleadrope.content.capability.inter.ILeashDataCapability;
|
||||
import top.r3944realms.superleadrope.content.entity.SuperLeashEntity;
|
||||
import top.r3944realms.superleadrope.content.entity.SuperLeashKnotEntity;
|
||||
import top.r3944realms.superleadrope.content.item.SuperLeadRopeItem;
|
||||
import top.r3944realms.superleadrope.core.leash.LeashInteractHandler;
|
||||
import top.r3944realms.superleadrope.core.leash.LeashSyncManager;
|
||||
import top.r3944realms.superleadrope.core.register.SLPItems;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
public class CommonEventHandler {
|
||||
@net.minecraftforge.fml.common.Mod.EventBusSubscriber(modid = SuperLeadRope.MOD_ID, bus = net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus.FORGE)
|
||||
|
|
@ -62,18 +78,84 @@ public class CommonEventHandler {
|
|||
}
|
||||
}
|
||||
@SubscribeEvent
|
||||
public static void onPlayerHitOnBlock (PlayerInteractEvent.RightClickBlock event) {
|
||||
public static void onPlayerRightHitOnBlock(PlayerInteractEvent.RightClickBlock event) {
|
||||
Level level = event.getLevel();
|
||||
if (level.isClientSide) {
|
||||
return ;
|
||||
return;
|
||||
}
|
||||
BlockPos blockPos = event.getHitVec().getBlockPos();
|
||||
BlockState blockState = level.getBlockState(blockPos);
|
||||
Player player = event.getEntity();
|
||||
ItemStack itemInHand = player.getItemInHand(InteractionHand.MAIN_HAND);
|
||||
if (SuperLeashKnotEntity.isSupportBlock(blockState)) {
|
||||
SuperLeadRopeItem.bindToBlock(event.getEntity(), level, blockPos, event.getItemStack());
|
||||
boolean shouldConsume = SuperLeadRopeItem.bindToBlock(player, level, blockPos, event.getItemStack(), itemInHand.is(SLPItems.SUPER_LEAD_ROPE.get()));
|
||||
if (shouldConsume) {
|
||||
event.setCancellationResult(InteractionResult.CONSUME);
|
||||
event.setCanceled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@SubscribeEvent
|
||||
public static void onEntityTeleport(EntityTeleportEvent event) {
|
||||
Entity telEntity = event.getEntity();
|
||||
Vec3 targetPos = event.getTarget();
|
||||
Level level = telEntity.level();
|
||||
if (level instanceof ServerLevel serverLevel) {
|
||||
List<Entity> entities = LeashDataImpl.leashableInArea(telEntity);
|
||||
// 为每个实体创建状态快照
|
||||
entities.forEach(beLeashedEntity -> {
|
||||
// 保存原来的旋转角度
|
||||
|
||||
Pose originalPose = beLeashedEntity.getPose();
|
||||
boolean originalIsSprinting = beLeashedEntity.isSprinting();
|
||||
float originalYaw = beLeashedEntity.getYRot();
|
||||
float originalPitch = beLeashedEntity.getXRot();
|
||||
Vec3 originalDeltaMovement = beLeashedEntity.getDeltaMovement();
|
||||
AtomicReference<ILeashDataCapability.LeashInfo> originalLeashInfo = new AtomicReference<>();
|
||||
beLeashedEntity.getCapability(CapabilityHandler.LEASH_DATA_CAP)
|
||||
.ifPresent(cap -> {
|
||||
originalLeashInfo.set(cap.getLeashInfo(telEntity).orElse(null));
|
||||
cap.removeLeash(telEntity);
|
||||
});
|
||||
|
||||
if (beLeashedEntity.level() == telEntity.level()) {
|
||||
// 使用空集合表示所有值都是绝对的
|
||||
if (beLeashedEntity instanceof ServerPlayer serverPlayer) {
|
||||
serverPlayer.connection.teleport(
|
||||
targetPos.x, targetPos.y, targetPos.z,
|
||||
originalYaw, originalPitch,
|
||||
Collections.emptySet() // 所有值都是绝对的
|
||||
);
|
||||
} else {
|
||||
beLeashedEntity.teleportTo(
|
||||
serverLevel,
|
||||
targetPos.x,
|
||||
targetPos.y,
|
||||
targetPos.z,
|
||||
Collections.emptySet(),
|
||||
originalYaw, originalPitch
|
||||
);
|
||||
}
|
||||
} else {
|
||||
beLeashedEntity.teleportTo(
|
||||
serverLevel,
|
||||
targetPos.x,
|
||||
targetPos.y,
|
||||
targetPos.z,
|
||||
Collections.emptySet(),
|
||||
originalYaw, originalPitch
|
||||
);
|
||||
}
|
||||
beLeashedEntity.setDeltaMovement(originalDeltaMovement);
|
||||
beLeashedEntity.setSprinting(originalIsSprinting);
|
||||
beLeashedEntity.setPose(originalPose);
|
||||
ILeashDataCapability.LeashInfo leashInfoOrDefault = Optional.ofNullable(originalLeashInfo.get()).map(i -> i.transferHolder(telEntity)).orElse(ILeashDataCapability.LeashInfo.EMPTY);
|
||||
beLeashedEntity.getCapability(CapabilityHandler.LEASH_DATA_CAP).ifPresent(
|
||||
cap -> cap.addLeash(telEntity, leashInfoOrDefault)
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
@SubscribeEvent
|
||||
public static void onPlayerClone(PlayerEvent.Clone event) {
|
||||
CapabilityRemainder.onPlayerClone(event);
|
||||
|
|
@ -88,7 +170,7 @@ public class CommonEventHandler {
|
|||
|
||||
@SubscribeEvent
|
||||
public static void onEntityInteract (PlayerInteractEvent.EntityInteract event) {
|
||||
LeashInteractHandler.onEntityInteract(event); //处理实体互动
|
||||
LeashInteractHandler.onEntityInteract(event.getLevel(), event.getHand(), event.getTarget(), event.getEntity(), event); //处理实体互动
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
|
|
@ -107,6 +189,12 @@ public class CommonEventHandler {
|
|||
public static void registerCapability(RegisterCapabilitiesEvent event) {
|
||||
CapabilityHandler.registerCapability(event);
|
||||
}
|
||||
@SubscribeEvent
|
||||
public static void onCreativeTab (BuildCreativeModeTabContentsEvent event) {
|
||||
if (event.getTabKey() == CreativeModeTabs.TOOLS_AND_UTILITIES) {
|
||||
event.accept(SLPItems.SUPER_LEAD_ROPE);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import net.minecraft.client.renderer.entity.EntityRenderer;
|
|||
import net.minecraft.client.renderer.entity.EntityRendererProvider;
|
||||
import net.minecraft.client.renderer.texture.OverlayTexture;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
|
@ -41,18 +42,30 @@ public class SuperLeashKnotRenderer extends EntityRenderer<SuperLeashKnotEntity>
|
|||
|
||||
@Override
|
||||
public void render(@NotNull SuperLeashKnotEntity entity, float entityYaw, float partialTick, @NotNull PoseStack poseStack, @NotNull MultiBufferSource buffer, int packedLight) {
|
||||
// 根据实体的实际 hitbox 调整缩放
|
||||
float scaleX = entity.getBbWidth() / 0.5f; // 默认 0.5f,对应 scale=1
|
||||
float scaleY = entity.getBbHeight() / 0.5f; // 默认 0.5f,对应 scale=1
|
||||
float scaleZ = scaleX; // 宽度对称
|
||||
AABB box = entity.getBoundingBox();
|
||||
float boxWidth = (float) box.getXsize();
|
||||
float boxHeight = (float) box.getYsize();
|
||||
|
||||
poseStack.scale(-1.5F * scaleX, -1.5F * scaleY, 1.5F * scaleZ);
|
||||
// 模型原始尺寸(像素 → 方块)
|
||||
float modelWidthBlocks = 6.0F / 16.0F;
|
||||
float modelHeightBlocks = 8.0F / 16.0F;
|
||||
|
||||
// 位置微调(避免浮空)
|
||||
poseStack.translate(0.0D, 0.15D * scaleY, 0.0D);
|
||||
// 缩放比例
|
||||
float scaleX = boxWidth / modelWidthBlocks;
|
||||
float scaleY = boxHeight / modelHeightBlocks;
|
||||
float scaleZ = scaleX;
|
||||
|
||||
poseStack.pushPose();
|
||||
|
||||
// 先缩放
|
||||
poseStack.scale(scaleX, scaleY, scaleZ);
|
||||
|
||||
// 再平移:把模型抬到碰撞箱底部
|
||||
poseStack.translate(0.0D, boxHeight / scaleY, 0.0D);
|
||||
|
||||
this.model.setupAnim(entity, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F);
|
||||
VertexConsumer vertexConsumer = buffer.getBuffer(this.model.renderType(KNOT_LOCATION));
|
||||
|
||||
this.model.renderToBuffer(poseStack, vertexConsumer, packedLight, OverlayTexture.NO_OVERLAY,
|
||||
1.0F, 1.0F, 1.0F, 1.0F);
|
||||
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ public class SuperLeashRenderer extends EntityRenderer<SuperLeashEntity> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getTextureLocation(@NotNull SuperLeashEntity entity) {
|
||||
return null; // 使用自定义渲染类型,不需要纹理
|
||||
public @NotNull ResourceLocation getTextureLocation(@NotNull SuperLeashEntity entity) {
|
||||
return new ResourceLocation("unknown"); // 使用自定义渲染类型,不需要纹理
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,9 +19,14 @@ import net.minecraft.core.BlockPos;
|
|||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.ListTag;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.*;
|
||||
import net.minecraft.world.entity.ai.goal.Goal;
|
||||
import net.minecraft.world.entity.ai.goal.RandomLookAroundGoal;
|
||||
import net.minecraft.world.entity.ai.goal.RandomStrollGoal;
|
||||
import net.minecraft.world.entity.animal.Animal;
|
||||
import net.minecraft.world.entity.animal.horse.Llama;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.entity.vehicle.Boat;
|
||||
import net.minecraft.world.entity.vehicle.Minecart;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
|
@ -36,9 +41,11 @@ import top.r3944realms.superleadrope.content.capability.inter.ILeashDataCapabili
|
|||
import top.r3944realms.superleadrope.content.entity.SuperLeashKnotEntity;
|
||||
import top.r3944realms.superleadrope.network.NetworkHandler;
|
||||
import top.r3944realms.superleadrope.network.toClient.LeashDataSyncPacket;
|
||||
import top.r3944realms.superleadrope.network.toClient.UpdatePlayerMovementPacket;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
|
|
@ -84,6 +91,7 @@ public class LeashDataImpl implements ILeashDataCapability {
|
|||
// 引入解决 绳结不保存导致第二进入持有者不存在的问题
|
||||
private final Map<BlockPos, LeashInfo> leashKnots = new ConcurrentHashMap<>();
|
||||
private CompoundTag lastSyncedData = new CompoundTag();
|
||||
|
||||
public LeashDataImpl(Entity entity) {
|
||||
this.entity = entity;
|
||||
}
|
||||
|
|
@ -155,6 +163,11 @@ public class LeashDataImpl implements ILeashDataCapability {
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addLeash(Entity holder, LeashInfo leashInfo) {
|
||||
return addLeash(holder, ItemStack.EMPTY, leashInfo.maxDistance(), leashInfo.elasticDistance(), leashInfo.maxKeepLeashTicks());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setMaxDistance(Entity holder, double newMaxDistance) {
|
||||
return holder instanceof SuperLeashKnotEntity superLeashKnotEntity ?
|
||||
|
|
@ -331,32 +344,82 @@ public class LeashDataImpl implements ILeashDataCapability {
|
|||
return true;
|
||||
}
|
||||
|
||||
// 计算拴绳拉力(防抖动逻辑)
|
||||
/**
|
||||
* 计算拴绳拉力(防抖动逻辑)
|
||||
*/
|
||||
@Override
|
||||
public void applyLeashForces() {
|
||||
for (Map.Entry<UUID, LeashInfo> uuidLeashInfoEntry : leashHolders.entrySet()) {
|
||||
internalUUIDApplyLeashForces(uuidLeashInfoEntry);
|
||||
Vec3 combinedForce = Vec3.ZERO; // 初始化合力向量
|
||||
|
||||
// 计算所有拴绳的合力
|
||||
for (Map.Entry<UUID, LeashInfo> entry : leashHolders.entrySet()) {
|
||||
Vec3 force = calculateLeashForceForUUID(entry);
|
||||
if (force != null) {
|
||||
combinedForce = combinedForce.add(force);
|
||||
}
|
||||
}
|
||||
for (Map.Entry<BlockPos, LeashInfo> blockPosLeashInfoEntry : leashKnots.entrySet()) {
|
||||
internalBlockPosApplyLeashForce(blockPosLeashInfoEntry);
|
||||
|
||||
for (Map.Entry<BlockPos, LeashInfo> entry : leashKnots.entrySet()) {
|
||||
Vec3 force = calculateLeashForceForBlockPos(entry);
|
||||
if (force != null) {
|
||||
combinedForce = combinedForce.add(force);
|
||||
}
|
||||
}
|
||||
|
||||
// 应用合力
|
||||
if (!combinedForce.equals(Vec3.ZERO)) {
|
||||
if(entity instanceof ServerPlayer serverPlayer) { //对于玩家发包交给客户端处理移动
|
||||
NetworkHandler.sendToPlayer(
|
||||
new UpdatePlayerMovementPacket(
|
||||
UpdatePlayerMovementPacket.Operation.ADD,
|
||||
combinedForce
|
||||
), serverPlayer
|
||||
);
|
||||
return; //后面的逻辑肯定与该分支无关,直接返回
|
||||
} else {
|
||||
entity.setDeltaMovement(entity.getDeltaMovement().add(combinedForce));
|
||||
entity.hurtMarked = true;
|
||||
}
|
||||
|
||||
// 有拴绳时:禁用移动控制
|
||||
if (entity instanceof Animal mob) {
|
||||
mob.goalSelector.disableControlFlag(Goal.Flag.MOVE);
|
||||
entity.resetFallDistance();
|
||||
}
|
||||
} else {
|
||||
// 无拴绳时:恢复移动控制
|
||||
if (entity instanceof Animal mob) {
|
||||
mob.goalSelector.enableControlFlag(Goal.Flag.MOVE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void internalUUIDApplyLeashForces(Map.Entry<UUID, LeashInfo> entry) {
|
||||
/**
|
||||
* 为UUID拴绳计算力
|
||||
*/
|
||||
private Vec3 calculateLeashForceForUUID(Map.Entry<UUID, LeashInfo> entry) {
|
||||
UUID uuid = entry.getKey();
|
||||
Entity uuidHolder = ((ServerLevel) entity.level()).getEntity(uuid);
|
||||
if (uuidHolder != null) {
|
||||
internalApplyLeashForces(uuidHolder, entry);
|
||||
return calculateLeashForce(uuidHolder, entry);
|
||||
} else {
|
||||
SuperLeadRope.logger.error("Could not apply leash forces for {}, because it is not found.", uuid);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void internalBlockPosApplyLeashForce(Map.Entry<BlockPos, LeashInfo> entry) {
|
||||
/**
|
||||
* 为方块位置拴绳计算力
|
||||
*/
|
||||
private Vec3 calculateLeashForceForBlockPos(Map.Entry<BlockPos, LeashInfo> entry) {
|
||||
SuperLeashKnotEntity orCreateKnot = SuperLeashKnotEntity.getOrCreateKnot(entity.level(), entry.getKey());
|
||||
internalApplyLeashForces(orCreateKnot, entry);
|
||||
return calculateLeashForce(orCreateKnot, entry);
|
||||
}
|
||||
private void internalApplyLeashForces(Entity holder, Map.Entry<?, LeashInfo> entry) {
|
||||
|
||||
/**
|
||||
* 计算单个拴绳的力
|
||||
*/
|
||||
private Vec3 calculateLeashForce(Entity holder, Map.Entry<?, LeashInfo> entry) {
|
||||
Vec3 holderPos = holder.position().add(0, holder.getBbHeight() * 0.7, 0);
|
||||
LeashInfo info = entry.getValue();
|
||||
Vec3 entityPos = entity.position().add(info.attachOffset());
|
||||
|
|
@ -365,41 +428,49 @@ public class LeashDataImpl implements ILeashDataCapability {
|
|||
|
||||
// 1. 检查是否超出断裂距离
|
||||
if (distance > extremeSnapDist) {
|
||||
// 如果还有剩余缓冲Tick,施加更强拉力并减少计数
|
||||
if (info.keepLeashTicks() > 0) {
|
||||
// 计算临界拉力(距离越远,拉力越强)
|
||||
// 计算临界拉力
|
||||
Vec3 pullForce = calculateCriticalPullForce(holderPos, entityPos, distance, info);
|
||||
entity.setDeltaMovement(entity.getDeltaMovement().add(pullForce));
|
||||
entity.hurtMarked = true;
|
||||
entry.setValue(info.decrementKeepLeashTicks());
|
||||
return;
|
||||
return pullForce;
|
||||
}
|
||||
// 否则立即断裂
|
||||
// 断裂
|
||||
removeLeash(holder);
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
// 2. 正常弹性拉力逻辑(保持不变)
|
||||
// 2. 正常弹性拉力逻辑
|
||||
Vec3 pullForce = Vec3.ZERO;
|
||||
if (distance > info.elasticDistance()) {
|
||||
Vec3 pullForce = calculatePullForce(holderPos, entityPos, distance, info);
|
||||
entity.setDeltaMovement(entity.getDeltaMovement().add(pullForce));
|
||||
entity.hurtMarked = true;
|
||||
pullForce = calculatePullForce(holderPos, entityPos, distance, info);
|
||||
|
||||
// 生物添加跟随逻辑(保持不变)
|
||||
if(entity instanceof Mob mob) {
|
||||
Vec3 vec3 = (new Vec3(holder.getX() - entity.getX(), holder.getY() - entity.getY(), holder.getZ() - entity.getZ()))
|
||||
.normalize()
|
||||
.scale(Math.max(distance - 2.0F, 0.0F));
|
||||
double speed = mob instanceof Llama ? 2.0 : 1.0;
|
||||
mob.getNavigation().moveTo(entity.getX() + vec3.x, entity.getY() + vec3.y, entity.getZ() + vec3.z, speed);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 重置缓冲Tick(如果回到安全距离)
|
||||
// 3. 重置缓冲Tick
|
||||
if (distance <= info.maxDistance() && info.keepLeashTicks() < info.maxKeepLeashTicks()) {
|
||||
entry.setValue(info.resetKeepLeashTicks());
|
||||
}
|
||||
|
||||
return pullForce;
|
||||
}
|
||||
|
||||
// 计算正常拉力(保持不变)
|
||||
@Contract("_, _, _, _ -> new")
|
||||
private @NotNull Vec3 calculatePullForce(@NotNull Vec3 holderPos, Vec3 entityPos, double distance, @NotNull LeashInfo info) {
|
||||
Vec3 pullDirection = holderPos.subtract(entityPos).normalize();
|
||||
double pullStrength = 0.2;
|
||||
|
||||
// 增强拉力(如果超出maxDistance但未达断裂距离)
|
||||
if (distance > info.maxDistance()) {
|
||||
double excessRatio = (distance - info.maxDistance()) / (info.maxDistance());
|
||||
pullStrength += excessRatio * 0.8; // 最高1.0倍基础拉力
|
||||
double excessRatio = (distance - info.maxDistance()) / info.maxDistance();
|
||||
pullStrength += excessRatio * 0.8;
|
||||
}
|
||||
|
||||
Vec3 pullForce = pullDirection.scale(
|
||||
|
|
@ -412,18 +483,17 @@ public class LeashDataImpl implements ILeashDataCapability {
|
|||
pullForce.z * AXIS_SPECIFIC_ELASTICITY.z
|
||||
);
|
||||
}
|
||||
|
||||
// 计算临界拉力(保持不变)
|
||||
private @NotNull Vec3 calculateCriticalPullForce(@NotNull Vec3 holderPos, Vec3 entityPos, double distance, @NotNull LeashInfo info) {
|
||||
Vec3 pullDirection = holderPos.subtract(entityPos).normalize();
|
||||
|
||||
// 非线性增强拉力(距离越远,拉力越强)
|
||||
double excessRatio = (distance - info.maxDistance()) / (info.maxDistance());
|
||||
double pullStrength = 1.0 + excessRatio * 2.0; // 基础1.0 + 额外增强(最高3.0倍)
|
||||
double excessRatio = (distance - info.maxDistance()) / info.maxDistance();
|
||||
double pullStrength = 1.0 + excessRatio * 2.0;
|
||||
|
||||
Vec3 pullForce = pullDirection.scale(
|
||||
(distance - info.elasticDistance()) * pullStrength * SPRING_DAMPENING
|
||||
);
|
||||
|
||||
// 应用轴向弹性系数(减少Y轴抖动)
|
||||
return new Vec3(
|
||||
pullForce.x * AXIS_SPECIFIC_ELASTICITY.x,
|
||||
pullForce.y * AXIS_SPECIFIC_ELASTICITY.y,
|
||||
|
|
@ -702,11 +772,14 @@ public class LeashDataImpl implements ILeashDataCapability {
|
|||
public static @NotNull List<Entity> leashableInArea(Entity entity, Predicate<Entity> filter) {
|
||||
return leashableInArea(entity, filter, 1024D);
|
||||
}
|
||||
public static @NotNull List<Entity> leashableInArea(Entity holder) {
|
||||
return leashableInArea(holder, i -> isLeashHolder(i, holder), 1024D);
|
||||
}
|
||||
public boolean canBeAttachedTo(Entity pEntity) {
|
||||
if(pEntity == entity) {
|
||||
return false;
|
||||
} else {
|
||||
Optional<LeashInfo> leashInfo = getLeashInfo(pEntity.getUUID());
|
||||
Optional<LeashInfo> leashInfo = getLeashInfo(pEntity);
|
||||
return leashInfo.isEmpty() && (entity.distanceTo(pEntity) <= LEASH_ELASTIC_DIST * LEASH_EXTREME_SNAP_DIST_FACTOR) && canBeLeashed();//距离最大,则不可以被固定或转移
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ public interface ILeashDataCapability extends INBTSerializable<CompoundTag> {
|
|||
// 原LeashData的方法接口
|
||||
boolean addLeash(Entity holder, ItemStack leashStack, double maxDistance);
|
||||
boolean addLeash(Entity holder, ItemStack leashStack, double maxDistance, double elasticDistance, int maxKeepLeashTicks);
|
||||
boolean addLeash(Entity holder, LeashInfo leashInfo);
|
||||
|
||||
boolean setMaxDistance(Entity holder, double newMaxDistance);
|
||||
boolean setMaxDistance(Entity holder,double newMaxDistance, int newMaxKeepLeashTicks);
|
||||
|
|
@ -82,6 +83,10 @@ public interface ILeashDataCapability extends INBTSerializable<CompoundTag> {
|
|||
int keepLeashTicks, // 新增:保持拴绳的剩余Tick数
|
||||
int maxKeepLeashTicks // 新增:最大保持Tick数(可配置)
|
||||
) {
|
||||
public static final LeashInfo EMPTY = new LeashInfo(
|
||||
Optional.empty(), Optional.empty(), Optional.empty(),
|
||||
"", Vec3.ZERO, 12.0D, 6.0D, 0, 0
|
||||
);
|
||||
public static LeashInfo CreateLeashInfo(
|
||||
Entity entity,
|
||||
String reserved,
|
||||
|
|
|
|||
|
|
@ -52,6 +52,11 @@ public class SuperLeashEntity extends Entity {
|
|||
if(!level().isClientSide) controlled.getCapability(CapabilityHandler.LEASH_DATA_CAP).ifPresent(ILeashDataCapability::applyLeashForces);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void kill() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void defineSynchedData() {
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import net.minecraft.tags.BlockTags;
|
|||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.damagesource.DamageSource;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.decoration.LeashFenceKnotEntity;
|
||||
|
|
@ -57,11 +58,31 @@ public class SuperLeashKnotEntity extends LeashFenceKnotEntity {
|
|||
super(pEntityType, pLevel);
|
||||
}
|
||||
|
||||
|
||||
public SuperLeashKnotEntity(Level pLevel, BlockPos pPos) {
|
||||
this(SLPEntityTypes.SUPER_LEAD_KNOT.get(), pLevel);
|
||||
this.setPos(pPos.getX(), pPos.getY(), pPos.getZ());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hurt(@NotNull DamageSource source, float amount) {
|
||||
if (this.isInvulnerableTo(source)) {
|
||||
return false;
|
||||
} else {
|
||||
if (!this.isRemoved() && !this.level().isClientSide) {
|
||||
this.kill();
|
||||
this.markHurt();
|
||||
List<Entity> entities = LeashDataImpl.leashableInArea(this.level(), pos.getCenter(), entity -> LeashDataImpl.isLeashHolder(entity, this));
|
||||
entities.forEach(entity -> entity
|
||||
.getCapability(CapabilityHandler.LEASH_DATA_CAP)
|
||||
.map(iLeashDataCapability -> iLeashDataCapability.removeLeash(this))
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean survives() {
|
||||
|
|
@ -73,7 +94,15 @@ public class SuperLeashKnotEntity extends LeashFenceKnotEntity {
|
|||
int j = pPos.getY();
|
||||
int k = pPos.getZ();
|
||||
|
||||
for(SuperLeashKnotEntity superLeashKnotEntity : pLevel.getEntitiesOfClass(SuperLeashKnotEntity.class, new AABB((double)i - 1.0D, (double)j - 1.0D, (double)k - 1.0D, (double)i + 1.0D, (double)j + 1.0D, (double)k + 1.0D))) {
|
||||
for(SuperLeashKnotEntity superLeashKnotEntity :
|
||||
pLevel.getEntitiesOfClass(
|
||||
SuperLeashKnotEntity.class,
|
||||
new AABB((double)i - 1.0D,
|
||||
(double)j - 1.0D,
|
||||
(double)k - 1.0D,
|
||||
(double)i + 1.0D,
|
||||
(double)j + 1.0D,
|
||||
(double)k + 1.0D))) {
|
||||
if (superLeashKnotEntity.getPos().equals(pPos)) {
|
||||
return superLeashKnotEntity;
|
||||
}
|
||||
|
|
@ -87,6 +116,7 @@ public class SuperLeashKnotEntity extends LeashFenceKnotEntity {
|
|||
@Override
|
||||
protected void recalculateBoundingBox() {
|
||||
updateDimensionsBasedOnBlock();
|
||||
setPosRaw(this.pos.getX() + 0.5, this.pos.getY() + 0.20, this.pos.getZ() + 0.5);
|
||||
double halfWidth = currentWidth / 2.0f;
|
||||
this.setBoundingBox(new AABB(
|
||||
this.getX() - halfWidth,
|
||||
|
|
@ -104,7 +134,7 @@ public class SuperLeashKnotEntity extends LeashFenceKnotEntity {
|
|||
// 根据方块类型调整尺寸
|
||||
if (state.is(BlockTags.WALLS)) {
|
||||
// 墙类方块 - 更窄更高
|
||||
currentWidth = 0.25f;
|
||||
currentWidth = 0.75f;
|
||||
currentHeight = 0.75f;
|
||||
} else {
|
||||
// 默认栅栏尺寸
|
||||
|
|
@ -113,13 +143,6 @@ public class SuperLeashKnotEntity extends LeashFenceKnotEntity {
|
|||
}
|
||||
//TODO: 未来扩展可配置化大小
|
||||
}
|
||||
@Override
|
||||
public void setPos(double x, double y, double z) {
|
||||
super.setPos(x, y, z);
|
||||
// 确保位置与方块中心对齐
|
||||
BlockPos pos = this.getPos();
|
||||
this.setPosRaw(pos.getX() + 0.5, pos.getY() + 0.375, pos.getZ() + 0.5);
|
||||
}
|
||||
|
||||
public static boolean isSupportBlock(BlockState state) {
|
||||
for(TagKey<Block> tagKey : SUPPORTED_BLOCK) {
|
||||
|
|
@ -135,7 +158,7 @@ public class SuperLeashKnotEntity extends LeashFenceKnotEntity {
|
|||
return InteractionResult.SUCCESS;
|
||||
}
|
||||
AtomicBoolean isTransferLeash = new AtomicBoolean(false);
|
||||
List<Entity> entities = LeashDataImpl.leashableInArea(player, ing -> true);
|
||||
List<Entity> entities = LeashDataImpl.leashableInArea(player);
|
||||
for(Entity entity : entities) {
|
||||
if (LeashDataImpl.isLeashHolder(entity, player.getUUID()))
|
||||
entity.getCapability(CapabilityHandler.LEASH_DATA_CAP).ifPresent(i -> {
|
||||
|
|
@ -143,10 +166,22 @@ public class SuperLeashKnotEntity extends LeashFenceKnotEntity {
|
|||
isTransferLeash.set(true);
|
||||
});
|
||||
|
||||
}
|
||||
AtomicBoolean isRemoveLeashKnot = new AtomicBoolean(false);
|
||||
if (!isTransferLeash.get()) {
|
||||
this.discard();
|
||||
if (player.getAbilities().instabuild) {
|
||||
entities.forEach(
|
||||
entity -> entity
|
||||
.getCapability(CapabilityHandler.LEASH_DATA_CAP)
|
||||
.ifPresent(iLeashDataCapability -> {
|
||||
iLeashDataCapability.removeLeash(this);
|
||||
isRemoveLeashKnot.set(true);
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
if (isTransferLeash.get() ) {
|
||||
}
|
||||
if (isTransferLeash.get() || isRemoveLeashKnot.get()) {
|
||||
this.gameEvent(GameEvent.BLOCK_ATTACH, player);
|
||||
}
|
||||
return InteractionResult.CONSUME;
|
||||
|
|
|
|||
|
|
@ -51,7 +51,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
// 实现拴生物,在生物的interact方法里去写相关逻辑
|
||||
// (尝试0 mixin 实现 加强拴绳逻辑)
|
||||
public class SuperLeadRopeItem extends LeadItem implements IForgeItem {
|
||||
|
||||
// 配置常量
|
||||
// 【手动调节,可以通过附魔获取更远抛掷和抛掷距离 - x1.3】//TODO:将可抛掷实现留到下次编写
|
||||
// 可以做个大于一定距离时远距离使用时抛出拴绳的实体,击中生物才栓中的
|
||||
|
|
@ -92,12 +91,32 @@ public class SuperLeadRopeItem extends LeadItem implements IForgeItem {
|
|||
if(SuperLeashKnotEntity.isSupportBlock(state)) {
|
||||
Player player = context.getPlayer();
|
||||
if(!level.isClientSide && player != null) {
|
||||
return bindToBlock(player, level, pos, itemStack);
|
||||
return bindToBlock(player, level, pos, itemStack, false) ? InteractionResult.CONSUME : InteractionResult.PASS;
|
||||
}
|
||||
}
|
||||
return InteractionResult.PASS;
|
||||
}
|
||||
public static InteractionResult bindToEntity (Entity newHolder, Player player, Level level, BlockPos pos, ItemStack leashStack) {
|
||||
/**
|
||||
* 右键蹲下绑定到另一实体上
|
||||
* @param newHolder 新实体
|
||||
* @param player 明确持有玩家
|
||||
* @param level 维度世界
|
||||
* @param leashStack 拴绳物品实例
|
||||
* @return 是否成功
|
||||
*/
|
||||
public static boolean bindToEntity(Entity newHolder, Player player, Level level, ItemStack leashStack) {
|
||||
return bindToEntity(newHolder, player, level, player.getOnPos(), leashStack);
|
||||
}
|
||||
/**
|
||||
* 右键蹲下绑定到另一实体上
|
||||
* @param newHolder 新实体
|
||||
* @param player 明确持有玩家
|
||||
* @param level 维度世界
|
||||
* @param pos 坐标(一般是明确持有玩家的位置)
|
||||
* @param leashStack 拴绳物品实例
|
||||
* @return 是否成功
|
||||
*/
|
||||
public static boolean bindToEntity(Entity newHolder, Player player, Level level, BlockPos pos, ItemStack leashStack) {
|
||||
AtomicBoolean isSuccess = new AtomicBoolean(false);
|
||||
List<Entity> list = LeashDataImpl.leashableInArea(level, pos.getCenter(), entity -> LeashDataImpl.isLeashHolder(entity, player.getUUID()));
|
||||
for(Entity e : list) {
|
||||
|
|
@ -118,22 +137,43 @@ public class SuperLeadRopeItem extends LeadItem implements IForgeItem {
|
|||
}
|
||||
}
|
||||
if(!isSuccess.get()) {
|
||||
return InteractionResult.PASS;
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
level.gameEvent(GameEvent.ENTITY_INTERACT, pos, GameEvent.Context.of(player));
|
||||
newHolder.playSound(SLPSoundEvents.LEAD_TIED.get());
|
||||
return InteractionResult.CONSUME;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
public static InteractionResult bindToBlock(Player player, Level level, BlockPos pos, ItemStack leashStack) {
|
||||
|
||||
/**
|
||||
* 右键蹲下绑定到支持方块上
|
||||
* @param player 明确持有玩家
|
||||
* @param level 维度世界
|
||||
* @param leashStack 拴绳物品实例
|
||||
* @param shouldBindSelf 是否应该触发拴自己逻辑检查
|
||||
* @return 是否成功
|
||||
*/
|
||||
public static boolean bindToBlock(Player player, Level level, ItemStack leashStack, boolean shouldBindSelf) {
|
||||
return bindToBlock(player, level, player.getOnPos(), leashStack, shouldBindSelf);
|
||||
}
|
||||
/**
|
||||
* 右键蹲下绑定到支持方块上
|
||||
* @param player 明确持有玩家
|
||||
* @param level 维度世界
|
||||
* @param pos 坐标(一般是明确持有玩家的位置)
|
||||
* @param leashStack 拴绳物品实例
|
||||
* @param shouldBindSelf 是否应该触发拴自己逻辑检查
|
||||
* @return 是否成功
|
||||
*/
|
||||
public static boolean bindToBlock(Player player, Level level, BlockPos pos, ItemStack leashStack, boolean shouldBindSelf) {
|
||||
//实现个加强绳结实体
|
||||
SuperLeashKnotEntity knot = null;
|
||||
AtomicBoolean isSuccess = new AtomicBoolean(false);
|
||||
UUID uuid = player.getUUID();
|
||||
List<Entity> list = LeashDataImpl.leashableInArea(level, pos.getCenter(), entity -> LeashDataImpl.isLeashHolder(entity, uuid));
|
||||
if(list.isEmpty()) {//拴自己 to new knot
|
||||
if (leashStack.isEmpty() || !canUse(leashStack)) return InteractionResult.PASS;
|
||||
if (shouldBindSelf && list.isEmpty()) {//拴自己 to new knot
|
||||
if (leashStack.isEmpty() || !canUse(leashStack)) return false;
|
||||
knot = SuperLeashKnotEntity.getOrCreateKnot(level, pos);;
|
||||
knot.playPlacementSound();
|
||||
|
||||
|
|
@ -146,10 +186,10 @@ public class SuperLeadRopeItem extends LeadItem implements IForgeItem {
|
|||
});
|
||||
|
||||
}
|
||||
else {
|
||||
else if (!list.isEmpty()) {
|
||||
for(Entity e : list) {
|
||||
if(knot == null) {
|
||||
knot = SuperLeashKnotEntity.getOrCreateKnot(level, pos);;
|
||||
knot = SuperLeashKnotEntity.getOrCreateKnot(level, pos);
|
||||
knot.playPlacementSound();
|
||||
}
|
||||
AtomicBoolean canBeAttachedTo = new AtomicBoolean(false);
|
||||
|
|
@ -171,12 +211,9 @@ public class SuperLeadRopeItem extends LeadItem implements IForgeItem {
|
|||
}
|
||||
}
|
||||
if (isSuccess.get()) {
|
||||
if(!player.isCreative()) {
|
||||
leashStack.hurtAndBreak(50, player, e->{});
|
||||
}
|
||||
level.gameEvent(GameEvent.BLOCK_ATTACH, pos, GameEvent.Context.of(player));
|
||||
return InteractionResult.SUCCESS;
|
||||
} else
|
||||
return InteractionResult.PASS;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,9 +17,11 @@ package top.r3944realms.superleadrope.core.leash;
|
|||
|
||||
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.gameevent.GameEvent;
|
||||
import net.minecraftforge.common.util.LazyOptional;
|
||||
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
|
||||
|
|
@ -32,18 +34,16 @@ import top.r3944realms.superleadrope.core.register.SLPSoundEvents;
|
|||
|
||||
public class LeashInteractHandler {
|
||||
//只有玩家可以互动触发(其它的暂不支持(考虑到0 Mixin)
|
||||
public static void onEntityInteract (PlayerInteractEvent.EntityInteract event) {
|
||||
public static void onEntityInteract (Level level, InteractionHand hand, Entity target , Player player, PlayerInteractEvent.EntityInteract event) {
|
||||
//WARNING: 主手和副手都会触发一次该事件
|
||||
|
||||
// ===== 卫语句 =====
|
||||
if (event.getLevel().isClientSide) {
|
||||
if (level.isClientSide) {
|
||||
return;
|
||||
}
|
||||
InteractionHand hand = event.getHand();
|
||||
if (hand == InteractionHand.OFF_HAND) {
|
||||
return;
|
||||
}
|
||||
Entity target = event.getTarget();
|
||||
|
||||
if (!LeashDataImpl.isLeashable(target)) {
|
||||
return;
|
||||
|
|
@ -52,7 +52,6 @@ public class LeashInteractHandler {
|
|||
if (!LeashCap.isPresent()) {
|
||||
return;
|
||||
}
|
||||
Player player = event.getEntity();
|
||||
|
||||
ItemStack mainHandItem = player.getItemInHand(InteractionHand.MAIN_HAND);
|
||||
ItemStack offHandItem = player.getItemInHand(InteractionHand.OFF_HAND);
|
||||
|
|
@ -64,7 +63,11 @@ public class LeashInteractHandler {
|
|||
|
||||
) {
|
||||
|
||||
SuperLeadRopeItem.bindToEntity(target, player, player.level(), target.getOnPos(), ItemStack.EMPTY);
|
||||
boolean isSuccess = SuperLeadRopeItem.bindToEntity(target, player, player.level(), player.getOnPos(), ItemStack.EMPTY);
|
||||
if (isSuccess) {
|
||||
event.setCanceled(true);
|
||||
event.setCancellationResult(InteractionResult.CONSUME);
|
||||
}
|
||||
} else {
|
||||
if(LeashDataImpl.isLeashHolder(target, player)) {
|
||||
LeashCap.ifPresent(
|
||||
|
|
@ -83,18 +86,18 @@ public class LeashInteractHandler {
|
|||
itemStack = ItemStack.EMPTY;
|
||||
}
|
||||
if (!itemStack.isEmpty()) {
|
||||
if (itemStack.getItem() == SLPItems.SUPER_LEAD_ROPE.get()) {}
|
||||
LeashCap.ifPresent(iLeashDataCapability -> {
|
||||
if (iLeashDataCapability.canBeAttachedTo(player)) {
|
||||
boolean success = iLeashDataCapability.addLeash(player, itemStack, 12);
|
||||
if (success) {
|
||||
if(!player.isCreative())
|
||||
itemStack.hurtAndBreak(50, player, e->{});
|
||||
target.playSound(SLPSoundEvents.LEAD_TIED.get());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (itemStack.getItem() == SLPItems.SUPER_LEAD_ROPE.get()) {
|
||||
LeashCap.ifPresent(iLeashDataCapability -> {
|
||||
if (iLeashDataCapability.canBeAttachedTo(player)) {
|
||||
boolean success = iLeashDataCapability.addLeash(player, itemStack, 12);
|
||||
if (success) {
|
||||
if(!player.isCreative())
|
||||
itemStack.hurtAndBreak(50, player, e->{});
|
||||
target.playSound(SLPSoundEvents.LEAD_TIED.get());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,10 +31,10 @@ public class SLPEntityTypes {
|
|||
public static RegistryObject<EntityType<SuperLeashKnotEntity>> SUPER_LEAD_KNOT = ENTITY_TYPES.register(
|
||||
"super_lead_knot",
|
||||
() -> EntityType.Builder.<SuperLeashKnotEntity>of(SuperLeashKnotEntity::new, MobCategory.MISC)
|
||||
.sized(1.0F, 1.0F)
|
||||
.clientTrackingRange(4)
|
||||
.updateInterval(20)
|
||||
.noSave()
|
||||
.sized(0.375F, 0.5F)
|
||||
.clientTrackingRange(10)
|
||||
.updateInterval(Integer.MAX_VALUE)
|
||||
.build("super_lead_knot")
|
||||
);
|
||||
public static RegistryObject<EntityType<SuperLeashEntity>> SUPER_LEASH = ENTITY_TYPES.register(
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import net.minecraftforge.network.PacketDistributor;
|
|||
import net.minecraftforge.network.simple.SimpleChannel;
|
||||
import top.r3944realms.superleadrope.SuperLeadRope;
|
||||
import top.r3944realms.superleadrope.network.toClient.LeashDataSyncPacket;
|
||||
import top.r3944realms.superleadrope.network.toClient.UpdatePlayerMovementPacket;
|
||||
|
||||
public class NetworkHandler {
|
||||
private static final String PROTOCOL_VERSION = "1";
|
||||
|
|
@ -39,6 +40,11 @@ public class NetworkHandler {
|
|||
.encoder(LeashDataSyncPacket::encode)
|
||||
.consumerNetworkThread(LeashDataSyncPacket::handle)
|
||||
.add();
|
||||
INSTANCE.messageBuilder(UpdatePlayerMovementPacket.class, cid++, NetworkDirection.PLAY_TO_CLIENT)
|
||||
.decoder(UpdatePlayerMovementPacket::decode)
|
||||
.encoder(UpdatePlayerMovementPacket::encode)
|
||||
.consumerNetworkThread(UpdatePlayerMovementPacket::handle)
|
||||
.add();
|
||||
}
|
||||
public static <MSG> void sendAllPlayer(MSG message){
|
||||
INSTANCE.send(PacketDistributor.ALL.noArg(), message);
|
||||
|
|
|
|||
|
|
@ -19,20 +19,15 @@ import net.minecraft.client.Minecraft;
|
|||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraftforge.network.ICustomPacket;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
import top.r3944realms.superleadrope.content.capability.CapabilityHandler;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class LeashDataSyncPacket {
|
||||
private final int entityId;
|
||||
private final CompoundTag leashData;
|
||||
|
||||
public LeashDataSyncPacket(int entityId, CompoundTag leashData) {
|
||||
this.entityId = entityId;
|
||||
this.leashData = leashData;
|
||||
}
|
||||
public record LeashDataSyncPacket(int entityId, CompoundTag leashData) {
|
||||
|
||||
public static void encode(LeashDataSyncPacket msg, FriendlyByteBuf buffer) {
|
||||
buffer.writeInt(msg.entityId);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Super Lead rope mod
|
||||
* Copyright (C) 2025 R3944Realms
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.r3944realms.superleadrope.network.toClient;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.player.LocalPlayer;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public record UpdatePlayerMovementPacket(Operation operation, double x, double y, double z) {
|
||||
public static void encode(UpdatePlayerMovementPacket packet, FriendlyByteBuf buffer) {
|
||||
buffer.writeEnum(packet.operation());
|
||||
buffer.writeDouble(packet.x());
|
||||
buffer.writeDouble(packet.y());
|
||||
buffer.writeDouble(packet.z());
|
||||
}
|
||||
public UpdatePlayerMovementPacket(Operation operation, Vec3 vec) {
|
||||
this(operation, vec.x, vec.y, vec.z);
|
||||
}
|
||||
|
||||
public static UpdatePlayerMovementPacket decode(FriendlyByteBuf buffer) {
|
||||
return new UpdatePlayerMovementPacket(
|
||||
buffer.readEnum(Operation.class),
|
||||
buffer.readDouble(),
|
||||
buffer.readDouble(),
|
||||
buffer.readDouble()
|
||||
);
|
||||
}
|
||||
|
||||
public static void handle(UpdatePlayerMovementPacket packet, Supplier<NetworkEvent.Context> ctx) {
|
||||
NetworkEvent.Context context = ctx.get();
|
||||
context.enqueueWork(() -> {
|
||||
LocalPlayer player = Minecraft.getInstance().player;
|
||||
assert player != null;
|
||||
switch (packet.operation) {
|
||||
case ADD -> player.addDeltaMovement(new Vec3(packet.x, packet.y, packet.z));
|
||||
case SET -> player.setDeltaMovement(new Vec3(packet.x, packet.y, packet.z));
|
||||
case MULTIPLY -> player.addDeltaMovement(player.getDeltaMovement().multiply(packet.x, packet.y, packet.z));
|
||||
}
|
||||
}
|
||||
);
|
||||
context.setPacketHandled(true);
|
||||
}
|
||||
public enum Operation {
|
||||
SET,
|
||||
ADD,
|
||||
MULTIPLY
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user