commit c3bff64c616c8df2deb8887a9a384c40b68cfb15 Author: 3944Realms Date: Thu Apr 10 09:03:09 2025 +0800 init 1.21.5 diff --git a/src/main/java/com/r3944realms/leashedplayer/ClientEventHandler.java b/src/main/java/com/r3944realms/leashedplayer/ClientEventHandler.java new file mode 100644 index 0000000..1921138 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/ClientEventHandler.java @@ -0,0 +1,125 @@ +package com.r3944realms.leashedplayer; + +import com.r3944realms.leashedplayer.client.renderer.LeashRendererUtil; +import com.r3944realms.leashedplayer.client.renderer.PlayerLeashState; +import com.r3944realms.leashedplayer.client.renderer.entities.ChestItemLayerRenderer; +import com.r3944realms.leashedplayer.client.renderer.entities.LeashRopeArrowRenderer; +import com.r3944realms.leashedplayer.client.renderer.entities.SpectralLeashRopeArrowRenderer; +import com.r3944realms.leashedplayer.client.renderer.item.ConditionalRangeItemModel; +import com.r3944realms.leashedplayer.client.renderer.item.properties.conditional.ChargeExtend; +import com.r3944realms.leashedplayer.content.ModKeyMapping; +import com.r3944realms.leashedplayer.content.entities.ModEntityRegister; +import com.r3944realms.leashedplayer.modInterface.IPlayerRenderStateExtension; +import com.r3944realms.leashedplayer.modInterface.PlayerLeashable; +import com.r3944realms.leashedplayer.network.server.Code; +import com.r3944realms.leashedplayer.network.server.DecreaseLeashRopeLength; +import com.r3944realms.leashedplayer.network.server.IncreaseLeashRopeLength; +import com.r3944realms.leashedplayer.utils.Util; +import net.minecraft.client.Minecraft; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.client.renderer.entity.player.PlayerRenderer; +import net.minecraft.client.renderer.entity.state.PlayerRenderState; +import net.minecraft.client.resources.PlayerSkin; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.phys.HitResult; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.common.EventBusSubscriber; +import net.neoforged.neoforge.client.event.*; +import net.neoforged.neoforge.network.PacketDistributor; + +import java.util.List; + + +public class ClientEventHandler { + @EventBusSubscriber(value = Dist.CLIENT, bus = EventBusSubscriber.Bus.GAME, modid = LeashedPlayer.MOD_ID) + public static class Game { + @SubscribeEvent + public static void onPlayerRendererEventPre(RenderPlayerEvent.Post event) { + PlayerRenderState renderState = event.getRenderState(); + PlayerLeashState playerLeashState = ((IPlayerRenderStateExtension) renderState).getPlayerLeashState(); + if(playerLeashState != null) { + if (playerLeashState.vanilaLeashState != null) + LeashRendererUtil.renderLeash(event.getPoseStack(), event.getMultiBufferSource(), playerLeashState.vanilaLeashState); + else + LeashRendererUtil.renderLeash(event.getPoseStack(), event.getMultiBufferSource(), renderState); + } + } + @SubscribeEvent + public static void onKetBoardInput(InputEvent.Key event) { + Minecraft minecraft = Minecraft.getInstance(); + LocalPlayer player = minecraft.player; + assert player != null; + if (ModKeyMapping.KEY_ADD_LEASH_LENGTH.isDown()) { + PlayerLeashable playerLeashable = (PlayerLeashable) player; + if (playerLeashable.getLeashDataFromEntityData() == null) { + assert minecraft.level != null; + List refLookAtEntityHitResult = Util.getRefLookAtEntityHitResult(player, minecraft.level, 32, entity -> entity instanceof LivingEntity); + Entity theNearestEntityFromHitResultList = Util.getTheNearestEntityFromHitResultList(player, refLookAtEntityHitResult); + if(theNearestEntityFromHitResultList != null) { + if (theNearestEntityFromHitResultList instanceof Player refPlayer) { + PacketDistributor.sendToServer(new IncreaseLeashRopeLength(Code.OTHER_ST, refPlayer.getStringUUID())); + } + } + } + if (playerLeashable.getLeashDataFromEntityData() != null) { + PacketDistributor.sendToServer(new IncreaseLeashRopeLength(Code.SELF, player.getStringUUID())); + } + } + if (ModKeyMapping.KEY_SUB_LEASH_LENGTH.isDown()) { + PlayerLeashable playerLeashable = (PlayerLeashable) player; + if (playerLeashable.getLeashDataFromEntityData() == null) { + assert minecraft.level != null; + List refLookAtEntityHitResult = Util.getRefLookAtEntityHitResult(player, minecraft.level, 32, entity -> entity instanceof LivingEntity); + Entity theNearestEntityFromHitResultList = Util.getTheNearestEntityFromHitResultList(player, refLookAtEntityHitResult); + if(theNearestEntityFromHitResultList != null) { + if (theNearestEntityFromHitResultList instanceof Player refPlayer) { + PacketDistributor.sendToServer(new DecreaseLeashRopeLength(Code.OTHER_ST, refPlayer.getStringUUID())); + } + } + } + if (playerLeashable.getLeashDataFromEntityData() != null) { + PacketDistributor.sendToServer(new DecreaseLeashRopeLength(Code.SELF, player.getStringUUID())); + } + } + } + } + + @EventBusSubscriber(value = Dist.CLIENT, bus = EventBusSubscriber.Bus.MOD, modid = LeashedPlayer.MOD_ID) + public static class Mod { + @SubscribeEvent + public static void onRegisterItemModels(RegisterItemModelsEvent event) { + event.register(ResourceLocation.fromNamespaceAndPath(LeashedPlayer.MOD_ID, "conditional_range"), ConditionalRangeItemModel.Unbaked.MAP_CODEC); + } + @SubscribeEvent + public static void registrySelectItemModelProperty(RegisterSelectItemModelPropertyEvent event) { + event.register(ResourceLocation.fromNamespaceAndPath(LeashedPlayer.MOD_ID, "charge_extend_type"), ChargeExtend.TYPE); + } + @SubscribeEvent + public static void registerKeyMapping(RegisterKeyMappingsEvent event) { + event.register(ModKeyMapping.KEY_ADD_LEASH_LENGTH); + event.register(ModKeyMapping.KEY_SUB_LEASH_LENGTH); + } + @SubscribeEvent + public static void onAddLayers (EntityRenderersEvent.AddLayers event){ + PlayerRenderer renderer = event.getSkin(PlayerSkin.Model.WIDE); + if (renderer instanceof PlayerRenderer playerRenderer) { + playerRenderer.addLayer(new ChestItemLayerRenderer<>(playerRenderer, event.getContext().getEntityRenderDispatcher().getItemInHandRenderer())); + } + PlayerRenderer slimRenderer = event.getSkin(PlayerSkin.Model.SLIM); + if (slimRenderer instanceof PlayerRenderer slimPlayerRenderer) { + slimPlayerRenderer.addLayer(new ChestItemLayerRenderer<>(slimPlayerRenderer, event.getContext().getEntityRenderDispatcher().getItemInHandRenderer())); + } + } + @SubscribeEvent + public static void RegisterRenderer(EntityRenderersEvent.RegisterRenderers event) { + event.registerEntityRenderer(ModEntityRegister.LEASH_ROPE_ARROW.get(), LeashRopeArrowRenderer::new); + event.registerEntityRenderer(ModEntityRegister.SPECTRAL_LEASH_ROPE_ARROW.get(), SpectralLeashRopeArrowRenderer::new); + } + } + + +} diff --git a/src/main/java/com/r3944realms/leashedplayer/CommonEventHandler.java b/src/main/java/com/r3944realms/leashedplayer/CommonEventHandler.java new file mode 100644 index 0000000..bd55ea3 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/CommonEventHandler.java @@ -0,0 +1,108 @@ +package com.r3944realms.leashedplayer; + +import com.mojang.brigadier.CommandDispatcher; +import com.r3944realms.leashedplayer.content.commands.LeashCommand; +import com.r3944realms.leashedplayer.content.commands.MotionCommand; +import com.r3944realms.leashedplayer.content.commands.TickCommand; +import com.r3944realms.leashedplayer.content.effects.ModPotionRegister; +import com.r3944realms.leashedplayer.content.items.ModItemRegister; +import com.r3944realms.leashedplayer.content.misc.LeadBreakItemBehavior; +import com.r3944realms.leashedplayer.utils.Util; +import net.minecraft.ChatFormatting; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.core.component.DataComponents; +import net.minecraft.network.chat.Component; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.tags.ItemTags; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.animal.Fox; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.alchemy.PotionBrewing; +import net.minecraft.world.item.alchemy.Potions; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.DispenserBlock; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.common.EventBusSubscriber; +import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent; +import net.neoforged.neoforge.event.AnvilUpdateEvent; +import net.neoforged.neoforge.event.RegisterCommandsEvent; +import net.neoforged.neoforge.event.brewing.RegisterBrewingRecipesEvent; +import net.neoforged.neoforge.event.tick.EntityTickEvent; + + +public class CommonEventHandler { + @EventBusSubscriber(modid = LeashedPlayer.MOD_ID) + public static class Game extends CommonEventHandler { + @SubscribeEvent + public static void onRegisterCommander(RegisterCommandsEvent event) { + CommandDispatcher dispatcher = event.getDispatcher(); + LeashCommand.register(dispatcher); + MotionCommand.register(dispatcher); + TickCommand.register(dispatcher); + } + @SubscribeEvent + public static void OnRegisterPotionBrewing(RegisterBrewingRecipesEvent event) { + PotionBrewing.Builder builder = event.getBuilder(); + builder.addMix(Potions.WATER, Items.SLIME_BALL, ModPotionRegister.NO_LEASH); + } + @SubscribeEvent + public static void OnLivingTickEvent(EntityTickEvent.Post event) { + Entity entity = event.getEntity(); + Level level = entity.level(); + if (level.isClientSide()) { + return; + } + if (entity instanceof Fox fox) { + if (fox.getMainHandItem().is(ItemTags.ANVIL)) { + fox.setItemSlot(EquipmentSlot.MAINHAND, ItemStack.EMPTY); + Util.throwItemTowardsLook(fox, ModItemRegister.NEOFORGE.get(), 0.3f, 0.1f); + fox.playSound(SoundEvents.FOX_EAT); + } else if (fox.getMainHandItem().is(ModItemRegister.NEOFORGE.get())) { + // 繞圈參數 + float rotationSpeed = 10.0f; // 每 tick 旋轉的角度 + // 計算新的旋轉角度 + fox.yBodyRot += rotationSpeed; // 身體旋轉 + fox.yHeadRot += rotationSpeed; // 頭部旋轉 + fox.yRotO += rotationSpeed; // 當前旋轉角度 + fox.yHeadRotO += rotationSpeed; // 頭部的當前旋轉角度 + + // 確保旋轉角度不超出 360 度,重置為 0 以便持續旋轉 + if (fox.yBodyRot >= 360) fox.yBodyRot -= 360; + if (fox.yHeadRot >= 360) fox.yHeadRot -= 360; + + } + } + } + @SubscribeEvent + public static void OnAnvilUpdated(AnvilUpdateEvent event) { + String name = event.getName(); + ItemStack left = event.getLeft(); + if (left.is(Items.ANVIL) && name != null && name.equals("NeoForge")) { + event.setCost(1); + event.setOutput(ModItemRegister.NEOFORGE.get().getDefaultInstance()); + } else if (left.is(ModItemRegister.NEOFORGE.get().asItem()) && name != null && name.equals("Forge")) { + ItemStack instance = Items.ANVIL.getDefaultInstance(); + instance.set(DataComponents.CUSTOM_NAME, Component.literal("Forge").withStyle(ChatFormatting.BOLD).withStyle(ChatFormatting.AQUA)); + event.setOutput(instance); + event.setCost(1); + } + } + } + @EventBusSubscriber(modid = LeashedPlayer.MOD_ID, bus = EventBusSubscriber.Bus.MOD) + public static class Mod extends CommonEventHandler { + @SubscribeEvent + public static void onCommonSetup(FMLCommonSetupEvent event) { + event.enqueueWork(() -> { + DispenserBlock.registerProjectileBehavior(ModItemRegister.LEASH_ROPE_ARROW.get()); + DispenserBlock.registerProjectileBehavior(ModItemRegister.TIPPED_LEASH_ROPE_ARROW.get()); + DispenserBlock.registerProjectileBehavior(ModItemRegister.SPECTRAL_LEASH_ROPE_ARROW.get()); + DispenserBlock.registerBehavior(ModItemRegister.AMETHYST_SHEARS.get(), new LeadBreakItemBehavior()); + }); + } + } + + + +} diff --git a/src/main/java/com/r3944realms/leashedplayer/LeashedPlayer.java b/src/main/java/com/r3944realms/leashedplayer/LeashedPlayer.java new file mode 100644 index 0000000..e07a65f --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/LeashedPlayer.java @@ -0,0 +1,70 @@ +package com.r3944realms.leashedplayer; + +import com.r3944realms.leashedplayer.config.LeashPlayerCommonConfig; +import com.r3944realms.leashedplayer.content.criteriaTriggers.ModCriteriaTriggers; +import com.r3944realms.leashedplayer.content.effects.ModEffectRegister; +import com.r3944realms.leashedplayer.content.effects.ModPotionRegister; +import com.r3944realms.leashedplayer.content.entities.ModEntityRegister; +import com.r3944realms.leashedplayer.content.items.ModCreativeTab; +import com.r3944realms.leashedplayer.content.items.ModItemRegister; +import com.r3944realms.leashedplayer.content.items.repcipe.ModRecipeRegister; +import com.r3944realms.leashedplayer.content.paintings.ModPaintingsRegister; +import com.r3944realms.leashedplayer.content.sounds.ModSoundRegister; +import com.r3944realms.leashedplayer.utils.Util; +import net.neoforged.bus.api.IEventBus; +import net.neoforged.fml.ModLoadingContext; +import net.neoforged.fml.common.Mod; +import net.neoforged.fml.config.ModConfig; + +import static com.r3944realms.leashedplayer.utils.Logger.logger; +@Mod(LeashedPlayer.MOD_ID) +public class LeashedPlayer { + public static final String MOD_ID = "leashedplayer"; + private static Double M1;//拴繩掉落距離倍基數 + private static Double M2;//繩箭拴繩掉落距離倍基數 + private static Integer M3; //拴绳最小长度 + private static Integer M4; //拴绳最大长度 + public LeashedPlayer(IEventBus event) { + ModItemRegister.register(event); + ModRecipeRegister.register(event); + ModSoundRegister.register(event); + ModPaintingsRegister.register(event); + ModEffectRegister.register(event); + ModPotionRegister.register(event); + ModEntityRegister.register(event); + ModCreativeTab.register(event); + ModCriteriaTriggers.register(event); + initiation(); + } + private void initiation() { + logger.info("Initializing LeashedPlayer Mod"); + String leashedPlayerCommonConfig = "LeashedPlayerCommonConfig"; + Util.configFileCreate(new String[]{leashedPlayerCommonConfig}); + ModLoadingContext.get().getActiveContainer().registerConfig(ModConfig.Type.COMMON, LeashPlayerCommonConfig.SPEC, MOD_ID + "/" + leashedPlayerCommonConfig + "/LeashPlayer.toml"); + } + public static Double M1() { + if(M1 == null) { + M1 = LeashPlayerCommonConfig.TheLeashBreakLengthTimesBase.get(); + } + return M1; + } + public static Double M2() { + if(M2 == null) { + M2 = LeashPlayerCommonConfig.TheMultipleThatLeashRopeArrowBreakLength.get(); + } + return M2; + } + public static Integer M3() { + if(M3 == null) { + M3 = LeashPlayerCommonConfig.MinimumLeashLengthCanBeSet.get(); + } + return M3; + } + public static Integer M4() { + if(M4 == null) { + M4 = LeashPlayerCommonConfig.MaximumLeashLengthCanBeSet.get(); + } + return M4; + } + +} diff --git a/src/main/java/com/r3944realms/leashedplayer/client/processBar/IProcessBar.java b/src/main/java/com/r3944realms/leashedplayer/client/processBar/IProcessBar.java new file mode 100644 index 0000000..c1462b0 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/client/processBar/IProcessBar.java @@ -0,0 +1,53 @@ +package com.r3944realms.leashedplayer.client.processBar; + +/** + * 进度条 + + */ +public interface IProcessBar { + /** + * + * @return 存活计数 (<=0 则会被清除) + */ + int aliveCount(); + /** + * 维持计数 + */ + void retain(); + void decreaseAliveCount(); + void setRenderStatus(boolean status); + boolean shouldRender(); + int getCurrentProcess(); + int getMaxProcess(); + void setMaxProcess(int maxProcess); + void setProcess(int process); + void resetProcess(); + + /** + * 完成进度条执行的任务,由实现者实现 + */ + void completeTask(); + default void decrease() { + int updateValue = getCurrentProcess() - 1; + if(updateValue < 0) { + setRenderStatus(false); + setProcess(0); + } + else { + setRenderStatus(true); + setProcess(updateValue); + } + } + default void increase() { + int updateValue = getCurrentProcess() + 1; + if(updateValue > getMaxProcess()) { + setRenderStatus(false); + setProcess(getMaxProcess()); + completeTask();//执行任务 + } + else { + setRenderStatus(true); + setProcess(updateValue); + } + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/client/processBar/TestProcessBar.java b/src/main/java/com/r3944realms/leashedplayer/client/processBar/TestProcessBar.java new file mode 100644 index 0000000..a514639 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/client/processBar/TestProcessBar.java @@ -0,0 +1,92 @@ +package com.r3944realms.leashedplayer.client.processBar; + +import net.minecraft.client.Minecraft; +import net.minecraft.network.chat.ChatType; +import net.minecraft.network.chat.Component; + +import java.util.concurrent.CompletableFuture; + +public class TestProcessBar implements IProcessBar { + private int aliveCount = 0; + private boolean isRendering; + private int maxProgress; + private int progress; + public TestProcessBar() { + aliveCount++; + isRendering = false; + maxProgress = 100; + progress = 0; + } + public TestProcessBar(int maxProgress) { + aliveCount++; + isRendering = false; + this.maxProgress = maxProgress; + progress = 0; + } + public TestProcessBar(int maxProgress, int progress) { + aliveCount++; + isRendering = false; + this.maxProgress = maxProgress; + this.progress = progress; + } + + @Override + public int aliveCount() { + return aliveCount; + } + + @Override + public void retain() { + aliveCount++; + } + + @Override + public void decreaseAliveCount() { + aliveCount = aliveCount > 0 ? aliveCount - 1 : 0; + } + + @Override + public void setRenderStatus(boolean status) { + isRendering = status; + } + + @Override + public boolean shouldRender() { + return isRendering; + } + + @Override + public int getCurrentProcess() { + return progress; + } + + @Override + public int getMaxProcess() { + return maxProgress; + } + + @Override + public void setMaxProcess(int maxProcess) { + this.maxProgress = maxProcess; + } + + @Override + public void setProcess(int process) { + this.progress = process < 0 ? 0 : (Math.min(process, maxProgress)); + } + + @Override + public void resetProcess() { + this.progress = 0; + } + + @Override + public void completeTask() { + Runnable runnable = () -> { + if (Minecraft.getInstance().player != null) { + Minecraft.getInstance().getChatListener().handleDisguisedChatMessage(Component.literal("Completed"), ChatType.bind(ChatType.MSG_COMMAND_OUTGOING, Minecraft.getInstance().player)); + } + }; + CompletableFuture.runAsync(runnable); + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/client/renderer/LeashRendererUtil.java b/src/main/java/com/r3944realms/leashedplayer/client/renderer/LeashRendererUtil.java new file mode 100644 index 0000000..145ead1 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/client/renderer/LeashRendererUtil.java @@ -0,0 +1,540 @@ +package com.r3944realms.leashedplayer.client.renderer; + +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import com.mojang.datafixers.util.Either; +import com.r3944realms.leashedplayer.LeashedPlayer; +import com.r3944realms.leashedplayer.content.entities.LeashRopeArrow; +import com.r3944realms.leashedplayer.modInterface.ILivingEntityExtension; +import com.r3944realms.leashedplayer.modInterface.IPlayerRenderStateExtension; +import com.r3944realms.leashedplayer.modInterface.PlayerLeashable; +import net.minecraft.client.Camera; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.player.AbstractClientPlayer; +import net.minecraft.client.renderer.LightTexture; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.entity.state.EntityRenderState; +import net.minecraft.client.renderer.entity.state.PlayerRenderState; +import net.minecraft.core.BlockPos; +import net.minecraft.util.Mth; +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.entity.vehicle.AbstractMinecart; +import net.minecraft.world.entity.vehicle.NewMinecartBehavior; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.LightLayer; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.api.distmarker.OnlyIn; +import org.joml.Matrix4f; + +import java.util.List; +import java.util.UUID; + +@OnlyIn(Dist.CLIENT) +public class LeashRendererUtil { + public static void renderLeash(PoseStack poseStack, MultiBufferSource buffer, EntityRenderState.LeashState leashState) { + float f = 0.025F; + + float f1 = (float)(leashState.end.x - leashState.start.x); + float f2 = (float)(leashState.end.y - leashState.start.y + 0.2); + float f3 = (float)(leashState.end.z - leashState.start.z); + float f4 = Mth.invSqrt(f1 * f1 + f3 * f3) * 0.025F / 2.0F; + float f5 = f3 * f4; + float f6 = f1 * f4; + poseStack.pushPose(); + poseStack.translate(leashState.offset.add(0, -0.2, -0.2)); + VertexConsumer vertexconsumer = buffer.getBuffer(RenderType.leash()); + Matrix4f matrix4f = poseStack.last().pose(); + + for (int i = 0; i <= 24; i++) { + addVertexPair( + vertexconsumer, + matrix4f, + f1, + f2, + f3, + leashState.startBlockLight, + leashState.endBlockLight, + leashState.startSkyLight, + leashState.endSkyLight, + 0.025F, + 0.025F, + f5, + f6, + i, + false + ); + } + + for (int j = 24; j >= 0; j--) { + addVertexPair( + vertexconsumer, + matrix4f, + f1, + f2, + f3, + leashState.startBlockLight, + leashState.endBlockLight, + leashState.startSkyLight, + leashState.endSkyLight, + 0.025F, + 0.0F, + f5, + f6, + j, + true + ); + } + + poseStack.popPose(); + } + /** + *

1. 角度与弧度转换

+ * {@snippet lang=java : + * double d0 = (double)(pEntity.getPreciseBodyRotation(pPartialTick) * (float)(Math.PI / 180.0)) + (Math.PI / 2); + * } + *
    + *
  • pEntity.getPreciseBodyRotation(pPartialTick) 返回实体的旋转角度(通常是以度为单位)。/li> + *
  • (Math.PI / 180.0) 是将度数转换为弧度的乘数,因为大多数三角函数(如 cossin)都需要弧度值。
  • + *
  • + (Math.PI / 2) 用于将结果平移90度(四分之一圆),可能是为了校正方向或设置起始方向。
  • + *
+ * + *

+ *

2. 三角函数计算位移

+ * {@snippet lang=java : + * double d1 = Math.cos(d0) * vec31.z + Math.sin(d0) * vec31.x; + * double d2 = Math.sin(d0) * vec31.z - Math.cos(d0) * vec31.x; + * } + *
    + *
  • d1d2 是利用三角函数 cossin 计算出来的位移量,用于确定实体相对于其旋转的实际位置。
  • + *
  • Math.cos(d0) * vec31.zMath.sin(d0) * vec31.x 分别计算沿 X 和 Z 轴的位移分量,这种计算通常用于旋转一个点或向量。
  • + *
  • 两个公式结合起来用于旋转平面内的一个点 (vec31.x, vec31.z),从而得到旋转后的新坐标。
  • + *
+ *

+ *

3. 线性插值 (Lerp)

+ * {@snippet lang=java : + * double d3 = Mth.lerp(pPartialTick, pEntity.xo, pEntity.getX()) + d1; + * double d4 = Mth.lerp(pPartialTick, pEntity.yo, pEntity.getY()) + vec31.y; + * double d5 = Mth.lerp(pPartialTick, pEntity.zo, pEntity.getZ()) + d2; + * } + *
    + *
  • Mth.lerp 是线性插值函数,通常用于在两个值之间平滑过渡。
  • + *
  • pEntity.xo, pEntity.yo, pEntity.zo 是实体在上一个刻度(tick)中的位置,而 pEntity.getX(), pEntity.getY(), pEntity.getZ() 是当前刻度的位置。
  • + *
  • pPartialTick 介于 01 之间,用来平滑过渡,使得动画更加流畅。
  • + *
+ *

+ *

4. 向量差值

+ * {@snippet lang=java : + * float f = (float)(vec3.x - d3); + * float f1 = (float)(vec3.y - d4); + * float f2 = (float)(vec3.z - d5); + * } + *
    + *
  • 计算两个点(vec3(d3, d4, d5))之间的差值,得到的 ff1f2 是向量差,用于后续的渲染计算。
  • + *
+ *

+ *

5. 逆平方根与比例因子

+ * {@snippet lang=java : + * float f4 = Mth.invSqrt(f * f + f2 * f2) * 0.025F / 2.0F; + * } + *
    + *
  • Mth.invSqrt 计算的是逆平方根(通常用于归一化向量或调整比例)。
  • + *
  • f * f + f2 * f2 是计算向量 (f, f2) 的平方和,用于得到其长度的平方。
  • + *
  • 乘以 0.025F / 2.0F 用于缩放结果,使得线条在渲染时具有合适的比例。
  • + *
+ *

+ *

6. 循环绘制

+ * {@snippet lang=java : + * for (int i1 = 0; i1 <= 24; i1++) { + * addVertexPair(vertexconsumer, matrix4f, f, f1, f2, i, j, k, l, 0.025F, 0.025F, f5, f6, i1, false); + * } + * } + *
    + *
  • 循环从 024,用于创建24个顶点对,形成一个链状结构(或绳索)的外观。
  • + *
  • 每个循环迭代都会更新顶点的位置、颜色、光照等属性,使得链状结构被绘制出来。
  • + *
+ *

+ *

总结

+ * 这些数学运算主要用于计算实体在三维空间中的位置和方向,以确保在渲染链状结构(如拴住的绳索)时,链条能够跟随实体的移动和旋转并正确显示。在图形编程中,这些计算非常常见,尤其是在处理旋转、插值和光照效果时。 + */ + + public static void renderLeash( + PoseStack poseStack, + MultiBufferSource bufferSource, + PlayerRenderState playerRenderState + ) { + float f = 0.025F; + // 获得绳索持有者的位置 + PlayerLeashState leashState = ((IPlayerRenderStateExtension)playerRenderState).getPlayerLeashState(); + if (leashState == null) + return; + Vec3 Holder = leashState.pos;//TODO:问题修复 + Vec3 o = leashState.o; + // 计算实体的朝向角度(弧度) + float partialTick = playerRenderState.partialTick; + double entityRotationAngleRadians = (double)(leashState.getPreciseBodyRotation(partialTick) * (float) (Math.PI / 180.0)) + (Math.PI / 2); + // 计算实体的绳索偏移,此处add偏移让渲染拴绳显示在玩家头部下(大约在脖子处 +// Logger.logger.info("eyeHeight:{}",playerRenderState.eyeHeight); + Vec3 cameraEntityLeashOffset = new Vec3(0.0, leashState.eyeHeight , playerRenderState.boundingBoxWidth * 0.4F).add(0, -0.2, -0.2); + double leashOffsetX = Math.cos(entityRotationAngleRadians) * cameraEntityLeashOffset.z + Math.sin(entityRotationAngleRadians) * cameraEntityLeashOffset.x; + double leashOffsetZ = Math.sin(entityRotationAngleRadians) * cameraEntityLeashOffset.z - Math.cos(entityRotationAngleRadians) * cameraEntityLeashOffset.x; + // 计算实体当前的实际位置 + double entityPosX = Mth.lerp(partialTick, o.x, playerRenderState.x) + leashOffsetX; + double entityPosY = Mth.lerp(partialTick, o.y, playerRenderState.y) + cameraEntityLeashOffset.y; + double entityPosZ = Mth.lerp(partialTick, o.z, playerRenderState.z) + leashOffsetZ; + + // 计算绳索的相对位置差 + float deltaX = (float)(Holder.x - entityPosX); + float deltaY = (float)(Holder.y - entityPosY); + float deltaZ = (float)(Holder.z - entityPosZ); + // 计算比例因子,用于调节绳索的粗细 + float leashLengthRatio = Mth.invSqrt(deltaX * deltaX + deltaZ * deltaZ) * 0.025F / 2.0F; + // 计算比例因子,用于调节绳索的粗细 + float leashXZScaleX = deltaZ * leashLengthRatio; + float leashXZScaleZ = deltaX * leashLengthRatio; + poseStack.pushPose(); + poseStack.translate(leashOffsetX, cameraEntityLeashOffset.y,leashOffsetZ); + VertexConsumer vertexconsumer = bufferSource.getBuffer(RenderType.leash()); + Matrix4f matrix4f = poseStack.last().pose(); + + for (int i = 0; i <= 24; i++) { + addVertexPair( + vertexconsumer, + matrix4f, + deltaX, + deltaY, + deltaZ, + leashState.startBlockLight, + leashState.endBlockLight, + leashState.startSkyLight, + leashState.endSkyLight, + 0.025F, + 0.025F, + leashXZScaleX, + leashXZScaleZ, + i, + false + ); + } + + for (int j = 24; j >= 0; j--) { + addVertexPair( + vertexconsumer, + matrix4f, + deltaX, + deltaY, + deltaZ, + leashState.startBlockLight, + leashState.endBlockLight, + leashState.startSkyLight, + leashState.endSkyLight, + 0.025F, + 0.0F, + leashXZScaleX, + leashXZScaleZ, + j, + true + ); + } + + poseStack.popPose(); + } + + protected static void addVertexPair( + VertexConsumer buffer, + Matrix4f pose, + float startX, + float startY, + float startZ, + int entityBlockLight, + int holderBlockLight, + int entitySkyLight, + int holderSkyLight, + float yOffset, + float dy, + float dx, + float dz, + int index, + boolean reverse + ) { + float f = (float)index / 24.0F; + int i = (int)Mth.lerp(f, (float)entityBlockLight, (float)holderBlockLight); + int j = (int)Mth.lerp(f, (float)entitySkyLight, (float)holderSkyLight); + int k = LightTexture.pack(i, j); + float f1 = index % 2 == (reverse ? 1 : 0) ? 0.7F : 1.0F; + float f2 = 0.5F * f1; + float f3 = 0.4F * f1; + float f4 = 0.3F * f1; + float f5 = startX * f; + float f6 = startY > 0.0F ? startY * f * f : startY - startY * (1.0F - f) * (1.0F - f); + float f7 = startZ * f; + buffer.addVertex(pose, f5 - dx, f6 + dy, f7 + dz).setColor(f2, f3, f4, 1.0F).setLight(k); + buffer.addVertex(pose, f5 + dx, f6 + yOffset - dy, f7 - dz).setColor(f2, f3, f4, 1.0F).setLight(k); + } + public static void renderLeashForCamera( + Camera camera, + float partialTick, + PoseStack poseStack, + MultiBufferSource bufferSource, + E leashHolder, + Vec3 holderOffset + ) { + + poseStack.pushPose(); +// Logger.logger.info("eyeHeight{}", camera.getEntity().getEyeHeight()); + // 获得绳索持有者的位置 + Vec3 leashHolderPosition = leashHolder.getRopeHoldPosition(partialTick).add(holderOffset); + + // 获取当前观察的实体 + Entity cameraEntity = camera.getEntity(); + + // 计算实体的朝向角度(弧度) + double entityRotationAngleRadians = (double)(cameraEntity.getPreciseBodyRotation(partialTick) * (float) (Math.PI / 180.0)) + (Math.PI / 2); + + // 计算实体的绳索偏移,此处add偏移让渲染拴绳显示在玩家头部下(大约在脖子处 + Vec3 cameraEntityLeashOffset = cameraEntity.getLeashOffset(partialTick).add(0, -0.2, -0.5); + double leashOffsetX = Math.cos(entityRotationAngleRadians) * cameraEntityLeashOffset.z + Math.sin(entityRotationAngleRadians) * cameraEntityLeashOffset.x; + double leashOffsetZ = Math.sin(entityRotationAngleRadians) * cameraEntityLeashOffset.z - Math.cos(entityRotationAngleRadians) * cameraEntityLeashOffset.x; + + // 计算实体当前的实际位置 + double entityPosX = Mth.lerp(partialTick, cameraEntity.xo, cameraEntity.getX()) + leashOffsetX; + double entityPosY = Mth.lerp(partialTick, cameraEntity.yo, cameraEntity.getY()) + cameraEntityLeashOffset.y; + double entityPosZ = Mth.lerp(partialTick, cameraEntity.zo, cameraEntity.getZ()) + leashOffsetZ; + + // 在当前变换矩阵上应用偏移 + poseStack.translate(leashOffsetX, cameraEntityLeashOffset.y , leashOffsetZ); + + // 计算绳索的相对位置差 + float deltaX = (float)(leashHolderPosition.x - entityPosX); + float deltaY = (float)(leashHolderPosition.y - entityPosY); + float deltaZ = (float)(leashHolderPosition.z - entityPosZ); + + // 获取顶点消费者,用于绘制绳索 + VertexConsumer vertexConsumer = bufferSource.getBuffer(RenderType.leash()); + Matrix4f matrix = poseStack.last().pose(); + + // 计算比例因子,用于调节绳索的粗细 + float leashLengthRatio = Mth.invSqrt(deltaX * deltaX + deltaZ * deltaZ) * 0.025F / 2.0F; + float leashXZScaleX = deltaZ * leashLengthRatio; + float leashXZScaleZ = deltaX * leashLengthRatio; + + // 获取光照信息 + BlockPos cameraEntityBlockPos = BlockPos.containing(cameraEntity.getEyePosition(partialTick)); + BlockPos leashHolderBlockPos = BlockPos.containing(leashHolder.getEyePosition(partialTick)); + int cameraEntityBlockLightLevel = getBlockLightLevel(cameraEntity, cameraEntityBlockPos); + int leashHolderBlockLightLevel = getBlockLightLevel(leashHolder, leashHolderBlockPos); //getBlockLightLevel(leashHolder, leashHolderBlockPos); + int cameraEntitySkyLightLevel = cameraEntity.level().getBrightness(LightLayer.SKY, cameraEntityBlockPos); + int leashHolderSkyLightLevel = cameraEntity.level().getBrightness(LightLayer.SKY, leashHolderBlockPos); + + // 绘制绳索的上半部分 + for (int segment = 0; segment <= 24; segment++) { + addVertexPair(vertexConsumer, matrix, deltaX, deltaY, deltaZ, cameraEntityBlockLightLevel, leashHolderBlockLightLevel, cameraEntitySkyLightLevel, leashHolderSkyLightLevel, 0.025F, 0.025F, leashXZScaleX, leashXZScaleZ, segment, false); + } + + // 绘制绳索的下半部分 + for (int segment = 24; segment >= 0; segment--) { + addVertexPair(vertexConsumer, matrix, deltaX, deltaY, deltaZ, cameraEntityBlockLightLevel, leashHolderBlockLightLevel, cameraEntitySkyLightLevel, leashHolderSkyLightLevel, 0.025F, 0.0F, leashXZScaleX, leashXZScaleZ, segment, true); + } + + poseStack.popPose(); + } + public static void levelRenderLeash(ClientLevel level, Camera pCamera, PoseStack poseStack, MultiBufferSource.BufferSource multibuffersource$buffersource) { + for(Entity entity : level.entitiesForRendering()) { + //对于玩家实体拴绳渲染(从第一人称视角) + if (entity instanceof AbstractClientPlayer abstractClientPlayer) { + if(!(pCamera.getEntity() instanceof AbstractClientPlayer)) continue; + Minecraft mc = Minecraft.getInstance(); + if (mc.options.getCameraType().isFirstPerson()) { + Leashable.LeashData leashDataFromEntityData = ((PlayerLeashable) abstractClientPlayer).getLeashDataFromEntityData(); + if(leashDataFromEntityData == null) continue; + Either delayedLeashInfo = leashDataFromEntityData.delayedLeashInfo; + if(delayedLeashInfo != null) { + float partialTickTime = pCamera.getPartialTickTime(); + Vec3 position = pCamera.getPosition(); + double dX = Mth.lerp(partialTickTime, abstractClientPlayer.xOld, abstractClientPlayer.getX()) - position.x; + double dY = Mth.lerp(partialTickTime, abstractClientPlayer.yOld, abstractClientPlayer.getY()) - position.y; + double dZ = Mth.lerp(partialTickTime, abstractClientPlayer.zOld, abstractClientPlayer.getZ()) - position.z; + Vec3 vec3 = getRenderOffset(entity, partialTickTime); + double dX_ = dX + vec3.x(); + double dY_ = dY + vec3.y(); + double dZ_ = dZ + vec3.z(); + poseStack.pushPose(); + poseStack.translate(dX_, dY_, dZ_); + if (delayedLeashInfo.right().isPresent() && delayedLeashInfo.left().isEmpty()) { + renderLeashForCamera(pCamera, partialTickTime, poseStack, multibuffersource$buffersource, LeashFenceKnotEntity.getOrCreateKnot(level, delayedLeashInfo.right().get()), Vec3.ZERO); + } else if (delayedLeashInfo.right().isEmpty() && delayedLeashInfo.left().isPresent()) { + Player playerByUUID = level.getPlayerByUUID(delayedLeashInfo.left().get()); + if (playerByUUID != null) { + renderLeashForCamera(pCamera, partialTickTime, poseStack, multibuffersource$buffersource, playerByUUID, Vec3.ZERO); + } else { + double MaxLeashLength = ((ILivingEntityExtension) abstractClientPlayer).getLeashLength() * LeashedPlayer.M1() * LeashedPlayer.M2(); + List entities = level.getEntities( + null, + new AABB( + abstractClientPlayer.getX() - MaxLeashLength, + abstractClientPlayer.getY() - MaxLeashLength, + abstractClientPlayer.getZ() - MaxLeashLength, + abstractClientPlayer.getX() + MaxLeashLength, + abstractClientPlayer.getY() + MaxLeashLength, + abstractClientPlayer.getZ() + MaxLeashLength + ) + ); + Entity holder = null; + for (Entity entity_ : entities) { + if(entity_.getUUID().equals(delayedLeashInfo.left().get())) { + holder = entity_; + break; + } + } + if (holder != null) { + if(holder instanceof LeashRopeArrow) { + renderLeashForCamera(pCamera, partialTickTime, poseStack, multibuffersource$buffersource, holder, new Vec3(0.,-0.09, 0));//TODO: 待擴展Vec3吗? + } + else renderLeashForCamera(pCamera, partialTickTime, poseStack, multibuffersource$buffersource, holder, Vec3.ZERO); + } + } + break; + } + + } + } + } + } + } + public static Vec3 getRenderOffset(Entity entity, float partialTickTime) { + Vec3 ret = Vec3.ZERO; + if(entity.isPassenger() + && entity.getVehicle() instanceof AbstractMinecart abstractminecart + && abstractminecart.getBehavior() instanceof NewMinecartBehavior newminecartbehavior + && newminecartbehavior.cartHasPosRotLerp() + ) { + double d2 = Mth.lerp(partialTickTime, abstractminecart.xOld, abstractminecart.getX()); + double d0 = Mth.lerp(partialTickTime, abstractminecart.yOld, abstractminecart.getY()); + double d1 = Mth.lerp(partialTickTime, abstractminecart.zOld, abstractminecart.getZ()); + ret = newminecartbehavior.getCartLerpPosition(partialTickTime).subtract(new Vec3(d2, d0, d1)); + } + return ret; + } + + public static void createPlayerLeashState(AbstractClientPlayer abstractClientPlayer, IPlayerRenderStateExtension playerRenderState, float partialTick) { + Leashable.LeashData leashDataFromEntityData = ((PlayerLeashable) abstractClientPlayer).getLeashDataFromEntityData(); + Leashable.LeashData leashData = ((PlayerLeashable) abstractClientPlayer).getLeashData(); + PlayerRenderState playerRs = (PlayerRenderState)playerRenderState; + if (leashDataFromEntityData != null) { + Entity leashHolder = leashDataFromEntityData.leashHolder; + Either delayedLeashInfo = leashDataFromEntityData.delayedLeashInfo; + PlayerLeashState playerLeashState = new PlayerLeashState(); + if (delayedLeashInfo != null) { + createPlayerLeashState_(abstractClientPlayer, playerRenderState, leashDataFromEntityData, delayedLeashInfo, playerLeashState, partialTick); + BlockPos blockpos1 = BlockPos.containing(abstractClientPlayer.getEyePosition(partialTick)); + playerLeashState.startBlockLight = getBlockLightLevel(abstractClientPlayer, blockpos1); + playerLeashState.startSkyLight = abstractClientPlayer.level().getBrightness(LightLayer.SKY, blockpos1); + } else if(leashHolder != null) { + createPlayerLeashState__(abstractClientPlayer, playerRenderState, leashHolder, playerLeashState, partialTick); + } else { + playerRenderState.setPlayerLeashState(null); + } + } else if(leashData != null) { + playerRs.leashState = null; + Entity leashHolder = ((PlayerLeashable) abstractClientPlayer).getLeashHolder(); + PlayerLeashState playerLeashState = playerRenderState.getPlayerLeashState(); + if (leashHolder != null & playerLeashState != null) { + float f = leashHolder.getPreciseBodyRotation(partialTick) * (float) (Math.PI / 180.0); + Vec3 vec3 = abstractClientPlayer.getLeashOffset(partialTick).yRot(-f); + BlockPos blockpos1 = BlockPos.containing(abstractClientPlayer.getEyePosition(partialTick)); + BlockPos blockpos = BlockPos.containing(leashHolder.getEyePosition(partialTick)); + playerLeashState.vanilaLeashState = new EntityRenderState.LeashState(); + + EntityRenderState.LeashState entityrenderstate$leashstate = playerLeashState.vanilaLeashState; + entityrenderstate$leashstate.offset = vec3; + entityrenderstate$leashstate.start = abstractClientPlayer.getPosition(partialTick).add(vec3); + entityrenderstate$leashstate.end = leashHolder.getRopeHoldPosition(partialTick); + entityrenderstate$leashstate.startBlockLight = getBlockLightLevel(abstractClientPlayer, blockpos1); + entityrenderstate$leashstate.endBlockLight = getBlockLightLevel(leashHolder, blockpos); + entityrenderstate$leashstate.startSkyLight = abstractClientPlayer.level().getBrightness(LightLayer.SKY, blockpos1); + entityrenderstate$leashstate.endSkyLight = abstractClientPlayer.level().getBrightness(LightLayer.SKY, blockpos); + } else { + playerRenderState.setPlayerLeashState(null); + } + } else { + playerRenderState.setPlayerLeashState(null); + } + } + private static int getBlockLightLevel(T entity, BlockPos pos) { + return entity.isOnFire() ? 15 : entity.level().getBrightness(LightLayer.BLOCK, pos); + } + private static int getBlockLightLevel(Level level, BlockPos pos) { + return level.getBrightness(LightLayer.BLOCK, pos); + } + + + private static void createPlayerLeashState__(AbstractClientPlayer abstractClientPlayer, IPlayerRenderStateExtension playerRenderState, Entity leashHolder, PlayerLeashState playerLeashState, float partialTick) { + playerLeashState.pos = leashHolder.position().add(0.0, leashHolder.getEyeHeight() * 0.6, -0.2); + playerLeashState.o = abstractClientPlayer.getPosition(partialTick); + playerLeashState.yRotO = abstractClientPlayer.yRotO; + playerLeashState.yRot = abstractClientPlayer.getYRot(); + playerLeashState.eyeHeight = abstractClientPlayer.getEyeHeight(); + playerRenderState.setPlayerLeashState(playerLeashState); + } + + private static void createPlayerLeashState_(AbstractClientPlayer abstractClientPlayer, IPlayerRenderStateExtension playerRenderState, Leashable.LeashData leashDataFromEntityData, Either delayedLeashInfo, PlayerLeashState playerLeashState, float partialTick) { + Minecraft mc = Minecraft.getInstance(); + ClientLevel level = mc.level; + if (delayedLeashInfo.right().isPresent() && delayedLeashInfo.left().isEmpty()) { + assert level != null; + playerLeashState.pos = delayedLeashInfo.right().get().getCenter(); + playerLeashState.endBlockLight = getBlockLightLevel(level, delayedLeashInfo.right().get()); + playerLeashState.endSkyLight = level.getBrightness(LightLayer.SKY, delayedLeashInfo.right().get()); + } else if (delayedLeashInfo.right().isEmpty() && delayedLeashInfo.left().isPresent()) { + assert level != null; + Player playerByUUID = level.getPlayerByUUID(delayedLeashInfo.left().get()); + if (playerByUUID != null) { + playerLeashState.pos = playerByUUID.position().add(0.0, playerByUUID.getEyeHeight() * 0.6, 0); + } else { + double breakDistanceTime = (leashDataFromEntityData.leashHolder instanceof LeashRopeArrow) ? LeashedPlayer.M1() * LeashedPlayer.M2() : LeashedPlayer.M1(); + double MaxLeashLength = ((ILivingEntityExtension) abstractClientPlayer).getLeashLength() * breakDistanceTime; + List entities = level.getEntities( + null, + new AABB( + abstractClientPlayer.getX() - MaxLeashLength * LeashedPlayer.M2(), + abstractClientPlayer.getY() - MaxLeashLength * LeashedPlayer.M2(), + abstractClientPlayer.getZ() - MaxLeashLength * LeashedPlayer.M2(), + abstractClientPlayer.getX() + MaxLeashLength * LeashedPlayer.M2(), + abstractClientPlayer.getY() + MaxLeashLength * LeashedPlayer.M2(), + abstractClientPlayer.getZ() + MaxLeashLength * LeashedPlayer.M2() + ) + ); + Entity holder = null; + for (Entity entity_ : entities) { + if (entity_.getUUID().equals(delayedLeashInfo.left().get())) { + holder = entity_; + break; + } + } + if (holder != null) { + playerLeashState.pos = holder.position().add(0.0, holder.getEyeHeight() * 0.6, 0);//TODO: 待擴展Vec3 + playerLeashState.endBlockLight = getBlockLightLevel(level, holder.getOnPos()); + playerLeashState.endSkyLight = level.getBrightness(LightLayer.SKY, holder.getOnPos()); + } else { + playerLeashState.pos = abstractClientPlayer.position(); + } + + } + } + + playerLeashState.o = abstractClientPlayer.getPosition(partialTick); + playerLeashState.yRotO = abstractClientPlayer.yRotO; + playerLeashState.yRot = abstractClientPlayer.getYRot(); + playerLeashState.eyeHeight = abstractClientPlayer.getEyeHeight(); + + playerRenderState.setPlayerLeashState(playerLeashState); + } + + +} diff --git a/src/main/java/com/r3944realms/leashedplayer/client/renderer/PlayerLeashState.java b/src/main/java/com/r3944realms/leashedplayer/client/renderer/PlayerLeashState.java new file mode 100644 index 0000000..c8e7b86 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/client/renderer/PlayerLeashState.java @@ -0,0 +1,41 @@ +package com.r3944realms.leashedplayer.client.renderer; + +import net.minecraft.client.renderer.entity.state.EntityRenderState; +import net.minecraft.util.Mth; +import net.minecraft.world.phys.Vec3; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.api.distmarker.OnlyIn; + +import javax.annotation.Nullable; + +@OnlyIn(Dist.CLIENT) +public class PlayerLeashState { + public Vec3 o, pos, offset; + public float yRotO, yRot; + public float eyeHeight; + public int startBlockLight; + public int endBlockLight; + public int startSkyLight; + public int endSkyLight; + @Nullable + public EntityRenderState.LeashState vanilaLeashState; + + public float getPreciseBodyRotation(float pPartialTick) { + return Mth.lerp(pPartialTick, this.yRotO, this.yRot); + } + + + public PlayerLeashState() { + this.offset = Vec3.ZERO; + this.o = Vec3.ZERO; + this.pos = Vec3.ZERO; + this.yRot = 0.0f; + this.yRotO = 0.0f; + this.eyeHeight = 0.0f; + this.startBlockLight = 0; + this.endBlockLight = 0; + this.startSkyLight = 15; + this.endSkyLight = 15; + this.vanilaLeashState = null; + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/client/renderer/PlayerSlotItemLayerState.java b/src/main/java/com/r3944realms/leashedplayer/client/renderer/PlayerSlotItemLayerState.java new file mode 100644 index 0000000..e9a0017 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/client/renderer/PlayerSlotItemLayerState.java @@ -0,0 +1,13 @@ +package com.r3944realms.leashedplayer.client.renderer; + +import net.minecraft.world.entity.LivingEntity; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.api.distmarker.OnlyIn; + +@OnlyIn(Dist.CLIENT) +public class PlayerSlotItemLayerState { + public LivingEntity entity; + public PlayerSlotItemLayerState(LivingEntity entity) { + this.entity = entity; + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/client/renderer/entities/ChestItemLayerRenderer.java b/src/main/java/com/r3944realms/leashedplayer/client/renderer/entities/ChestItemLayerRenderer.java new file mode 100644 index 0000000..5e598c2 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/client/renderer/entities/ChestItemLayerRenderer.java @@ -0,0 +1,120 @@ +package com.r3944realms.leashedplayer.client.renderer.entities; + +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.math.Axis; +import com.r3944realms.leashedplayer.modInterface.IPlayerRenderStateExtension; +import net.minecraft.client.model.EntityModel; +import net.minecraft.client.model.PlayerModel; +import net.minecraft.client.renderer.ItemInHandRenderer; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.entity.RenderLayerParent; +import net.minecraft.client.renderer.entity.layers.RenderLayer; +import net.minecraft.client.renderer.entity.state.PlayerRenderState; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.item.ItemDisplayContext; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.NotNull; + +public class ChestItemLayerRenderer> extends RenderLayer { + + private final ItemInHandRenderer heldItemRenderer; + + public ChestItemLayerRenderer(RenderLayerParent context, ItemInHandRenderer heldItemRenderer) { + super(context); + this.heldItemRenderer = heldItemRenderer; + } + + + @Override + public void render(@NotNull PoseStack poseStack, @NotNull MultiBufferSource bufferSource, int packedLight, @NotNull S renderState, float yRot, float xRot) { + ItemDisplayContext mode = ItemDisplayContext.FIXED; + IPlayerRenderStateExtension rs = (IPlayerRenderStateExtension) renderState; + LivingEntity entity = rs.getPlayerSlotItemLayerState().entity; + ItemStack chestStack = entity.getItemBySlot(EquipmentSlot.CHEST); + if (!chestStack.isEmpty()) { + if (!(entity.getEquipmentSlotForItem(chestStack).equals(EquipmentSlot.CHEST))) { + poseStack.pushPose(); + ((PlayerModel) this.getParentModel()).body.translateAndRotate(poseStack); + poseStack.mulPose(Axis.XP.rotationDegrees(180)); + poseStack.translate(0, -0.24f, 0); + poseStack.mulPose(Axis.YP.rotationDegrees(180)); + this.heldItemRenderer.renderItem(entity, chestStack, mode, poseStack, bufferSource, packedLight); + poseStack.scale(1.01f, 1.01f, 1.01f); + poseStack.translate(0, -1 / 4f, 0); + this.heldItemRenderer.renderItem(entity, chestStack, mode, poseStack, bufferSource, packedLight); + poseStack.popPose(); + poseStack.pushPose(); + ((PlayerModel) this.getParentModel()).rightArm.translateAndRotate(poseStack); + poseStack.mulPose(Axis.XP.rotationDegrees(180)); + poseStack.scale(2/3f, 2/3f, 2/3f); + poseStack.translate(-1/12f, 0, 0); + poseStack.mulPose(Axis.YP.rotationDegrees(180)); + this.heldItemRenderer.renderItem(entity, chestStack, mode, poseStack, bufferSource, packedLight); + poseStack.scale(0.99f, 0.99f, 0.99f); + poseStack.translate(0, -1/2f, 0); + this.heldItemRenderer.renderItem(entity, chestStack, mode, poseStack, bufferSource, packedLight); + poseStack.popPose(); + poseStack.pushPose(); + ((PlayerModel) this.getParentModel()).leftArm.translateAndRotate(poseStack); + poseStack.mulPose(Axis.XP.rotationDegrees(180)); + poseStack.scale(2/3f, 2/3f, 2/3f); + poseStack.translate(1/12f, 0, 0); + poseStack.mulPose(Axis.YP.rotationDegrees(180)); + this.heldItemRenderer.renderItem(entity, chestStack, mode, poseStack, bufferSource, packedLight); + poseStack.scale(0.99f, 0.99f, 0.99f); + poseStack.translate(0, -1/2f, 0); + this.heldItemRenderer.renderItem(entity, chestStack, mode, poseStack, bufferSource, packedLight); + poseStack.popPose(); + } + } + ItemStack legsStack = entity.getItemBySlot(EquipmentSlot.LEGS); + if (!legsStack.isEmpty()) { + if (!(entity.getEquipmentSlotForItem(legsStack).equals(EquipmentSlot.LEGS))) { + poseStack.pushPose(); + ((PlayerModel) this.getParentModel()).rightLeg.translateAndRotate(poseStack); + poseStack.mulPose(Axis.XP.rotationDegrees(180)); + poseStack.scale(2/3f, 2/3f, 2/3f); + poseStack.translate(0, -1/6f, 0); + poseStack.mulPose(Axis.YP.rotationDegrees(180)); + this.heldItemRenderer.renderItem(entity, legsStack, mode, poseStack, bufferSource, packedLight); + poseStack.scale(1.01f, 1.01f, 1.01f); + poseStack.translate(0, -1/3f, 0); + this.heldItemRenderer.renderItem(entity, legsStack, mode, poseStack, bufferSource, packedLight); + poseStack.popPose(); + poseStack.pushPose(); + ((PlayerModel) this.getParentModel()).leftLeg.translateAndRotate(poseStack); + poseStack.mulPose(Axis.XP.rotationDegrees(180)); + poseStack.scale(2/3f, 2/3f, 2/3f); + poseStack.translate(0, -1/6f, 0); + poseStack.mulPose(Axis.YP.rotationDegrees(180)); + this.heldItemRenderer.renderItem(entity, legsStack, mode, poseStack, bufferSource, packedLight); + poseStack.scale(1.01f, 1.01f, 1.01f); + poseStack.translate(0, -1/3f, 0); + this.heldItemRenderer.renderItem(entity, legsStack, mode, poseStack, bufferSource, packedLight); + poseStack.popPose(); + } + } + ItemStack feetStack = entity.getItemBySlot(EquipmentSlot.FEET); + if (!feetStack.isEmpty()) { + if (!(entity.getEquipmentSlotForItem(feetStack).equals(EquipmentSlot.FEET))) { + poseStack.pushPose(); + ((PlayerModel) this.getParentModel()).rightLeg.translateAndRotate(poseStack); + poseStack.mulPose(Axis.XP.rotationDegrees(180)); + poseStack.scale(0.75f, 0.75f, 0.75f); + poseStack.translate(0, -0.8f, 0); + poseStack.mulPose(Axis.YP.rotationDegrees(180)); + this.heldItemRenderer.renderItem(entity, feetStack, mode, poseStack, bufferSource, packedLight); + poseStack.popPose(); + poseStack.pushPose(); + ((PlayerModel) this.getParentModel()).leftLeg.translateAndRotate(poseStack); + poseStack.mulPose(Axis.XP.rotationDegrees(180)); + poseStack.scale(0.75f, 0.75f, 0.75f); + poseStack.translate(0, -0.8f, 0); + poseStack.mulPose(Axis.YP.rotationDegrees(180)); + this.heldItemRenderer.renderItem(entity, feetStack, mode, poseStack, bufferSource, packedLight); + poseStack.popPose(); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/com/r3944realms/leashedplayer/client/renderer/entities/LeashRopeArrowRenderer.java b/src/main/java/com/r3944realms/leashedplayer/client/renderer/entities/LeashRopeArrowRenderer.java new file mode 100644 index 0000000..6ba7df6 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/client/renderer/entities/LeashRopeArrowRenderer.java @@ -0,0 +1,30 @@ +package com.r3944realms.leashedplayer.client.renderer.entities; + +import com.r3944realms.leashedplayer.LeashedPlayer; +import com.r3944realms.leashedplayer.content.entities.LeashRopeArrow; +import net.minecraft.client.renderer.entity.ArrowRenderer; +import net.minecraft.client.renderer.entity.EntityRendererProvider; +import net.minecraft.client.renderer.entity.state.ArrowRenderState; +import net.minecraft.resources.ResourceLocation; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.api.distmarker.OnlyIn; +import org.jetbrains.annotations.NotNull; + +@OnlyIn(Dist.CLIENT) +public class LeashRopeArrowRenderer extends ArrowRenderer { + public static final ResourceLocation LEASH_ROPE_ARROW = ResourceLocation.fromNamespaceAndPath(LeashedPlayer.MOD_ID, "textures/entity/projectiles/leash_rope_arrow.png"); + public LeashRopeArrowRenderer(EntityRendererProvider.Context pContext) { + super(pContext); + } + + @Override + public @NotNull ArrowRenderState createRenderState() { + return new ArrowRenderState(); + } + + @Override + protected @NotNull ResourceLocation getTextureLocation(@NotNull ArrowRenderState arrowRenderState) { + return LEASH_ROPE_ARROW; + } + +} diff --git a/src/main/java/com/r3944realms/leashedplayer/client/renderer/entities/SpectralLeashRopeArrowRenderer.java b/src/main/java/com/r3944realms/leashedplayer/client/renderer/entities/SpectralLeashRopeArrowRenderer.java new file mode 100644 index 0000000..711ec59 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/client/renderer/entities/SpectralLeashRopeArrowRenderer.java @@ -0,0 +1,31 @@ +package com.r3944realms.leashedplayer.client.renderer.entities; + +import com.r3944realms.leashedplayer.LeashedPlayer; +import com.r3944realms.leashedplayer.content.entities.SpectralLeashRopeArrow; +import net.minecraft.client.renderer.entity.ArrowRenderer; +import net.minecraft.client.renderer.entity.EntityRendererProvider; +import net.minecraft.client.renderer.entity.state.ArrowRenderState; +import net.minecraft.resources.ResourceLocation; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.api.distmarker.OnlyIn; +import org.jetbrains.annotations.NotNull; + +@OnlyIn(Dist.CLIENT) +public class SpectralLeashRopeArrowRenderer extends ArrowRenderer { + public static final ResourceLocation SPECTRAL_LEASH_ROPE_ARROW = ResourceLocation.fromNamespaceAndPath(LeashedPlayer.MOD_ID, "textures/entity/projectiles/spectral_leash_rope_arrow.png"); + + public SpectralLeashRopeArrowRenderer(EntityRendererProvider.Context pContext) { + super(pContext); + } + + @Override + public @NotNull ArrowRenderState createRenderState() { + return new ArrowRenderState(); + } + + @Override + protected @NotNull ResourceLocation getTextureLocation(@NotNull ArrowRenderState arrowRenderState) { + return SPECTRAL_LEASH_ROPE_ARROW; + } + +} diff --git a/src/main/java/com/r3944realms/leashedplayer/client/renderer/gui/AdaptiveGuiRendererHandler.java b/src/main/java/com/r3944realms/leashedplayer/client/renderer/gui/AdaptiveGuiRendererHandler.java new file mode 100644 index 0000000..863f0d8 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/client/renderer/gui/AdaptiveGuiRendererHandler.java @@ -0,0 +1,42 @@ +package com.r3944realms.leashedplayer.client.renderer.gui; + +import com.google.common.collect.Maps; +import com.mojang.blaze3d.vertex.PoseStack; +import com.r3944realms.leashedplayer.LeashedPlayer; +import com.r3944realms.leashedplayer.client.processBar.IProcessBar; +import net.minecraft.client.gui.GuiGraphics; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.common.EventBusSubscriber; +import net.neoforged.neoforge.client.event.RenderGuiEvent; + +import java.util.Map; + +@EventBusSubscriber(modid = LeashedPlayer.MOD_ID, value = Dist.CLIENT) +public class AdaptiveGuiRendererHandler { + private static final Map> processBars = Maps.newConcurrentMap(); + public static void addProcessBar(IProcessBar processBar, IProcessBarRenderer renderer) { + processBars.put(processBar, renderer); + } + public static IProcessBarRenderer getProcessBarRenderer(IProcessBar processBar) { + return processBars.get(processBar); + } + + @SubscribeEvent + public static void onRendererLevel(RenderGuiEvent.Pre event) { + PoseStack matrixStack = event.getGuiGraphics().pose(); + GuiGraphics guiGraphics = event.getGuiGraphics(); + processBars.keySet().forEach( + processBar -> { + IProcessBarRenderer renderer = processBars.get(processBar); + if(processBar.shouldRender()) { + renderer.renderProcessBar(matrixStack, guiGraphics); + } else { + if (processBar.aliveCount() <= 0) {//计数为非正时移除 + processBars.remove(processBar); + } + } + } + ); + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/client/renderer/gui/IFadingProcessBarRenderer.java b/src/main/java/com/r3944realms/leashedplayer/client/renderer/gui/IFadingProcessBarRenderer.java new file mode 100644 index 0000000..3dfeed2 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/client/renderer/gui/IFadingProcessBarRenderer.java @@ -0,0 +1,83 @@ +package com.r3944realms.leashedplayer.client.renderer.gui; + +import com.mojang.blaze3d.vertex.PoseStack; +import com.r3944realms.leashedplayer.client.processBar.IProcessBar; +import com.r3944realms.leashedplayer.client.processBar.TestProcessBar; +import net.minecraft.client.gui.GuiGraphics; + +public interface IFadingProcessBarRenderer extends IProcessBarRenderer { + /** + * @return 当前透明度,0.0(完全透明)到 1.0(完全不透明) + */ + float getFadeAlpha(); + void setFadeAlpha(float alpha); + /** + * @return 是否淡入 + */ + boolean isFadingIn(); + void setFadingIn(boolean fadingIn); + /** + * @return 是否淡出 + */ + boolean isFadingOut(); + void setFadingOut(boolean fadingOut); + /** + * @return 淡化速度 + */ + float getFadeRate(); + void setFadeRate(float fadeRate); + /** + * @return 完全淡入/淡出所需时间(秒) + */ + float getFadeDuration(); + void setFadeDuration(float fadeDuration); + /** + * 更新淡化效果 + */ + default void updateFadeEffect() { + float fadeSpeed = getFadeRate() / getFadeDuration(); + if (isFadingIn()) { + fadeInTick(fadeSpeed); + + } else if (isFadingOut()) { + fadeOutTick(fadeSpeed); + if(getFadeAlpha() <= 0.0F){ + if (getProcessBar().getCurrentProcess() != 0 || getProcessBar().getCurrentProcess() != getProcessBar().getMaxProcess()) { + getProcessBar().retain(); + } + stopRender(); + } + } + } + + /** + * Do Not Override, instead of using renderProcessBar0()
+ * 请勿重载,请重载renderProcessBar0()替代 + * @deprecated + */ + @Deprecated + @Override + default void renderProcessBar(PoseStack poseStack, GuiGraphics guiGraphics) { + updateFadeEffect(); + renderProcessBar0(poseStack, guiGraphics); + } + void renderProcessBar0(PoseStack poseStack, GuiGraphics guiGraphics); + + default void fadeInTick(float fadeSpeed) { + if (getFadeAlpha() >= 1.0f) { + setFadingIn(false); + return; + } + setFadeAlpha(Math.min(getFadeAlpha() + fadeSpeed, 1.0f)); + + } + default void fadeOutTick(float fadeSpeed) { + if (getFadeAlpha() >= 1.0f) { + setFadingOut(false); + return; + } + setFadeAlpha(Math.max(getFadeAlpha() - fadeSpeed, 0.0f)); + } + + void setProcessBar(TestProcessBar processBar); +} diff --git a/src/main/java/com/r3944realms/leashedplayer/client/renderer/gui/IProcessBarRenderer.java b/src/main/java/com/r3944realms/leashedplayer/client/renderer/gui/IProcessBarRenderer.java new file mode 100644 index 0000000..d52f830 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/client/renderer/gui/IProcessBarRenderer.java @@ -0,0 +1,16 @@ +package com.r3944realms.leashedplayer.client.renderer.gui; + +import com.mojang.blaze3d.vertex.PoseStack; +import com.r3944realms.leashedplayer.client.processBar.IProcessBar; +import net.minecraft.client.gui.GuiGraphics; + +public interface IProcessBarRenderer { + T getProcessBar(); + void setProcessBar(T processBar); + void renderProcessBar(PoseStack poseStack, GuiGraphics guiGraphics); + default void stopRender() { + T processBar = getProcessBar(); + processBar.decreaseAliveCount(); + processBar.setRenderStatus(false); + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/client/renderer/gui/ProcessBarRenderer.java b/src/main/java/com/r3944realms/leashedplayer/client/renderer/gui/ProcessBarRenderer.java new file mode 100644 index 0000000..51cd033 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/client/renderer/gui/ProcessBarRenderer.java @@ -0,0 +1,90 @@ +package com.r3944realms.leashedplayer.client.renderer.gui; + +import com.mojang.blaze3d.vertex.PoseStack; +import com.r3944realms.leashedplayer.LeashedPlayer; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.world.entity.player.Player; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.common.EventBusSubscriber; +import net.neoforged.neoforge.client.event.ClientTickEvent; +import net.neoforged.neoforge.client.event.RenderGuiEvent; + +@EventBusSubscriber(modid = LeashedPlayer.MOD_ID, value = Dist.CLIENT) +public class ProcessBarRenderer { + + private static boolean isRendering = false; + private static final int progressDuration = 100; + private static int currentProgress = 0; + public static void startRenderingProgressBar(Player player) { + isRendering = true; + currentProgress = 0; + } + public static void stopRenderingProgressBar() { + isRendering = false; + } + @SubscribeEvent + public static void onClientTick(ClientTickEvent.Pre event) { + if (isRendering) { + currentProgress++; + if (currentProgress >= progressDuration) { + stopRenderingProgressBar(); + } + } + } + @SubscribeEvent + public static void onRendererLevel(RenderGuiEvent.Pre event) { + PoseStack matrixStack = event.getGuiGraphics().pose(); + GuiGraphics guiGraphics = event.getGuiGraphics(); + if(isRendering) { + renderProgressBar(matrixStack, guiGraphics); + } + } + public static float getProgress() { + return (float) currentProgress / progressDuration; + } + private static void renderProgressBar(PoseStack matrixStack, GuiGraphics guiGraphics) { + Minecraft mc = Minecraft.getInstance(); + int screenWidth = mc.getWindow().getGuiScaledWidth(); + int screenHeight = mc.getWindow().getGuiScaledHeight(); + + // 设置进度条的位置和尺寸 + int barWidth = screenWidth / 4; + int barHeight = 12; + int x = (screenWidth - barWidth) / 2; + int y = screenHeight - 47; + + // 绘制背景(灰色) + guiGraphics.fill(x, y, x + barWidth, y + barHeight, 0xFF555555); + + // 计算当前进度并绘制前景(渐变颜色) + float progress = getProgress(); + int progressWidth = (int) (progress * barWidth); + + // 渐变颜色效果:从红色到绿色 + int color = lerpColor(progress); + guiGraphics.fill(x, y, x + progressWidth, y + barHeight, color); + + // 绘制边框:分别绘制四条边 + int borderColor = 0xFFFFFFFF; // 白色 + guiGraphics.fill(x - 1, y - 1, x + barWidth + 1, y, borderColor); // 顶边 + guiGraphics.fill(x - 1, y + barHeight, x + barWidth + 1, y + barHeight + 1, borderColor); // 底边 + guiGraphics.fill(x - 1, y, x, y + barHeight, borderColor); // 左边 + guiGraphics.fill(x + barWidth, y, x + barWidth + 1, y + barHeight, borderColor); // 右边 + } + + // 渐变颜色计算 + private static int lerpColor(float progress) { + int a = (int) (getAlpha(-65536) * (1 - progress) + getAlpha(-16711936) * progress); + int r = (int) (getRed(-65536) * (1 - progress) + getRed(-16711936) * progress); + int g = (int) (getGreen(-65536) * (1 - progress) + getGreen(-16711936) * progress); + int b = (int) (getBlue(-65536) * (1 - progress) + getBlue(-16711936) * progress); + return (a << 24) | (r << 16) | (g << 8) | b; + } + + private static int getAlpha(int color) { return (color >> 24) & 0xFF; } + private static int getRed(int color) { return (color >> 16) & 0xFF; } + private static int getGreen(int color) { return (color >> 8) & 0xFF; } + private static int getBlue(int color) { return color & 0xFF; } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/client/renderer/gui/TestProcessBarRenderer.java b/src/main/java/com/r3944realms/leashedplayer/client/renderer/gui/TestProcessBarRenderer.java new file mode 100644 index 0000000..6a794ae --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/client/renderer/gui/TestProcessBarRenderer.java @@ -0,0 +1,161 @@ +package com.r3944realms.leashedplayer.client.renderer.gui; + +import com.mojang.blaze3d.vertex.PoseStack; +import com.r3944realms.leashedplayer.client.processBar.IProcessBar; +import com.r3944realms.leashedplayer.client.processBar.TestProcessBar; +import com.r3944realms.leashedplayer.utils.ColorUtil; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphics; +import org.jetbrains.annotations.Nullable; + +public class TestProcessBarRenderer implements IFadingProcessBarRenderer{ + @Nullable + private TestProcessBar processBar; + private float FadeAlpha; + private boolean fadingIn, fadingOut; + private float FadeRate; + private float FadeDuration; + + public TestProcessBarRenderer(boolean fadingIn, boolean fadingOut, float FadeRate, float FadeDuration){ + this.fadingIn = fadingIn; + this.fadingOut = fadingOut; + this.FadeRate = FadeRate; + this.FadeDuration = FadeDuration; + this.FadeAlpha = 0.0F; + } + + public TestProcessBarRenderer(boolean fadingIn, boolean fadingOut, float FadeRate, float FadeDuration, float FadeAlpha){ + this.fadingIn = fadingIn; + this.fadingOut = fadingOut; + this.FadeRate = FadeRate; + this.FadeDuration = FadeDuration; + this.FadeAlpha = FadeAlpha; + } + + public TestProcessBarRenderer(TestProcessBar bar, boolean fadingIn, boolean fadingOut, float FadeRate, float FadeDuration) { + this(fadingIn, fadingOut, FadeRate, FadeDuration); + setProcessBar(bar); + } + + public TestProcessBarRenderer(TestProcessBar bar, boolean fadingIn, boolean fadingOut, float FadeRate, float FadeDuration, float FadeAlpha) { + this(fadingIn, fadingOut, FadeRate, FadeDuration, FadeAlpha); + setProcessBar(bar); + } + + public TestProcessBarRenderer() { + this(true, false, 0.02f, 10.0f, 0.0f); + } + + public TestProcessBarRenderer(TestProcessBar bar) { + this(true, false, 0.02f, 10.0f); + setProcessBar(bar); + } + + + @Override + public float getFadeAlpha() { + return this.FadeAlpha; + } + + @Override + public void setFadeAlpha(float alpha) { + this.FadeAlpha = alpha; + } + + @Override + public boolean isFadingIn() { + return this.fadingIn; + } + + @Override + public void setFadingIn(boolean fadingIn) { + this.fadingIn = fadingIn; + } + + @Override + public boolean isFadingOut() { + return this.fadingOut; + } + + @Override + public void setFadingOut(boolean fadingOut) { + this.fadingOut = fadingOut; + } + + @Override + public float getFadeRate() { + return this.FadeRate; + } + + @Override + public void setFadeRate(float fadeRate) { + this.FadeRate = fadeRate; + } + + @Override + public float getFadeDuration() { + return this.FadeDuration; + } + + @Override + public void setFadeDuration(float fadeDuration) { + this.FadeDuration = fadeDuration; + } + + @Override + public @Nullable TestProcessBar getProcessBar() { + return this.processBar; + } + + @Override + public void setProcessBar(IProcessBar processBar) { + if(processBar instanceof TestProcessBar pB){ + setProcessBar(pB); + } else throw new UnsupportedOperationException("Not supported Non-TestProcessBar"); + } + + @Override + public void setProcessBar(TestProcessBar processBar) { + this.processBar = processBar; + } + + + + @Override + public void renderProcessBar0(PoseStack poseStack, GuiGraphics guiGraphics) { + Minecraft mc = Minecraft.getInstance(); + int screenWidth = mc.getWindow().getGuiScaledWidth(); + int screenHeight = mc.getWindow().getGuiScaledHeight(); + + int elementWidth = 200; + int elementHeight = 12; + int x = (screenWidth - elementWidth) / 2; + int y = screenHeight - elementHeight - 47; // 控制 GUI 元素的位置 + + // 计算透明度 + float progress = 0; + if (getProcessBar() != null) { + progress = (float) getProcessBar().getCurrentProcess() / getProcessBar().getMaxProcess(); + } + int alpha = (int) (FadeAlpha * 255); + int backgroundColor = (alpha << 24) | 0xFF555555; // 背景颜色 + int foregroundColor = (alpha << 24) | ColorUtil.lerpColor(progress);; // 前景颜色 + + // 绘制背景 + guiGraphics.fill(x, y, x + elementWidth, y + elementHeight, backgroundColor); + + // 绘制前景(例如进度条) + + int progressWidth = (int) (progress * elementWidth); + guiGraphics.fill(x, y, x + progressWidth, y + elementHeight, foregroundColor); + + // 绘制边框 + int borderColor = 0xFFFFFFFF; // 白色边框 + guiGraphics.fill(x - 1, y - 1, x + elementWidth + 1, y, borderColor); // 顶边 + guiGraphics.fill(x - 1, y + elementHeight, x + elementWidth + 1, y + elementHeight + 1, borderColor); // 底边 + guiGraphics.fill(x - 1, y, x, y + elementHeight, borderColor); // 左边 + guiGraphics.fill(x + elementWidth, y, x + elementWidth + 1, y + elementHeight, borderColor); // 右边 + + } + +} diff --git a/src/main/java/com/r3944realms/leashedplayer/client/renderer/item/ConditionalRangeItemModel.java b/src/main/java/com/r3944realms/leashedplayer/client/renderer/item/ConditionalRangeItemModel.java new file mode 100644 index 0000000..7545190 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/client/renderer/item/ConditionalRangeItemModel.java @@ -0,0 +1,139 @@ +package com.r3944realms.leashedplayer.client.renderer.item; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import com.r3944realms.leashedplayer.client.renderer.item.properties.conditionalRange.ConditionalRangeItemModelProperties; +import com.r3944realms.leashedplayer.client.renderer.item.properties.conditionalRange.ConditionalRangeItemModelProperty; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.renderer.item.ItemModel; +import net.minecraft.client.renderer.item.ItemModelResolver; +import net.minecraft.client.renderer.item.ItemModels; +import net.minecraft.client.renderer.item.ItemStackRenderState; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.item.ItemDisplayContext; +import net.minecraft.world.item.ItemStack; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.api.distmarker.OnlyIn; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +@OnlyIn(Dist.CLIENT) +public class ConditionalRangeItemModel implements ItemModel { + private final ConditionalRangeItemModelProperty property; + private final float scale; + private final float[] onTrueThresholds; + private final float[] onFalseThresholds; + private final ItemModel[] onTrueModels; + private final ItemModel[] onFalseModels; + private final ItemModel fallback; + public ConditionalRangeItemModel(ConditionalRangeItemModelProperty property, float scale, float[] onTrueThresholds, float[] onFalseThresholds, ItemModel[] onTrueModels, ItemModel[] onFalseModels, ItemModel fallback) { + this.property = property; + this.scale = scale; + this.onTrueThresholds = onTrueThresholds; + this.onFalseThresholds = onFalseThresholds; + this.onTrueModels = onTrueModels; + this.onFalseModels = onFalseModels; + this.fallback = fallback; + } + private static int lastIndexLessOrEqual(float[] thresholds, float value) { + if (thresholds.length < 16) { + for (int k = 0; k < thresholds.length; k++) { + if (thresholds[k] > value) { + return k - 1; + } + } + + return thresholds.length - 1; + } else { + int i = Arrays.binarySearch(thresholds, value); + if (i < 0) { + int j = ~i; + return j - 1; + } else { + return i; + } + } + } + @Override + public void update(@NotNull ItemStackRenderState renderState, @NotNull ItemStack stack, @NotNull ItemModelResolver itemModelResolver, @NotNull ItemDisplayContext displayContext, @Nullable ClientLevel level, @Nullable LivingEntity entity, int seed) { + ConditionalRangeItemModelProperty.ConditionalRangeData conditionalRangeData = this.property.get(stack, level, entity, seed, displayContext); + float f = conditionalRangeData.floatValue() * this.scale; + boolean b = conditionalRangeData.boolValue(); + ItemModel itemModel; + if(Float.isNaN(f)) { + itemModel = this.fallback; ; + } else { + int i; + if (b) { + i = lastIndexLessOrEqual(this.onTrueThresholds, f); + } + else { + i = lastIndexLessOrEqual(this.onFalseThresholds, f); + } + itemModel = i == -1 ? this.fallback : b ? onTrueModels[i] : onFalseModels[i]; + + } + itemModel.update(renderState, stack, itemModelResolver, displayContext, level, entity, seed); + } + public record Entry(float threshold, ItemModel.Unbaked model) { + public static final Codec CODEC = RecordCodecBuilder.create( + instance -> instance.group( + Codec.FLOAT.fieldOf("threshold").forGetter(Entry::threshold), + ItemModels.CODEC.fieldOf("model").forGetter(Entry::model) + ).apply(instance, Entry::new) + ); + public static final Comparator BY_THRESHOLD = Comparator.comparingDouble(Entry::threshold); + } + public record Unbaked( + ConditionalRangeItemModelProperty property, float scale, List trueModels, List falseModels, Optional fallback + ) implements ItemModel.Unbaked { + public static final MapCodec MAP_CODEC = RecordCodecBuilder.mapCodec( + instance -> instance.group( + ConditionalRangeItemModelProperties.MAP_CODEC.forGetter(Unbaked::property), + Codec.FLOAT.optionalFieldOf("scale", 1.0F).forGetter(Unbaked::scale), + Entry.CODEC.listOf().fieldOf("trueModels").forGetter(Unbaked::trueModels), + Entry.CODEC.listOf().fieldOf("falseModels").forGetter(Unbaked::falseModels), + ItemModels.CODEC.optionalFieldOf("fallback").forGetter(Unbaked::fallback) + ).apply(instance, Unbaked::new) + ); + @Override + public @NotNull MapCodec type() { + return MAP_CODEC; + } + + @Override + public @NotNull ItemModel bake(@NotNull BakingContext context) { + float[] floatArrA = new float[this.trueModels.size()]; + float[] floatArrB = new float[this.falseModels.size()]; + ItemModel[] itemModelArrA = new ItemModel[this.trueModels.size()]; + ItemModel[] itemModelArrB = new ItemModel[this.falseModels.size()]; + List trueModelList = new ArrayList<>(this.trueModels); + List falseModelList = new ArrayList<>(this.falseModels); + trueModelList.sort(Entry.BY_THRESHOLD); + falseModelList.sort(Entry.BY_THRESHOLD); + for (int i = 0; i < trueModelList.size(); i++) { + Entry rangeselectitemmodel$entry = trueModelList.get(i); + floatArrA[i] = rangeselectitemmodel$entry.threshold; + itemModelArrA[i] = rangeselectitemmodel$entry.model.bake(context); + } + for (int i = 0; i < falseModels.size(); i++) { + Entry rangeselectitemmodel$entry = falseModelList.get(i); + floatArrB[i] = rangeselectitemmodel$entry.threshold; + itemModelArrB[i] = rangeselectitemmodel$entry.model.bake(context); + } + ItemModel itemmodel = this.fallback.map(p_387030_ -> p_387030_.bake(context)).orElse(context.missingItemModel()); + return new ConditionalRangeItemModel(this.property, this.scale, floatArrA, floatArrB, itemModelArrA, itemModelArrB, itemmodel); + } + + @Override + public void resolveDependencies(@NotNull Resolver resolver) { + this.fallback.ifPresent(itemModel -> itemModel.resolveDependencies(resolver)); + this.trueModels.forEach(entry -> entry.model.resolveDependencies(resolver)); + this.falseModels.forEach(entry -> entry.model.resolveDependencies(resolver)); + } + } + +} diff --git a/src/main/java/com/r3944realms/leashedplayer/client/renderer/item/properties/conditional/ChargeExtend.java b/src/main/java/com/r3944realms/leashedplayer/client/renderer/item/properties/conditional/ChargeExtend.java new file mode 100644 index 0000000..598d1ea --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/client/renderer/item/properties/conditional/ChargeExtend.java @@ -0,0 +1,46 @@ +package com.r3944realms.leashedplayer.client.renderer.item.properties.conditional; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.MapCodec; +import com.r3944realms.leashedplayer.content.items.type.ILeashRopeArrow; +import com.r3944realms.leashedplayer.extend.CrossbowItem$ChargeTypeExtend; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.renderer.item.properties.select.SelectItemModelProperty; +import net.minecraft.core.component.DataComponents; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.item.ItemDisplayContext; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.component.ChargedProjectiles; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.api.distmarker.OnlyIn; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +@OnlyIn(Dist.CLIENT) +public record ChargeExtend() implements SelectItemModelProperty { + public static Codec CODEC = CrossbowItem$ChargeTypeExtend.CODEC; + public static Type TYPE = Type.create( + MapCodec.unit(new ChargeExtend()), CODEC + ); + @Override + public @NotNull CrossbowItem$ChargeTypeExtend get(@NotNull ItemStack stack, @Nullable ClientLevel level, @Nullable LivingEntity entity, int seed, @NotNull ItemDisplayContext displayContext) { + ChargedProjectiles chargedprojectiles = stack.get(DataComponents.CHARGED_PROJECTILES); + if(chargedprojectiles == null || chargedprojectiles.isEmpty()) { + return CrossbowItem$ChargeTypeExtend.NONE; + } else { + return ILeashRopeArrow.isLeashRopeArrow(stack, entity) ? CrossbowItem$ChargeTypeExtend.LEASH_ROPE_ARROW : + chargedprojectiles.contains(Items.FIREWORK_ROCKET) ? CrossbowItem$ChargeTypeExtend.ROCKET : CrossbowItem$ChargeTypeExtend.ARROW; + } + } + + @Override + public @NotNull Codec valueCodec() { + return CODEC; + } + + @Override + public @NotNull Type, CrossbowItem$ChargeTypeExtend> type() { + return TYPE; + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/client/renderer/item/properties/conditionalRange/BowPull.java b/src/main/java/com/r3944realms/leashedplayer/client/renderer/item/properties/conditionalRange/BowPull.java new file mode 100644 index 0000000..768a330 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/client/renderer/item/properties/conditionalRange/BowPull.java @@ -0,0 +1,27 @@ +package com.r3944realms.leashedplayer.client.renderer.item.properties.conditionalRange; + +import com.mojang.serialization.MapCodec; +import com.r3944realms.leashedplayer.content.items.type.ILeashRopeArrow; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.renderer.item.properties.numeric.UseDuration; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.item.ItemDisplayContext; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.Nullable; + +public record BowPull() implements ConditionalRangeItemModelProperty { + public static final MapCodec MAP_CODEC = MapCodec.unit(new BowPull()); + @Override + public ConditionalRangeData get(ItemStack stack, @Nullable ClientLevel level, @Nullable LivingEntity entity, int seed, ItemDisplayContext displayContext) { + + if (entity != null && entity.getUseItem() == stack) { + return ConditionalRangeData.create(ILeashRopeArrow.isLeashRopeArrow(stack, entity), (float)UseDuration.useDuration(stack, entity)); + } + else return ConditionalRangeData.create(false, 0.0F); + } + + @Override + public MapCodec type() { + return MAP_CODEC; + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/client/renderer/item/properties/conditionalRange/ConditionalRangeItemModelProperties.java b/src/main/java/com/r3944realms/leashedplayer/client/renderer/item/properties/conditionalRange/ConditionalRangeItemModelProperties.java new file mode 100644 index 0000000..a7f961b --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/client/renderer/item/properties/conditionalRange/ConditionalRangeItemModelProperties.java @@ -0,0 +1,19 @@ +package com.r3944realms.leashedplayer.client.renderer.item.properties.conditionalRange; + +import com.mojang.serialization.MapCodec; +import com.r3944realms.leashedplayer.LeashedPlayer; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.ExtraCodecs; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.api.distmarker.OnlyIn; + +@OnlyIn(Dist.CLIENT) +public class ConditionalRangeItemModelProperties { + private static final ExtraCodecs.LateBoundIdMapper> ID_MAPPER = new ExtraCodecs.LateBoundIdMapper<>(); + public static final MapCodec MAP_CODEC = ID_MAPPER.codec(ResourceLocation.CODEC) + .dispatchMap("property", ConditionalRangeItemModelProperty::type, mapCodec -> mapCodec); + public static void bootstrap() { + ID_MAPPER.put(ResourceLocation.fromNamespaceAndPath(LeashedPlayer.MOD_ID, "bow_pull"), BowPull.MAP_CODEC); + net.neoforged.fml.ModLoader.postEvent(new com.r3944realms.leashedplayer.content.events.RegisterConditionalRangeItemPropertyEvent(ID_MAPPER)); + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/client/renderer/item/properties/conditionalRange/ConditionalRangeItemModelProperty.java b/src/main/java/com/r3944realms/leashedplayer/client/renderer/item/properties/conditionalRange/ConditionalRangeItemModelProperty.java new file mode 100644 index 0000000..7c5f3bd --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/client/renderer/item/properties/conditionalRange/ConditionalRangeItemModelProperty.java @@ -0,0 +1,23 @@ +package com.r3944realms.leashedplayer.client.renderer.item.properties.conditionalRange; + +import com.mojang.serialization.MapCodec; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.item.ItemDisplayContext; +import net.minecraft.world.item.ItemStack; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.api.distmarker.OnlyIn; + +import javax.annotation.Nullable; + +@OnlyIn(Dist.CLIENT) +public interface ConditionalRangeItemModelProperty { + ConditionalRangeData get(ItemStack stack, @Nullable ClientLevel level, @Nullable LivingEntity entity, int seed, ItemDisplayContext displayContext); + + MapCodec type(); + record ConditionalRangeData(boolean boolValue, float floatValue) { + public static ConditionalRangeData create(boolean boolValue, float floatValue) { + return new ConditionalRangeData(boolValue, floatValue); + } + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/config/LeashPlayerCommonConfig.java b/src/main/java/com/r3944realms/leashedplayer/config/LeashPlayerCommonConfig.java new file mode 100644 index 0000000..5c93369 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/config/LeashPlayerCommonConfig.java @@ -0,0 +1,39 @@ +package com.r3944realms.leashedplayer.config; + +import net.neoforged.neoforge.common.ModConfigSpec; + +public class LeashPlayerCommonConfig { + public static ModConfigSpec.Builder BUILDER = new ModConfigSpec.Builder(); + public static final ModConfigSpec SPEC; + public static final ModConfigSpec.ConfigValue LeashedPlayerModCommandPrefix; + public static final ModConfigSpec.BooleanValue EnableLeashPlayerCommandPrefix; + public static ModConfigSpec.IntValue MinimumLeashLengthCanBeSet, MaximumLeashLengthCanBeSet, TheLeashArrowMaxLifeTime; + public static ModConfigSpec.DoubleValue TheMultipleThatLeashRopeArrowBreakLength, TheLeashBreakLengthTimesBase; + static { + BUILDER.comment("Leash Player Config"); + + BUILDER.comment("Leash Player Command"); + BUILDER.push("Command"); + EnableLeashPlayerCommandPrefix = BUILDER.comment("The prefix of this mod's commands"," [ Default: true] ").define("EnableLeashPlayerCommand", true); + LeashedPlayerModCommandPrefix = BUILDER.comment("The prefix of this mod's commands"," [ Default:'lp'] ").define("LeashedPlayerModCommandPrefix", "lp"); + + BUILDER.pop(); + BUILDER.comment("Leash Player Arrow"); + BUILDER.push("LeashRopeArrow"); + TheMultipleThatLeashRopeArrowBreakLength = BUILDER.comment("How many times is the length of the arrow rope based on BreakLength TimeBase", "[ Default : 5.0f, Invalid Range:[2.0f, 10.0f] ]").defineInRange("TheMultipleArrowBreak", 5.0f, 2.0f , 10.0f); + TheLeashArrowMaxLifeTime = BUILDER.comment("If the LeashArrowEntity's life is bigger than this value ,it will be discarded", "[ Default : 2400, Invalid Range:[1200 , 10240]]").defineInRange("TheLeashArrowMaxLifeTime",2400, 1200, 10240); + BUILDER.pop(); + BUILDER.comment("Leash Player Misc"); + BUILDER.push("Misc"); + BUILDER.comment("Leash Player Length"); + BUILDER.push("LeashLength"); + TheLeashBreakLengthTimesBase = BUILDER.comment("When it exceeds how many times, the leash will drop"," [ Default:3.0f, Valid Range:[3.0f, 6.0f] ]").defineInRange("BreakLengthTimeBase", 3.0f, 3.0f ,6.0f); + MinimumLeashLengthCanBeSet = BUILDER.comment("The minimum integer's length of Leash", " [ Default:5, Invalid Range:[2,10] ]").defineInRange("MinLeashLength", 5, 2, 10); + MaximumLeashLengthCanBeSet = BUILDER.comment("The maximum integer's length of Leash", " [ Default:1024, Invalid Range:[32, 1024] ]").defineInRange("MaxLeashLength", 1024, 32, 1024); + BUILDER.pop().pop(); + + + + SPEC = BUILDER.build(); + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/content/ModKeyMapping.java b/src/main/java/com/r3944realms/leashedplayer/content/ModKeyMapping.java new file mode 100644 index 0000000..868dd5f --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/content/ModKeyMapping.java @@ -0,0 +1,37 @@ +package com.r3944realms.leashedplayer.content; + +import com.mojang.blaze3d.platform.InputConstants; +import net.minecraft.client.KeyMapping; +import net.neoforged.neoforge.client.settings.KeyConflictContext; +import net.neoforged.neoforge.client.settings.KeyModifier; +import org.lwjgl.glfw.GLFW; + +public class ModKeyMapping { + static String KEY_ROOT_ = "key.leashedplayer."; + public static String CATEGORY = "key.leashedplayer.category"; + public static String + ADD_LEASH_LENGTH_KEY = KEY_ROOT_ + "leash_length.add", + SUB_LEASH_LENGTH_KEY = KEY_ROOT_ + "leash_length.sub", + NOT_SUPPORT_TO_NOT_PLAYER_ENTITY = KEY_ROOT_ + "leash_length.not_support_to_not_player_entity"; + + + public static final KeyMapping KEY_ADD_LEASH_LENGTH = + new KeyMapping( + ADD_LEASH_LENGTH_KEY, + KeyConflictContext.IN_GAME, + KeyModifier.CONTROL, + InputConstants.Type.KEYSYM, + GLFW.GLFW_KEY_UP, + CATEGORY + ); + public static final KeyMapping KEY_SUB_LEASH_LENGTH = + new KeyMapping( + SUB_LEASH_LENGTH_KEY, + KeyConflictContext.IN_GAME, + KeyModifier.CONTROL, + InputConstants.Type.KEYSYM, + GLFW.GLFW_KEY_DOWN, + CATEGORY + ); +} + diff --git a/src/main/java/com/r3944realms/leashedplayer/content/commands/Command.java b/src/main/java/com/r3944realms/leashedplayer/content/commands/Command.java new file mode 100644 index 0000000..2d97ed3 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/content/commands/Command.java @@ -0,0 +1,22 @@ +package com.r3944realms.leashedplayer.content.commands; + +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.r3944realms.leashedplayer.config.LeashPlayerCommonConfig; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public class Command { + public static final String PREFIX = LeashPlayerCommonConfig.LeashedPlayerModCommandPrefix.get(); + public static boolean SHOULD_USE_PREFIX = LeashPlayerCommonConfig.EnableLeashPlayerCommandPrefix.get(); + static LiteralArgumentBuilder getLiterArgumentBuilderOfCSS(String name, boolean shouldAddToList, @Nullable List> list) { + LiteralArgumentBuilder literal = Commands.literal(name); + if (shouldAddToList) { + assert list != null; + list.add(literal); + } + return literal; + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/content/commands/DebugCommand.java b/src/main/java/com/r3944realms/leashedplayer/content/commands/DebugCommand.java new file mode 100644 index 0000000..9190616 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/content/commands/DebugCommand.java @@ -0,0 +1,58 @@ +package com.r3944realms.leashedplayer.content.commands; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.commands.arguments.EntityArgument; +import net.minecraft.network.Connection; +import net.minecraft.network.PacketSendListener; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.network.protocol.common.ClientboundDisconnectPacket; +import net.minecraft.server.level.ServerPlayer; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; + +import static com.r3944realms.leashedplayer.content.commands.Command.SHOULD_USE_PREFIX; +import static com.r3944realms.leashedplayer.content.commands.Command.getLiterArgumentBuilderOfCSS; + +public class DebugCommand { + public static void register(CommandDispatcher dispatcher) { + @Nullable List> nodeList = SHOULD_USE_PREFIX ? null : new ArrayList<>(); + LiteralArgumentBuilder literalArgumentBuilder = Commands.literal(Command.PREFIX); + LiteralArgumentBuilder $$DebugRoot = getLiterArgumentBuilderOfCSS("debug", !SHOULD_USE_PREFIX, nodeList); + com.mojang.brigadier.Command kickTargets = context -> { + for(ServerPlayer entity : EntityArgument.getPlayers(context, "targets")){ + MutableComponent literal = Component.literal("You are already connected to this proxy!"); + Connection connection = entity.connection.getConnection(); + connection.send(new ClientboundDisconnectPacket(literal), PacketSendListener.thenRun(() -> connection.disconnect(literal))); + } + return 0; + }; + com.mojang.brigadier.Command kickSelf = context -> { + CommandSourceStack source = context.getSource(); + if (source.getEntity() instanceof ServerPlayer serverPlayer) { + MutableComponent literal = Component.literal("You are already connected to this proxy!"); + Connection connection = serverPlayer.connection.getConnection(); + connection.send(new ClientboundDisconnectPacket(literal), PacketSendListener.thenRun(() -> connection.disconnect(literal))); + } + return 0; + }; + LiteralArgumentBuilder debugKick = $$DebugRoot.requires(commandSourceStack -> commandSourceStack.hasPermission(2)) + .then( + Commands.literal("kick").executes(kickSelf) + .then(Commands.argument("targets", EntityArgument.entities()) + .executes(kickTargets) + ) + ); + if(SHOULD_USE_PREFIX){ + literalArgumentBuilder.then(debugKick); + dispatcher.register(literalArgumentBuilder); + } else { + nodeList.forEach(dispatcher::register); + } + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/content/commands/LeashCommand.java b/src/main/java/com/r3944realms/leashedplayer/content/commands/LeashCommand.java new file mode 100644 index 0000000..4adcfd4 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/content/commands/LeashCommand.java @@ -0,0 +1,485 @@ +package com.r3944realms.leashedplayer.content.commands; + +import com.mojang.brigadier.Command; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.FloatArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.r3944realms.leashedplayer.config.LeashPlayerCommonConfig; +import com.r3944realms.leashedplayer.content.gamerules.GameruleRegistry; +import com.r3944realms.leashedplayer.content.gamerules.Server.CreateLeashFenceKnotEntityIfAbsent; +import com.r3944realms.leashedplayer.modInterface.ILivingEntityExtension; +import com.r3944realms.leashedplayer.modInterface.PlayerLeashable; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.commands.arguments.EntityArgument; +import net.minecraft.commands.arguments.coordinates.BlockPosArgument; +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.tags.BlockTags; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.Leashable; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import static com.r3944realms.leashedplayer.content.commands.Command.getLiterArgumentBuilderOfCSS; + +public class LeashCommand { + public static final Integer MIN_VALUE = LeashPlayerCommonConfig.MinimumLeashLengthCanBeSet.get(); + public static final Integer MAX_VALUE = LeashPlayerCommonConfig.MaximumLeashLengthCanBeSet.get(); + private final static String LEASHEDPLAYER_LEASH_MESSAGE_ = "leashedplayer.command.leash.message."; + + public final static String LEASH_FAILED = LEASHEDPLAYER_LEASH_MESSAGE_ + "leash.length.failed"; + + public final static String LEASH_LENGTH_SHOW = LEASHEDPLAYER_LEASH_MESSAGE_ + "leash.length.show", + LEASH_LENGTH_SET = LEASHEDPLAYER_LEASH_MESSAGE_ + "leash.length.set", + LEASH_DATA = LEASHEDPLAYER_LEASH_MESSAGE_ + "leash.data", + LEASH_DATA_CLEAR = LEASH_DATA + ".clear" + ; + + public final static String NO_LEASH_DATA = LEASH_DATA + ".null", + LEASH_DATA_SHOW = LEASH_DATA + ".show", + LEASH_DATA_SET = LEASH_DATA + ".set", + LEASH_DATA_SET_FAILED_DIFF_LEVEL = LEASH_DATA_SET + ".failed.diff_level", + LEASH_DATA_SET_FAILED_TOO_FAR = LEASH_DATA_SET + ".failed.too_far", + LEASH_DATA_SET_FAILED_NO_KNOT_EXISTED_IN_THAT_POS = LEASH_DATA_SET + ".failed.no_knot_exist_in_that_pos", + LEASH_DATA_SET_FAILED_FORBID_SAME_ENTITY = LEASH_DATA_SET + ".failed.forbid_same_entity", + LEASH_DATA_CLEAR_FAILED_NO_DATA = LEASH_DATA_CLEAR + ".leash.clear.failed.no_data" + ; + + public static void register(CommandDispatcher dispatcher) { + boolean shouldUsePrefix = LeashPlayerCommonConfig.EnableLeashPlayerCommandPrefix.get(); + @Nullable List> nodeList = shouldUsePrefix ? null : new ArrayList<>(); + LiteralArgumentBuilder literalArgumentBuilder = Commands.literal(com.r3944realms.leashedplayer.content.commands.Command.PREFIX); + LiteralArgumentBuilder $$leashRoot = getLiterArgumentBuilderOfCSS("leash", !shouldUsePrefix, nodeList); + + Command getSelfLeashLength = context -> { + CommandSourceStack source = context.getSource(); + try { + ServerPlayer player = source.getPlayerOrException(); + float leashLength = ((ILivingEntityExtension)player).getLeashLength(); + source.sendSuccess(() -> Component.translatable(LEASH_LENGTH_SHOW, player.getDisplayName(), leashLength), true); + } catch (Exception e) { + source.sendFailure(Component.translatable(LEASH_FAILED)); + return -1; + } + return 0; + }; + Command getRefPlayerLeashLength = context -> { + CommandSourceStack source = context.getSource(); + try { + ServerPlayer player = EntityArgument.getPlayer(context, "targetPlayer"); + float leashLength = ((ILivingEntityExtension)player).getLeashLength(); + source.sendSuccess(() -> Component.translatable(LEASH_LENGTH_SHOW, player.getDisplayName(), leashLength), true); + } catch (Exception e) { + source.sendFailure(Component.translatable(LEASH_FAILED)); + return -1; + } + return 0; + }; + Command getRefPlayersLeashLength = context -> { + CommandSourceStack source = context.getSource(); + try { + Collection playerCol = EntityArgument.getPlayers(context, "targetPlayers"); + playerCol.forEach(player -> { + float leashLength = ((ILivingEntityExtension) player).getLeashLength(); + source.sendSuccess(() -> Component.translatable(LEASH_LENGTH_SHOW, player.getDisplayName(), leashLength), true); + }); + } catch (Exception e) { + source.sendFailure(Component.translatable(LEASH_FAILED)); + return -1; + } + return 0; + }; + Command setSelfLengthLeashLength = context -> { + CommandSourceStack source = context.getSource(); + try { + ServerPlayer player = source.getPlayerOrException(); + float leashLength = context.getArgument("leashLength", Float.class); + ((ILivingEntityExtension)player).setLeashLength(leashLength); + source.sendSuccess(() -> Component.translatable(LEASH_LENGTH_SET, player.getDisplayName(), leashLength), true); + } catch (Exception e) { + source.sendFailure(Component.translatable(LEASH_FAILED)); + return -1; + } + return 0; + }; + Command setRefPlayerLengthLeashLength = context -> { + CommandSourceStack source = context.getSource(); + try { + ServerPlayer player = EntityArgument.getPlayer(context, "targetPlayer"); + float leashLength = context.getArgument("leashLength", Float.class); + ((ILivingEntityExtension)player).setLeashLength(leashLength); + source.sendSuccess(() -> Component.translatable(LEASH_LENGTH_SET, player.getDisplayName(), leashLength), true); + } catch (Exception e) { + source.sendFailure(Component.translatable(LEASH_FAILED)); + return -1; + } + return 0; + }; + Command setRefPlayersLengthLeashLength = context -> { + CommandSourceStack source = context.getSource(); + float leashLength = context.getArgument("leashLength", Float.class); + try { + Collection playerCol = EntityArgument.getPlayers(context, "targetPlayers"); + playerCol.forEach(player -> { + try { + ((ILivingEntityExtension)player).setLeashLength(leashLength); + source.sendSuccess(() -> Component.translatable(LEASH_LENGTH_SET, player.getDisplayName(), leashLength), true); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + + } catch (Exception e) { + source.sendFailure(Component.translatable(LEASH_FAILED)); + return -1; + } + return 0; + }; + //获取Data 构造一个MutableComponent显示数据 + Command geSelfLeashData = context -> { + CommandSourceStack source = context.getSource(); + try { + ServerPlayer player = source.getPlayerOrException(); + Integer x = LeashLengthGetResultInt(player, source); + if (x != null) return x; + } catch (Exception e) { + source.sendFailure(Component.translatable(LEASH_FAILED)); + return -1; + } + return 0; + }; + + Command getRefPlayerLeashData = context -> { + CommandSourceStack source = context.getSource(); + try { + ServerPlayer player = EntityArgument.getPlayer(context, "targetPlayer"); + Integer x = LeashLengthGetResultInt(player, source); + if (x != null) return x; + } catch (Exception e) { + source.sendFailure(Component.translatable(LEASH_FAILED)); + return -1; + } + return 0; + }; + Command getRefPlayersLeashData = context -> { + CommandSourceStack source = context.getSource(); + try { + Collection playerCol = EntityArgument.getPlayers(context, "targetPlayers"); + playerCol.forEach(player -> { + try { + LeashLengthGetResultInt(player, source); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + + } catch (Exception e) { + source.sendFailure(Component.translatable(LEASH_FAILED)); + return -1; + } + return 0; + }; + //设置前要判断其实体距离(同一维度,且距离不得大于其绳长的1.2倍(待定,也许可以设置在配置文件里) + Command setSelfLeashDataEntity = context -> { + CommandSourceStack source = context.getSource(); + try { + ServerPlayer player = source.getPlayerOrException(); + Integer x = LeashDataEntitySetResultInt(context, player, source); + if (x != null) return x; + + } catch (Exception e) { + source.sendFailure(Component.translatable(LEASH_FAILED)); + return -1; + } + return 0; + }; + Command setSelfLeashDataByBlockPos = context -> { + CommandSourceStack source = context.getSource(); + try { + ServerPlayer player = source.getPlayerOrException(); + Integer x = LeashDataBlockPosSetResultInt(context, source, player); + if (x != null) return x; + + + } catch (Exception e) { + source.sendFailure(Component.translatable(LEASH_FAILED)); + return -1; + } + return 0; + }; + + Command setRefPlayerLeashDataEntity = context -> { + CommandSourceStack source = context.getSource(); + try { + ServerPlayer player = EntityArgument.getPlayer(context, "targetPlayer"); + Integer x = LeashDataEntitySetResultInt(context, player, source); + if (x != null) return x; + + } catch (Exception e) { + source.sendFailure(Component.translatable(LEASH_FAILED)); + return -1; + } + return 0; + }; + Command setRefPlayersLeashDataEntity = context -> { + CommandSourceStack source = context.getSource(); + try { + Collection playerCol = EntityArgument.getPlayers(context, "targetPlayers"); + playerCol.forEach(player -> { + try { + LeashDataEntitySetResultInt(context, player, source); + } catch (CommandSyntaxException e) { + throw new RuntimeException(e); + } + }); + } catch (Exception e) { + source.sendFailure(Component.translatable(LEASH_FAILED)); + return -1; + } + return 0; + }; + Command setRefPlayersLeashDataByBlockPos = context -> { + CommandSourceStack source = context.getSource(); + try { + Collection playerCol = EntityArgument.getPlayers(context, "targetPlayers"); + playerCol.forEach(player -> { + LeashDataBlockPosSetResultInt(context, source, player); + }); + + } catch (Exception e) { + source.sendFailure(Component.translatable(LEASH_FAILED)); + return -1; + } + return 0; + }; + Command setRefPlayerLeashDataByBlockPos = context -> { + CommandSourceStack source = context.getSource(); + try { + ServerPlayer player = EntityArgument.getPlayer(context, "targetPlayer"); + Integer x = LeashDataBlockPosSetResultInt(context, source, player); + if (x != null) return x; + + } catch (Exception e) { + source.sendFailure(Component.translatable(LEASH_FAILED)); + return -1; + } + return 0; + }; + + Command clearSelfLeashData = context -> { + CommandSourceStack source = context.getSource(); + try { + ServerPlayer player = source.getPlayerOrException(); + Integer x = LeashDataClearResultInt(source, PlayerLeashable.getLeashDataEntity(player, source.getLevel()), player); + if (x != null) return x; + + } catch (Exception e) { + source.sendFailure(Component.translatable(LEASH_FAILED)); + return -1; + } + return 0; + }; + Command clearRefPlayerLeashData = context -> { + CommandSourceStack source = context.getSource(); + try { + ServerPlayer player = EntityArgument.getPlayer(context, "targetPlayer"); + Integer x = LeashDataClearResultInt(source, PlayerLeashable.getLeashDataEntity(player, source.getLevel()),player); + if (x != null) return x; + } catch (Exception e) { + source.sendFailure(Component.translatable(LEASH_FAILED)); + return -1; + } + return 0; + }; + Command clearRefPlayersLeashData = context -> { + CommandSourceStack source = context.getSource(); + try { + Collection playerCol = EntityArgument.getPlayers(context, "targetPlayers"); + playerCol.forEach(player -> { + try { + LeashDataClearResultInt(source, PlayerLeashable.getLeashDataEntity(player, source.getLevel()),player); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + + } catch (Exception e) { + source.sendFailure(Component.translatable(LEASH_FAILED)); + return -1; + } + return 0; + }; + + + LiteralArgumentBuilder SelfLeashLength = $$leashRoot.then(Commands.literal("length").executes(getSelfLeashLength) + .then(Commands.literal("get").executes(getSelfLeashLength)) + .then(Commands.literal("set").requires(cs -> cs.hasPermission(2)) + .then(Commands.argument("leashLength", FloatArgumentType.floatArg(MIN_VALUE, MAX_VALUE)).executes(setSelfLengthLeashLength)) + ) + ); + + LiteralArgumentBuilder RefPlayerLeashLength = $$leashRoot.then( + Commands.literal("length") + .then(Commands.argument("targetPlayer", EntityArgument.player()).executes(getRefPlayerLeashLength) + .then(Commands.literal("get").executes(getRefPlayerLeashLength)) + .then(Commands.literal("set").requires(cs -> cs.hasPermission(2)) + .then( + Commands.argument("leashLength", FloatArgumentType.floatArg(MIN_VALUE, MAX_VALUE)).executes(setRefPlayerLengthLeashLength) + ) + ) + ) + .then(Commands.argument("targetPlayers", EntityArgument.players()).executes(getRefPlayersLeashLength) + .then(Commands.literal("get").executes(getRefPlayersLeashLength)) + .then(Commands.literal("set").requires(cs -> cs.hasPermission(2)) + .then( + Commands.argument("leashLength", FloatArgumentType.floatArg(MIN_VALUE, MAX_VALUE)).executes(setRefPlayersLengthLeashLength) + ) + ) + ) + ); + + LiteralArgumentBuilder RefPLayerData = $$leashRoot.then( + Commands.literal("data") + .then(Commands.argument("targetPlayer", EntityArgument.player()).executes(getRefPlayerLeashData) + .then(Commands.literal("get") + .executes(getRefPlayerLeashData) + ) + .then(Commands.literal("set").requires(cs -> cs.hasPermission(2)) + .then(Commands.argument("holderEntity", EntityArgument.entity()) + .executes(setRefPlayerLeashDataEntity) + ) + .then(Commands.argument("BlockPos", BlockPosArgument.blockPos()) + .executes(setRefPlayerLeashDataByBlockPos) + ) + ) + .then(Commands.literal("clear").requires(cs -> cs.hasPermission(2)).executes(clearRefPlayerLeashData)) + ) + .then(Commands.argument("targetPlayers", EntityArgument.players()).executes(getRefPlayersLeashData) + .then(Commands.literal("get") + .executes(getRefPlayersLeashData) + ) + .then(Commands.literal("set").requires(cs -> cs.hasPermission(2)) + .then(Commands.argument("holderEntity", EntityArgument.entity()) + .executes(setRefPlayersLeashDataEntity) + ) + .then(Commands.argument("BlockPos", BlockPosArgument.blockPos()) + .executes(setRefPlayersLeashDataByBlockPos) + ) + ) + .then(Commands.literal("clear").requires(cs -> cs.hasPermission(2)).executes(clearRefPlayersLeashData)) + )); + + LiteralArgumentBuilder SelfData = $$leashRoot.then( + Commands.literal("data") + .then(Commands.literal("get") + .executes(geSelfLeashData) + ) + .then(Commands.literal("set").requires(cs -> cs.hasPermission(2)) + .then(Commands.argument("holderEntity", EntityArgument.entity()) + .executes(setSelfLeashDataEntity) + ) + .then(Commands.argument("BlockPos", BlockPosArgument.blockPos()) + .executes(setSelfLeashDataByBlockPos) + ) + ) + .then(Commands.literal("clear").requires(cs -> cs.hasPermission(2)) + .executes(clearSelfLeashData) + ) + ); + + if(shouldUsePrefix) { + literalArgumentBuilder + .then(RefPlayerLeashLength) + .then(SelfLeashLength) + .then(RefPLayerData) + .then(SelfData); + dispatcher.register(literalArgumentBuilder); + } else { + nodeList.forEach(dispatcher::register); + } + + } + + private static @Nullable Integer LeashLengthGetResultInt(ServerPlayer player, CommandSourceStack source) throws Exception { + Leashable.LeashData leashDataFromEntityData = ((PlayerLeashable) player).getLeashDataFromEntityData(); + + if(leashDataFromEntityData == null) { + source.sendSuccess(() -> Component.translatable(NO_LEASH_DATA, player.getDisplayName()), true); + return 1; + } else { + Entity leashDataEntity = PlayerLeashable.getLeashDataEntityOrThrown(leashDataFromEntityData, source.getLevel()); + source.sendSuccess(() -> Component.translatable(LEASH_DATA_SHOW, player.getDisplayName(), leashDataEntity.getDisplayName()), true); + } + return null; + } + + private static @Nullable Integer LeashDataBlockPosSetResultInt(CommandContext context, CommandSourceStack source, ServerPlayer player) { + BlockPos blockPos = BlockPosArgument.getBlockPos(context, "BlockPos"); + Entity leashDataEntity = PlayerLeashable.getLeashFenceKnotEntity(source.getLevel(), blockPos); + PlayerLeashable leashedPlayer = (PlayerLeashable) player; + Component targetPlayerDisplayName = player.getDisplayName(); + if(leashDataEntity == null) { + ServerLevel level = context.getSource().getLevel(); + if(GameruleRegistry.getGameruleBoolValue(level,CreateLeashFenceKnotEntityIfAbsent.ID)) { + if(level.getBlockState(blockPos).is(BlockTags.FENCES)) { + Entity leashKnotFence = PlayerLeashable.createLeashKnotFence(level, blockPos); + leashedPlayer.setLeashedTo(leashKnotFence, true); + source.sendSuccess(() -> Component.translatable(LEASH_DATA_SET, targetPlayerDisplayName, leashKnotFence.getDisplayName()), true); + return null; + } + } + source.sendFailure(Component.translatable(LEASH_DATA_SET_FAILED_NO_KNOT_EXISTED_IN_THAT_POS, blockPos.getX(), blockPos.getY(), blockPos.getZ())); + return 1; + } + Component leashDataEntityDisplayName = leashDataEntity.getDisplayName(); + + return LeashDataCommonPartSetResultInt(source, player, leashDataEntity, leashedPlayer, targetPlayerDisplayName, leashDataEntityDisplayName); + } + + private static @Nullable Integer LeashDataEntitySetResultInt(CommandContext context, ServerPlayer player, CommandSourceStack source) throws CommandSyntaxException { + Entity leashDataEntity = EntityArgument.getEntity(context, "holderEntity"); + PlayerLeashable leashedPlayer = (PlayerLeashable) player; + Component targetPlayerDisplayName = player.getDisplayName(); + Component leashDataEntityDisplayName = leashDataEntity.getDisplayName(); + if(player.equals(leashDataEntity)) { + source.sendFailure(Component.translatable(LEASH_DATA_SET_FAILED_FORBID_SAME_ENTITY)); + return 1; + } + return LeashDataCommonPartSetResultInt(source, player, leashDataEntity, leashedPlayer, targetPlayerDisplayName, leashDataEntityDisplayName); + } + + private static @Nullable Integer LeashDataCommonPartSetResultInt(CommandSourceStack source, ServerPlayer player, Entity leashDataEntity, PlayerLeashable leashedPlayer, Component targetPlayerDisplayName, Component leashDataEntityDisplayName) { + if(player.level() != leashDataEntity.level()) { + source.sendFailure(Component.translatable(LEASH_DATA_SET_FAILED_DIFF_LEVEL, targetPlayerDisplayName, leashDataEntityDisplayName)); + return 2; + } + ILivingEntityExtension targetPlayerExtension = (ILivingEntityExtension) player; + if (player.distanceTo(leashDataEntity) > targetPlayerExtension.getLeashLength() * 1.2f) { + source.sendFailure(Component.translatable(LEASH_DATA_SET_FAILED_TOO_FAR, targetPlayerDisplayName, leashDataEntityDisplayName, targetPlayerExtension.getLeashLength())); + return 3; + } + leashedPlayer.setLeashedTo(leashDataEntity, true); + source.sendSuccess(() -> Component.translatable(LEASH_DATA_SET, targetPlayerDisplayName, leashDataEntityDisplayName), true); + return null; + } + + private static @Nullable Integer LeashDataClearResultInt(CommandSourceStack source, Entity leashDataEntity, ServerPlayer serverPlayer) { + if(leashDataEntity == null) { + source.sendFailure(Component.translatable(LEASH_DATA_CLEAR_FAILED_NO_DATA, serverPlayer.getDisplayName())); + return 1; + } + Leashable.dropLeash((Entity & Leashable) serverPlayer, true, false); + source.sendSuccess(() -> Component.translatable(LEASH_DATA_CLEAR, serverPlayer.getDisplayName(), leashDataEntity.getDisplayName()), true); + return null; + } + + +} diff --git a/src/main/java/com/r3944realms/leashedplayer/content/commands/MotionCommand.java b/src/main/java/com/r3944realms/leashedplayer/content/commands/MotionCommand.java new file mode 100644 index 0000000..21c81d6 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/content/commands/MotionCommand.java @@ -0,0 +1,129 @@ +package com.r3944realms.leashedplayer.content.commands; + +import com.mojang.brigadier.Command; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.DoubleArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.r3944realms.leashedplayer.config.LeashPlayerCommonConfig; +import com.r3944realms.leashedplayer.network.client.UpdatePlayerMovement; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.commands.arguments.EntityArgument; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.phys.Vec3; +import net.neoforged.neoforge.network.PacketDistributor; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; + +import static com.r3944realms.leashedplayer.content.commands.Command.getLiterArgumentBuilderOfCSS; + +public class MotionCommand { + private final static String LEASHED_PLAYER_MOTION_MESSAGE_ = "leashedplayer.command.motion.message."; + public final static String MOTION_SETTER_SUCCESSFUL = LEASHED_PLAYER_MOTION_MESSAGE_ + "setter.successful", + MOTION_ADDER_SUCCESSFUL = LEASHED_PLAYER_MOTION_MESSAGE_ + "adder.successful", + MOTION_MULTIPLY_SUCCESSFUL = LEASHED_PLAYER_MOTION_MESSAGE_ + "multiply.successful"; + public static void register(CommandDispatcher dispatcher) { + boolean shouldUsePrefix = LeashPlayerCommonConfig.EnableLeashPlayerCommandPrefix.get(); + @Nullable List> nodeList = shouldUsePrefix ? null : new ArrayList<>(); + LiteralArgumentBuilder literalArgumentBuilder = Commands.literal(com.r3944realms.leashedplayer.content.commands.Command.PREFIX); + LiteralArgumentBuilder $$motionRoot = getLiterArgumentBuilderOfCSS("motion", !shouldUsePrefix, nodeList); + Command motionVecAdder = context -> { + CommandSourceStack source = context.getSource(); + for(Entity entity : EntityArgument.getEntities(context, "targets")){ + Vec3 motionVec = new Vec3( + DoubleArgumentType.getDouble(context, "vecX"), + DoubleArgumentType.getDouble(context, "vecY"), + DoubleArgumentType.getDouble(context, "vecZ") + ); + if(entity instanceof ServerPlayer player) { + PacketDistributor.sendToPlayer(player, new UpdatePlayerMovement(UpdatePlayerMovement.Operation.ADD, motionVec.x, motionVec.y, motionVec.z)); + player.addDeltaMovement(new Vec3(motionVec.x, motionVec.y, motionVec.z)); + } else { + entity.addDeltaMovement(motionVec); + } + double vecX = entity.getDeltaMovement().x, vecY = entity.getDeltaMovement().y, vecZ = entity.getDeltaMovement().z; + source.sendSuccess(() -> Component.translatable(MOTION_ADDER_SUCCESSFUL, entity.getName().copy().withStyle(), vecX, vecY, vecZ), true); + } + return 0; + }; + Command motionVecSetter = context -> { + CommandSourceStack source = context.getSource(); + for(Entity entity : EntityArgument.getEntities(context, "targets")){ + Vec3 motionVec = new Vec3( + DoubleArgumentType.getDouble(context, "vecX"), + DoubleArgumentType.getDouble(context, "vecY"), + DoubleArgumentType.getDouble(context, "vecZ") + ); + if(entity instanceof ServerPlayer player) { + PacketDistributor.sendToPlayer(player, new UpdatePlayerMovement(UpdatePlayerMovement.Operation.SET, motionVec.x, motionVec.y, motionVec.z)); + player.setDeltaMovement(new Vec3(motionVec.x, motionVec.y, motionVec.z)); + } else { + entity.setDeltaMovement(motionVec); + } + double vecX = entity.getDeltaMovement().x, vecY = entity.getDeltaMovement().y, vecZ = entity.getDeltaMovement().z; + source.sendSuccess(() -> Component.translatable(MOTION_SETTER_SUCCESSFUL, entity.getName().copy(), vecX, vecY, vecZ), true); + } + return 0; + }; + Command motionVecMultiply = context -> { + CommandSourceStack source = context.getSource(); + for(Entity entity : EntityArgument.getEntities(context, "targets")){ + Vec3 motionFactorVec = new Vec3( + DoubleArgumentType.getDouble(context, "vecXFactor"), + DoubleArgumentType.getDouble(context, "vecYFactor"), + DoubleArgumentType.getDouble(context, "vecZFactor") + ); + if(entity instanceof ServerPlayer player) { + PacketDistributor.sendToPlayer(player, new UpdatePlayerMovement(UpdatePlayerMovement.Operation.MULTIPLY, motionFactorVec.x, motionFactorVec.y, motionFactorVec.z)); + player.setDeltaMovement(entity.getDeltaMovement().multiply(motionFactorVec)); + } else { + entity.setDeltaMovement(entity.getDeltaMovement().multiply(motionFactorVec)); + } + double vecX = entity.getDeltaMovement().x, vecY = entity.getDeltaMovement().y, vecZ = entity.getDeltaMovement().z; + source.sendSuccess(() -> Component.translatable(MOTION_MULTIPLY_SUCCESSFUL, entity.getName().copy(), vecX, vecY, vecZ), true); + } + return 0; + }; + + LiteralArgumentBuilder Motion = $$motionRoot.requires(cs -> cs.hasPermission(2)) + .then(Commands.argument("targets", EntityArgument.entities()) + .then(Commands.literal("add") + .then(Commands.argument("vecX", DoubleArgumentType.doubleArg()) + .then(Commands.argument("vecY", DoubleArgumentType.doubleArg()) + .then(Commands.argument("vecZ", DoubleArgumentType.doubleArg()) + .executes(motionVecAdder) + ) + ) + ) + ) + .then(Commands.literal("set") + .then(Commands.argument("vecX", DoubleArgumentType.doubleArg()) + .then(Commands.argument("vecY", DoubleArgumentType.doubleArg()) + .then(Commands.argument("vecZ", DoubleArgumentType.doubleArg()) + .executes(motionVecSetter) + ) + ) + ) + ) + .then(Commands.literal("multiply") + .then(Commands.argument("vecXFactor", DoubleArgumentType.doubleArg()) + .then(Commands.argument("vecYFactor", DoubleArgumentType.doubleArg()) + .then(Commands.argument("vecZFactor", DoubleArgumentType.doubleArg()) + .executes(motionVecMultiply) + ) + ) + ) + ) + ); + if(shouldUsePrefix){ + literalArgumentBuilder.then(Motion); + dispatcher.register(literalArgumentBuilder); + } else { + nodeList.forEach(dispatcher::register); + } + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/content/commands/TickCommand.java b/src/main/java/com/r3944realms/leashedplayer/content/commands/TickCommand.java new file mode 100644 index 0000000..d2cf732 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/content/commands/TickCommand.java @@ -0,0 +1,181 @@ +package com.r3944realms.leashedplayer.content.commands; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.FloatArgumentType; +import com.mojang.brigadier.arguments.IntegerArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.r3944realms.leashedplayer.config.LeashPlayerCommonConfig; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.commands.SharedSuggestionProvider; +import net.minecraft.commands.arguments.TimeArgument; +import net.minecraft.network.chat.Component; +import net.minecraft.server.ServerTickRateManager; +import net.minecraft.util.TimeUtil; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static com.r3944realms.leashedplayer.content.commands.Command.getLiterArgumentBuilderOfCSS; + +public class TickCommand { + private static final float MAX_TICKRATE = 10000.0F; + private static final String DEFAULT_TICKRATE = String.valueOf(20); + + public static void register(CommandDispatcher dispatcher) { + boolean shouldUsePrefix = LeashPlayerCommonConfig.EnableLeashPlayerCommandPrefix.get(); + @Nullable List> nodeList = shouldUsePrefix ? null : new ArrayList<>(); + LiteralArgumentBuilder literalArgumentBuilder = Commands.literal(Command.PREFIX); + LiteralArgumentBuilder $$TickRoot = getLiterArgumentBuilderOfCSS("tick", !shouldUsePrefix, nodeList); + + LiteralArgumentBuilder Tick = $$TickRoot.requires(p_308941_ -> p_308941_.hasPermission(2)) + .then(Commands.literal("query").executes(p_308950_ -> tickQuery(p_308950_.getSource()))) + .then( + Commands.literal("rate") + .then( + Commands.argument("rate", FloatArgumentType.floatArg(1.0F, 10000.0F)) + .suggests((p_308897_, p_308880_) -> SharedSuggestionProvider.suggest(new String[]{DEFAULT_TICKRATE}, p_308880_)) + .executes(p_309119_ -> setTickingRate(p_309119_.getSource(), FloatArgumentType.getFloat(p_309119_, "rate"))) + ) + ) + .then( + Commands.literal("step") + .executes(p_309496_ -> step(p_309496_.getSource(), 1)) + .then(Commands.literal("stop").executes(p_309035_ -> stopStepping(p_309035_.getSource()))) + .then( + Commands.argument("time", TimeArgument.time(1)) + .suggests((p_309113_, p_309105_) -> SharedSuggestionProvider.suggest(new String[]{"1t", "1s"}, p_309105_)) + .executes(p_308930_ -> step(p_308930_.getSource(), IntegerArgumentType.getInteger(p_308930_, "time"))) + ) + ) + .then( + Commands.literal("sprint") + .then(Commands.literal("stop").executes(p_309190_ -> stopSprinting(p_309190_.getSource()))) + .then( + Commands.argument("time", TimeArgument.time(1)) + .suggests((p_308987_, p_309101_) -> SharedSuggestionProvider.suggest(new String[]{"60s", "1d", "3d"}, p_309101_)) + .executes(p_308904_ -> sprint(p_308904_.getSource(), IntegerArgumentType.getInteger(p_308904_, "time"))) + ) + ) + .then(Commands.literal("unfreeze").executes(p_309184_ -> setFreeze(p_309184_.getSource(), false))) + .then(Commands.literal("freeze").executes(p_309070_ -> setFreeze(p_309070_.getSource(), true))); + if(shouldUsePrefix){ + literalArgumentBuilder.then(Tick); + dispatcher.register(literalArgumentBuilder); + } else { + nodeList.forEach(dispatcher::register); + } + } + + private static String nanosToMilisString(long nanos) { + return String.format("%.1f", (float)nanos / (float) TimeUtil.NANOSECONDS_PER_MILLISECOND); + } + + private static int setTickingRate(CommandSourceStack source, float tickRate) { + ServerTickRateManager servertickratemanager = source.getServer().tickRateManager(); + servertickratemanager.setTickRate(tickRate); + String s = String.format("%.1f", tickRate); + source.sendSuccess(() -> Component.translatable("commands.tick.rate.success", s), true); + return (int)tickRate; + } + + private static int tickQuery(CommandSourceStack source) { + ServerTickRateManager servertickratemanager = source.getServer().tickRateManager(); + String s = nanosToMilisString(source.getServer().getAverageTickTimeNanos()); + float f = servertickratemanager.tickrate(); + String s1 = String.format("%.1f", f); + if (servertickratemanager.isSprinting()) { + source.sendSuccess(() -> Component.translatable("commands.tick.status.sprinting"), false); + source.sendSuccess(() -> Component.translatable("commands.tick.query.rate.sprinting", s1, s), false); + } else { + if (servertickratemanager.isFrozen()) { + source.sendSuccess(() -> Component.translatable("commands.tick.status.frozen"), false); + } else if (servertickratemanager.nanosecondsPerTick() < source.getServer().getAverageTickTimeNanos()) { + source.sendSuccess(() -> Component.translatable("commands.tick.status.lagging"), false); + } else { + source.sendSuccess(() -> Component.translatable("commands.tick.status.running"), false); + } + + String s2 = nanosToMilisString(servertickratemanager.nanosecondsPerTick()); + source.sendSuccess(() -> Component.translatable("commands.tick.query.rate.running", s1, s, s2), false); + } + + long[] along = Arrays.copyOf(source.getServer().getTickTimesNanos(), source.getServer().getTickTimesNanos().length); + Arrays.sort(along); + String s3 = nanosToMilisString(along[along.length / 2]); + String s4 = nanosToMilisString(along[(int)((double)along.length * 0.95)]); + String s5 = nanosToMilisString(along[(int)((double)along.length * 0.99)]); + source.sendSuccess(() -> Component.translatable("commands.tick.query.percentiles", s3, s4, s5, along.length), false); + return (int)f; + } + + private static int sprint(CommandSourceStack source, int sprintTime) { + boolean flag = source.getServer().tickRateManager().requestGameToSprint(sprintTime); + if (flag) { + source.sendSuccess(() -> Component.translatable("commands.tick.sprint.stop.success"), true); + } + + source.sendSuccess(() -> Component.translatable("commands.tick.status.sprinting"), true); + return 1; + } + + private static int setFreeze(CommandSourceStack source, boolean frozen) { + ServerTickRateManager servertickratemanager = source.getServer().tickRateManager(); + if (frozen) { + if (servertickratemanager.isSprinting()) { + servertickratemanager.stopSprinting(); + } + + if (servertickratemanager.isSteppingForward()) { + servertickratemanager.stopStepping(); + } + } + + servertickratemanager.setFrozen(frozen); + if (frozen) { + source.sendSuccess(() -> Component.translatable("commands.tick.status.frozen"), true); + } else { + source.sendSuccess(() -> Component.translatable("commands.tick.status.running"), true); + } + + return frozen ? 1 : 0; + } + + private static int step(CommandSourceStack source, int ticks) { + ServerTickRateManager servertickratemanager = source.getServer().tickRateManager(); + boolean flag = servertickratemanager.stepGameIfPaused(ticks); + if (flag) { + source.sendSuccess(() -> Component.translatable("commands.tick.step.success", ticks), true); + } else { + source.sendFailure(Component.translatable("commands.tick.step.fail")); + } + + return 1; + } + + private static int stopStepping(CommandSourceStack source) { + ServerTickRateManager servertickratemanager = source.getServer().tickRateManager(); + boolean flag = servertickratemanager.stopStepping(); + if (flag) { + source.sendSuccess(() -> Component.translatable("commands.tick.step.stop.success"), true); + return 1; + } else { + source.sendFailure(Component.translatable("commands.tick.step.stop.fail")); + return 0; + } + } + + private static int stopSprinting(CommandSourceStack source) { + ServerTickRateManager servertickratemanager = source.getServer().tickRateManager(); + boolean flag = servertickratemanager.stopSprinting(); + if (flag) { + source.sendSuccess(() -> Component.translatable("commands.tick.sprint.stop.success"), true); + return 1; + } else { + source.sendFailure(Component.translatable("commands.tick.sprint.stop.fail")); + return 0; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/r3944realms/leashedplayer/content/criteriaTriggers/LeashPlayerTrigger.java b/src/main/java/com/r3944realms/leashedplayer/content/criteriaTriggers/LeashPlayerTrigger.java new file mode 100644 index 0000000..b2b651a --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/content/criteriaTriggers/LeashPlayerTrigger.java @@ -0,0 +1,47 @@ +package com.r3944realms.leashedplayer.content.criteriaTriggers; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import com.r3944realms.leashedplayer.modInterface.PlayerLeashable; +import net.minecraft.advancements.Criterion; +import net.minecraft.advancements.critereon.ContextAwarePredicate; +import net.minecraft.advancements.critereon.EntityPredicate; +import net.minecraft.advancements.critereon.SimpleCriterionTrigger; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.Entity; +import org.jetbrains.annotations.NotNull; + +import java.util.Optional; + +public class LeashPlayerTrigger extends SimpleCriterionTrigger { + + @Override + public @NotNull Codec codec() { + return TriggerInstance.CODEC; + } + public void trigger(ServerPlayer pPlayer, Entity pHolderEntity) { + this.trigger(pPlayer, pl -> pl.matches(pPlayer, pHolderEntity)); + } + + public record TriggerInstance(Optional player, Optional holder) implements SimpleInstance { + public static final Codec CODEC = + RecordCodecBuilder.create(instance -> + instance.group( + EntityPredicate.ADVANCEMENT_CODEC.optionalFieldOf("player").forGetter(TriggerInstance::player), + EntityPredicate.CODEC.optionalFieldOf("holder").forGetter(TriggerInstance::holder) + ) + .apply(instance, TriggerInstance::new)); +// public static Criterion LeashSelf() { +// return ModCriteriaTriggers.LEASH_PLAYER_TRIGGER.get().createCriterion(new TriggerInstance(Optional.empty(), Optional.empty())); +// } + public static Criterion LeashPlayer(EntityPredicate.Builder holder) { + return ModCriteriaTriggers.LEASH_PLAYER_TRIGGER.get().createCriterion(new TriggerInstance(Optional.empty(), Optional.of(holder.build()))); + } + + public boolean matches(ServerPlayer player, Entity holder) { + PlayerLeashable ppl = (PlayerLeashable) player; + return ppl.isLeashed() && holder.equals(PlayerLeashable.getLeashDataEntity(player, player.serverLevel())) && this.holder.isPresent() && this.holder.get().matches(player, holder); + } + } + +} diff --git a/src/main/java/com/r3944realms/leashedplayer/content/criteriaTriggers/ModCriteriaTriggers.java b/src/main/java/com/r3944realms/leashedplayer/content/criteriaTriggers/ModCriteriaTriggers.java new file mode 100644 index 0000000..bacf918 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/content/criteriaTriggers/ModCriteriaTriggers.java @@ -0,0 +1,17 @@ +package com.r3944realms.leashedplayer.content.criteriaTriggers; + +import com.r3944realms.leashedplayer.LeashedPlayer; +import net.minecraft.advancements.CriterionTrigger; +import net.minecraft.core.registries.Registries; +import net.neoforged.bus.api.IEventBus; +import net.neoforged.neoforge.registries.DeferredHolder; +import net.neoforged.neoforge.registries.DeferredRegister; + +public class ModCriteriaTriggers { + public static final DeferredRegister> TRIGGERS = DeferredRegister.create(Registries.TRIGGER_TYPE, LeashedPlayer.MOD_ID); + public static final DeferredHolder, LeashPlayerTrigger> LEASH_PLAYER_TRIGGER = + TRIGGERS.register("leash_player", LeashPlayerTrigger::new); + public static void register(IEventBus eventBus) { + TRIGGERS.register(eventBus); + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/content/effects/ModEffectRegister.java b/src/main/java/com/r3944realms/leashedplayer/content/effects/ModEffectRegister.java new file mode 100644 index 0000000..0188783 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/content/effects/ModEffectRegister.java @@ -0,0 +1,33 @@ +package com.r3944realms.leashedplayer.content.effects; + +import com.r3944realms.leashedplayer.LeashedPlayer; +import com.r3944realms.leashedplayer.content.effects.type.NoLeashEffect; +import net.minecraft.core.registries.Registries; +import net.minecraft.world.effect.MobEffect; +import net.minecraft.world.effect.MobEffectCategory; +import net.neoforged.bus.api.IEventBus; +import net.neoforged.neoforge.registries.DeferredHolder; +import net.neoforged.neoforge.registries.DeferredRegister; + +import java.util.function.Supplier; + +public class ModEffectRegister { + public static DeferredRegister MOB_EFFECT = DeferredRegister.create(Registries.MOB_EFFECT, LeashedPlayer.MOD_ID); + public static DeferredHolder NO_LEASH_EFFECT = register( + "no_leash", + () -> new NoLeashEffect(MobEffectCategory.NEUTRAL, 12063764) + ); + public static DeferredHolder register(String name, Supplier effect) { + return MOB_EFFECT.register(name, effect); + } + + public static String getEffectKey(MobEffect effect) { + return effect.getDescriptionId(); + } + public static String getModEffectKey(DeferredHolder effect) { + return getEffectKey(effect.get()); + } + public static void register(IEventBus eventBus) { + MOB_EFFECT.register(eventBus); + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/content/effects/ModPotionRegister.java b/src/main/java/com/r3944realms/leashedplayer/content/effects/ModPotionRegister.java new file mode 100644 index 0000000..e409d3b --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/content/effects/ModPotionRegister.java @@ -0,0 +1,41 @@ +package com.r3944realms.leashedplayer.content.effects; + +import com.r3944realms.leashedplayer.LeashedPlayer; +import net.minecraft.core.registries.Registries; +import net.minecraft.world.effect.MobEffectInstance; +import net.minecraft.world.item.alchemy.Potion; +import net.neoforged.bus.api.IEventBus; +import net.neoforged.neoforge.registries.DeferredHolder; +import net.neoforged.neoforge.registries.DeferredRegister; + +import java.util.function.Supplier; + +public class ModPotionRegister { + public static DeferredRegister POTIONS = DeferredRegister.create(Registries.POTION, LeashedPlayer.MOD_ID); + public static final DeferredHolder NO_LEASH = register("no_leash", + () -> new Potion("no_leash", new MobEffectInstance(ModEffectRegister.NO_LEASH_EFFECT, 7200, 0)) + ); + public static DeferredHolder register(String Name, Supplier supplier) { + return POTIONS.register(Name, supplier); + } + /** + * + * @param name the Name of Potion + * @param type (char)
[
0 & 3 ~ 255 : potion
1 : lingering_potion
2 : splash_potion
] + * @return Language Key + */ + public static String getPotionNameKey(String name, char type) { + return "item.minecraft." + + (type == 1 ? "lingering_potion" : + (type == 2 ? "splash_potion" : "potion") + ) + + ".effect." + name; + } + public static String getTippedArrowNameKey(String Name) { + return "item.minecraft.tipped_arrow.effect." + Name; + } + + public static void register(IEventBus eventBus) { + POTIONS.register(eventBus); + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/content/effects/type/NoLeashEffect.java b/src/main/java/com/r3944realms/leashedplayer/content/effects/type/NoLeashEffect.java new file mode 100644 index 0000000..ae0545e --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/content/effects/type/NoLeashEffect.java @@ -0,0 +1,38 @@ +package com.r3944realms.leashedplayer.content.effects.type; + +import com.r3944realms.leashedplayer.content.effects.ModEffectRegister; +import com.r3944realms.leashedplayer.content.entities.LeashRopeArrow; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.effect.MobEffect; +import net.minecraft.world.effect.MobEffectCategory; +import net.minecraft.world.effect.MobEffectInstance; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.Leashable; +import net.minecraft.world.entity.LivingEntity; +import org.jetbrains.annotations.NotNull; + +public class NoLeashEffect extends MobEffect { + public NoLeashEffect(MobEffectCategory pCategory, int pColor) { + super(pCategory, pColor); + } + + @Override + public boolean applyEffectTick(@NotNull ServerLevel level, @NotNull LivingEntity pLivingEntity, int pAmplifier) { + MobEffectInstance effect = pLivingEntity.getEffect(ModEffectRegister.NO_LEASH_EFFECT); + if(effect != null && effect.getDuration() != 0) { + if (pLivingEntity instanceof Leashable leashable) { + if (leashable.getLeashHolder() instanceof LeashRopeArrow arrow) { + arrow.dropLeashHandler(); + Leashable.dropLeash((Entity & Leashable) leashable,true, false); + } + Leashable.dropLeash((Entity & Leashable) leashable,true, true); + } + } + return true; + } + + @Override + public boolean shouldApplyEffectTickThisTick(int pDuration, int pAmplifier) { + return true; + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/content/effects/type/StunnedEffect.java b/src/main/java/com/r3944realms/leashedplayer/content/effects/type/StunnedEffect.java new file mode 100644 index 0000000..ed3926c --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/content/effects/type/StunnedEffect.java @@ -0,0 +1,11 @@ +package com.r3944realms.leashedplayer.content.effects.type; + +import net.minecraft.world.effect.MobEffect; +import net.minecraft.world.effect.MobEffectCategory; + + +public class StunnedEffect extends MobEffect { + public StunnedEffect() { + super(MobEffectCategory.HARMFUL, 0XFFFBC5); + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/content/entities/LeashRopeArrow.java b/src/main/java/com/r3944realms/leashedplayer/content/entities/LeashRopeArrow.java new file mode 100644 index 0000000..ac92c5f --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/content/entities/LeashRopeArrow.java @@ -0,0 +1,457 @@ +package com.r3944realms.leashedplayer.content.entities; + +import com.r3944realms.leashedplayer.config.LeashPlayerCommonConfig; +import com.r3944realms.leashedplayer.content.effects.ModEffectRegister; +import com.r3944realms.leashedplayer.content.gamerules.GameruleRegistry; +import com.r3944realms.leashedplayer.content.gamerules.Server.KeepLeashNotDropTime; +import com.r3944realms.leashedplayer.content.items.ModItemRegister; +import com.r3944realms.leashedplayer.modInterface.ILivingEntityExtension; +import com.r3944realms.leashedplayer.modInterface.PlayerLeashable; +import net.minecraft.core.component.DataComponents; +import net.minecraft.core.particles.ColorParticleOption; +import net.minecraft.core.particles.ParticleTypes; +import net.minecraft.network.chat.Component; +import net.minecraft.network.syncher.EntityDataAccessor; +import net.minecraft.network.syncher.EntityDataSerializers; +import net.minecraft.network.syncher.SynchedEntityData; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.tags.BlockTags; +import net.minecraft.world.effect.MobEffectInstance; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.Leashable; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.decoration.LeashFenceKnotEntity; +import net.minecraft.world.entity.item.ItemEntity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.entity.projectile.AbstractArrow; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.alchemy.PotionContents; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.EntityHitResult; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class LeashRopeArrow extends AbstractArrow { + public static final String PUSH_SHIFT_TO_PICKUP_QUICKLY = "leashedplayer.leash_rope_arrow.try_to_pickup.push_shift_tip"; + private static final int EXPOSED_POTION_DECAY_TIME = 600; + private static final int NO_EFFECT_COLOR = -1; + private static final EntityDataAccessor ID_EFFECT_COLOR = SynchedEntityData.defineId(LeashRopeArrow.class, EntityDataSerializers.INT); + private static final byte EVENT_POTION_PUFF = 0; + private static final int maxLifeTime = LeashPlayerCommonConfig.TheLeashArrowMaxLifeTime.get(); + protected LeashRopeArrow(EntityType entityType,Level pLevel) { + super(entityType, pLevel); + + } + private PotionContents getPotionContents() { + ItemStack pickupItemStackOrigin = this.getPickupItemStackOrigin(); + pickupItemStackOrigin.setCount(1); + return pickupItemStackOrigin.getOrDefault(DataComponents.POTION_CONTENTS, PotionContents.EMPTY); + } + + public void addEffect(MobEffectInstance pEffectInstance) { + this.setPotionContents(this.getPotionContents().withEffectAdded(pEffectInstance)); + } + + private void setPotionContents(PotionContents pPotionContents) { + this.getPickupItemStackOrigin().set(DataComponents.POTION_CONTENTS, pPotionContents); + this.updateColor(); + } + + private void updateColor() { + PotionContents potioncontents = this.getPotionContents(); + this.entityData.set(ID_EFFECT_COLOR, potioncontents.equals(PotionContents.EMPTY) ? -1 : potioncontents.getColor()); + } + + @Override + protected void defineSynchedData(SynchedEntityData.@NotNull Builder pBuilder) { + super.defineSynchedData(pBuilder); + pBuilder.define(ID_EFFECT_COLOR, -1); + } + + + @Override + protected void setPickupItemStack(@NotNull ItemStack pPickupItemStack) { + super.setPickupItemStack(pPickupItemStack); + this.updateColor(); + } + + public LeashRopeArrow(EntityType entityType, double pX, double pY, double pZ, Level pLevel, ItemStack pPickupItemStack, @Nullable ItemStack pFiredFromWeapon, @Nullable ServerPlayer serverPlayer) { + super(entityType, pX, pY, pZ, pLevel, pPickupItemStack, pFiredFromWeapon); + this.updateColor(); + if(serverPlayer != null && !level().isClientSide) { + Entity leashDataEntity = PlayerLeashable.getLeashDataEntity(serverPlayer, (ServerLevel) level()); + if(leashDataEntity instanceof LeashRopeArrow leashRopeArrow) { + leashRopeArrow.setOwner(null);//将先前的箭矢置空 + } + ((PlayerLeashable)serverPlayer).setLeashedTo(this, true); + } + } + + public LeashRopeArrow(EntityType entityType, LivingEntity pOwner, Level pLevel, ItemStack pPickupItemStack, @Nullable ItemStack pFiredFromWeapon) { + super(entityType, pOwner, pLevel, pPickupItemStack, pFiredFromWeapon); + this.updateColor(); + if (pOwner instanceof PlayerLeashable lPlayer && !level().isClientSide) { + Entity leashDataEntity = PlayerLeashable.getLeashDataEntity((ServerPlayer) lPlayer, (ServerLevel) level()); + if(leashDataEntity instanceof LeashRopeArrow leashRopeArrow) { + leashRopeArrow.setOwner(null); + } + lPlayer.setLeashedTo(this, true); + } + } + + @Override + protected @NotNull ItemStack getDefaultPickupItem() { + return new ItemStack(ModItemRegister.LEASH_ROPE_ARROW.get()); + } + + @Override + public void setOwner(@Nullable Entity pEntity) { + boolean isNull = pEntity == null; + this.ownerUUID = isNull ? null : pEntity.getUUID(); + this.cachedOwner = isNull ? null : pEntity; + this.pickup = this.pickup == Pickup.CREATIVE_ONLY ? this.pickup : Pickup.DISALLOWED; + } + + @Override + protected boolean tryPickup(@NotNull Player pPlayer) { + //时间1.40 禁止 + //时间2.240 + // 如果(非仅创造拾取) + // 如果 (按Shift ) + // 如果(拥有者) -> 拾取到完整箭,取消绑定(super给父类处理) + // 否则:时间仍为原需时间 ->不能获取完整的箭,重绑定(当前拥有者的Holder是否为本箭,“是”才重绑定) + // 否则: 禁止 + // 否则: + // 如果 (按Shift ) + // 如果(拥有者) -> 且拾取到完整箭,取消绑定 + // 否则:时间仍为原需时间 ->不能获取完整的箭,重绑定 + // 否则: 禁止 + //时间3 + // 如果(拥有者) -> 拾取到完整箭,取消绑定 + // 否则:不能获取完整的箭,重绑定 + + if(life <= 40 ) { + return false; + } else { + PlayerLeashable playerLeashable = (PlayerLeashable) pPlayer; + if(this.getOwner() != null) {//未有Owner始终可检 + if(life <= 240) { + if(pPlayer.isShiftKeyDown()) { + Entity leashDataEntity = this.getOwner() instanceof PlayerLeashable ? PlayerLeashable.getLeashDataEntity((ServerPlayer) this.getOwner(), (ServerLevel) level()) : this.getOwner(); + if(this.ownedBy(pPlayer)) { + this.pickup = Pickup.ALLOWED; + if(this.equals(leashDataEntity)) { + pPlayer.playSound(SoundEvents.LEASH_KNOT_BREAK, 1, 1); + Leashable.dropLeash((Entity & Leashable)playerLeashable, true, false); + } + } else { + if(life >= 120) { + Entity owner = getOwner(); + if( owner != null ) { +// if(this.equals(leashDataEntity)) { + if(owner instanceof PlayerLeashable player) { + player.setLeashedTo(pPlayer, true); + pPlayer.playSound(SoundEvents.LEASH_KNOT_PLACE, 1, 1); + } else if(owner instanceof Leashable leashable) { + leashable.setLeashedTo(pPlayer, true); + pPlayer.playSound(SoundEvents.LEASH_KNOT_PLACE, 1, 1); + } + ItemEntity itemEntity = new ItemEntity(level(), getX(), getY(), getZ(), getOrginalItemStack()); + level().addFreshEntity(itemEntity); + discard(); +// } + } + } else return false; + } + } else { + ((ServerPlayer)pPlayer).sendSystemMessage(Component.translatable(PUSH_SHIFT_TO_PICKUP_QUICKLY), true); + return false; + } + + } else { + Entity leashDataEntity = this.getOwner() instanceof PlayerLeashable ? PlayerLeashable.getLeashDataEntity((ServerPlayer) this.getOwner(), (ServerLevel) level()) : this.getOwner(); + if(this.ownedBy(pPlayer)) { + this.pickup = Pickup.ALLOWED; + if(this.equals(leashDataEntity)) { + pPlayer.playSound(SoundEvents.LEASH_KNOT_BREAK, 1, 1); + Leashable.dropLeash((Entity & Leashable) playerLeashable,true, false); + } + } else { + Entity owner = getOwner(); + if(owner instanceof PlayerLeashable player) { + player.setLeashedTo(pPlayer, true); + pPlayer.playSound(SoundEvents.LEASH_KNOT_PLACE, 1, 1); + } else if(owner instanceof Leashable leashable) { + leashable.setLeashedTo(pPlayer, true); + pPlayer.playSound(SoundEvents.LEASH_KNOT_PLACE, 1, 1); + } + ItemEntity itemEntity = new ItemEntity(level(), getX(), getY(), getZ(), getOrginalItemStack()); + level().addFreshEntity(itemEntity); + discard(); + + } + } + } else if (this.pickup != Pickup.CREATIVE_ONLY) this.pickup = Pickup.ALLOWED; + else return pPlayer.hasInfiniteMaterials() && this.pickup == Pickup.CREATIVE_ONLY; + + } + + return super.tryPickup(pPlayer); + } + protected void hitOnEntityHandler(Entity pEntity) { + if(pEntity instanceof LivingEntity pLiving) { + super.doPostHurtEffects(pLiving); + Entity entity = this.getEffectSource(); + PotionContents potioncontents = this.getPotionContents(); + if (potioncontents.potion().isPresent()) { + for (MobEffectInstance mobeffectinstance : potioncontents.potion().get().value().getEffects()) { + pLiving.addEffect( + new MobEffectInstance( + mobeffectinstance.getEffect(), + Math.max(mobeffectinstance.mapDuration(p_268168_ -> p_268168_ / 8), 1), + mobeffectinstance.getAmplifier(), + mobeffectinstance.isAmbient(), + mobeffectinstance.isVisible() + ), + entity + ); + } + } + for (MobEffectInstance effectInstance : potioncontents.customEffects()) { + pLiving.addEffect(effectInstance, entity); + } + } + } + protected ItemStack getOrginalItemStack() { + return Items.ARROW.getDefaultInstance(); + } + protected ItemStack getSelfItemStack() { + return ModItemRegister.LEASH_ROPE_ARROW.get().getDefaultInstance(); + } + + @Override + protected void tickDespawn() { + this.life++; + if (this.life >= maxLifeTime) { + ItemEntity leash_rope_arrow = new ItemEntity(this.level(), this.position().x, this.position().y, this.position().z, getSelfItemStack()); + this.level().addFreshEntity(leash_rope_arrow); + this.discard(); + } + } + @Override + public void tick() { + super.tick(); + if (this.level().isClientSide) { + if (this.isInGround()) { + if (this.inGroundTime % 5 == 0) { + this.makeParticle(1); + } + } else { + this.makeParticle(2); + } + } else if (this.isInGround() && this.inGroundTime != 0 && !this.getPotionContents().equals(PotionContents.EMPTY) && this.inGroundTime >= 600) { + this.level().broadcastEntityEvent(this, (byte)0); + this.setPickupItemStack(new ItemStack(getSelfItemStack().getItem())); + } + } + + private void makeParticle(int pParticleAmount) { + int i = this.getColor(); + if (i != -1 && pParticleAmount > 0) { + for (int j = 0; j < pParticleAmount; j++) { + this.level() + .addParticle( + ColorParticleOption.create(ParticleTypes.ENTITY_EFFECT, i), + this.getRandomX(0.5), + this.getRandomY(), + this.getRandomZ(0.5), + 0.0, + 0.0, + 0.0 + ); + } + } + } + + public int getColor() { + return this.entityData.get(ID_EFFECT_COLOR); + } + + + @Override + protected void onHitBlock(@NotNull BlockHitResult pResult) { + if(!level().isClientSide) { + if (getOwner() instanceof PlayerLeashable pL) { + if (this.level().getBlockState(pResult.getBlockPos()).is(BlockTags.FENCES)) { + Entity leashDataEntity = PlayerLeashable.getLeashDataEntity((ServerPlayer) getOwner(), (ServerLevel) level()); + if(leashDataEntity != null) { + Leashable.dropLeash((Entity & Leashable) pL, true, !(leashDataEntity instanceof LeashRopeArrow)); + if(leashDataEntity instanceof LeashRopeArrow leashRopeArrow) { + leashRopeArrow.dropLeashHandler(); + } + } + Entity leashKnotFence = PlayerLeashable.createLeashKnotFence((ServerLevel) this.level(), pResult.getBlockPos()); + ILivingEntityExtension pLL = (ILivingEntityExtension) pL; + pLL.setKeepLeashTick(GameruleRegistry.getGameruleIntValue(level(), KeepLeashNotDropTime.ID)); + pL.setLeashedTo(leashKnotFence, true); + leashKnotFence.playSound(SoundEvents.LEASH_KNOT_PLACE, 1, 1); + ItemEntity arrow = new ItemEntity(this.level(), this.position().x, this.position().y, this.position().z, getOrginalItemStack()); + this.level().addFreshEntity(arrow); + discard(); + } + } else if(getOwner() instanceof Leashable L) { + if (this.level().getBlockState(pResult.getBlockPos()).is(BlockTags.FENCES)) { + Entity leashDataEntity = this.getOwner(); + if(leashDataEntity != null) { + leashDataEntity.playSound(SoundEvents.LEASH_KNOT_BREAK, 1, 1); + Leashable.dropLeash((Entity & Leashable) L,true, false); + if(leashDataEntity instanceof LeashRopeArrow leashRopeArrow) { + leashRopeArrow.setOwner(null); + } + } + Entity leashKnotFence = LeashFenceKnotEntity.getOrCreateKnot(this.level(), pResult.getBlockPos()); + L.setLeashedTo(leashKnotFence, true); + leashKnotFence.playSound(SoundEvents.LEASH_KNOT_PLACE, 1, 1); + ItemEntity arrow = new ItemEntity(this.level(), this.position().x, this.position().y, this.position().z, getOrginalItemStack()); + this.level().addFreshEntity(arrow); + discard(); + } + } + } + super.onHitBlock(pResult); + + } + + @Override + protected void onHitEntity(@NotNull EntityHitResult pResult) { + if (!level().isClientSide()) { + Entity entity = pResult.getEntity(); + hitOnEntityHandler(entity); + if (this.getOwner() instanceof LivingEntity livingEntity ) { + MobEffectInstance effect = livingEntity.getEffect(ModEffectRegister.NO_LEASH_EFFECT); + if (effect != null && effect.getDuration() != 0) { + this.dropLeashHandler(); + } + } + if(entity instanceof LivingEntity livingEntity) { + if(livingEntity.equals(this.getOwner())) return; + if(this.getOwner() == null && livingEntity instanceof PlayerLeashable pL) { //发射器发出或命令生成 + setOwner(livingEntity); + Entity leashDataEntity = PlayerLeashable.getLeashDataEntity((ServerPlayer) getOwner(), (ServerLevel) level()); + if(leashDataEntity != null) { + Leashable.dropLeash((Entity & Leashable) pL,true, !(leashDataEntity instanceof LeashRopeArrow)); + if(leashDataEntity instanceof LeashRopeArrow leashRopeArrow) { + leashRopeArrow.dropLeashHandler(); + } + } + ILivingEntityExtension pLL = (ILivingEntityExtension) pL; + pLL.setKeepLeashTick(GameruleRegistry.getGameruleIntValue(level(), KeepLeashNotDropTime.ID)); + livingEntity.playSound(SoundEvents.LEASH_KNOT_PLACE, 1, 1); + pL.setLeashedTo(this, true); + } else if (this.getOwner() instanceof PlayerLeashable pL) { + Entity leashDataEntity = PlayerLeashable.getLeashDataEntity((ServerPlayer) getOwner(), (ServerLevel) level()); + if(leashDataEntity != null) { + Leashable.dropLeash((Entity & Leashable) pL, true, !(leashDataEntity instanceof LeashRopeArrow)); + if (leashDataEntity instanceof LeashRopeArrow leashRopeArrow) { + leashRopeArrow.dropLeashHandler(); + } + } + ItemEntity arrow = new ItemEntity(this.level(), this.position().x, this.position().y, this.position().z, getOrginalItemStack()); + ILivingEntityExtension pLL = (ILivingEntityExtension) pL; + pLL.setKeepLeashTick(GameruleRegistry.getGameruleIntValue(level(), KeepLeashNotDropTime.ID)); + livingEntity.playSound(SoundEvents.LEASH_KNOT_PLACE, 1, 1); + pL.setLeashedTo(pResult.getEntity(), true); + this.level().addFreshEntity(arrow); + discard(); + } else { + if (entity instanceof Leashable leashable) { + if (getOwner() == null) { + Entity leashDataEntity = leashable.getLeashHolder(); + if (leashDataEntity != null) { + Leashable.dropLeash((Entity & Leashable) leashable,true, !(leashDataEntity instanceof LeashRopeArrow)); + if (leashDataEntity instanceof LeashRopeArrow leashRopeArrow) { + leashRopeArrow.dropLeashHandler(); + } + } + livingEntity.playSound(SoundEvents.LEASH_KNOT_PLACE, 1, 1); + leashable.setLeashedTo(this, true); + this.setOwner(entity); + return; + } + } + if (entity instanceof LivingEntity living) { + if (this.getOwner() != null && this.getOwner()instanceof Leashable leashable) { + livingEntity.playSound(SoundEvents.LEASH_KNOT_PLACE, 1, 1); + leashable.setLeashedTo(living, true); + ItemEntity arrow = new ItemEntity(this.level(), this.position().x, this.position().y, this.position().z, getOrginalItemStack()); + this.level().addFreshEntity(arrow); + discard(); + return; + } + } + ItemEntity lead = new ItemEntity(this.level(), this.position().x, this.position().y, this.position().z, Items.LEAD.getDefaultInstance()); + this.level().addFreshEntity(lead); + } + } + else if (entity instanceof LeashFenceKnotEntity leashKnotFence) { + if (getOwner() instanceof PlayerLeashable pL) { + Entity leashDataEntity = PlayerLeashable.getLeashDataEntity((ServerPlayer) getOwner(), (ServerLevel) level()); + if (leashDataEntity != null) { + Leashable.dropLeash((Entity & Leashable) pL,true, true); + if (leashDataEntity instanceof LeashRopeArrow leashRopeArrow) { + leashRopeArrow.dropLeashHandler(); + } + } + ItemEntity arrow = new ItemEntity(this.level(), this.position().x, this.position().y, this.position().z, getOrginalItemStack()); + ILivingEntityExtension pLL = (ILivingEntityExtension) pL; + pLL.setKeepLeashTick(GameruleRegistry.getGameruleIntValue(level(), KeepLeashNotDropTime.ID)); + leashKnotFence.playSound(SoundEvents.LEASH_KNOT_PLACE, 1, 1); + pL.setLeashedTo(leashKnotFence, true); + this.level().addFreshEntity(arrow); + discard(); + } + } else { + ItemEntity lead = new ItemEntity(this.level(), this.position().x, this.position().y, this.position().z, Items.LEAD.getDefaultInstance()); + this.level().addFreshEntity(lead); + } + } + } + /** + * Handles an entity event received from a {@link net.minecraft.network.protocol.game.ClientboundEntityEventPacket}. + */ + @Override + public void handleEntityEvent(byte pId) { + if (pId == 0) { + int i = this.getColor(); + if (i != -1) { + float f = (float)(i >> 16 & 0xFF) / 255.0F; + float f1 = (float)(i >> 8 & 0xFF) / 255.0F; + float f2 = (float)(i & 0xFF) / 255.0F; + + for (int j = 0; j < 20; j++) { + this.level() + .addParticle( + ColorParticleOption.create(ParticleTypes.ENTITY_EFFECT, f, f1, f2), + this.getRandomX(0.5), + this.getRandomY(), + this.getRandomZ(0.5), + 0.0, + 0.0, + 0.0 + ); + } + } + } else { + super.handleEntityEvent(pId); + } + } + public void dropLeashHandler() { + this.playSound(SoundEvents.LEASH_KNOT_PLACE, 1, 1); + this.setOwner(null); + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/content/entities/ModEntityRegister.java b/src/main/java/com/r3944realms/leashedplayer/content/entities/ModEntityRegister.java new file mode 100644 index 0000000..ed7ac13 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/content/entities/ModEntityRegister.java @@ -0,0 +1,40 @@ +package com.r3944realms.leashedplayer.content.entities; + +import com.r3944realms.leashedplayer.LeashedPlayer; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.MobCategory; +import net.neoforged.bus.api.IEventBus; +import net.neoforged.neoforge.registries.DeferredHolder; +import net.neoforged.neoforge.registries.DeferredRegister; + +public class ModEntityRegister { + public static final DeferredRegister> ENTITY_TYPE = DeferredRegister.create(Registries.ENTITY_TYPE, LeashedPlayer.MOD_ID); + public static final DeferredHolder ,EntityType> LEASH_ROPE_ARROW = ENTITY_TYPE.register( + "leash_rope_arrow", + () -> EntityType.Builder.of(LeashRopeArrow::new, MobCategory.MISC) + .sized(0.5F, 0.5F) + .eyeHeight(0.13F) + .clientTrackingRange(4) + .updateInterval(20) + .build(ResourceKey.create(Registries.ENTITY_TYPE, ResourceLocation.fromNamespaceAndPath(LeashedPlayer.MOD_ID, "leash_rope_arrow"))) + ); + public static final DeferredHolder, EntityType> SPECTRAL_LEASH_ROPE_ARROW = ENTITY_TYPE.register( + "spectral_leash_rope_arrow", + () -> EntityType.Builder.of(SpectralLeashRopeArrow::new, MobCategory.MISC) + .sized(0.5F, 0.5F) + .eyeHeight(0.13F) + .clientTrackingRange(4) + .updateInterval(20) + .build(ResourceKey.create(Registries.ENTITY_TYPE, ResourceLocation.fromNamespaceAndPath(LeashedPlayer.MOD_ID, "spectral_leash_rope_arrow"))) + ); + + public static String getEntityNameKey(String entityName) { + return "entity." + LeashedPlayer.MOD_ID + "." + entityName; + } + public static void register(IEventBus eventBus) { + ENTITY_TYPE.register(eventBus); + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/content/entities/SpectralLeashRopeArrow.java b/src/main/java/com/r3944realms/leashedplayer/content/entities/SpectralLeashRopeArrow.java new file mode 100644 index 0000000..4a5f5e1 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/content/entities/SpectralLeashRopeArrow.java @@ -0,0 +1,81 @@ +package com.r3944realms.leashedplayer.content.entities; + +import com.r3944realms.leashedplayer.content.items.ModItemRegister; +import net.minecraft.core.particles.ParticleTypes; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.effect.MobEffectInstance; +import net.minecraft.world.effect.MobEffects; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.projectile.AbstractArrow; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.level.Level; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class SpectralLeashRopeArrow extends LeashRopeArrow { + private int duration = 200; + + public SpectralLeashRopeArrow(EntityType entityType, double pX, double pY, double pZ, Level pLevel, ItemStack pPickupItemStack, @Nullable ItemStack pFiredFromWeapon, @Nullable ServerPlayer serverPlayer) { + super(entityType, pX, pY, pZ, pLevel, pPickupItemStack, pFiredFromWeapon, serverPlayer); + } + + protected SpectralLeashRopeArrow(EntityType entityType, Level pLevel) { + super(entityType, pLevel); + } + + public SpectralLeashRopeArrow(EntityType entityType, LivingEntity pOwner, Level pLevel, ItemStack pPickupItemStack, @Nullable ItemStack pFiredFromWeapon) { + super(entityType, pOwner, pLevel, pPickupItemStack, pFiredFromWeapon); + } + @Override + public void tick() { + super.tick(); + if (this.level().isClientSide && !this.isInGround()) { + this.level().addParticle(ParticleTypes.INSTANT_EFFECT, this.getX(), this.getY(), this.getZ(), 0.0, 0.0, 0.0); + } + } + @Override + protected void doPostHurtEffects(@NotNull LivingEntity pLiving) { + super.doPostHurtEffects(pLiving); + MobEffectInstance mobeffectinstance = new MobEffectInstance(MobEffects.GLOWING, this.duration, 0); + pLiving.addEffect(mobeffectinstance, this.getEffectSource()); + } + + /** + * (abstract) Protected helper method to read subclass entity data from NBT. + */ + @Override + public void readAdditionalSaveData(@NotNull CompoundTag pCompound) { + super.readAdditionalSaveData(pCompound); + if (pCompound.contains("Duration")) { + this.duration = pCompound.getInt("Duration").orElse(200); + } + } + + @Override + public void addAdditionalSaveData(@NotNull CompoundTag pCompound) { + super.addAdditionalSaveData(pCompound); + pCompound.putInt("Duration", this.duration); + } + + @Override + protected ItemStack getOrginalItemStack() { + return Items.SPECTRAL_ARROW.getDefaultInstance(); + } + @Override + protected ItemStack getSelfItemStack() { + return ModItemRegister.SPECTRAL_LEASH_ROPE_ARROW.get().getDefaultInstance(); + } + + @Override + protected void hitOnEntityHandler(Entity pEntity) { + if(pEntity instanceof LivingEntity livingEntity) { + livingEntity.addEffect(new MobEffectInstance(MobEffects.GLOWING, this.duration, 0)); + } else { + super.hitOnEntityHandler(pEntity); + } + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/content/events/RegisterConditionalRangeItemPropertyEvent.java b/src/main/java/com/r3944realms/leashedplayer/content/events/RegisterConditionalRangeItemPropertyEvent.java new file mode 100644 index 0000000..f3f7877 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/content/events/RegisterConditionalRangeItemPropertyEvent.java @@ -0,0 +1,29 @@ +package com.r3944realms.leashedplayer.content.events; + + +import com.mojang.serialization.MapCodec; +import com.r3944realms.leashedplayer.client.renderer.item.properties.conditionalRange.ConditionalRangeItemModelProperty; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.ExtraCodecs; +import net.neoforged.bus.api.Event; +import net.neoforged.fml.event.IModBusEvent; +import org.jetbrains.annotations.ApiStatus; +/** + * Event fired when special model renderers are registered. + *

+ * This event is fired during the model registration process for conditional range select item model properties. + * It is used to register property codecs which can be used to create custom conditional range select item model properties. + *

+ * This event is fired on the mod event bus. + */ +public class RegisterConditionalRangeItemPropertyEvent extends Event implements IModBusEvent { + private final ExtraCodecs.LateBoundIdMapper> idMapper; + @ApiStatus.Internal + public RegisterConditionalRangeItemPropertyEvent(ExtraCodecs.LateBoundIdMapper> idMapper) { + this.idMapper = idMapper; + } + + public void register(ResourceLocation location, MapCodec source) { + this.idMapper.put(location, source); + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/content/gamerules/GameRules.java b/src/main/java/com/r3944realms/leashedplayer/content/gamerules/GameRules.java new file mode 100644 index 0000000..6494eaa --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/content/gamerules/GameRules.java @@ -0,0 +1,107 @@ +package com.r3944realms.leashedplayer.content.gamerules; + +import com.mojang.brigadier.arguments.FloatArgumentType; +import com.mojang.brigadier.context.CommandContext; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.server.MinecraftServer; +import net.minecraft.world.flag.FeatureFlagSet; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; +import java.util.function.BiConsumer; + +public class GameRules { + public static final String GAMERULE_PREFIX = "LP."; + public static final GameruleRegistry GAMERULE_REGISTRY = GameruleRegistry.INSTANCE; + public static final HashMap gamerulesBooleanValuesClient = new HashMap<>(); + public static final HashMap gameruleIntegerValuesClient = new HashMap<>(); + public static final HashMap gameruleFloatValuesClient = new HashMap<>(); + public static final String RULE_KEY_PERFix_ = "gamerule." + GAMERULE_PREFIX; + public static String getDescriptionKey(Class gameRuleClass) { + return RULE_KEY_PERFix_ + gameRuleClass.getSimpleName() + ".description"; + } + public static String getDescriptionKey(String gameRuleName) { + return RULE_KEY_PERFix_ + gameRuleName + ".description"; + } + + public static String getNameKey(Class gameRuleClass) { + return RULE_KEY_PERFix_ + gameRuleClass.getSimpleName(); + } + //此次定义了一个浮点数类型的游戏规则 + public static class FloatValue extends net.minecraft.world.level.GameRules.Value { + private float value; + + public static net.minecraft.world.level.GameRules.Type create( + float pDefaultValue, BiConsumer pChangeListener + ) { + return new net.minecraft.world.level.GameRules.Type<> + (FloatArgumentType::floatArg, + pType -> new FloatValue(pType, pDefaultValue), + pChangeListener, + net.minecraft.world.level.GameRules.GameRuleTypeVisitor::visit, + FloatValue.class, + FeatureFlagSet.of() + ); + } + public static net.minecraft.world.level.GameRules.Type create( + float pDefaultValue, float pMin, float pMax , BiConsumer pChangeListener + ) { + return new net.minecraft.world.level.GameRules.Type<>( + () -> FloatArgumentType.floatArg(pMin, pMax), + pType -> new GameRules.FloatValue(pType, pDefaultValue),// + pChangeListener, + net.minecraft.world.level.GameRules.GameRuleTypeVisitor::visit, + FloatValue.class, + FeatureFlagSet.of() + ); + } + public FloatValue(net.minecraft.world.level.GameRules.Type pType, float value) { + super(pType); + this.value = value; + } + + @Override + protected void updateFromArgument(@NotNull CommandContext pContext, @NotNull String pParamName) { + this.value = FloatArgumentType.getFloat(pContext, pParamName); + } + public float get() { + return this.value; + } + + public void set(float pValue, @Nullable MinecraftServer pServer) { + this.value = pValue; + this.onChanged(pServer); + } + @Override + protected void deserialize(@NotNull String pValue) { + this.value = Float.parseFloat(pValue); + } + + @Override + public @NotNull String serialize() { + return Float.toString(this.value); + } + + @Override + public int getCommandResult() { + return 1; + } + + @Override + protected @NotNull FloatValue getSelf() { + return this; + } + + @Override + protected @NotNull FloatValue copy() { + return new FloatValue(this.type, this.value); + } + + @Override + public void setFrom(FloatValue pValue, @Nullable MinecraftServer pServer) { + this.value = pValue.value; + this.onChanged(pServer); + } + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/content/gamerules/GameruleRegistry.java b/src/main/java/com/r3944realms/leashedplayer/content/gamerules/GameruleRegistry.java new file mode 100644 index 0000000..4fdbc5c --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/content/gamerules/GameruleRegistry.java @@ -0,0 +1,71 @@ +package com.r3944realms.leashedplayer.content.gamerules; + +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.flag.FeatureFlagSet; +import net.minecraft.world.level.Level; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.BiConsumer; + +public enum GameruleRegistry { + INSTANCE; + public static final Map> gamerules = new HashMap<>();; + public static final Map gameruleDataTypes = new HashMap<>(); + public enum RuleDataType { + BOOLEAN, + INTEGER, + } + @SuppressWarnings("unchecked") + + public static boolean getGameruleBoolValue(Level level, String gameruleName) { + if (level.isClientSide && GameRules.gamerulesBooleanValuesClient.containsKey(gameruleName)) { + return GameRules.gamerulesBooleanValuesClient.get(gameruleName); + } + if (gameruleDataTypes.get(gameruleName) != RuleDataType.BOOLEAN) { + return false; + } + return ((ServerLevel)level).getGameRules().getBoolean((net.minecraft.world.level.GameRules.Key) gamerules.get(gameruleName)); + } + @SuppressWarnings("unchecked") + public static Integer getGameruleIntValue(Level level, String gameruleName) { + if (level.isClientSide && GameRules.gameruleIntegerValuesClient.containsKey(gameruleName)) { + return GameRules.gameruleIntegerValuesClient.get(gameruleName); + } + if (gameruleDataTypes.get(gameruleName) != RuleDataType.INTEGER) { + return 0; + } + return ((ServerLevel)level).getGameRules().getInt((net.minecraft.world.level.GameRules.Key)gamerules.get(gameruleName)); + } + + public void registerGamerule(String gameruleName, net.minecraft.world.level.GameRules.Category category, boolean pDefault) { + registerGamerule(gameruleName, category, pDefault, (s,i)->{});//最后一个仅占位无用 + } + public void registerGamerule(String gameruleName, net.minecraft.world.level.GameRules.Category category, boolean pDefault, BiConsumer pChangeListener) { + gamerules.put(gameruleName, net.minecraft.world.level.GameRules.register(gameruleName, category, net.minecraft.world.level.GameRules.BooleanValue.create(pDefault, pChangeListener))); + gameruleDataTypes.put(gameruleName, RuleDataType.BOOLEAN); + } + public void registerGamerule(String gameruleName, net.minecraft.world.level.GameRules.Category category, int pDefault) { + registerGamerule(gameruleName, category, pDefault, (BiConsumer) (s, i)->{});//最后一个仅占位无用 + } + public void registerGamerule(String gameruleName, net.minecraft.world.level.GameRules.Category category, int pDefault, BiConsumer pChangeListener) { + gamerules.put(gameruleName, net.minecraft.world.level.GameRules.register(gameruleName, category, net.minecraft.world.level.GameRules.IntegerValue.create(pDefault, pChangeListener))); + gameruleDataTypes.put(gameruleName, RuleDataType.INTEGER); + } + public void registerGamerule(String gameruleName, net.minecraft.world.level.GameRules.Category category, int pDefault, int pMin, int pMax, BiConsumer pChangeListener) { + gamerules.put(gameruleName, net.minecraft.world.level.GameRules.register(gameruleName, category, net.minecraft.world.level.GameRules.IntegerValue.create(pDefault, pMin, pMax, FeatureFlagSet.of(), pChangeListener))); + gameruleDataTypes.put(gameruleName, RuleDataType.INTEGER); + } + public void registerGamerule(String gameruleName, net.minecraft.world.level.GameRules.Category category, float value) { + registerGamerule(gameruleName, category, value, (s,i)->{}); + } + public void registerGamerule(String gameruleName, net.minecraft.world.level.GameRules.Category category, float pDefault, BiConsumer pChangeListener) { + gamerules.put(gameruleName, net.minecraft.world.level.GameRules.register(gameruleName, category, GameRules.FloatValue.create(pDefault, pChangeListener))); + gameruleDataTypes.put(gameruleName, RuleDataType.INTEGER); + } + public void registerGamerule(String gameruleName, net.minecraft.world.level.GameRules.Category category, float pDefault, float pMin, float pMax, BiConsumer pChangeListener) { + gamerules.put(gameruleName, net.minecraft.world.level.GameRules.register(gameruleName, category, GameRules.FloatValue.create(pDefault, pMin, pMax,pChangeListener))); + gameruleDataTypes.put(gameruleName, RuleDataType.INTEGER); + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/content/gamerules/Server/CreateLeashFenceKnotEntityIfAbsent.java b/src/main/java/com/r3944realms/leashedplayer/content/gamerules/Server/CreateLeashFenceKnotEntityIfAbsent.java new file mode 100644 index 0000000..de51285 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/content/gamerules/Server/CreateLeashFenceKnotEntityIfAbsent.java @@ -0,0 +1,24 @@ +package com.r3944realms.leashedplayer.content.gamerules.Server; + +import com.r3944realms.leashedplayer.LeashedPlayer; +import com.r3944realms.leashedplayer.content.gamerules.GameRules; +import com.r3944realms.leashedplayer.utils.Util; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.common.EventBusSubscriber; +import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent; + +import static com.r3944realms.leashedplayer.content.gamerules.GameRules.GAMERULE_REGISTRY; + +@EventBusSubscriber(modid = LeashedPlayer.MOD_ID, bus = EventBusSubscriber.Bus.MOD) +public class CreateLeashFenceKnotEntityIfAbsent { + public static final boolean DEFAULT_VALUE = true; + public static final String ID = Util.getGameruleName(CreateLeashFenceKnotEntityIfAbsent.class); + public static final String DESCRIPTION_KEY = GameRules.getDescriptionKey(CreateLeashFenceKnotEntityIfAbsent.class); + public static final String NAME_KEY = GameRules.getNameKey(CreateLeashFenceKnotEntityIfAbsent.class); + public static final net.minecraft.world.level.GameRules.Category CATEGORY = net.minecraft.world.level.GameRules.Category.PLAYER; + + @SubscribeEvent + public static void onCommonSetup(final FMLCommonSetupEvent event) { + GAMERULE_REGISTRY.registerGamerule(ID, CATEGORY, DEFAULT_VALUE); + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/content/gamerules/Server/KeepLeashNotDropTime.java b/src/main/java/com/r3944realms/leashedplayer/content/gamerules/Server/KeepLeashNotDropTime.java new file mode 100644 index 0000000..9cde758 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/content/gamerules/Server/KeepLeashNotDropTime.java @@ -0,0 +1,27 @@ +package com.r3944realms.leashedplayer.content.gamerules.Server; + +import com.r3944realms.leashedplayer.LeashedPlayer; +import com.r3944realms.leashedplayer.content.gamerules.GameRules; +import com.r3944realms.leashedplayer.utils.Util; +import net.minecraft.server.MinecraftServer; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.common.EventBusSubscriber; +import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent; + +import java.util.function.BiConsumer; + +import static com.r3944realms.leashedplayer.content.gamerules.GameRules.GAMERULE_REGISTRY; + +@EventBusSubscriber(modid = LeashedPlayer.MOD_ID, bus = EventBusSubscriber.Bus.MOD) +public class KeepLeashNotDropTime { + public static final int DEFAULT_VALUE = 240; + public static final String ID = Util.getGameruleName(KeepLeashNotDropTime.class); + public static final String DESCRIPTION_KEY = GameRules.getDescriptionKey(KeepLeashNotDropTime.class); + public static final String NAME_KEY = GameRules.getNameKey(KeepLeashNotDropTime.class); + public static final net.minecraft.world.level.GameRules.Category CATEGORY = net.minecraft.world.level.GameRules.Category.MISC; + + @SubscribeEvent + public static void onCommonSetup(final FMLCommonSetupEvent event) { + GAMERULE_REGISTRY.registerGamerule(ID, CATEGORY, DEFAULT_VALUE, 80, 1200, (BiConsumer) (i, j)->{}); + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/content/gamerules/Server/TeleportWithLeashedPlayers.java b/src/main/java/com/r3944realms/leashedplayer/content/gamerules/Server/TeleportWithLeashedPlayers.java new file mode 100644 index 0000000..7384c77 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/content/gamerules/Server/TeleportWithLeashedPlayers.java @@ -0,0 +1,25 @@ +package com.r3944realms.leashedplayer.content.gamerules.Server; + +import com.r3944realms.leashedplayer.LeashedPlayer; +import com.r3944realms.leashedplayer.content.gamerules.GameRules; +import com.r3944realms.leashedplayer.utils.Util; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.common.EventBusSubscriber; +import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent; + +import static com.r3944realms.leashedplayer.content.gamerules.GameRules.GAMERULE_REGISTRY; + +//It had so Long Name at first.( +@EventBusSubscriber(modid = LeashedPlayer.MOD_ID, bus = EventBusSubscriber.Bus.MOD) +public class TeleportWithLeashedPlayers { + public static final boolean DEFAULT_VALUE = true; + public static final String ID = Util.getGameruleName(TeleportWithLeashedPlayers.class); + public static final String DESCRIPTION_KEY = GameRules.getDescriptionKey(TeleportWithLeashedPlayers.class); + public static final String NAME_KEY = GameRules.getNameKey(TeleportWithLeashedPlayers.class); + public static final net.minecraft.world.level.GameRules.Category CATEGORY = net.minecraft.world.level.GameRules.Category.PLAYER; + + @SubscribeEvent + public static void onCommonSetup(final FMLCommonSetupEvent event) { + GAMERULE_REGISTRY.registerGamerule(ID, CATEGORY, DEFAULT_VALUE); + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/content/items/ModCreativeTab.java b/src/main/java/com/r3944realms/leashedplayer/content/items/ModCreativeTab.java new file mode 100644 index 0000000..ca35dfb --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/content/items/ModCreativeTab.java @@ -0,0 +1,85 @@ +package com.r3944realms.leashedplayer.content.items; + +import com.r3944realms.leashedplayer.LeashedPlayer; +import net.minecraft.core.HolderLookup; +import net.minecraft.core.registries.Registries; +import net.minecraft.network.chat.Component; +import net.minecraft.world.flag.FeatureFlagSet; +import net.minecraft.world.item.CreativeModeTab; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.alchemy.Potion; +import net.minecraft.world.item.alchemy.PotionContents; +import net.neoforged.bus.api.IEventBus; +import net.neoforged.neoforge.common.CommonHooks; +import net.neoforged.neoforge.registries.DeferredRegister; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; +import java.util.function.Supplier; + +public class ModCreativeTab { + public static final DeferredRegister CREATIVE_MODE_TABS = + DeferredRegister.create(Registries.CREATIVE_MODE_TAB, LeashedPlayer.MOD_ID); + public static final String LEASHED_PLAYER_TAB_STRING = "creativetab." + LeashedPlayer.MOD_ID; + public static final String LEASHED_PLAYER_ITEM = "leashedplayer_tab"; + public static final Supplier TEST_TAB = CREATIVE_MODE_TABS.register(LEASHED_PLAYER_ITEM,() -> CreativeModeTab.builder() + .title(Component.translatable(getCreativeMod(LEASHED_PLAYER_ITEM))) + .icon(() -> ModItemRegister.LEASH_ROPE_ARROW.get().getDefaultInstance()) + .displayItems(((pParameters, pOutput) -> { + pOutput.accept(Items.LEAD); + pOutput.accept(Items.BOW); + pOutput.accept(Items.CROSSBOW); + pOutput.accept(ModItemRegister.LEASH_ROPE_ARROW.get()); + pOutput.accept(ModItemRegister.SPECTRAL_LEASH_ROPE_ARROW.get()); + pOutput.accept(ModItemRegister.AMETHYST_SHEARS.get()); + pOutput.accept(ModItemRegister.NEOFORGE.get()); + pParameters.holders() + .lookup(Registries.POTION) + .ifPresent(potionRegistryLookup1 -> + potionRegistryLookup1.listElements() + .filter(potionReference -> Objects.requireNonNull(potionReference.getKey()).location().getNamespace().equals(LeashedPlayer.MOD_ID)) + .map(potionReference -> PotionContents.createItemStack(Items.POTION, potionReference)) + .forEach(pOutput::accept)); + pParameters.holders() + .lookup(Registries.POTION) + .ifPresent(potionRegistryLookup1 -> + potionRegistryLookup1.listElements() + .filter(potionReference -> Objects.requireNonNull(potionReference.getKey()).location().getNamespace().equals(LeashedPlayer.MOD_ID)) + .map(potionReference -> PotionContents.createItemStack(Items.SPLASH_POTION, potionReference)) + .forEach(pOutput::accept)); + pParameters.holders() + .lookup(Registries.POTION) + .ifPresent(potionRegistryLookup1 -> + potionRegistryLookup1.listElements() + .filter(potionReference -> Objects.requireNonNull(potionReference.getKey()).location().getNamespace().equals(LeashedPlayer.MOD_ID)) + .map(potionReference -> PotionContents.createItemStack(Items.LINGERING_POTION, potionReference)) + .forEach(pOutput::accept)); + pParameters.holders() + .lookup(Registries.POTION) + .ifPresent( + pPotions -> generatePotionEffectTypes( + pOutput, + pPotions, + ModItemRegister.TIPPED_LEASH_ROPE_ARROW.get(), + CreativeModeTab.TabVisibility.PARENT_AND_SEARCH_TABS, + pParameters.enabledFeatures() + ) + ); + })).build()); + private static void generatePotionEffectTypes( + CreativeModeTab.Output pOutput, HolderLookup pPotions, Item pItem, CreativeModeTab.TabVisibility pTabVisibility, FeatureFlagSet pRequiredFeatures + ) { + pPotions.listElements() + .filter(potionReference -> potionReference.value().isEnabled(pRequiredFeatures)) + .map(potionReference -> PotionContents.createItemStack(pItem, potionReference)) + .forEach(itemStack -> pOutput.accept(itemStack, pTabVisibility)); + } + public static String getCreativeMod(@NotNull String tabs) { + return LEASHED_PLAYER_TAB_STRING + "." + tabs; + } + public static void register(IEventBus eventBus) { + CREATIVE_MODE_TABS.register(eventBus); + } + +} diff --git a/src/main/java/com/r3944realms/leashedplayer/content/items/ModItemRegister.java b/src/main/java/com/r3944realms/leashedplayer/content/items/ModItemRegister.java new file mode 100644 index 0000000..89f16c3 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/content/items/ModItemRegister.java @@ -0,0 +1,65 @@ +package com.r3944realms.leashedplayer.content.items; + +import com.r3944realms.leashedplayer.LeashedPlayer; +import com.r3944realms.leashedplayer.content.items.type.*; +import com.r3944realms.leashedplayer.datagen.provider.attributes.ModJukeboxSongs; +import net.minecraft.core.component.DataComponents; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceKey; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.JukeboxSong; +import net.minecraft.world.item.Rarity; +import net.minecraft.world.item.ShearsItem; +import net.minecraft.world.item.alchemy.PotionContents; +import net.neoforged.bus.api.IEventBus; +import net.neoforged.neoforge.registries.DeferredHolder; +import net.neoforged.neoforge.registries.DeferredRegister; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Supplier; + +public class ModItemRegister { + public static final DeferredRegister ITEMS = + DeferredRegister.create(BuiltInRegistries.ITEM, LeashedPlayer.MOD_ID); + public static final List> ITEM_SUPPLIER = new ArrayList<>(); + public static final DeferredHolder LEASH_ROPE_ARROW = ModItemRegister.register("leash_rope_arrow", + () -> new LeashRopeArrowItem(new Item.Properties().stacksTo(16) + .setId(ModItemResourceKeys.LEASH_ROPE_ARROW.getResourceKey())) + ); + public static final DeferredHolder SPECTRAL_LEASH_ROPE_ARROW = ModItemRegister.register("spectral_leash_rope_arrow", + () -> new SpectralLeashRopeArrowItem(new Item.Properties().stacksTo(16) + .setId(ModItemResourceKeys.SPECTRAL_LEASH_ROPE_ARROW.getResourceKey()))); + + public static final DeferredHolder TIPPED_LEASH_ROPE_ARROW = ModItemRegister.register("tipped_leash_rope_arrow", + () -> new TippedLeashRopeArrowItem(new Item.Properties().stacksTo(16).component(DataComponents.POTION_CONTENTS, PotionContents.EMPTY) + .setId(ModItemResourceKeys.TIPPED_LEASH_ROPE_ARROW.getResourceKey()))); + public static final DeferredHolder AMETHYST_SHEARS = ModItemRegister.register("amethyst_shears", + () -> new LeadBreakerItem(ModToolMaterials.AMETHYST, new Item.Properties().durability(100).component(DataComponents.TOOL, LeadBreakerItem.createToolProperties()) + .stacksTo(1) + .setId(ModItemResourceKeys.AMETHYST_SHEARS.getResourceKey()))); + public static final DeferredHolder FABRIC = ModItemRegister.register("fabric", + () -> new TestItem(new Item.Properties().stacksTo(1) + .setId(ModItemResourceKeys.FABRIC.getResourceKey()))); + public static final DeferredHolder NEOFORGE = ModItemRegister.register("neoforge", + () -> new Item(DistProperties(ModJukeboxSongs.FOX_MUSIC) + .setId(ModItemResourceKeys.NEOFORGE.getResourceKey()))); + + public static Item.Properties DistProperties(ResourceKey song) { + return new Item.Properties().stacksTo(1).rarity(Rarity.RARE).jukeboxPlayable(song); + } + + public static DeferredHolder register(String name, Supplier supplier) { + return register(name, supplier, true); + } + + public static DeferredHolder register(String name, Supplier supplier, boolean shouldJoinSupplierLists) { + DeferredHolder supplierItem = ITEMS.register(name, supplier); + if(shouldJoinSupplierLists) ITEM_SUPPLIER.add(supplierItem); + return supplierItem; + } + + public static void register(IEventBus eventBus) { + ITEMS.register(eventBus); + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/content/items/ModItemResourceKeys.java b/src/main/java/com/r3944realms/leashedplayer/content/items/ModItemResourceKeys.java new file mode 100644 index 0000000..96a4289 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/content/items/ModItemResourceKeys.java @@ -0,0 +1,29 @@ +package com.r3944realms.leashedplayer.content.items; + +import com.r3944realms.leashedplayer.LeashedPlayer; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.Item; +import net.neoforged.neoforge.registries.DeferredHolder; + +public enum ModItemResourceKeys { + LEASH_ROPE_ARROW(ModItemRegister.LEASH_ROPE_ARROW), + SPECTRAL_LEASH_ROPE_ARROW(ModItemRegister.SPECTRAL_LEASH_ROPE_ARROW), + TIPPED_LEASH_ROPE_ARROW (ModItemRegister.TIPPED_LEASH_ROPE_ARROW), + AMETHYST_SHEARS(ModItemRegister.AMETHYST_SHEARS), + FABRIC(ModItemRegister.FABRIC), + NEOFORGE(ModItemRegister.NEOFORGE) + ; + private final ResourceKey resourceKey; + ModItemResourceKeys(String name) { + resourceKey = ResourceKey.create(Registries.ITEM, ResourceLocation.fromNamespaceAndPath(LeashedPlayer.MOD_ID, name)); + } + ModItemResourceKeys(DeferredHolder item) { + resourceKey = ResourceKey.create(Registries.ITEM, item.getId()); + } + + public ResourceKey getResourceKey() { + return resourceKey; + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/content/items/ModToolMaterials.java b/src/main/java/com/r3944realms/leashedplayer/content/items/ModToolMaterials.java new file mode 100644 index 0000000..82f6105 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/content/items/ModToolMaterials.java @@ -0,0 +1,13 @@ +package com.r3944realms.leashedplayer.content.items; + +import com.r3944realms.leashedplayer.datagen.LanguageAndOtherData.ModItemTags; +import net.minecraft.tags.BlockTags; +import net.minecraft.world.item.ToolMaterial; + +public class ModToolMaterials { + public static final ToolMaterial AMETHYST = + new ToolMaterial(BlockTags.INCORRECT_FOR_DIAMOND_TOOL, + 100, 8.0F, 2.0F, 15, + ModItemTags.AMETHYST_TOOL_MATERIALS + ); +} diff --git a/src/main/java/com/r3944realms/leashedplayer/content/items/repcipe/ModRecipeRegister.java b/src/main/java/com/r3944realms/leashedplayer/content/items/repcipe/ModRecipeRegister.java new file mode 100644 index 0000000..9b4dd5b --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/content/items/repcipe/ModRecipeRegister.java @@ -0,0 +1,27 @@ +package com.r3944realms.leashedplayer.content.items.repcipe; + +import com.r3944realms.leashedplayer.LeashedPlayer; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.world.item.crafting.CustomRecipe; +import net.minecraft.world.item.crafting.RecipeSerializer; +import net.neoforged.bus.api.IEventBus; +import net.neoforged.neoforge.registries.DeferredHolder; +import net.neoforged.neoforge.registries.DeferredRegister; + +public class ModRecipeRegister { + public static final DeferredRegister> RECIPE_SERIALIZER = + DeferredRegister.create(BuiltInRegistries.RECIPE_SERIALIZER, LeashedPlayer.MOD_ID); + + public static final DeferredHolder, RecipeSerializer> TIPPED_LEASH_ROPE_ARROW_A_RECIPE = + RECIPE_SERIALIZER.register("tipped_leash_rope_arrow_a_recipe", () -> + new CustomRecipe.Serializer<>(TippedLeashRopeArrowRecipe.TippedLeashRopeArrowARecipe::new) + ); + public static final DeferredHolder, RecipeSerializer> TIPPED_LEASH_ROPE_ARROW_B_RECIPE = + RECIPE_SERIALIZER.register("tipped_leash_rope_arrow_b_recipe", () -> + new CustomRecipe.Serializer<>(TippedLeashRopeArrowRecipe.TippedLeashRopeArrowBRecipe::new) + ); + + public static void register(IEventBus eventBus) { + RECIPE_SERIALIZER.register(eventBus); + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/content/items/repcipe/TippedLeashRopeArrowRecipe.java b/src/main/java/com/r3944realms/leashedplayer/content/items/repcipe/TippedLeashRopeArrowRecipe.java new file mode 100644 index 0000000..ee9329a --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/content/items/repcipe/TippedLeashRopeArrowRecipe.java @@ -0,0 +1,106 @@ +package com.r3944realms.leashedplayer.content.items.repcipe; + +import com.r3944realms.leashedplayer.content.items.ModItemRegister; +import net.minecraft.core.HolderLookup; +import net.minecraft.core.component.DataComponents; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.crafting.CraftingBookCategory; +import net.minecraft.world.item.crafting.CraftingInput; +import net.minecraft.world.item.crafting.CustomRecipe; +import net.minecraft.world.item.crafting.RecipeSerializer; +import net.minecraft.world.level.Level; +import org.jetbrains.annotations.NotNull; + +public abstract class TippedLeashRopeArrowRecipe { + public static class TippedLeashRopeArrowARecipe extends CustomRecipe { + public TippedLeashRopeArrowARecipe(CraftingBookCategory pCategory) { + super(pCategory); + } + + public boolean matches(CraftingInput pInput, @NotNull Level pLevel) { + if (pInput.width() == 3 && pInput.height() == 3) { + for (int i = 0; i < pInput.height(); i++) { + for (int j = 0; j < pInput.width(); j++) { + ItemStack itemstack = pInput.getItem(j, i); + if (itemstack.isEmpty()) { + return false; + } + + if (j == 1 && i == 1) { + if (!itemstack.is(Items.LINGERING_POTION)) { + return false; + } + } else if (!itemstack.is(ModItemRegister.LEASH_ROPE_ARROW.get())) { + return false; + } + } + } + + return true; + } else { + return false; + } + } + + public @NotNull ItemStack assemble(CraftingInput pInput, HolderLookup.@NotNull Provider pRegistries) { + ItemStack itemstack = pInput.getItem(1, 1); + if (!itemstack.is(Items.LINGERING_POTION)) { + return ItemStack.EMPTY; + } else { + ItemStack itemstack1 = new ItemStack(ModItemRegister.TIPPED_LEASH_ROPE_ARROW.get(), 8); + itemstack1.set(DataComponents.POTION_CONTENTS, itemstack.get(DataComponents.POTION_CONTENTS)); + return itemstack1; + } + } + + @Override + public @NotNull RecipeSerializer getSerializer() { + return ModRecipeRegister.TIPPED_LEASH_ROPE_ARROW_A_RECIPE.get(); + } + } + public static class TippedLeashRopeArrowBRecipe extends CustomRecipe { + + public TippedLeashRopeArrowBRecipe(CraftingBookCategory pCategory) { + super(pCategory); + } + + @Override + public boolean matches(CraftingInput pInput, @NotNull Level pLevel) { + int tipped_arrow_count = 0, lead_count = 0; + for (int i = 0; i < pInput.width(); i++) { + for (int j = 0; j < pInput.height(); j++) { + if (pInput.getItem(i, j).is(Items.TIPPED_ARROW)) + tipped_arrow_count++; + else if(pInput.getItem(i, j).is(Items.LEAD)) + lead_count++; + } + } + return tipped_arrow_count == 1 && lead_count == 1; + } + + @Override + public @NotNull ItemStack assemble(@NotNull CraftingInput pInput, HolderLookup.@NotNull Provider pRegistries) { + ItemStack tipped_arrow = null; + NODE:for (int i = 0; i < pInput.width(); i++) { + for (int j = 0; j < pInput.height(); j++) { + if (pInput.getItem(i, j).is(Items.TIPPED_ARROW)) { + tipped_arrow = pInput.getItem(i, j); + break NODE; + } + } + } + if(tipped_arrow != null) { + ItemStack itemstack1 = new ItemStack(ModItemRegister.TIPPED_LEASH_ROPE_ARROW.get(), 1); + itemstack1.set(DataComponents.POTION_CONTENTS, tipped_arrow.get(DataComponents.POTION_CONTENTS)); + return itemstack1; + } + return ItemStack.EMPTY; + } + @Override + public @NotNull RecipeSerializer getSerializer() { + return ModRecipeRegister.TIPPED_LEASH_ROPE_ARROW_B_RECIPE.get(); + } + + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/content/items/type/ILeashRopeArrow.java b/src/main/java/com/r3944realms/leashedplayer/content/items/type/ILeashRopeArrow.java new file mode 100644 index 0000000..bb713fa --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/content/items/type/ILeashRopeArrow.java @@ -0,0 +1,47 @@ +package com.r3944realms.leashedplayer.content.items.type; + +import com.r3944realms.leashedplayer.content.items.ModItemRegister; +import net.minecraft.core.component.DataComponents; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.item.BowItem; +import net.minecraft.world.item.CrossbowItem; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.component.ChargedProjectiles; + +public interface ILeashRopeArrow { + static boolean isLeashRopeArrow(ItemStack bowStack, LivingEntity entity) { + Item item = bowStack.getItem(); + if(item instanceof BowItem) { + if (entity == null) return false; + ItemStack projectile = entity.getProjectile(bowStack); + return projectile.getItem() instanceof ILeashRopeArrow; + + } else if (item instanceof CrossbowItem) { + ChargedProjectiles chargedProjectiles = bowStack.get(DataComponents.CHARGED_PROJECTILES); + return chargedProjectiles != null && + (chargedProjectiles.contains(ModItemRegister.LEASH_ROPE_ARROW.get()) || + chargedProjectiles.contains(ModItemRegister.SPECTRAL_LEASH_ROPE_ARROW.get()) || + chargedProjectiles.contains(ModItemRegister.TIPPED_LEASH_ROPE_ARROW.get())); + } +// if (entity instanceof Player player) { +// if (bowStack.getItem() instanceof CrossbowItem) {} +// // 获取将要发射的弹药 +// ItemStack projectileStack = player.getProjectile(bowStack); +// +// // 判断该弹药是否为拴绳箭 +// return projectileStack.getItem() instanceof ILeashRopeArrow; +// } else if (entity == null) { +// if (bowStack.getItem() instanceof CrossbowItem ) { +// ChargedProjectiles chargedProjectiles = bowStack.get(DataComponents.CHARGED_PROJECTILES); +// if (chargedProjectiles == null || chargedProjectiles.isEmpty()) { +// return false; +// } +// return chargedProjectiles.contains(ModItemRegister.LEASH_ROPE_ARROW.get()) || +// chargedProjectiles.contains(ModItemRegister.SPECTRAL_LEASH_ROPE_ARROW.get()) || +// chargedProjectiles.contains(ModItemRegister.TIPPED_LEASH_ROPE_ARROW.get()); +// } +// } + return false; + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/content/items/type/LeadBreakerItem.java b/src/main/java/com/r3944realms/leashedplayer/content/items/type/LeadBreakerItem.java new file mode 100644 index 0000000..0bdb6ae --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/content/items/type/LeadBreakerItem.java @@ -0,0 +1,128 @@ +package com.r3944realms.leashedplayer.content.items.type; + +import com.r3944realms.leashedplayer.content.entities.LeashRopeArrow; +import com.r3944realms.leashedplayer.datagen.LanguageAndOtherData.ModItemTags; +import com.r3944realms.leashedplayer.modInterface.PlayerLeashable; +import net.minecraft.core.HolderGetter; +import net.minecraft.core.HolderSet; +import net.minecraft.core.component.DataComponents; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.tags.BlockTags; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.Leashable; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.ShearsItem; +import net.minecraft.world.item.ToolMaterial; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.item.component.Tool; +import net.minecraft.world.item.component.TooltipDisplay; +import net.minecraft.world.item.enchantment.Enchantable; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.function.Consumer; + +public class LeadBreakerItem extends ShearsItem { + private final ToolMaterial toolMaterial; + public static final String HOVER_KEY = "leashedplayer.lead_breaker.item.desc", + MESSAGE_USE_SUF = "leashedplayer.lead_breaker.item.use_suf", + MESSAGE_USE_FAI = "leashedplayer.lead_breaker.item.use_fai"; + public LeadBreakerItem(ToolMaterial toolMaterial, Properties properties) { + super(properties.durability(toolMaterial.durability()) + .component(DataComponents.ENCHANTABLE, new Enchantable(toolMaterial.enchantmentValue())) + .repairable(ModItemTags.AMETHYST_TOOL_MATERIALS) + .enchantable(toolMaterial.enchantmentValue())); + this.toolMaterial = toolMaterial; + } + + public static Tool createToolProperties() { + HolderGetter holdergetter = BuiltInRegistries.acquireBootstrapRegistrationLookup(BuiltInRegistries.BLOCK); + return new Tool( + List.of( + Tool.Rule.minesAndDrops(HolderSet.direct(BuiltInRegistries.BLOCK.wrapAsHolder(Blocks.COBWEB)), 18.0F), + Tool.Rule.overrideSpeed(holdergetter.getOrThrow(BlockTags.LEAVES), 25.0F), + Tool.Rule.overrideSpeed(holdergetter.getOrThrow(BlockTags.WOOL), 8.0F), + Tool.Rule.overrideSpeed(HolderSet.direct(BuiltInRegistries.BLOCK.wrapAsHolder(Blocks.VINE), BuiltInRegistries.BLOCK.wrapAsHolder(Blocks.GLOW_LICHEN)), 4.0F) + ), + 1.0F, + 1, + true + ); + } + @Override + public @NotNull InteractionResult interactLivingEntity(@NotNull ItemStack stack, @NotNull Player player, @NotNull LivingEntity entity, @NotNull InteractionHand hand) { + if(!entity.level().isClientSide) { + if (entity instanceof PlayerLeashable playerLeashable) { + if (playerLeashable.isLeashed()){ + entity.playSound(SoundEvents.SHEEP_SHEAR, 1.0F, 1.0F); + Entity leashHolder = playerLeashable.getLeashHolder(); + Leashable.dropLeash((Entity & Leashable) playerLeashable,true, !(leashHolder instanceof LeashRopeArrow)); + if (leashHolder instanceof LeashRopeArrow leashRopeArrow) { + leashRopeArrow.setOwner(null); + } + if (!player.isCreative()) stack.hurtAndBreak(1, player, LivingEntity.getSlotForHand(hand)); + if (leashHolder != null) { + ((ServerPlayer)player).sendSystemMessage(Component.translatable(MESSAGE_USE_SUF, entity.getDisplayName(), leashHolder.getDisplayName()), true); + } + return InteractionResult.SUCCESS; + } else return InteractionResult.PASS; + } else if (entity instanceof Leashable leashable) { + if (leashable.isLeashed()){ + entity.playSound(SoundEvents.SHEEP_SHEAR, 1.0F, 1.0F); + Entity leashHolder = leashable.getLeashHolder(); + Leashable.dropLeash((Entity & Leashable) leashable, true, !(leashHolder instanceof LeashRopeArrow)); + if (leashable.getLeashHolder() instanceof LeashRopeArrow leashRopeArrow) { + leashRopeArrow.setOwner(null); + } + if (!player.isCreative()) stack.hurtAndBreak(1, player, LivingEntity.getSlotForHand(hand)); + if (leashHolder != null) { + ((ServerPlayer)player).sendSystemMessage(Component.translatable(MESSAGE_USE_SUF, entity.getDisplayName(), leashHolder.getDisplayName()), true); + } + return InteractionResult.SUCCESS; + } else return InteractionResult.PASS; + } + } + return InteractionResult.PASS; + } + + @Override + public @NotNull InteractionResult use(@NotNull Level pLevel, @NotNull Player pPlayer, @NotNull InteractionHand pUsedHand) { + if (!pLevel.isClientSide && pUsedHand == InteractionHand.MAIN_HAND) { + PlayerLeashable playerLeashable = (PlayerLeashable) pPlayer; + if (playerLeashable.isLeashed()) { + Entity leashHolder = playerLeashable.getLeashHolder(); + if(!(leashHolder instanceof LeashRopeArrow)) { + Leashable.dropLeash((Entity & Leashable) playerLeashable,true, true); + pPlayer.getItemInHand(pUsedHand).hurtAndBreak(10, pPlayer, LivingEntity.getSlotForHand(pUsedHand)); + if (leashHolder != null) { + ((ServerPlayer)pPlayer).sendSystemMessage(Component.translatable(MESSAGE_USE_SUF, pPlayer.getDisplayName(), leashHolder.getDisplayName()), true); + } + return InteractionResult.SUCCESS; + } + ((ServerPlayer)pPlayer).sendSystemMessage(Component.translatable(MESSAGE_USE_FAI, pPlayer.getDisplayName(), leashHolder.getDisplayName()), true); + } + } + if(pLevel.isClientSide) { + PlayerLeashable playerLeashable = (PlayerLeashable) pPlayer; + if (playerLeashable.getLeashDataFromEntityData() != null) { + pPlayer.playSound(SoundEvents.SHEEP_SHEAR, 1.0F, 1.0F); + } + } + return super.use(pLevel, pPlayer, pUsedHand); + } + + @Override + public void appendHoverText(@NotNull ItemStack pItemStack, @NotNull TooltipContext pTooltipComponents, @NotNull TooltipDisplay pTooltipDisplay, Consumer pConsumer, @NotNull TooltipFlag pTooltipFlag) { + pConsumer.accept(Component.translatable(HOVER_KEY)); + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/content/items/type/LeashRopeArrowItem.java b/src/main/java/com/r3944realms/leashedplayer/content/items/type/LeashRopeArrowItem.java new file mode 100644 index 0000000..3ed77e6 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/content/items/type/LeashRopeArrowItem.java @@ -0,0 +1,57 @@ +package com.r3944realms.leashedplayer.content.items.type; + +import com.r3944realms.leashedplayer.content.entities.LeashRopeArrow; +import com.r3944realms.leashedplayer.content.entities.ModEntityRegister; +import net.minecraft.ChatFormatting; +import net.minecraft.core.Direction; +import net.minecraft.core.Position; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.projectile.AbstractArrow; +import net.minecraft.world.entity.projectile.Projectile; +import net.minecraft.world.item.ArrowItem; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.item.component.TooltipDisplay; +import net.minecraft.world.level.Level; +import org.jetbrains.annotations.NotNull; + +import javax.annotation.Nullable; +import java.util.List; +import java.util.function.Consumer; + +public class LeashRopeArrowItem extends ArrowItem implements ILeashRopeArrow{ + public static final String descKey = "item.leash_rope_arrow.description", + DESC_1 = "item.leash_rope_arrow.desc.1", + DESC_2 = "item.leash_rope_arrow.desc.2", + DESC_3 = "item.leash_rope_arrow.desc.3", + DESC_4 = "item.leash_rope_arrow.desc.4", + DESC_5 = "item.leash_rope_arrow.desc.5"; + public static final String + DESC_V_1 = "item.variant.leash_rope_arrow.desc.1", + DESC_V_2 = "item.variant.leash_rope_arrow.desc.2"; + public LeashRopeArrowItem(Properties pProperties) { + super(pProperties) ; + } + + public @NotNull AbstractArrow createArrow(@NotNull Level pLevel, @NotNull ItemStack pAmmo, @NotNull LivingEntity pShooter, @Nullable ItemStack pWeapon) { + return new LeashRopeArrow(ModEntityRegister.LEASH_ROPE_ARROW.get(),pShooter, pLevel, pAmmo.copyWithCount(1), pWeapon); + } + + @Override + public @NotNull Projectile asProjectile(@NotNull Level pLevel, @NotNull Position pPos, @NotNull ItemStack pStack, @NotNull Direction pDirection) { + LeashRopeArrow arrow = new LeashRopeArrow(ModEntityRegister.LEASH_ROPE_ARROW.get(), pPos.x(), pPos.y(), pPos.z(), pLevel, pStack,null, null); + arrow.pickup = AbstractArrow.Pickup.DISALLOWED; + return arrow; + } + + + @Override + public void appendHoverText(@NotNull ItemStack pItemStack, @NotNull TooltipContext pTooltipComponents, @NotNull TooltipDisplay pTooltipDisplay, Consumer pConsumer, @NotNull TooltipFlag pTooltipFlag) { + pConsumer.accept(Component.translatable(LeashRopeArrowItem.DESC_1)); + pConsumer.accept(Component.translatable(LeashRopeArrowItem.DESC_2)); + pConsumer.accept(Component.translatable(LeashRopeArrowItem.DESC_3)); + pConsumer.accept(Component.translatable(LeashRopeArrowItem.DESC_4)); + pConsumer.accept(Component.translatable(LeashRopeArrowItem.DESC_5)); + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/content/items/type/SpectralLeashRopeArrowItem.java b/src/main/java/com/r3944realms/leashedplayer/content/items/type/SpectralLeashRopeArrowItem.java new file mode 100644 index 0000000..9d9d831 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/content/items/type/SpectralLeashRopeArrowItem.java @@ -0,0 +1,47 @@ +package com.r3944realms.leashedplayer.content.items.type; + +import com.r3944realms.leashedplayer.content.entities.LeashRopeArrow; +import com.r3944realms.leashedplayer.content.entities.ModEntityRegister; +import com.r3944realms.leashedplayer.content.entities.SpectralLeashRopeArrow; +import net.minecraft.ChatFormatting; +import net.minecraft.core.Direction; +import net.minecraft.core.Position; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.projectile.AbstractArrow; +import net.minecraft.world.entity.projectile.Projectile; +import net.minecraft.world.item.ArrowItem; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.item.component.TooltipDisplay; +import net.minecraft.world.level.Level; +import org.jetbrains.annotations.NotNull; + +import javax.annotation.Nullable; +import java.util.List; +import java.util.function.Consumer; + +public class SpectralLeashRopeArrowItem extends ArrowItem implements ILeashRopeArrow{ + public static final String descKey = "item.spectral_leash_rope_arrow.description", + DESC = "item.spectral_leash_rope_arrow.desc"; + public SpectralLeashRopeArrowItem(Properties pProperties) { + super(pProperties); + } + public @NotNull AbstractArrow createArrow(@NotNull Level pLevel, @NotNull ItemStack pAmmo, @NotNull LivingEntity pShooter, @Nullable ItemStack pWeapon) { + return new SpectralLeashRopeArrow(ModEntityRegister.SPECTRAL_LEASH_ROPE_ARROW.get(), pShooter, pLevel, pAmmo.copyWithCount(1), pWeapon); + } + + @Override + public @NotNull Projectile asProjectile(@NotNull Level pLevel, @NotNull Position pPos, @NotNull ItemStack pStack, @NotNull Direction pDirection) { + LeashRopeArrow arrow = new SpectralLeashRopeArrow(ModEntityRegister.SPECTRAL_LEASH_ROPE_ARROW.get(), pPos.x(), pPos.y(), pPos.z(), pLevel, this.getDefaultInstance(),null, null); + arrow.pickup = AbstractArrow.Pickup.DISALLOWED; + return arrow; + } + + @Override + public void appendHoverText(@NotNull ItemStack pItemStack, @NotNull TooltipContext pTooltipComponents, @NotNull TooltipDisplay pTooltipDisplay, Consumer pConsumer, @NotNull TooltipFlag pTooltipFlag) { + pConsumer.accept(Component.translatable(LeashRopeArrowItem.DESC_V_1)); + pConsumer.accept(Component.translatable(LeashRopeArrowItem.DESC_V_2)); + pConsumer.accept(Component.translatable(DESC)); + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/content/items/type/TestItem.java b/src/main/java/com/r3944realms/leashedplayer/content/items/type/TestItem.java new file mode 100644 index 0000000..748f359 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/content/items/type/TestItem.java @@ -0,0 +1,47 @@ +package com.r3944realms.leashedplayer.content.items.type; + +import com.r3944realms.leashedplayer.client.processBar.IProcessBar; +import com.r3944realms.leashedplayer.client.processBar.TestProcessBar; +import com.r3944realms.leashedplayer.client.renderer.gui.AdaptiveGuiRendererHandler; +import com.r3944realms.leashedplayer.client.renderer.gui.IFadingProcessBarRenderer; +import com.r3944realms.leashedplayer.client.renderer.gui.IProcessBarRenderer; +import com.r3944realms.leashedplayer.client.renderer.gui.TestProcessBarRenderer; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; +import net.minecraft.world.level.Level; +import org.jetbrains.annotations.NotNull; + +public class TestItem extends Item { + public TestProcessBar testProcessBar = null; + public TestItem(Properties pProperties) { + super(pProperties); + } + + @Override + public @NotNull InteractionResult use(@NotNull Level pLevel, @NotNull Player pPlayer, @NotNull InteractionHand pUsedHand) { + if(!pLevel.isClientSide()){ + //some lo + } + else { + if (testProcessBar == null) { + testProcessBar = new TestProcessBar(); + testProcessBar.setRenderStatus(true); + TestProcessBarRenderer testProcessBarRenderer = new TestProcessBarRenderer(testProcessBar); + AdaptiveGuiRendererHandler.addProcessBar(testProcessBar, testProcessBarRenderer); + } else { + if(testProcessBar.getCurrentProcess() >= testProcessBar.getMaxProcess()) { + testProcessBar.increase(); + IProcessBarRenderer processBarRenderer = AdaptiveGuiRendererHandler.getProcessBarRenderer(testProcessBar); + ((IFadingProcessBarRenderer)processBarRenderer).setFadingOut(true); + testProcessBar = null; + } else { + testProcessBar.increase(); + } + } + + } + return super.use(pLevel, pPlayer, pUsedHand); + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/content/items/type/TippedLeashRopeArrowItem.java b/src/main/java/com/r3944realms/leashedplayer/content/items/type/TippedLeashRopeArrowItem.java new file mode 100644 index 0000000..68eb79d --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/content/items/type/TippedLeashRopeArrowItem.java @@ -0,0 +1,49 @@ +package com.r3944realms.leashedplayer.content.items.type; + +import net.minecraft.core.Holder; +import net.minecraft.core.component.DataComponents; +import net.minecraft.network.chat.Component; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.item.alchemy.Potion; +import net.minecraft.world.item.alchemy.PotionContents; +import net.minecraft.world.item.alchemy.Potions; +import net.minecraft.world.item.component.TooltipDisplay; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Optional; +import java.util.function.Consumer; + +public class TippedLeashRopeArrowItem extends LeashRopeArrowItem { + public static final String TIPPED_LEASH_ROPE_ARROW_NAME = "item.tipped_leash_rope_arrow.name"; + public static final String DESC = "item.tipped_leash_rope_arrow.desc"; + public TippedLeashRopeArrowItem(Properties pProperties) { + super(pProperties); + } + @Override + public @NotNull ItemStack getDefaultInstance() { + ItemStack itemstack = super.getDefaultInstance(); + itemstack.set(DataComponents.POTION_CONTENTS, new PotionContents(Potions.POISON)); + return itemstack; + } + + @Override + public void appendHoverText(@NotNull ItemStack pItemStack, @NotNull TooltipContext pTooltipComponents, @NotNull TooltipDisplay pTooltipDisplay, Consumer pConsumer, @NotNull TooltipFlag pTooltipFlag) { + super.appendHoverText(pItemStack, pTooltipComponents, pTooltipDisplay, pConsumer, pTooltipFlag); + } + + @Override + public @NotNull Component getName(@NotNull ItemStack pStack) { + Optional> pPotion = pStack.getOrDefault(DataComponents.POTION_CONTENTS, PotionContents.EMPTY).potion(); + String s = null,s1; + if (pPotion.isPresent()) { + s = pPotion.get().value().name(); + } + boolean flag = s == null; + s1 = pPotion.flatMap(Holder::unwrapKey).map(p_331494_ -> p_331494_.location().getPath()).orElse("empty"); + String potionTranslateKey = "item.minecraft.potion.effect." + (flag ? s1: s); + return Component.translatable(TIPPED_LEASH_ROPE_ARROW_NAME, Component.translatable(potionTranslateKey)); + } + +} diff --git a/src/main/java/com/r3944realms/leashedplayer/content/misc/LeadBreakItemBehavior.java b/src/main/java/com/r3944realms/leashedplayer/content/misc/LeadBreakItemBehavior.java new file mode 100644 index 0000000..a6cf081 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/content/misc/LeadBreakItemBehavior.java @@ -0,0 +1,78 @@ +package com.r3944realms.leashedplayer.content.misc; + +import com.r3944realms.leashedplayer.content.entities.LeashRopeArrow; +import com.r3944realms.leashedplayer.modInterface.PlayerLeashable; +import net.minecraft.core.BlockPos; +import net.minecraft.core.dispenser.BlockSource; +import net.minecraft.core.dispenser.OptionalDispenseItemBehavior; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.sounds.SoundSource; +import net.minecraft.tags.BlockTags; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntitySelector; +import net.minecraft.world.entity.Leashable; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.BeehiveBlock; +import net.minecraft.world.level.block.DispenserBlock; +import net.minecraft.world.level.block.entity.BeehiveBlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.gameevent.GameEvent; +import net.minecraft.world.phys.AABB; +import org.jetbrains.annotations.NotNull; + +public class LeadBreakItemBehavior extends OptionalDispenseItemBehavior { + @Override + protected @NotNull ItemStack execute(@NotNull BlockSource blockSource, @NotNull ItemStack item) { + ServerLevel serverLevel = blockSource.level(); + if(!serverLevel.isClientSide()) { + BlockPos blockPos = blockSource.pos().relative(blockSource.state().getValue(DispenserBlock.FACING)); + this.setSuccess(tryShearBeehive(serverLevel, blockPos) || tryShearLivingEntity(serverLevel, blockPos, item)); + if (this.isSuccess()) { + item.hurtAndBreak(1, serverLevel, null, p_348118_ -> { + }); + } + } + return item; + } + private static boolean tryShearBeehive(ServerLevel level, BlockPos pos) { + BlockState blockstate = level.getBlockState(pos); + if (blockstate.is(BlockTags.BEEHIVES, p_202454_ -> p_202454_.hasProperty(BeehiveBlock.HONEY_LEVEL) && p_202454_.getBlock() instanceof BeehiveBlock)) { + int i = blockstate.getValue(BeehiveBlock.HONEY_LEVEL); + if (i >= 5) { + level.playSound(null, pos, SoundEvents.BEEHIVE_SHEAR, SoundSource.BLOCKS, 1.0F, 1.0F); + BeehiveBlock.dropHoneycomb(level, pos); + ((BeehiveBlock)blockstate.getBlock()) + .releaseBeesAndResetHoneyLevel(level, blockstate, pos, null, BeehiveBlockEntity.BeeReleaseStatus.BEE_RELEASED); + level.gameEvent(null, GameEvent.SHEAR, pos); + return true; + } + } + + return false; + } + + private static boolean tryShearLivingEntity(ServerLevel level, BlockPos pos, ItemStack item) { + for (LivingEntity livingentity : level.getEntitiesOfClass(LivingEntity.class, new AABB(pos), EntitySelector.NO_SPECTATORS)) { + if (livingentity instanceof PlayerLeashable playerLeashable) { + if (playerLeashable.isLeashed()){ + Leashable.dropLeash((Entity & Leashable)playerLeashable, true, !(playerLeashable.getLeashHolder() instanceof LeashRopeArrow)); + if (playerLeashable.getLeashHolder() instanceof LeashRopeArrow leashRopeArrow) { + leashRopeArrow.setOwner(null); + } + return true; + } else return false; + } else if (livingentity instanceof Leashable leashable) { + if (leashable.isLeashed()) { + Leashable.dropLeash((Entity & Leashable) leashable,true, !(leashable.getLeashHolder() instanceof LeashRopeArrow)); + if (leashable.getLeashHolder() instanceof LeashRopeArrow leashRopeArrow) { + leashRopeArrow.setOwner(null); + } + return true; + } else return false; + } + } + return false; + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/content/paintings/ModPaintingsRegister.java b/src/main/java/com/r3944realms/leashedplayer/content/paintings/ModPaintingsRegister.java new file mode 100644 index 0000000..016831c --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/content/paintings/ModPaintingsRegister.java @@ -0,0 +1,28 @@ +package com.r3944realms.leashedplayer.content.paintings; + +import com.r3944realms.leashedplayer.LeashedPlayer; +import com.r3944realms.leashedplayer.datagen.provider.attributes.ModPaintingVariants; +import net.minecraft.core.registries.Registries; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.decoration.PaintingVariant; +import net.neoforged.bus.api.IEventBus; +import net.neoforged.neoforge.registries.DeferredRegister; +import org.jetbrains.annotations.NotNull; + +import java.util.Optional; +import java.util.function.Supplier; + +public class ModPaintingsRegister { + public static final DeferredRegister PAINTING_VARIANT = + DeferredRegister.create(Registries.PAINTING_VARIANT, LeashedPlayer.MOD_ID); + public static final Supplier GROUP_PHOTO = PAINTING_VARIANT.register("group_photo", () -> new PaintingVariant(1920, 1080, getAssetId("group_photo"), Optional.of(Component.translatable(ModPaintingVariants.getPaintingVariantTitleKey(ModPaintingVariants.GROUP_PHOTO))), Optional.of(Component.translatable(ModPaintingVariants.getPaintingVariantAuthorKey(ModPaintingVariants.GROUP_PHOTO))))); + + private static @NotNull ResourceLocation getAssetId(String paint_name) { + return ResourceLocation.fromNamespaceAndPath(LeashedPlayer.MOD_ID, "textures/painting/"+paint_name+".png"); + } + + public static void register(IEventBus eventBus) { + PAINTING_VARIANT.register(eventBus); + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/content/sounds/ModSoundRegister.java b/src/main/java/com/r3944realms/leashedplayer/content/sounds/ModSoundRegister.java new file mode 100644 index 0000000..d9e1359 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/content/sounds/ModSoundRegister.java @@ -0,0 +1,35 @@ +package com.r3944realms.leashedplayer.content.sounds; + +import com.r3944realms.leashedplayer.LeashedPlayer; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.sounds.SoundEvent; +import net.neoforged.bus.api.IEventBus; +import net.neoforged.neoforge.registries.DeferredHolder; +import net.neoforged.neoforge.registries.DeferredRegister; + +import java.util.function.Supplier; + +public class ModSoundRegister { + public static DeferredRegister SOUNDS = DeferredRegister.create(BuiltInRegistries.SOUND_EVENT, LeashedPlayer.MOD_ID); + public static ResourceLocation RL_FOX_MUSIC = ResourceLocation.fromNamespaceAndPath(LeashedPlayer.MOD_ID, "music/what_does_the_fox_say"); + public static final DeferredHolder FOX_MUSIC = + ModSoundRegister.register("what_does_the_fox_say", () -> SoundEvent.createFixedRangeEvent( + RL_FOX_MUSIC, + 128 + )); + + public static DeferredHolder register(String name, Supplier supplier){ + return SOUNDS.register(name, supplier); + + } + public static void register(IEventBus modBus){ + SOUNDS.register(modBus); + } + public static String getJukeboxSongTranslateKey(String name) { + return "jukebox_song." + LeashedPlayer.MOD_ID + "." + name; + } + public static String getSubTitleTranslateKey(String name) { + return "sound." + LeashedPlayer.MOD_ID + ".subtitle." + name; + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/datagen/LanguageAndOtherData/ModAdvancementKey.java b/src/main/java/com/r3944realms/leashedplayer/datagen/LanguageAndOtherData/ModAdvancementKey.java new file mode 100644 index 0000000..ecc983a --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/datagen/LanguageAndOtherData/ModAdvancementKey.java @@ -0,0 +1,43 @@ +package com.r3944realms.leashedplayer.datagen.LanguageAndOtherData; + + + +import com.r3944realms.leashedplayer.LeashedPlayer; + +import javax.annotation.Nullable; + +public enum ModAdvancementKey { + LEASH_START("leash_start", null), + LEASHED_FRIEND("leashed_friend", LEASH_START), + LEASHED_SELF("leashed_self", LEASH_START), + LEASH_ARROW("leash_arrow", LEASH_START), + ADVANCEMENT_LEASH_ARROW("advancement_leash_arrow", LEASH_ARROW), + FOLLOW_LEASH_ARROW("follow_arrow", LEASH_ARROW), + DOG_RUNNING_PLAYER("dog_running_player", LEASH_ARROW), + NO_LEASH("no_leash", LEASH_START), + LEASH_TERMINATOR("leash_terminator", LEASH_START), + TIPPED_LEASH_ARROW("tipped_leash_arrow", LEASH_ARROW), + NEO_FOX("neo_fox", LEASH_START), + ; + private final String Name; + @Nullable + private final ModAdvancementKey Parent; + ModAdvancementKey(String name, @Nullable ModAdvancementKey parent) { + this.Name = name; + this.Parent = parent; + } + + public @Nullable ModAdvancementKey getParent() { + return Parent; + } + public String getNameKey() { + return "advancement." + LeashedPlayer.MOD_ID + "." + Name; + } + + public String getDescKey() { + return this.getNameKey() + ".desc"; + } + public String getNameWithNameSpace() { + return LeashedPlayer.MOD_ID + ":" + this.Name; + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/datagen/LanguageAndOtherData/ModItemTags.java b/src/main/java/com/r3944realms/leashedplayer/datagen/LanguageAndOtherData/ModItemTags.java new file mode 100644 index 0000000..1371e68 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/datagen/LanguageAndOtherData/ModItemTags.java @@ -0,0 +1,21 @@ +package com.r3944realms.leashedplayer.datagen.LanguageAndOtherData; + +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.TagKey; +import net.minecraft.world.item.Item; + +public final class ModItemTags { + public static final TagKey AMETHYST_TOOL_MATERIALS = bind("amethyst_tool_materials"); + + private ModItemTags() { + } + + private static TagKey bind(String name) { + return TagKey.create(Registries.ITEM, ResourceLocation.withDefaultNamespace(name)); + } + + public static TagKey create(final ResourceLocation name) { + return TagKey.create(Registries.ITEM, name); + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/datagen/LanguageAndOtherData/ModLangKeyValue.java b/src/main/java/com/r3944realms/leashedplayer/datagen/LanguageAndOtherData/ModLangKeyValue.java new file mode 100644 index 0000000..7637e49 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/datagen/LanguageAndOtherData/ModLangKeyValue.java @@ -0,0 +1,235 @@ +package com.r3944realms.leashedplayer.datagen.LanguageAndOtherData; + +import com.r3944realms.leashedplayer.content.ModKeyMapping; +import com.r3944realms.leashedplayer.content.commands.LeashCommand; +import com.r3944realms.leashedplayer.content.commands.MotionCommand; +import com.r3944realms.leashedplayer.content.effects.ModEffectRegister; +import com.r3944realms.leashedplayer.content.effects.ModPotionRegister; +import com.r3944realms.leashedplayer.content.entities.LeashRopeArrow; +import com.r3944realms.leashedplayer.content.entities.ModEntityRegister; +import com.r3944realms.leashedplayer.content.gamerules.Server.CreateLeashFenceKnotEntityIfAbsent; +import com.r3944realms.leashedplayer.content.gamerules.Server.KeepLeashNotDropTime; +import com.r3944realms.leashedplayer.content.gamerules.Server.TeleportWithLeashedPlayers; +import com.r3944realms.leashedplayer.content.items.ModCreativeTab; +import com.r3944realms.leashedplayer.content.items.ModItemRegister; +import com.r3944realms.leashedplayer.content.items.type.LeadBreakerItem; +import com.r3944realms.leashedplayer.content.items.type.LeashRopeArrowItem; +import com.r3944realms.leashedplayer.content.items.type.SpectralLeashRopeArrowItem; +import com.r3944realms.leashedplayer.content.items.type.TippedLeashRopeArrowItem; +import com.r3944realms.leashedplayer.content.sounds.ModSoundRegister; +import com.r3944realms.leashedplayer.datagen.provider.attributes.ModPaintingVariants; +import com.r3944realms.leashedplayer.network.server.Code; +import com.r3944realms.leashedplayer.network.server.DecreaseLeashRopeLength; +import com.r3944realms.leashedplayer.network.server.IncreaseLeashRopeLength; +import com.r3944realms.leashedplayer.utils.Enum.LanguageEnum; +import com.r3944realms.leashedplayer.utils.Enum.ModPartEnum; +import net.minecraft.world.item.Item; +import net.minecraft.world.level.block.Block; +import org.jetbrains.annotations.NotNull; + +import javax.annotation.Nullable; +import java.util.function.Supplier; + +import static com.r3944realms.leashedplayer.content.items.ModCreativeTab.LEASHED_PLAYER_ITEM; + +public enum ModLangKeyValue { + KEY_CATEGORY(ModKeyMapping.CATEGORY, ModPartEnum.NAME, "Leashed Player", "可拴玩家", "可拴玩家", false), + KEY_ADD_LEASH_OTHER_LEASH_LENGTH(ModKeyMapping.ADD_LEASH_LENGTH_KEY, ModPartEnum.NAME, "Increase the Length of Leash Rope", "增加拴绳长度", "增加拴繩長度", false), + KEY_SUB_LEASH_OTHER_LEASH_LENGTH(ModKeyMapping.SUB_LEASH_LENGTH_KEY, ModPartEnum.NAME, "Decrease the Length of Leash Rope", "减小拴绳长度", "減小拴繩長度", false), + //ITEM + ITEM_LEASH_ROPE_ARROW(ModItemRegister.LEASH_ROPE_ARROW, ModPartEnum.ITEM, "Leash Rope Arrow", "拴绳箭", "拴繩箭", true), + ITEM_SPECTRAL_LEASH_ROPE_ARROW(ModItemRegister.SPECTRAL_LEASH_ROPE_ARROW, ModPartEnum.ITEM, "Spectral Leash Rope Arrow", "拴绳光灵箭", "拴繩光靈箭", true), + TEST_FABRIC_ITEM(ModItemRegister.FABRIC, ModPartEnum.ITEM, "Fabric", "Fabric", "Fabric", true), + AMETHYST_SHEARS(ModItemRegister.AMETHYST_SHEARS, ModPartEnum.ITEM, "Amethyst Shears", "紫水晶剪刀", "紫水晶剪刀", true), + NEO_FORGE(ModItemRegister.NEOFORGE, ModPartEnum.ITEM, "NeoForge", "NeoForge", "NeoForge", false), + TIPPED_LEASH_ROPE_ARROW(TippedLeashRopeArrowItem.TIPPED_LEASH_ROPE_ARROW_NAME, ModPartEnum.ITEM, "Tipped Leash Rope Arrow Soaked By %1$s", "用%1$s浸泡过的拴绳箭", "蘸有%1$s的拴繩箭", false), + //ITEM_DESC + DESC_ITEM_LEASH_R_ARROW_ONE(LeashRopeArrowItem.DESC_1, ModPartEnum.DESCRIPTION, "§7This arrow will carry the owner along with its flight:", "§7该箭将会携带拥有者随其飞行", "§7該箭將會攜帶擁有者隨其飛行:", false), + DESC_ITEM_LEASH_R_ARROW_TWO(LeashRopeArrowItem.DESC_2, ModPartEnum.DESCRIPTION, "§c1.§r If it hits a fence or an entity, it will leash the owner to it and drop as a normal arrow.", "§c1.§r 若击中栅栏或生物时,将持有者拴在其上并已普通箭形式掉落;", "§c1.§r 若擊中柵欄或生物時,將持有者拴在其上並以普通箭的形式掉落;", false), + DESC_ITEM_LEASH_R_ARROW_THREE(LeashRopeArrowItem.DESC_3, ModPartEnum.DESCRIPTION,"§c2.§r Crouching near the arrow allows for faster retrieval. If the arrow's owner is not the player, the owner will be leashed to the player who picks it up.", "§c2.§r 靠近该箭下蹲可以更快拾取该箭,如果该箭持有者不是自己,则持有者将被拾取者拴住;", "§c2.§r 靠近該箭下蹲可以更快拾取該箭,如果該箭的持有者不是自己,則持有者將被拾取者拴住;", false), + DESC_ITEM_LEASH_R_ARROW_FOUR(LeashRopeArrowItem.DESC_4, ModPartEnum.DESCRIPTION, "§c3.§r When fired from its launcher, the first entity hit will become the arrow's owner and will fly along with it.", "§c3.§r 当前其发射器里发射,第一个射中的生物将成为此箭的持有者并随箭飞行;", "§c3.§r 當箭從發射器發射時,第一個射中的生物將成為此箭的持有者並隨箭飛行;", false), + DESC_ITEM_LEASH_R_ARROW_FIVE(LeashRopeArrowItem.DESC_5, ModPartEnum.DESCRIPTION, "§c4.§r Under the §c§l\"no_leash\"§r effect, the behavior of the arrow when fired will follow the launcher’s behavior.", "§c4.§r 在§c§l禁拴§7(§c§lno_leash§7)§r效果下,射出的箭行为同发射器。","§c4.§r 在§c§l禁拴§7(§c§lno_leash§7)§r效果下,射出的箭行為將與發射器的行為相同。", false), + DESC_ITEM_LEASH_ROPE_ARROW(LeashRopeArrowItem.descKey, ModPartEnum.DESCRIPTION, "Arrows with ropes attached?","带有拴绳的箭矢?", "帶有拴繩的箭矢?", false), + DESC_ITEM_V_LEASH_R_ARROW_ONE(LeashRopeArrowItem.DESC_V_1, ModPartEnum.DESCRIPTION, "§7A variant of Leash Rope Arrow", "§7拴绳箭的一个变种", "§7拴繩箭矢的一個變種", false), + DESC_ITEM_V_LEASH_R_ARROW_TWO(LeashRopeArrowItem.DESC_V_2, ModPartEnum.DESCRIPTION, "§c1.§r The function is the same as its original one。", "§c1.§r 功能同其本体;", "§c1.§r 功能與本體一致;", false), + DESC_ITEM_S_LEASH_R_ARROW_THREE(SpectralLeashRopeArrowItem.DESC, ModPartEnum.DESCRIPTION, "§c2.§r Strike the entity to give it a §e§lGlowing§r effect.", "§c2.§r 击中实体给与其§e§l发光§7(§e§lGlowing§7)§r效果", "擊中實體給予其§e§l發光§7(§e§lGlowing§7)§r效果", false), + DESC_ITEM_T_LEASH_R_ARROW_THREE(TippedLeashRopeArrowItem.DESC, ModPartEnum.DESCRIPTION, "§c2.§rStrike the entity to give it a Potion effect.", "§c2.§r 击中实体给与其药水效果", "擊中實體給予其药水效果", false), + DESC_ITEM_LEAD_BREAKER(LeadBreakerItem.HOVER_KEY, ModPartEnum.DESCRIPTION, "§7can break the link of leash", "§7可以破坏拴绳链接", "§7可以破壞拴繩鏈接", false), + + //PAINTING + GROUP_PHOTO_TITLE(ModPaintingVariants.getPaintingVariantTitleKey(ModPaintingVariants.GROUP_PHOTO),ModPartEnum.TITLE, "§dGroup Photo §7[§6memorable§7]§r", "§d集体照 §7[§6纪念§7]§r", "§d集體照 §7[§6紀念§7]§r", false), + GROUP_PHOTO_AUTHOR(ModPaintingVariants.getPaintingVariantAuthorKey(ModPaintingVariants.GROUP_PHOTO),ModPartEnum.AUTHOR, "§9Leisure §4Time §eDock§r","§9闲趣§4时§e坞§r","§9閑趣§4時§e塢§r",false), + //ENTITY + LEASH_ROPE_ARROW(ModEntityRegister.getEntityNameKey("leash_rope_arrow"), ModPartEnum.ENTITY, "Leash Rope Arrow", "拴绳箭", "拴繩箭", false), + SPECTRAL_LEASH_ROPE_ARROW(ModEntityRegister.getEntityNameKey("spectral_leash_rope_arrow"), ModPartEnum.ENTITY, "Spectral Leash Rope Arrow", "拴绳光灵箭", "拴繩光靈箭", false), + kID(ModEntityRegister.getEntityNameKey("kid_player"), ModPartEnum.ENTITY, "Kid", "小孩", "小孩", "幼", false), + NESTLE_ROPE_ARROW(ModEntityRegister.getEntityNameKey("nestle_rope_arrow"), ModPartEnum.ENTITY, "Nestle Rope Arrow", "贴贴拴绳箭", "貼貼拴繩箭", false), + //DIST + ST_WHAT_DOES_THE_FOX_SAY(ModSoundRegister.getSubTitleTranslateKey("what_does_the_fox_say"), ModPartEnum.TITLE, "Great Chu will rise again! Chen She will be king!", "大楚兴~ 陈胜王~~", "大楚興~ 陳勝王~~", false), + JB_WHAT_DOES_THE_FOX_SAY(ModSoundRegister.getJukeboxSongTranslateKey("what_does_the_fox_say"), ModPartEnum.DESCRIPTION, "What does the fox say?", "狐狸是怎么叫的?", "狐狸是怎麽叫的?", false), + //CREATIVE_TAB + CREATIVE_TAB_NAME(ModCreativeTab.getCreativeMod(LEASHED_PLAYER_ITEM), ModPartEnum.CREATIVE_TAB, "Leashed Player","可拴玩家", "可拴玩家", false), + //COMMAND_MESSAGE + MESSAGE_LEASH_LENGTH_FAILED(LeashCommand.LEASH_FAILED, ModPartEnum.COMMAND, "Failed (Internal Error, maybe your command is incorrect)", "失败(内部错误,可能是你输的指令有误)", "失敗(内部錯誤,,可能你輸入的指令有誤)", false), + MESSAGE_LEASH_LENGTH_SHOW(LeashCommand.LEASH_LENGTH_SHOW, ModPartEnum.COMMAND, "The Leash Length of %1$s is %2$s blocks", "%1$s的拴绳长度为%2$s格", "%1$s的拴繩長度為%2$s格" , false), + MESSAGE_LEASH_LENGTH_SET(LeashCommand.LEASH_LENGTH_SET, ModPartEnum.COMMAND, "The Leash length of %1$s is set to %2$s blocks", "%1$s的拴绳长度被设置为%2$s格", "%1$s的拴繩長度被設置為%2$s格" , false), + MESSAGE_LEASH_GET_LEASH_DATA(LeashCommand.LEASH_DATA_SHOW, ModPartEnum.COMMAND, "%1$s's LeashDataEntity is %2$s", "%1$s的拴绳数据实体为%2$s", "%1$s拴繩數據實體為%2$s",false), + MESSAGE_LEASH_NO_LEASH_DATA(LeashCommand.NO_LEASH_DATA, ModPartEnum.COMMAND, "%1$s has no LeashDataEntity", "%1$s沒有拴绳数据实体", "%1$s沒有拴繩數據實體", false), + MESSAGE_LEASH_SET_LEASH_DATA(LeashCommand.LEASH_DATA_SET, ModPartEnum.COMMAND, "%1$s LeashDataEntity now is set as %2$s", "%1$s拴绳数据实体被设置为%2$s", "%1$s拴繩數據實體設置為%2$s", false), + MESSAGE_LEASH_SET_FAILED_DIFF_LEVEL(LeashCommand.LEASH_DATA_SET_FAILED_DIFF_LEVEL, ModPartEnum.COMMAND,"%1$s and %2$s are not at a same level", "%1$s和%2$s不在同一维度上", "%1$s和%2$s不在同一緯度上", false), + MESSAGE_LEASH_SET_FAILED_TOO_FAR(LeashCommand.LEASH_DATA_SET_FAILED_TOO_FAR, ModPartEnum.COMMAND,"The distance between %1$s and %2$s is larger than the 1.2 times of LeashLength, LeashLength is %3$s blocks", "%1$s到%2$s的距离超过了1.2倍 拴绳长度,原长:%3$s 格", "%1$s到%2$s的距離超過了1.2倍拴繩長度,原長:%3$s 格", false), + MESSAGE_LEASH_SET_FAILED_NO_KNOT_EXIST_IN_THAT_POS(LeashCommand.LEASH_DATA_SET_FAILED_NO_KNOT_EXISTED_IN_THAT_POS, ModPartEnum.COMMAND, "No knot found at (X:%f,Y:%f,Z:%f)", "未找到拴绳结在(X:%f, Y:%f, Z:%f)处", "未找到拴繩結在(X:%f, Y:%f, Z:%f)処", false), + MESSAGE_LEASH_CLEAR(LeashCommand.LEASH_DATA_CLEAR, ModPartEnum.COMMAND, "%1$s's LeashData(LeashHolderEntity: %2$s) now is clear", "%1$s的拴绳数据(拴绳持有者实体:%2$s)现在已清除", "%1$s的拴繩數據(拴繩持有者實體:%2$s)現在已清除", false), + MESSAGE_LEASH_SET_FAILED_FORBID_SAME_ENTITY(LeashCommand.LEASH_DATA_SET_FAILED_FORBID_SAME_ENTITY, ModPartEnum.COMMAND, "Prohibit setting the same entity","禁止设置同一实体", "禁止設置同一實體", false), + MESSAGE_LEASH_CLEAR_FAILED(LeashCommand.LEASH_DATA_CLEAR_FAILED_NO_DATA, ModPartEnum.COMMAND, "%1$s has no LeashData can be clear", "%1$s沒有拴绳数据可清除", "%1$s沒有拴繩數據實體可被清除", false), + MESSAGE_MOTION_ADDER_SUCCESSFUL(MotionCommand.MOTION_ADDER_SUCCESSFUL, ModPartEnum.COMMAND, "§bAdd Successfully.§a%s§7:§f[§eVec§7: §a(§f%f§7,§f%f§7,§f%f§a)§f]§r","§b添加成功.§a%s§7:§f[§e加速度§7:§a(§f%f§7,§f%f§7,§f%f§a)§f]§r","§b添加成功.§a%s§7:§f[§e加速度§7:§a(§f%f§7,§f%f§7,§f%f§a)§f]§r", false), + MESSAGE_MOTION_SETTER_SUCCESSFUL(MotionCommand.MOTION_SETTER_SUCCESSFUL, ModPartEnum.COMMAND, "§bSet Successfully.§a%s§7:§f[§eVec§7: §a(§f%f§7,§f%f§7,§f%f§a)§f]§r","§b设置成功.§a%s§7:§f[§e加速度§7:§a(§f%f§7,§f%f§7,§f%f§a)§f]§r","§b設置成功.§a%s§7:§f[§e加速度§7:§a(§f%f§7,§f%f§7,§f%f§a)§f]§r",false), + MESSAGE_MOTION_MULTIPLY_SUCCESSFUL(MotionCommand.MOTION_MULTIPLY_SUCCESSFUL, ModPartEnum.COMMAND, "§bMultiply Successfully.§a%s§7:§f[§eVec§7: §a(§f%f§7,§f%f§7,§f%f§a)§f]§r","§b倍乘成功.§a%s§7:§f[§e加速度§7:§a(§f%f§7,§f%f§7,§f%f§a)§f]§r","§b倍乘成功.§a%s§7:§f[§e加速度§7:§a(§f%f§7,§f%f§7,§f%f§a)§f]§r",false), + MESSAGE_LEASH_BREAKER_USE_SUF(LeadBreakerItem.MESSAGE_USE_SUF, ModPartEnum.MESSAGE, "§aSuccessfully break §f%1$s§a 's Leashed Link to §f%2$s ", "§a成功剪断§f%2$s§a对§f%1$s§a拴绳链接", "§a成功剪斷§f%2$s§a對§f%1$s§a拴繩鏈接", false), + MESSAGE_LEASH_BREAKER_USE_FAI(LeadBreakerItem.MESSAGE_USE_FAI, ModPartEnum.MESSAGE, "§cFailed to break §f%1$s§c 's Leashed Link to §f%2$s", "§c无法剪断§f%2$s§c对§f%1$s§c拴绳链接", "§c未能剪斷§f%2$s§c對§f%1$s§c拴繩鏈接", false), + MESSAGE_LEASH_NOT_SUPPORT_TO_NOT_PLAYER_ENTITY(ModKeyMapping.NOT_SUPPORT_TO_NOT_PLAYER_ENTITY, ModPartEnum.MESSAGE, "Only work on Players", "只在玩家身上有效", "僅對玩家有效", false), + MESSAGE_LEASH_ADD_LEASH_LENGTH(IncreaseLeashRopeLength.INCREASE_LEASH_ROPE_LENGTH, ModPartEnum.MESSAGE, "§aIncrease the §f%s §aLength of Leash Rope§7(§bLength§7:§e%d§7)", "§a增加§f%s的拴绳长度§a§7(§b长度§7:§e%d§7)", "§a增加§f%s§a的拴繩長度§7(§長度§7:§e%d§7)", false), + MESSAGE_LEASH_SUB_LEASH_LENGTH(DecreaseLeashRopeLength.DECREASE_LEASH_ROPE_LENGTH, ModPartEnum.MESSAGE, "§Decrease the §f%s §cLength of Leash Rope§7(§bLength§7:§e%d§7)", "§c减少§f%s的拴绳长度§c§7(§b长度§7:§e%d§7)", "§c減少§f%s§c的拴繩長度§7(§長度§7:§e%d§7)", false), + MESSAGE_LEASH_ADD_SELF_LEASH_LENGTH(IncreaseLeashRopeLength.INCREASE_SELF_LEASH_ROPE_LENGTH, ModPartEnum.MESSAGE, "§aIncrease the Length of Leash Rope§7(§bLength§7:§e%d§7)", "§a增加拴绳长度§a§7(§b长度§7:§e%d§7)", "§a增加拴繩長度§7(§長度§7:§e%d§7)", false), + MESSAGE_LEASH_SUB_SELF_LEASH_LENGTH(DecreaseLeashRopeLength.DECREASE_SELF_LEASH_ROPE_LENGTH, ModPartEnum.MESSAGE, "§cDecrease the Length of Leash Rope§7(§bLength§7:§e%d§7)", "§c减少拴绳长度§c§7(§b长度§7:§e%d§7)", "§c減少拴繩長度§7(§長度§7:§e%d§7)", false), + MESSAGE_LEASH_FAILED_SET_LENGTH(Code.LEASH_LENGTH_FAILED_SET, ModPartEnum.MESSAGE, "§cFailed", "§c失败", "§c失敗", false), + //GAME_RULE_NAME + TELEPORT_WITH_LEASHED_PLAYERS_NAME(TeleportWithLeashedPlayers.NAME_KEY, ModPartEnum.NAME, "Teleport leashed player with player holder", "被拴玩家随玩家持有者传送", "被拴玩家随玩家持有者傳送" ,false), + CREATE_LEASH_FENCE_KNOT_ENTITY_IF_ABSENT_NAME(CreateLeashFenceKnotEntityIfAbsent.NAME_KEY, ModPartEnum.NAME, "Create Leash Fence Knot Entity if absent", "如果缺失则创建拴绳结", "如果缺失則創建拴繩結", false), + KEEP_LEASH_NOT_DROP_TIME_NAME(KeepLeashNotDropTime.NAME_KEY, ModPartEnum.NAME, "Keep leash alive Time", "保持拴绳不掉落的时间", "保持其不掉落的時間", false), + + TRY_TO_PICKUP_LEASHED_ROPE_ARROW(LeashRopeArrow.PUSH_SHIFT_TO_PICKUP_QUICKLY, ModPartEnum.MESSAGE, "§aPush §f§lShift§a to pick up quickly", "§a按下§f§lShift键§a以加快拾取", "§a按下§f§lShift鍵§a以加速拾取", false), + //GAME_RULE_DESCRIPTION + TELEPORT_WITH_LEASHED_DESCRIPTION(TeleportWithLeashedPlayers.DESCRIPTION_KEY, ModPartEnum.DESCRIPTION, "Holder will teleport with their leashed players ", "传送时将被拴玩家与持有者一起传送", "將被拴玩家將隨持有者一起傳送" ,false), + CREATE_LEASH_FENCE_KNOT_ENTITY_IF_ABSENT_DESCRIPTION(CreateLeashFenceKnotEntityIfAbsent.DESCRIPTION_KEY, ModPartEnum.DESCRIPTION, "Create LeashKnot Entity if it's absent on fence", "如果在栅栏处缺失拴绳结,则创建它", "如果在柵欄処缺失拴繩結,則創建它", false), + KEEP_LEASH_NOT_DROP_TIME_DESCRIPTION(KeepLeashNotDropTime.DESCRIPTION_KEY, ModPartEnum.DESCRIPTION,"The time of Keep new leash which has far distance alive (Tick)", "当距离过远时,保持新建拴绳不掉落的时间 (刻)", "儅距離過遠時,保持其不掉落的時間(刻)", false), + + + //ADV_NAME + LEASH_START(ModAdvancementKey.LEASH_START.getNameKey(), ModPartEnum.NAME, "The Power of Traction", "牵引之力", "牽引之力", false), + LEASH_LR_ARROW(ModAdvancementKey.LEASH_ARROW.getNameKey(), ModPartEnum.NAME, "Arrow with a Tether?" , "拴绳之箭?", "拴繩之箭?", false), + LEASH_SLP_ARROW(ModAdvancementKey.ADVANCEMENT_LEASH_ARROW.getNameKey(), ModPartEnum.NAME, "More advanced flash arrow with a Tether?", "更闪亮的拴绳箭?", "更閃亮的拴繩箭?", false), + LEASH_SELF(ModAdvancementKey.LEASHED_SELF.getNameKey(), ModPartEnum.NAME, "Stable Connection", "稳固联结" ,"穩固聯結", false), + LEASH_PLAYER(ModAdvancementKey.LEASHED_FRIEND.getNameKey(),ModPartEnum.NAME, "Be bound by Rope", "拴绳链接", "拴繩鏈接" , false), + FOLLOW_ARROW(ModAdvancementKey.FOLLOW_LEASH_ARROW.getNameKey(), ModPartEnum.NAME, "Launch!!!", "启航!!!" , "啓航!!!",false), + FOLLOW_WOLF(ModAdvancementKey.DOG_RUNNING_PLAYER.getNameKey(), ModPartEnum.NAME, "It's Walking human time.", "遛“人”时间", "遛“人”時間",false), + TERMINATOR(ModAdvancementKey.LEASH_TERMINATOR.getNameKey(), ModPartEnum.NAME, "The Lead Terminator", "拴绳终结者","拴繩終結者", false), + NO_LEASH(ModAdvancementKey.NO_LEASH.getNameKey(), ModPartEnum.NAME, "Don't tie me up", "勿拴我", "請恁勿拴唔", false), + TIPPED_LEASH_ARROW(ModAdvancementKey.TIPPED_LEASH_ARROW.getNameKey(), ModPartEnum.NAME, "God said there should be more arrows", "神说要有更多箭矢", "神說要有更多箭矢", false), + NEO_FOX(ModAdvancementKey.NEO_FOX.getNameKey(), ModPartEnum.NAME, "NEOFORGE!", "NEOFORGE!", "NEOFORGE!", false), + //ADV_DESC + LEASH_START_DESC(ModAdvancementKey.LEASH_START.getDescKey(), ModPartEnum.DESCRIPTION, "Journey to becoming a Leash Expert", "拴绳大师之路", "拴繩大師之路", false), + LEASH_LR_ARROW_DESC(ModAdvancementKey.LEASH_ARROW.getDescKey(), ModPartEnum.DESCRIPTION, "Maybe you can using it to shoot some mob?", "也许可以用它来发射生物?", "也許可以用它發射生物?", false), + LEASH_SLP_ARROW_DESC(ModAdvancementKey.ADVANCEMENT_LEASH_ARROW.getDescKey(), ModPartEnum.DESCRIPTION, "Well, apart from glowing, there doesn't seem to be any other difference", "嗯,除了发光,似乎没有什么其它不同了" ,"嗯,除了發光,其它好像沒什麽不同", false), + LEASH_SELF_DESC(ModAdvancementKey.LEASHED_SELF.getDescKey(), ModPartEnum.DESCRIPTION, "“Restrain oneself with a rope", "用拴绳拴住自己" ,"用栓繩拴住自己", false), + LEASH_PLAYER_DESC(ModAdvancementKey.LEASHED_FRIEND.getDescKey(),ModPartEnum.DESCRIPTION, "Be Bond by player with lead", "被玩家用拴绳链接", "被玩家用拴繩鏈接", false), + FOLLOW_ARROW_DESC(ModAdvancementKey.FOLLOW_LEASH_ARROW.getDescKey(), ModPartEnum.DESCRIPTION, "Mc, what are you talking about in physics?", "抱歉,我的世界不存在物理学" , "抱歉,麦块不講物理學",false), + FOLLOW_WOLF_DESC(ModAdvancementKey.DOG_RUNNING_PLAYER.getDescKey(), ModPartEnum.DESCRIPTION, "In the park where dogs are not allowed to be walked, the dog decided to walk the human instead", "公园不能遛狗,于是狗站起来遛人", "公園裏不許遛狗,於是狗站起來遛人",false), + NO_LEASH_DESC(ModAdvancementKey.NO_LEASH.getDescKey(), ModPartEnum.DESCRIPTION, "You cannot be leashed by ANY", "你不会被任何东西拴住", "恁不會被任何拴住", false), + TERMINATOR_DESC(ModAdvancementKey.LEASH_TERMINATOR.getDescKey(), ModPartEnum.DESCRIPTION, "I am Lead Terminator!", "我來终结拴绳者!", "吾將終結拴繩!", false), + TIPPED_LEASH_ARROW_DESC(ModAdvancementKey.TIPPED_LEASH_ARROW.getDescKey(), ModPartEnum.DESCRIPTION, "A dazzling array of Leash Rope arrows", "真是琳琅满目啊", "真是琳琅滿目啊", false), + NEO_FOX_DESC(ModAdvancementKey.NEO_FOX.getDescKey(), ModPartEnum.DESCRIPTION, "It seems can be equipped.", "似乎可以戴头上", "似乎可以戴著", false), + + //MOB_EFFECT + NO_LEASH_EFFECT(ModEffectRegister.getEffectKey(ModEffectRegister.NO_LEASH_EFFECT.get()), ModPartEnum.NAME, "No Leash", "禁拴", "禁拴", false), + //POTION + NO_LEASH_POTION(ModPotionRegister.getPotionNameKey("no_leash", (char) 0), ModPartEnum.NAME, "No Leash Potion", "禁拴药水", "禁拴藥水", false), + NO_LEASH_POTION_SPLASH(ModPotionRegister.getPotionNameKey("no_leash", (char) 2), ModPartEnum.NAME, "Splash No Leash Potion", "喷溅型禁拴药水", "噴濺型禁拴藥水", false), + NO_LEASH_POTION_LINGERING(ModPotionRegister.getPotionNameKey("no_leash", (char) 1), ModPartEnum.NAME, "Splash No Leash Potion", "滞留型禁拴药水", "滯留型禁拴藥水", false), + //ARROW + NO_LEASH_ARROW(ModPotionRegister.getTippedArrowNameKey("no_leash"), ModPartEnum.ITEM, "Arrow of No Leash", "禁拴之箭", "禁拴之箭", false), + ; + private final Supplier supplier; + private String key; + private final String US_EN; + private final String SIM_CN; + private final String TRA_CN; + private final String LZH; + private final Boolean Default; + private final ModPartEnum MPE; + ModLangKeyValue(Supplier Supplier, ModPartEnum MPE, String US_EN, String SIM_CN, String TRA_CN, String LZH, Boolean isDefault) { + this.supplier = Supplier; + this.MPE = MPE; + this.US_EN = US_EN; + this.SIM_CN = SIM_CN; + this.TRA_CN = TRA_CN; + this.LZH = LZH; + this.Default = isDefault; + } + ModLangKeyValue(@NotNull String ResourceKey, ModPartEnum MPE, String US_EN, String SIM_CN, String TRA_CN, String LZH, Boolean isDefault) { + this.supplier = null; + this.key = ResourceKey; + this.MPE = MPE; + this.US_EN = US_EN; + this.SIM_CN = SIM_CN; + this.TRA_CN = TRA_CN; + this.LZH = LZH; + this.Default = isDefault; + } + ModLangKeyValue(Supplier Supplier, ModPartEnum MPE, String US_EN, String SIM_CN, String TRA_CN, Boolean isDefault) { + this(Supplier, MPE, US_EN, SIM_CN, TRA_CN, null, isDefault); + } + ModLangKeyValue(@NotNull String ResourceKey, ModPartEnum MPE, String US_EN, String SIM_CN, String TRA_CN, Boolean isDefault) { + this(ResourceKey, MPE, US_EN, SIM_CN, TRA_CN, null, isDefault); + } + public static String getLan(LanguageEnum lan, ModLangKeyValue key) { + if (lan == null || lan == LanguageEnum.English) return getEnglish(key); + else { + switch (lan) { + case SimpleChinese -> { + return getSimpleChinese(key); + } + case TraditionalChinese -> { + return getTraditionalChinese(key); + } + case LiteraryChinese -> { + return getLiteraryChinese(key); + } + default -> { + return getEnglish(key); + } + } + } + } + private static String getEnglish(ModLangKeyValue key) { + return key.US_EN; + } + private static String getSimpleChinese(ModLangKeyValue key) { + return key.SIM_CN; + } + private static String getTraditionalChinese(ModLangKeyValue key) { + return key.TRA_CN; + } + @Nullable + public static String getLiteraryChinese(ModLangKeyValue key) { + return key.LZH; + } + public String getKey() { + if(key == null){ + switch (MPE) {//Don't need to use "break;"[Java feature]; + case CREATIVE_TAB, MESSAGE, INFO, DEFAULT, COMMAND, CONFIG -> throw new UnsupportedOperationException("The Key value is NULL! Please use the correct constructor and write the parameters correctly"); + case ITEM -> key = (getItem()).getDescriptionId(); + case BLOCK -> key =(getBlock()).getDescriptionId(); + + } + //需要完善 + } + return key; + } + @SuppressWarnings("null") + public Item getItem() { + assert supplier != null; + return (Item)supplier.get(); + } + @SuppressWarnings("null") + public Block getBlock() { + assert supplier != null; + return (Block)supplier.get(); + } + public boolean isDefaultItem(){ + return MPE == ModPartEnum.ITEM && Default; + } + public boolean isDefaultBlock() { + return MPE == ModPartEnum.BLOCK && Default; + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/datagen/ModDataGeneratorHandler.java b/src/main/java/com/r3944realms/leashedplayer/datagen/ModDataGeneratorHandler.java new file mode 100644 index 0000000..ca96e8b --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/datagen/ModDataGeneratorHandler.java @@ -0,0 +1,94 @@ +package com.r3944realms.leashedplayer.datagen; + +import com.r3944realms.leashedplayer.LeashedPlayer; +import com.r3944realms.leashedplayer.datagen.provider.*; +import com.r3944realms.leashedplayer.utils.Enum.LanguageEnum; +import net.minecraft.core.HolderLookup; +import net.minecraft.data.DataProvider; +import net.minecraft.data.PackOutput; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.common.EventBusSubscriber; +import net.neoforged.neoforge.data.event.GatherDataEvent; + +import java.util.concurrent.CompletableFuture; + +@EventBusSubscriber(modid = LeashedPlayer.MOD_ID, bus = EventBusSubscriber.Bus.MOD) +public class ModDataGeneratorHandler { + @SubscribeEvent + public static void genData(GatherDataEvent.Client event) { + CompletableFuture holderFolder = event.getLookupProvider(); + /*Language Provider ENGLISH CHINESE(SIM/TRA)*/ + addLanguage(event, LanguageEnum.English); + addLanguage(event, LanguageEnum.SimpleChinese); + addLanguage(event, LanguageEnum.TraditionalChinese); + addLanguage(event, LanguageEnum.LiteraryChinese); + ModDataPackBuiltInEntriesProvider(event, holderFolder); + RecipeGenerator(event, holderFolder); + ModTagsProvider(event, holderFolder); + ModPaintVariantTagsProvider(event, holderFolder); + ModSoundProvider(event); + ModModelDataGenerate(event); + ModAdvancementProvider(event, holderFolder); + } + private static void addLanguage(GatherDataEvent event, LanguageEnum language){ + event.getGenerator().addProvider( + true, + (DataProvider.Factory) pOutput -> new ModLanguageProvider(pOutput, LeashedPlayer.MOD_ID, language) + ); + } + + private static void RecipeGenerator(GatherDataEvent event, CompletableFuture future) { + PackOutput packOutput = event.getGenerator().getPackOutput(); + event.getGenerator().addProvider( + true, + new ModRecipeProvider.Runner(packOutput, future) + ); + } + private static void ModTagsProvider(GatherDataEvent event, CompletableFuture completableFuture) { + ModBlockTagProvider modBlockTagProvider = event.getGenerator().addProvider( + true, + (DataProvider.Factory) pOutput -> { + return new ModBlockTagProvider(pOutput, completableFuture, LeashedPlayer.MOD_ID); + } + + ); + event.getGenerator().addProvider( + true, + (DataProvider.Factory) pOutput -> + new ModItemTagProvider(pOutput, completableFuture, modBlockTagProvider.contentsGetter()) + ); + } + private static void ModSoundProvider(GatherDataEvent event) { + event.getGenerator().addProvider( + true, + (DataProvider.Factory) pOutput -> + new ModSoundDefinitionsProvider(pOutput,LeashedPlayer.MOD_ID) + ); + } + private static void ModDataPackBuiltInEntriesProvider(GatherDataEvent event, CompletableFuture future) { + event.getGenerator().addProvider( + true, + (DataProvider.Factory) pOutput -> new ModDataPackBuiltInEntriesProvider(pOutput, future) + ); + } + private static void ModAdvancementProvider(GatherDataEvent event, CompletableFuture pLookUpProvider) { + event.getGenerator().addProvider( + true, + (DataProvider.Factory) pOutput -> new ModAdvancementProvider(pOutput, pLookUpProvider)); + } + private static void ModPaintVariantTagsProvider(GatherDataEvent event, CompletableFuture completableFuture) { + event.getGenerator().addProvider( + true, + (DataProvider.Factory) pOutput -> { + return new ModPaintingVariantTagsProvider(pOutput, completableFuture, LeashedPlayer.MOD_ID); + } + ); + } + private static void ModModelDataGenerate(GatherDataEvent event) { + event.getGenerator().addProvider( + true, + (DataProvider.Factory) pOutput -> new ModModelDataProvider(pOutput, LeashedPlayer.MOD_ID) + ); + } + +} diff --git a/src/main/java/com/r3944realms/leashedplayer/datagen/generator/ModAdvancementGenerator.java b/src/main/java/com/r3944realms/leashedplayer/datagen/generator/ModAdvancementGenerator.java new file mode 100644 index 0000000..ed25e47 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/datagen/generator/ModAdvancementGenerator.java @@ -0,0 +1,175 @@ +package com.r3944realms.leashedplayer.datagen.generator; + +import com.r3944realms.leashedplayer.LeashedPlayer; +import com.r3944realms.leashedplayer.content.criteriaTriggers.LeashPlayerTrigger; +import com.r3944realms.leashedplayer.content.effects.ModEffectRegister; +import com.r3944realms.leashedplayer.content.entities.ModEntityRegister; +import com.r3944realms.leashedplayer.content.items.ModItemRegister; +import com.r3944realms.leashedplayer.datagen.LanguageAndOtherData.ModAdvancementKey; +import net.minecraft.advancements.Advancement; +import net.minecraft.advancements.AdvancementHolder; +import net.minecraft.advancements.AdvancementType; +import net.minecraft.advancements.critereon.*; +import net.minecraft.core.HolderGetter; +import net.minecraft.core.HolderLookup; +import net.minecraft.core.registries.Registries; +import net.minecraft.data.advancements.AdvancementSubProvider; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.item.Items; + +import java.util.function.Consumer; + + +public class ModAdvancementGenerator implements AdvancementSubProvider { + private final ResourceLocation ADV_BG = ResourceLocation.fromNamespaceAndPath(LeashedPlayer.MOD_ID, "textures/gui/advancements/backgrounds/leashed_player"); + + @Override + public void generate(HolderLookup.Provider registries, Consumer saver) { + HolderGetter> holdergetter = registries.lookupOrThrow(Registries.ENTITY_TYPE); + AdvancementHolder hasLeashRopeItem = Advancement.Builder.advancement().display( + Items.LEAD, + Component.translatable(ModAdvancementKey.LEASH_START.getNameKey()), + Component.translatable(ModAdvancementKey.LEASH_START.getDescKey()), + ADV_BG, + AdvancementType.TASK, + true, + false, + true + ).addCriterion("has_leash_rope_item", InventoryChangeTrigger.TriggerInstance.hasItems(Items.LEAD)) + .save(saver, ModAdvancementKey.LEASH_START.getNameWithNameSpace()); + + AdvancementHolder hasLeashRopeArrow = Advancement.Builder.advancement().display( + ModItemRegister.LEASH_ROPE_ARROW.get(), + Component.translatable(ModAdvancementKey.LEASH_ARROW.getNameKey()), + Component.translatable(ModAdvancementKey.LEASH_ARROW.getDescKey()), + null, + AdvancementType.TASK, + true, + false, + true + ).addCriterion("has_leash_rope_item", InventoryChangeTrigger.TriggerInstance.hasItems(ModItemRegister.LEASH_ROPE_ARROW.get())) + .parent(hasLeashRopeItem) + .save(saver, ModAdvancementKey.LEASH_ARROW.getNameWithNameSpace()); + + AdvancementHolder hasFlashLeashRopeArrow = Advancement.Builder.advancement().display( + ModItemRegister.SPECTRAL_LEASH_ROPE_ARROW.get(), + Component.translatable(ModAdvancementKey.ADVANCEMENT_LEASH_ARROW.getNameKey()), + Component.translatable(ModAdvancementKey.ADVANCEMENT_LEASH_ARROW.getDescKey()), + null, + AdvancementType.TASK, + true, + false, + true + ).addCriterion("has_flash_leash_rope_item", InventoryChangeTrigger.TriggerInstance.hasItems(ModItemRegister.SPECTRAL_LEASH_ROPE_ARROW.get())) + .parent(hasLeashRopeArrow) + .save(saver, ModAdvancementKey.ADVANCEMENT_LEASH_ARROW.getNameWithNameSpace()); + + AdvancementHolder leashedMySelf = Advancement.Builder.advancement().display( + Items.PLAYER_HEAD, + Component.translatable(ModAdvancementKey.LEASHED_SELF.getNameKey()), + Component.translatable(ModAdvancementKey.LEASHED_SELF.getDescKey()), + null, + AdvancementType.TASK, + true, + true, + true + ).addCriterion("leash_self", LeashPlayerTrigger.TriggerInstance.LeashPlayer( + EntityPredicate.Builder.entity().entityType(EntityTypePredicate.of(holdergetter, EntityType.LEASH_KNOT)) + )) + .parent(hasLeashRopeItem).save(saver, ModAdvancementKey.LEASHED_SELF.getNameWithNameSpace()); + AdvancementHolder followLeashRopeArrow = Advancement.Builder.advancement().display( + ModItemRegister.LEASH_ROPE_ARROW.get(), + Component.translatable(ModAdvancementKey.FOLLOW_LEASH_ARROW.getNameKey()), + Component.translatable(ModAdvancementKey.FOLLOW_LEASH_ARROW.getDescKey()), + null, + AdvancementType.TASK, + true, + false, + true + ).addCriterion("leash_arrow", LeashPlayerTrigger.TriggerInstance.LeashPlayer( + EntityPredicate.Builder.entity().entityType(EntityTypePredicate.of(holdergetter, ModEntityRegister.LEASH_ROPE_ARROW.get())) + )) + .parent(hasLeashRopeArrow) + .save(saver, ModAdvancementKey.FOLLOW_LEASH_ARROW.getNameWithNameSpace()); + AdvancementHolder dogRunPlayer = Advancement.Builder.advancement().display( + Items.BONE, + Component.translatable(ModAdvancementKey.DOG_RUNNING_PLAYER.getNameKey()), + Component.translatable(ModAdvancementKey.DOG_RUNNING_PLAYER.getDescKey()), + null, + AdvancementType.CHALLENGE, + true, + true, + true + ).addCriterion("leash_by_wo_do", LeashPlayerTrigger.TriggerInstance.LeashPlayer( + EntityPredicate.Builder.entity().entityType(EntityTypePredicate.of(holdergetter, EntityType.WOLF)) + )) + .parent(hasLeashRopeArrow).save(saver, ModAdvancementKey.DOG_RUNNING_PLAYER.getNameWithNameSpace()); + + AdvancementHolder leashedOther = Advancement.Builder.advancement().display( + Items.LEAD, + Component.translatable(ModAdvancementKey.LEASHED_FRIEND.getNameKey()), + Component.translatable(ModAdvancementKey.LEASHED_FRIEND.getDescKey()), + null, + AdvancementType.TASK, + true, + true, + true + ).addCriterion("leash_other_player", + LeashPlayerTrigger.TriggerInstance.LeashPlayer( + EntityPredicate.Builder.entity().entityType(EntityTypePredicate.of(holdergetter, EntityType.PLAYER)) + ) + ) + .parent(hasLeashRopeItem).save(saver, ModAdvancementKey.LEASHED_FRIEND.getNameWithNameSpace()); + AdvancementHolder lead_rope_terminator = Advancement.Builder.advancement().display( + ModItemRegister.AMETHYST_SHEARS.get(), + Component.translatable(ModAdvancementKey.LEASH_TERMINATOR.getNameKey()), + Component.translatable(ModAdvancementKey.LEASH_TERMINATOR.getDescKey()), + null, + AdvancementType.TASK, + true, + true, + true + ).addCriterion("has_amethyst_shears", InventoryChangeTrigger.TriggerInstance.hasItems(ModItemRegister.AMETHYST_SHEARS.get()) + ).parent(hasLeashRopeItem).save(saver, ModAdvancementKey.LEASH_TERMINATOR.getNameWithNameSpace()); + + AdvancementHolder no_leash = Advancement.Builder.advancement().display( + Items.BARRIER, + Component.translatable(ModAdvancementKey.NO_LEASH.getNameKey()), + Component.translatable(ModAdvancementKey.NO_LEASH.getDescKey()), + null, + AdvancementType.GOAL, + true, + true, + true + ).addCriterion("no_leash", + EffectsChangedTrigger.TriggerInstance.hasEffects( + MobEffectsPredicate.Builder.effects() + .and(ModEffectRegister.NO_LEASH_EFFECT) + ) + ).parent(hasLeashRopeItem).save(saver, ModAdvancementKey.NO_LEASH.getNameWithNameSpace()); + AdvancementHolder tipped_leash_arrow = Advancement.Builder.advancement().display( + ModItemRegister.TIPPED_LEASH_ROPE_ARROW.get(), + Component.translatable(ModAdvancementKey.TIPPED_LEASH_ARROW.getNameKey()), + Component.translatable(ModAdvancementKey.TIPPED_LEASH_ARROW.getDescKey()), + null, + AdvancementType.GOAL, + true, + true, + true + ).addCriterion("has_tipped_leash_arrow", InventoryChangeTrigger.TriggerInstance.hasItems(ModItemRegister.TIPPED_LEASH_ROPE_ARROW.get()) + ).parent(hasLeashRopeArrow).save(saver, ModAdvancementKey.TIPPED_LEASH_ARROW.getNameWithNameSpace()); + AdvancementHolder neo_fox = Advancement.Builder.advancement().display( + ModItemRegister.NEOFORGE.get(), + Component.translatable(ModAdvancementKey.NEO_FOX.getNameKey()), + Component.translatable(ModAdvancementKey.NEO_FOX.getDescKey()), + null, + AdvancementType.GOAL, + true, + false, + true + ).addCriterion("has_neo_fox", InventoryChangeTrigger.TriggerInstance.hasItems(ModItemRegister.NEOFORGE.get()) + ).parent(hasLeashRopeItem).save(saver, ModAdvancementKey.NEO_FOX.getNameWithNameSpace()); + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/datagen/generator/ModBlockModelGenerator.java b/src/main/java/com/r3944realms/leashedplayer/datagen/generator/ModBlockModelGenerator.java new file mode 100644 index 0000000..8c3357e --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/datagen/generator/ModBlockModelGenerator.java @@ -0,0 +1,21 @@ +package com.r3944realms.leashedplayer.datagen.generator; + +import net.minecraft.client.data.models.BlockModelGenerators; +import net.minecraft.client.data.models.ItemModelOutput; +import net.minecraft.client.data.models.blockstates.BlockModelDefinitionGenerator; +import net.minecraft.client.data.models.model.ModelInstance; +import net.minecraft.resources.ResourceLocation; + +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +public class ModBlockModelGenerator extends BlockModelGenerators { + public ModBlockModelGenerator(Consumer blockStateOutput, ItemModelOutput itemModelOutput, BiConsumer modelOutput) { + super(blockStateOutput, itemModelOutput, modelOutput); + } + + @Override + public void run() { + + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/datagen/generator/ModItemModelGenerator.java b/src/main/java/com/r3944realms/leashedplayer/datagen/generator/ModItemModelGenerator.java new file mode 100644 index 0000000..7853880 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/datagen/generator/ModItemModelGenerator.java @@ -0,0 +1,129 @@ +package com.r3944realms.leashedplayer.datagen.generator; + + +import com.r3944realms.leashedplayer.LeashedPlayer; +import com.r3944realms.leashedplayer.client.renderer.item.ConditionalRangeItemModel; +import com.r3944realms.leashedplayer.client.renderer.item.properties.conditional.ChargeExtend; +import com.r3944realms.leashedplayer.client.renderer.item.properties.conditionalRange.BowPull; +import com.r3944realms.leashedplayer.content.items.ModItemRegister; +import com.r3944realms.leashedplayer.datagen.LanguageAndOtherData.ModLangKeyValue; +import com.r3944realms.leashedplayer.extend.CrossbowItem$ChargeTypeExtend; +import net.minecraft.client.data.models.ItemModelGenerators; +import net.minecraft.client.data.models.ItemModelOutput; +import net.minecraft.client.data.models.model.ItemModelUtils; +import net.minecraft.client.data.models.model.ModelInstance; +import net.minecraft.client.data.models.model.ModelLocationUtils; +import net.minecraft.client.data.models.model.ModelTemplates; +import net.minecraft.client.renderer.item.ItemModel; +import net.minecraft.client.renderer.item.properties.numeric.CrossbowPull; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.Items; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.BiConsumer; + +public class ModItemModelGenerator extends ItemModelGenerators { + private static List objectList; + public ModItemModelGenerator(ItemModelOutput itemModelOutput, BiConsumer modelOutput) { + super(itemModelOutput, modelOutput); + objectList = new ArrayList<>(); + init(); + } + + @Override + public void run() { + DefaultModItemModelRegister(); + generateTippedArrow(ModItemRegister.TIPPED_LEASH_ROPE_ARROW.get()); + generateCrossbow(Items.CROSSBOW); + generateBow(Items.BOW); + } + + private void init() { + for(ModLangKeyValue obj : ModLangKeyValue.values()) { + if(!obj.isDefaultItem()) continue; + objectList.add(obj.getItem()); + } + } + /** + * @implNote
 先有纹理才会成功构建 + */ + private void DefaultModItemModelRegister() { + objectList.forEach(i -> generateFlatItem(i, ModelTemplates.FLAT_ITEM)); + } + + @Override + public void generateBow(@NotNull Item bowItem) { + ItemModel.Unbaked itemmodel$unbaked = ItemModelUtils.plainModel(ModelLocationUtils.getModelLocation(bowItem)); + ItemModel.Unbaked itemmodel$unbaked1 = ItemModelUtils.plainModel(this.createFlatItemModel(bowItem, "_pulling_0", ModelTemplates.BOW)); + ItemModel.Unbaked itemmodel$unbaked2 = ItemModelUtils.plainModel(this.createFlatItemModel(bowItem, "_pulling_1", ModelTemplates.BOW)); + ItemModel.Unbaked itemmodel$unbaked3 = ItemModelUtils.plainModel(this.createFlatItemModel(bowItem, "_pulling_2", ModelTemplates.BOW)); + ItemModel.Unbaked itemmodel$unbaked4 = ItemModelUtils.plainModel( + com.r3944realms.leashedplayer.utils.dataModel.ItemModelUtils.createFlatItemModelByPath(LeashedPlayer.MOD_ID, "item/bow_lra_pulling_0", ModelTemplates.BOW, this.modelOutput) + ); + ItemModel.Unbaked itemmodel$unbaked5 = ItemModelUtils.plainModel( + com.r3944realms.leashedplayer.utils.dataModel.ItemModelUtils.createFlatItemModelByPath(LeashedPlayer.MOD_ID, "item/bow_lra_pulling_1", ModelTemplates.BOW, this.modelOutput) + + ); + ItemModel.Unbaked itemmodel$unbaked6 = ItemModelUtils.plainModel( + com.r3944realms.leashedplayer.utils.dataModel.ItemModelUtils.createFlatItemModelByPath(LeashedPlayer.MOD_ID, "item/bow_lra_pulling_2", ModelTemplates.BOW, this.modelOutput) + ); + this.itemModelOutput + .accept( + bowItem, + ItemModelUtils.conditional( + ItemModelUtils.isUsingItem(), + com.r3944realms.leashedplayer.utils.dataModel.ItemModelUtils.conditionalRange( + new BowPull(), + 0.05F, + itemmodel$unbaked1, + new ConditionalRangeItemModel.Entry[] { + com.r3944realms.leashedplayer.utils.dataModel.ItemModelUtils.override(itemmodel$unbaked4, 0.0F), + com.r3944realms.leashedplayer.utils.dataModel.ItemModelUtils.override(itemmodel$unbaked5, 0.65F), + com.r3944realms.leashedplayer.utils.dataModel.ItemModelUtils.override(itemmodel$unbaked6, 0.9F) + }, + new ConditionalRangeItemModel.Entry[] { + com.r3944realms.leashedplayer.utils.dataModel.ItemModelUtils.override(itemmodel$unbaked1, 0.0F), + com.r3944realms.leashedplayer.utils.dataModel.ItemModelUtils.override(itemmodel$unbaked2, 0.65F), + com.r3944realms.leashedplayer.utils.dataModel.ItemModelUtils.override(itemmodel$unbaked3, 0.9F) + } + ), + itemmodel$unbaked + ) + ); + } + @Override + public void generateCrossbow(@NotNull Item crossbowItem) { + ItemModel.Unbaked itemmodel$unbaked = ItemModelUtils.plainModel(ModelLocationUtils.getModelLocation(crossbowItem)); + ItemModel.Unbaked itemmodel$unbaked1 = ItemModelUtils.plainModel(this.createFlatItemModel(crossbowItem, "_pulling_0", ModelTemplates.CROSSBOW)); + ItemModel.Unbaked itemmodel$unbaked2 = ItemModelUtils.plainModel(this.createFlatItemModel(crossbowItem, "_pulling_1", ModelTemplates.CROSSBOW)); + ItemModel.Unbaked itemmodel$unbaked3 = ItemModelUtils.plainModel(this.createFlatItemModel(crossbowItem, "_pulling_2", ModelTemplates.CROSSBOW)); + ItemModel.Unbaked itemmodel$unbaked4 = ItemModelUtils.plainModel(this.createFlatItemModel(crossbowItem, "_arrow", ModelTemplates.CROSSBOW)); + ItemModel.Unbaked itemmodel$unbaked5 = ItemModelUtils.plainModel(this.createFlatItemModel(crossbowItem, "_firework", ModelTemplates.CROSSBOW)); + ItemModel.Unbaked itemmodel$unbaked6 = ItemModelUtils.plainModel( + com.r3944realms.leashedplayer.utils.dataModel.ItemModelUtils.createFlatItemModelByPath(LeashedPlayer.MOD_ID, "item/crossbow_leash_rope_arrow", ModelTemplates.CROSSBOW, this.modelOutput) + ); + this.itemModelOutput + .accept( + crossbowItem, + ItemModelUtils.conditional( + ItemModelUtils.isUsingItem(), + ItemModelUtils.rangeSelect( + new CrossbowPull(), + itemmodel$unbaked1, + ItemModelUtils.override(itemmodel$unbaked2, 0.58F), + ItemModelUtils.override(itemmodel$unbaked3, 1.0F) + ), + ItemModelUtils.select( + new ChargeExtend(), + itemmodel$unbaked, + ItemModelUtils.when(CrossbowItem$ChargeTypeExtend.ARROW, itemmodel$unbaked4), + ItemModelUtils.when(CrossbowItem$ChargeTypeExtend.ROCKET, itemmodel$unbaked5), + ItemModelUtils.when(CrossbowItem$ChargeTypeExtend.LEASH_ROPE_ARROW, itemmodel$unbaked6) + ) + ) + ); + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/datagen/provider/ModAdvancementProvider.java b/src/main/java/com/r3944realms/leashedplayer/datagen/provider/ModAdvancementProvider.java new file mode 100644 index 0000000..ea0b9d7 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/datagen/provider/ModAdvancementProvider.java @@ -0,0 +1,22 @@ +package com.r3944realms.leashedplayer.datagen.provider; + +import com.r3944realms.leashedplayer.datagen.generator.ModAdvancementGenerator; +import net.minecraft.core.HolderLookup; +import net.minecraft.data.PackOutput; +import net.minecraft.data.advancements.AdvancementProvider; + +import java.util.List; +import java.util.concurrent.CompletableFuture; + +public class ModAdvancementProvider extends AdvancementProvider { + /** + * Constructs an advancement provider using the generators to write the + * advancements to a file. + * + * @param output the target directory of the data generator + * @param registries a future of a lookup for registries and their objects + */ + public ModAdvancementProvider(PackOutput output, CompletableFuture registries) { + super(output, registries, List.of(new ModAdvancementGenerator())); + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/datagen/provider/ModBlockTagProvider.java b/src/main/java/com/r3944realms/leashedplayer/datagen/provider/ModBlockTagProvider.java new file mode 100644 index 0000000..3a015b1 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/datagen/provider/ModBlockTagProvider.java @@ -0,0 +1,19 @@ +package com.r3944realms.leashedplayer.datagen.provider; + +import net.minecraft.core.HolderLookup; +import net.minecraft.data.PackOutput; +import net.neoforged.neoforge.common.data.BlockTagsProvider; +import org.jetbrains.annotations.NotNull; + +import java.util.concurrent.CompletableFuture; + +public class ModBlockTagProvider extends BlockTagsProvider { + public ModBlockTagProvider(PackOutput output, CompletableFuture lookupProvider, String modId) { + super(output, lookupProvider, modId); + } + + @Override + protected void addTags(HolderLookup.@NotNull Provider pProvider) { + + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/datagen/provider/ModDataPackBuiltInEntriesProvider.java b/src/main/java/com/r3944realms/leashedplayer/datagen/provider/ModDataPackBuiltInEntriesProvider.java new file mode 100644 index 0000000..0f1b4d9 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/datagen/provider/ModDataPackBuiltInEntriesProvider.java @@ -0,0 +1,23 @@ +package com.r3944realms.leashedplayer.datagen.provider; + +import com.r3944realms.leashedplayer.LeashedPlayer; +import com.r3944realms.leashedplayer.datagen.provider.attributes.ModJukeboxSongs; +import com.r3944realms.leashedplayer.datagen.provider.attributes.ModPaintingVariants; +import net.minecraft.core.HolderLookup; +import net.minecraft.core.RegistrySetBuilder; +import net.minecraft.core.registries.Registries; +import net.minecraft.data.PackOutput; +import net.neoforged.neoforge.common.data.DatapackBuiltinEntriesProvider; + +import java.util.Set; +import java.util.concurrent.CompletableFuture; + +public class ModDataPackBuiltInEntriesProvider extends DatapackBuiltinEntriesProvider { + public static final RegistrySetBuilder BUILDER = new RegistrySetBuilder() + .add(Registries.PAINTING_VARIANT, ModPaintingVariants::bootstrap) + .add(Registries.JUKEBOX_SONG, ModJukeboxSongs::bootstrap); + public ModDataPackBuiltInEntriesProvider(PackOutput output, CompletableFuture registries) { + super(output, registries, BUILDER, Set.of(LeashedPlayer.MOD_ID)); + CompletableFuture registryProvider = getRegistryProvider(); + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/datagen/provider/ModItemTagProvider.java b/src/main/java/com/r3944realms/leashedplayer/datagen/provider/ModItemTagProvider.java new file mode 100644 index 0000000..fcc7ece --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/datagen/provider/ModItemTagProvider.java @@ -0,0 +1,43 @@ +package com.r3944realms.leashedplayer.datagen.provider; + +import com.r3944realms.leashedplayer.LeashedPlayer; +import com.r3944realms.leashedplayer.content.items.ModItemRegister; +import com.r3944realms.leashedplayer.datagen.LanguageAndOtherData.ModItemTags; +import net.minecraft.core.HolderLookup; +import net.minecraft.data.PackOutput; +import net.minecraft.data.tags.ItemTagsProvider; +import net.minecraft.tags.ItemTags; +import net.minecraft.world.item.Items; +import net.minecraft.world.level.block.Block; +import net.neoforged.neoforge.common.Tags; +import org.jetbrains.annotations.NotNull; + +import java.util.concurrent.CompletableFuture; + +public class ModItemTagProvider extends ItemTagsProvider { + + public ModItemTagProvider(PackOutput pOutput, CompletableFuture pLookupProvider, CompletableFuture> pBlockTags) { + super(pOutput, pLookupProvider, pBlockTags, LeashedPlayer.MOD_ID); + } + + @Override + protected void addTags(HolderLookup.@NotNull Provider pProvider) { + this.tag(ItemTags.ARROWS) + .add(ModItemRegister.LEASH_ROPE_ARROW.get()) + .add(ModItemRegister.SPECTRAL_LEASH_ROPE_ARROW.get()) + .add(ModItemRegister.TIPPED_LEASH_ROPE_ARROW.get()); + this.tag(ModItemTags.AMETHYST_TOOL_MATERIALS) + .add(Items.AMETHYST_SHARD); + this.tag(ItemTags.HEAD_ARMOR) + .add(ModItemRegister.NEOFORGE.get()) + .add(Items.LEAD); + this.tag(ItemTags.MINING_ENCHANTABLE) + .add(ModItemRegister.AMETHYST_SHEARS.get()); + this.tag(ItemTags.VANISHING_ENCHANTABLE) + .add(ModItemRegister.AMETHYST_SHEARS.get()); + this.tag(ItemTags.DURABILITY_ENCHANTABLE) + .add(ModItemRegister.AMETHYST_SHEARS.get()); + this.tag(Tags.Items.TOOLS_SHEAR) + .add(ModItemRegister.AMETHYST_SHEARS.get()); + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/datagen/provider/ModLanguageProvider.java b/src/main/java/com/r3944realms/leashedplayer/datagen/provider/ModLanguageProvider.java new file mode 100644 index 0000000..4254282 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/datagen/provider/ModLanguageProvider.java @@ -0,0 +1,40 @@ +package com.r3944realms.leashedplayer.datagen.provider; + +import com.r3944realms.leashedplayer.datagen.LanguageAndOtherData.ModLangKeyValue; +import com.r3944realms.leashedplayer.utils.Enum.LanguageEnum; +import net.minecraft.data.PackOutput; +import net.neoforged.neoforge.common.data.LanguageProvider; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static com.r3944realms.leashedplayer.datagen.LanguageAndOtherData.ModLangKeyValue.getLan; + + +public class ModLanguageProvider extends LanguageProvider { + private final LanguageEnum Language; + private final Map LanKeyMap; + private static final List objects = new ArrayList<>(); + public ModLanguageProvider(PackOutput output, String modId, LanguageEnum Lan) { + super(output, modId, Lan.local); + this.Language = Lan; + LanKeyMap = new HashMap<>(); + init(); + } + private void init() { + for (ModLangKeyValue key : ModLangKeyValue.values()) { + addLang(key.getKey(), getLan(Language, key)); + } + } + private void addLang(String Key, String value) { + if(!objects.contains(Key)) objects.add(Key); + LanKeyMap.put(Key, value); + } + + @Override + protected void addTranslations() { + objects.forEach(key -> add(key,LanKeyMap.get(key))); + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/datagen/provider/ModModelDataProvider.java b/src/main/java/com/r3944realms/leashedplayer/datagen/provider/ModModelDataProvider.java new file mode 100644 index 0000000..dfafd39 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/datagen/provider/ModModelDataProvider.java @@ -0,0 +1,189 @@ +package com.r3944realms.leashedplayer.datagen.provider; + +import com.google.common.collect.Maps; +import com.google.gson.JsonElement; +import com.r3944realms.leashedplayer.datagen.generator.ModBlockModelGenerator; +import com.r3944realms.leashedplayer.datagen.generator.ModItemModelGenerator; +import net.minecraft.client.data.models.ItemModelOutput; +import net.minecraft.client.data.models.ModelProvider; +import net.minecraft.client.data.models.blockstates.BlockModelDefinitionGenerator; +import net.minecraft.client.data.models.model.ItemModelUtils; +import net.minecraft.client.data.models.model.ModelInstance; +import net.minecraft.client.data.models.model.ModelLocationUtils; +import net.minecraft.client.renderer.block.model.BlockModelDefinition; +import net.minecraft.client.renderer.item.ClientItem; +import net.minecraft.client.renderer.item.ItemModel; +import net.minecraft.core.Holder; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.data.CachedOutput; +import net.minecraft.data.DataProvider; +import net.minecraft.data.PackOutput; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.Item; +import net.minecraft.world.level.block.Block; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.api.distmarker.OnlyIn; +import org.jetbrains.annotations.NotNull; + +import java.nio.file.Path; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Stream; +/** +* 借鉴于 @author baguchi + * */ +public class ModModelDataProvider extends ModelProvider { + private final PackOutput.PathProvider blockStatePathProvider; + private final PackOutput.PathProvider itemInfoPathProvider; + private final PackOutput.PathProvider modelPathProvider; + public ModModelDataProvider(PackOutput packOutput, String modId) { + super(packOutput, modId); + this.blockStatePathProvider = packOutput.createPathProvider(PackOutput.Target.RESOURCE_PACK, "blockstates"); + this.itemInfoPathProvider = packOutput.createPathProvider(PackOutput.Target.RESOURCE_PACK, "items"); + this.modelPathProvider = packOutput.createPathProvider(PackOutput.Target.RESOURCE_PACK, "models"); + } + + @Override + public @NotNull CompletableFuture run(@NotNull CachedOutput cachedOutput) { + + ItemInfoCollector itemModelOutput = new ItemInfoCollector(this::getKnownItems); + BlockStateGeneratorCollector blockModelOutput = new BlockStateGeneratorCollector(this::getKnownBlocks); + SimpleModelCollector modelOutput = new SimpleModelCollector(); + this.registerModels(new ModBlockModelGenerator(blockModelOutput, itemModelOutput, modelOutput), new ModItemModelGenerator(itemModelOutput, modelOutput)); + blockModelOutput.validate(); + itemModelOutput.finalizeAndValidate(); + return CompletableFuture.allOf(blockModelOutput.save(cachedOutput, this.blockStatePathProvider), modelOutput.save(cachedOutput, this.modelPathProvider), itemModelOutput.save(cachedOutput, this.itemInfoPathProvider)); + } + + @OnlyIn(Dist.CLIENT) + static class ItemInfoCollector implements ItemModelOutput { + private final Map itemInfos; + private final Map copies; + private final Supplier>> knownItems; + + public ItemInfoCollector(Supplier>> knownItems) { + this.itemInfos = new HashMap<>(); + this.copies = new HashMap<>(); + this.knownItems = knownItems; + } + + + public void accept(@NotNull Item item, ItemModel.@NotNull Unbaked model) { + this.register(item, new ClientItem(model, ClientItem.Properties.DEFAULT)); + } + + public void register(@NotNull Item item, @NotNull ClientItem clientItem) { + ClientItem clientitem = this.itemInfos.put(item, clientItem); + if (clientitem != null) { + throw new IllegalStateException("Duplicate item model definition for " + item); + } + } + + public void copy(@NotNull Item item, @NotNull Item copyItem) { + this.copies.put(copyItem, item); + } + + public void finalizeAndValidate() { + (this.knownItems.get()).map(Holder::value).forEach((item) -> { + if (!this.copies.containsKey(item) && item instanceof BlockItem blockitem) { + if (!this.itemInfos.containsKey(blockitem)) { + ResourceLocation resourcelocation = ModelLocationUtils.getModelLocation(blockitem.getBlock()); + this.accept(blockitem, ItemModelUtils.plainModel(resourcelocation)); + } + } + + }); + this.copies.forEach((item, item1) -> { + ClientItem clientitem = this.itemInfos.get(item1); + if (clientitem == null) { + String item1Name = String.valueOf(item1); + throw new IllegalStateException("Missing donor: " + item1Name + " -> " + item); + } else { + this.register(item, clientitem); + } + }); + List list = (this.knownItems.get()).filter((holder) -> !this.itemInfos.containsKey(holder.value())).map((holder) -> holder.unwrapKey().orElseThrow().location()).toList(); + + if (!list.isEmpty()) { + LOGGER.warn("Missing item model definitions for: {}", list); + } + } + + public CompletableFuture save(CachedOutput cachedOutput, PackOutput.PathProvider pathProvider) { + return DataProvider.saveAll(cachedOutput, ClientItem.CODEC, (item) -> pathProvider.json(item.builtInRegistryHolder().key().location()), this.itemInfos); + } + } + + @OnlyIn(Dist.CLIENT) + static class SimpleModelCollector implements BiConsumer { + private final Map models = new HashMap<>(); + + SimpleModelCollector() { + } + + public void accept(ResourceLocation resourceLocation, ModelInstance modelInstance) { + Supplier supplier = this.models.put(resourceLocation, modelInstance); + if (supplier != null) { + throw new IllegalStateException("Duplicate model definition for " + resourceLocation); + } + } + + public CompletableFuture save(CachedOutput cachedOutput, PackOutput.PathProvider pathProvider) { + Objects.requireNonNull(pathProvider); + return saveAll(cachedOutput, pathProvider::json, this.models); + } + + static CompletableFuture saveAll(CachedOutput cachedOutput, Function tPathFunction, Map> tMap) { + return DataProvider.saveAll(cachedOutput, Supplier::get, tPathFunction, tMap); + } + } + + @OnlyIn(Dist.CLIENT) + static class BlockStateGeneratorCollector implements Consumer { + private final Map generators = new HashMap<>(); + private final Supplier>> knownBlocks; + + public BlockStateGeneratorCollector(Supplier>> knownBlocks) { + this.knownBlocks = knownBlocks; + } + + @Deprecated // Neo: Provided for vanilla/multi-loader compatibility. Use constructor with Supplier parameter. + public BlockStateGeneratorCollector() { + this(BuiltInRegistries.BLOCK::listElements); + } + + public void accept(BlockModelDefinitionGenerator p_405192_) { + Block block = p_405192_.block(); + BlockModelDefinitionGenerator blockmodeldefinitiongenerator = this.generators.put(block, p_405192_); + if (blockmodeldefinitiongenerator != null) { + throw new IllegalStateException("Duplicate blockstate definition for " + block); + } + } + + public void validate() { + Stream> stream = knownBlocks.get(); + List list = stream.filter(p_386843_ -> !this.generators.containsKey(p_386843_.value())) + .map(p_386823_ -> p_386823_.unwrapKey().orElseThrow().location()) + .toList(); + if (!list.isEmpty()) { + throw new IllegalStateException("Missing blockstate definitions for: " + list); + } + } + + public CompletableFuture save(CachedOutput output, PackOutput.PathProvider pathProvider) { + Map map = Maps.transformValues(this.generators, BlockModelDefinitionGenerator::create); + Function function = p_387598_ -> pathProvider.json(p_387598_.builtInRegistryHolder().key().location()); + return DataProvider.saveAll(output, BlockModelDefinition.CODEC, function, map); + } + } + + +} diff --git a/src/main/java/com/r3944realms/leashedplayer/datagen/provider/ModPaintingVariantTagsProvider.java b/src/main/java/com/r3944realms/leashedplayer/datagen/provider/ModPaintingVariantTagsProvider.java new file mode 100644 index 0000000..33a66d5 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/datagen/provider/ModPaintingVariantTagsProvider.java @@ -0,0 +1,23 @@ +package com.r3944realms.leashedplayer.datagen.provider; + +import com.r3944realms.leashedplayer.datagen.provider.attributes.ModPaintingVariants; +import net.minecraft.core.HolderLookup; +import net.minecraft.data.PackOutput; +import net.minecraft.data.tags.PaintingVariantTagsProvider; +import net.minecraft.tags.PaintingVariantTags; +import org.jetbrains.annotations.NotNull; + +import java.util.concurrent.CompletableFuture; + +public class ModPaintingVariantTagsProvider extends PaintingVariantTagsProvider { + public ModPaintingVariantTagsProvider(PackOutput pOutput, CompletableFuture pProvider, String modId) { + super(pOutput, pProvider, modId); + } + + @Override + protected void addTags(HolderLookup.@NotNull Provider pProvider) { + this.tag(PaintingVariantTags.PLACEABLE) + .add(ModPaintingVariants.GROUP_PHOTO); + + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/datagen/provider/ModRecipeProvider.java b/src/main/java/com/r3944realms/leashedplayer/datagen/provider/ModRecipeProvider.java new file mode 100644 index 0000000..b6cf9da --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/datagen/provider/ModRecipeProvider.java @@ -0,0 +1,72 @@ +package com.r3944realms.leashedplayer.datagen.provider; + +import com.r3944realms.leashedplayer.content.items.ModItemRegister; +import com.r3944realms.leashedplayer.content.items.repcipe.TippedLeashRopeArrowRecipe; +import net.minecraft.core.HolderLookup; +import net.minecraft.data.PackOutput; +import net.minecraft.data.recipes.RecipeCategory; +import net.minecraft.data.recipes.RecipeOutput; +import net.minecraft.data.recipes.RecipeProvider; +import net.minecraft.data.recipes.SpecialRecipeBuilder; +import net.minecraft.world.item.Items; +import org.jetbrains.annotations.NotNull; + +import java.util.concurrent.CompletableFuture; + +public class ModRecipeProvider extends RecipeProvider { + public ModRecipeProvider(HolderLookup.Provider registries, RecipeOutput output) { + super(registries, output); + } + + @Override + protected void buildRecipes() { + this.shapeless(RecipeCategory.MISC, ModItemRegister.LEASH_ROPE_ARROW.get(), 1) + .requires(Items.LEAD) + .requires(Items.ARROW) + .unlockedBy("has_lead",has(Items.LEAD)) + .save(this.output); + this.shapeless(RecipeCategory.MISC, ModItemRegister.SPECTRAL_LEASH_ROPE_ARROW.get(),1) + .requires(Items.LEAD) + .requires(Items.SPECTRAL_ARROW) + .unlockedBy("has_lead",has(Items.LEAD)) + .unlockedBy("has_spectral_arrow",has(Items.SPECTRAL_ARROW)) + .save(this.output); + this.shaped(RecipeCategory.MISC, ModItemRegister.SPECTRAL_LEASH_ROPE_ARROW.get(),1) + .pattern(" $ ") + .pattern("$#$") + .pattern(" $ ") + .define('#', ModItemRegister.LEASH_ROPE_ARROW.get()) + .define('$', Items.GLOWSTONE_DUST) + .unlockedBy("has_lead",has(Items.LEAD)) + .unlockedBy("has_glowstone_dust",has(Items.GLOWSTONE_DUST)) + .save(this.output,"leash_rope_arrow_shape"); + this.shaped(RecipeCategory.MISC, ModItemRegister.AMETHYST_SHEARS.get(),1) + .pattern("#%") + .pattern("%#") + .define('#', Items.AMETHYST_SHARD) + .define('%', Items.STICK) + .unlockedBy("has_amethyst_shard",has(Items.AMETHYST_SHARD)) + .save(this.output); + SpecialRecipeBuilder.special(TippedLeashRopeArrowRecipe.TippedLeashRopeArrowARecipe::new).save(this.output, "tipped_leash_rope_arrow_a"); + SpecialRecipeBuilder.special(TippedLeashRopeArrowRecipe.TippedLeashRopeArrowBRecipe::new).save(this.output, "tipped_leash_rope_arrow_b"); + } + public static class Runner extends RecipeProvider.Runner { + + public Runner(PackOutput pPackOutput, CompletableFuture providerCompletableFuture) { + super(pPackOutput, providerCompletableFuture); + } + + @Override + @NotNull + public RecipeProvider createRecipeProvider(HolderLookup.@NotNull Provider provider, @NotNull RecipeOutput output) { + return new ModRecipeProvider(provider, output); + } + + @Override + public @NotNull String getName() { + return "LeashedPlayer Recipes"; + } + } + + +} \ No newline at end of file diff --git a/src/main/java/com/r3944realms/leashedplayer/datagen/provider/ModSoundDefinitionsProvider.java b/src/main/java/com/r3944realms/leashedplayer/datagen/provider/ModSoundDefinitionsProvider.java new file mode 100644 index 0000000..6dd8f12 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/datagen/provider/ModSoundDefinitionsProvider.java @@ -0,0 +1,35 @@ +package com.r3944realms.leashedplayer.datagen.provider; + + +import com.r3944realms.leashedplayer.content.sounds.ModSoundRegister; +import net.minecraft.data.PackOutput; +import net.neoforged.neoforge.common.data.SoundDefinition; +import net.neoforged.neoforge.common.data.SoundDefinitionsProvider; + +public class ModSoundDefinitionsProvider extends SoundDefinitionsProvider { + /** + * Creates a new instance of this data provider. + * + * @param output The {@linkplain PackOutput} instance provided by the data generator. + * @param modId The mod ID of the current mod. + */ + public ModSoundDefinitionsProvider(PackOutput output, String modId) { + super(output, modId); + } + + public SoundDefinition getSoundDefinition(String subTitle, SoundDefinition.Sound... sounds) { + return SoundDefinition.definition().subtitle(subTitle).with(sounds); + } + + @Override + public void registerSounds() { + add( + ModSoundRegister.FOX_MUSIC, + getSoundDefinition( + ModSoundRegister.getSubTitleTranslateKey("what_does_the_fox_say"), + sound(ModSoundRegister.RL_FOX_MUSIC, SoundDefinition.SoundType.SOUND) + ) + ); + + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/datagen/provider/attributes/ModJukeboxSongs.java b/src/main/java/com/r3944realms/leashedplayer/datagen/provider/attributes/ModJukeboxSongs.java new file mode 100644 index 0000000..c2f944a --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/datagen/provider/attributes/ModJukeboxSongs.java @@ -0,0 +1,39 @@ +package com.r3944realms.leashedplayer.datagen.provider.attributes; + +import com.r3944realms.leashedplayer.LeashedPlayer; +import com.r3944realms.leashedplayer.content.sounds.ModSoundRegister; +import net.minecraft.Util; +import net.minecraft.core.Holder; +import net.minecraft.core.registries.Registries; +import net.minecraft.data.worldgen.BootstrapContext; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.world.item.JukeboxSong; + +public class ModJukeboxSongs { + public static ResourceKey FOX_MUSIC = create("what_does_the_fox_say"); + + public static void bootstrap(BootstrapContext pContext) { + JukeboxSongBootstrap(pContext); + } + + public static void JukeboxSongBootstrap(BootstrapContext pContext) { + register(pContext, FOX_MUSIC, ModSoundRegister.FOX_MUSIC, 121, 15); + + } + + private static void register( + BootstrapContext pContext, ResourceKey pKey, Holder pSoundEvent, int pLengthInSeconds, int pComparatorOutput + ) { + pContext.register( + pKey, + new JukeboxSong(pSoundEvent, Component.translatable(Util.makeDescriptionId("jukebox_song", pKey.location())), (float)pLengthInSeconds, pComparatorOutput) + ); + } + private static ResourceKey create(String pName) { + return ResourceKey.create(Registries.JUKEBOX_SONG, ResourceLocation.fromNamespaceAndPath(LeashedPlayer.MOD_ID, pName)); + } + +} diff --git a/src/main/java/com/r3944realms/leashedplayer/datagen/provider/attributes/ModPaintingVariants.java b/src/main/java/com/r3944realms/leashedplayer/datagen/provider/attributes/ModPaintingVariants.java new file mode 100644 index 0000000..786159f --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/datagen/provider/attributes/ModPaintingVariants.java @@ -0,0 +1,45 @@ +package com.r3944realms.leashedplayer.datagen.provider.attributes; + +import com.r3944realms.leashedplayer.LeashedPlayer; +import net.minecraft.core.registries.Registries; +import net.minecraft.data.worldgen.BootstrapContext; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.decoration.PaintingVariant; + +import java.util.Optional; + +public class ModPaintingVariants { + public static final ResourceKey GROUP_PHOTO = create("group_photo"); + + public static void bootstrap(BootstrapContext pContext) { + PaintingVariantBootstrap(pContext); + } + public static void PaintingVariantBootstrap(BootstrapContext pContext) { + ModPaintingVariants.register(pContext, ModPaintingVariants.GROUP_PHOTO, 4, 3, Component.translatable(ModPaintingVariants.getPaintingVariantTitleKey(ModPaintingVariants.GROUP_PHOTO)), Component.translatable(ModPaintingVariants.getPaintingVariantAuthorKey(ModPaintingVariants.GROUP_PHOTO))); + } + private static void register(BootstrapContext pContext, ResourceKey pKey, int pWidth, int pHeight, Component title, Component author) { + pContext.register(pKey, new PaintingVariant(pWidth, pHeight, pKey.location(), Optional.of(title), Optional.of(author))); + } + + private static ResourceKey create(String pName) { + return ResourceKey.create(Registries.PAINTING_VARIANT, ResourceLocation.fromNamespaceAndPath(LeashedPlayer.MOD_ID, pName)); + } + + public static String getPaintingVariantTitleKey(ResourceKey pKey) { + return "painting." + pKey.location().getNamespace() + "." + pKey.location().getPath() + ".title"; + } + + public static String getPaintingVariantAuthorKey(ResourceKey pKey) { + return "painting." + pKey.location().getNamespace() + "." + pKey.location().getPath() + ".author"; + } + + public static String getPaintingVariantTitleKey(String pName) { + return "painting." + LeashedPlayer.MOD_ID + "." + pName + ".title"; + } + + public static String getPaintingVariantAuthorKey(String pName) { + return "painting." + LeashedPlayer.MOD_ID + "." + pName + ".author"; + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/extend/CrossbowItem$ChargeTypeExtend.java b/src/main/java/com/r3944realms/leashedplayer/extend/CrossbowItem$ChargeTypeExtend.java new file mode 100644 index 0000000..0db588f --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/extend/CrossbowItem$ChargeTypeExtend.java @@ -0,0 +1,25 @@ +package com.r3944realms.leashedplayer.extend; + +import com.mojang.serialization.Codec; +import net.minecraft.util.StringRepresentable; +import org.jetbrains.annotations.NotNull; + +public enum CrossbowItem$ChargeTypeExtend implements StringRepresentable { + NONE("none"), + ARROW("arrow"), + ROCKET("rocket"), + LEASH_ROPE_ARROW("leash_rope_arrow"), + ; + + public static final Codec CODEC = StringRepresentable.fromEnum(CrossbowItem$ChargeTypeExtend::values); + private final String name; + + CrossbowItem$ChargeTypeExtend(String name) { + this.name = name; + } + + @Override + public @NotNull String getSerializedName() { + return this.name; + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/mixin/MixinPlugin.java b/src/main/java/com/r3944realms/leashedplayer/mixin/MixinPlugin.java new file mode 100644 index 0000000..16053c7 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/mixin/MixinPlugin.java @@ -0,0 +1,53 @@ +package com.r3944realms.leashedplayer.mixin; + +import org.objectweb.asm.tree.ClassNode; +import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; +import org.spongepowered.asm.mixin.extensibility.IMixinInfo; + +import java.util.List; +import java.util.Set; + +public class MixinPlugin implements IMixinConfigPlugin { + @Override + public void onLoad(String mixinPackage) { + } + + @Override + public String getRefMapperConfig() { + return null; + } + + @Override + public boolean shouldApplyMixin(String targetClassName, String mixinClassName) { + // 检查当前是否在数据生成阶段 + // 根据需要添加逻辑来控制特定Mixin类的应用 + boolean isRegister = mixinClassName.split("com\\.r3944realms\\.leashedplayer\\.mixin\\.")[1].split("\\.")[0].equals("registry"); + if (isRegister) { + return isDataGeneration(); + } + return true; // 其他Mixin类默认应用 + } + + private boolean isDataGeneration() { + // 使用你的逻辑来检查是否在数据生成阶段 + String environment = System.getProperty("gradle.task"); + return "runData".equals(environment); + } + + @Override + public void acceptTargets(Set myTargets, Set otherTargets) { + } + + @Override + public List getMixins() { + return null; + } + + @Override + public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { + } + + @Override + public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/mixin/both/MixinEntity.java b/src/main/java/com/r3944realms/leashedplayer/mixin/both/MixinEntity.java new file mode 100644 index 0000000..10cbf4a --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/mixin/both/MixinEntity.java @@ -0,0 +1,37 @@ +package com.r3944realms.leashedplayer.mixin.both; + +import com.r3944realms.leashedplayer.modInterface.PlayerLeashable; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.Leashable; +import net.minecraft.world.level.Level; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(Entity.class) +public abstract class MixinEntity { + @Shadow public abstract void igniteForSeconds(float pSeconds); + + @Shadow public abstract Level level(); + + /** + * 这里重定向,当实体类实现了{@link PlayerLeashable}接口时,
+ * 阻止原版的{@link Leashable}中 的tickLeash方法调用,将其
+ * 我们需自己实现相关的逻辑 + * @param entity 实体 + * @param 实体类型 + */ + @Redirect( + method = "baseTick", + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Leashable;tickLeash(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/entity/Entity;)V") + ) + void checkAndCancelIfTure(ServerLevel f, E entity) { + if(!(entity instanceof PlayerLeashable) && level() instanceof ServerLevel serverLevel_) { + Leashable.tickLeash(serverLevel_, entity); + } + } + + +} diff --git a/src/main/java/com/r3944realms/leashedplayer/mixin/both/MixinLivingEntity.java b/src/main/java/com/r3944realms/leashedplayer/mixin/both/MixinLivingEntity.java new file mode 100644 index 0000000..5734074 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/mixin/both/MixinLivingEntity.java @@ -0,0 +1,48 @@ +package com.r3944realms.leashedplayer.mixin.both; + +import net.minecraft.util.Mth; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.level.Level; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +//OpenJDK21 JIT 激进的优化将TickHeadTurnAssign优化成死循环了 + +/** + * 保证 while 循环的条件永远不成立的 + * @author ustc-zzzz + */ +@Mixin(LivingEntity.class) +public abstract class MixinLivingEntity extends Entity { + @Shadow + public float yBodyRot; + @Shadow + public float yBodyRotO; + + @Shadow + public float yHeadRot; + @Shadow + public float yHeadRotO; + + public MixinLivingEntity(EntityType type, Level level) { + super(type, level); + } + + @Inject( + method = "tick()V", + at = @At(value = "INVOKE_ASSIGN", target = "Lnet/minecraft/world/entity/LivingEntity;tickHeadTurn(F)V") + ) + public void onTickHeadTurnAssign(CallbackInfo ignored) { + var yRot = this.getYRot(); + this.yRotO = yRot - Mth.wrapDegrees(yRot - this.yRotO); + this.yBodyRotO = this.yBodyRot - Mth.wrapDegrees(this.yBodyRot - this.yBodyRotO); + var xRot = this.getXRot(); + this.xRotO = xRot - Mth.wrapDegrees(xRot - this.xRotO); + this.yHeadRotO = this.yHeadRot - Mth.wrapDegrees(this.yHeadRot - this.yHeadRotO); + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/mixin/both/MixinPlayer.java b/src/main/java/com/r3944realms/leashedplayer/mixin/both/MixinPlayer.java new file mode 100644 index 0000000..64ae88c --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/mixin/both/MixinPlayer.java @@ -0,0 +1,326 @@ +package com.r3944realms.leashedplayer.mixin.both; + +import com.r3944realms.leashedplayer.LeashedPlayer; +import com.r3944realms.leashedplayer.content.commands.LeashCommand; +import com.r3944realms.leashedplayer.content.entities.LeashRopeArrow; +import com.r3944realms.leashedplayer.modInterface.ILivingEntityExtension; +import com.r3944realms.leashedplayer.modInterface.PlayerLeashable; +import com.r3944realms.leashedplayer.utils.Logger; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.syncher.EntityDataAccessor; +import net.minecraft.network.syncher.EntityDataSerializers; +import net.minecraft.network.syncher.SynchedEntityData; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.Leashable; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.ai.attributes.Attributes; +import net.minecraft.world.entity.decoration.LeashFenceKnotEntity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.Vec3; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import javax.annotation.Nullable; +import java.util.Objects; +import java.util.function.Consumer; + +@Mixin(Player.class) +public abstract class MixinPlayer extends LivingEntity implements PlayerLeashable, ILivingEntityExtension { + + @Unique + protected int Pl$LeashKeepTick;//保存状态,当超过断裂绳长时若LeashKeepTick大于0,则不断裂 + + @Unique + @Nullable + private LeashData Pl$LeashData;//Data + + @SuppressWarnings("WrongEntityDataParameterClass") + @Unique//客户端与服务器端的实体同步数据 + private static final EntityDataAccessor Pl$LEASH_DATA = SynchedEntityData.defineId(Player.class, EntityDataSerializers.COMPOUND_TAG); + + @Unique + @SuppressWarnings("WrongEntityDataParameterClass") + private static final EntityDataAccessor DATA_ENTITY_LEASH_LENGTH = SynchedEntityData.defineId(Player.class, EntityDataSerializers.FLOAT); + + @SuppressWarnings("AddedMixinMembersNamePattern") + @Override + public float getLeashLength() { + return this.entityData.get(DATA_ENTITY_LEASH_LENGTH); + } + @SuppressWarnings("AddedMixinMembersNamePattern") + @Override + public void setLeashLength(float length) { + this.entityData.set(DATA_ENTITY_LEASH_LENGTH, length); + } + + @SuppressWarnings("AddedMixinMembersNamePattern") + @Override + public void setKeepLeashTick(int keepTick) { + this.Pl$LeashKeepTick = Math.max(keepTick, 0); + } + @SuppressWarnings("AddedMixinMembersNamePattern") + @Override + public int getKeepLeashTick() { + return this.Pl$LeashKeepTick; + } + + + protected MixinPlayer(EntityType pEntityType, Level pLevel) { + super(pEntityType, pLevel); + } + + @Inject(method = {"tick"}, at = {@At("HEAD")}) + private void tickForLeash(CallbackInfo ci) { + if(!this.level().isClientSide) { + Pl$tickLeash();//服务器端每tick任务 + } + PlayerLeashable playerLeashable = this; + Entity leashHolder = playerLeashable.getLeashHolder(); + if(leashHolder != null ) { + //存在则更新 + Pl$UpdateLeash(leashHolder, (Entity) playerLeashable); + try { + Objects.requireNonNull(this.getAttribute(Attributes.STEP_HEIGHT)).setBaseValue(1.25f); + } catch (Exception e) { + Logger.logger.error(e.getMessage()); + } + } else { + try { + Objects.requireNonNull(this.getAttribute(Attributes.STEP_HEIGHT)).setBaseValue(0.6f); + } catch (Exception e) { + Logger.logger.error(e.getMessage()); + } + } + } + @Unique + private static void Pl$UpdateLeash(Entity holderEntity, Entity restrainedEntity) { + if (holderEntity == null || holderEntity.level() != restrainedEntity.level()) { + return; + } + + 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) { + // 计算朝向持有者的拉力方向 + 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 + ) + ); + + // 控制速度不要过快,避免偏激移动 + Whimsy$Brake(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; + }); + } + + // 降低坠落伤害 + restrainedEntity.checkSlowFallDistance(); + } + + + /** + * 刹车( + * @param pEntity 刹车的实体 + * @param pMaxX X方向的最大动量 + * @param pMaxY Y方向的最大动量 + * @param pMaxZ Z方向的最大动量 + */ + @Unique + private static void Whimsy$Brake(Entity pEntity, double pMaxX, double pMaxY, double pMaxZ) { + Vec3 deltaMovement = pEntity.getDeltaMovement(); + double dX = Math.abs(deltaMovement.x) > pMaxX ? 0 : deltaMovement.x; + double dY = Math.abs(deltaMovement.y) > pMaxY ? 0 : deltaMovement.y; + double dZ = Math.abs(deltaMovement.z) > pMaxZ ? 0 : deltaMovement.z; + pEntity.setDeltaMovement(dX, dY,dZ); + pEntity.hurtMarked = true; + } + /** + * 刹车( + * @param pEntity 刹车的实体 + * @param pOpt 自定义规则 + */ + @Unique + private static void Whimsy$Brake(Entity pEntity, @Nullable Consumer pOpt) { + Consumer consumer = pOpt; + if(pOpt == null) { + consumer = entity -> { + Vec3 deltaMovement = entity.getDeltaMovement(); + double dX = Math.abs(deltaMovement.x) > 1 ? 0 : deltaMovement.x; + double dY = Math.abs(deltaMovement.y) > 1 ? 0 : deltaMovement.y; + double dZ = Math.abs(deltaMovement.z) > 1 ? 0 : deltaMovement.z; + entity.setDeltaMovement(dX, dY,dZ); + entity.hurtMarked = true; + }; + } + consumer.accept(pEntity); + } + @Unique + protected void Pl$tickLeash() { + if(this.Pl$LeashData == null) return;//没有Data直接退出 + ILivingEntityExtension self = this; + int keepLeashTick = self.getKeepLeashTick(); + + //info -> Holder整理 + Pl$RestoreLeashFormSave(); + //默认值设为 + float leashLength = LeashCommand.MIN_VALUE; + Entity entity = this.Pl$LeashData.leashHolder; + //保存数据 + saveLeashData(Pl$LeashData); + ILivingEntityExtension iEntityExtension = this;//获取设定值 + float leashLengthSelf = iEntityExtension.getLeashLength(); + leashLength = leashLengthSelf > LeashCommand.MIN_VALUE ? leashLengthSelf : LeashCommand.MIN_VALUE; + if (entity != null) { + double breakDistanceTime = (entity instanceof LeashRopeArrow) ? LeashedPlayer.M1() * LeashedPlayer.M2() : LeashedPlayer.M1(); + if(!isAlive() || !entity.isAlive() ||( distanceTo(entity) > Math.max(leashLength * breakDistanceTime, LeashCommand.MIN_VALUE * breakDistanceTime) && keepLeashTick == 0)){ + //玩家死亡 或 持有者不存在 或 距离大于设定值的 breakDistanceTime 倍且keepTick <=0(长度的 breakDistanceTime 倍若低于 LeashCommand.MIN_VALUE 格,则选 LeashCommand.MIN_VALUE 格) , + // 则取消拴绳关系,并掉落拴绳 + boolean shouldDrop = !(entity instanceof LeashRopeArrow); + Leashable.dropLeash((Entity & Leashable) this, true, shouldDrop); + } else if(distanceTo(entity) > leashLength * 0.65f * breakDistanceTime && entity.onGround()) { + //大于eashLength * 0.65f * breakDistanceTime 倍绳长且在地面则会让其跳跃(在<1.25格阻拦情况下,跳跃阻拦//TODO:待擴展 + Entity applyMovementEntity = this.isPassenger() ? this.getVehicle() : this; + if(applyMovementEntity instanceof LivingEntity applyMovementLivingEntity) { + applyMovementLivingEntity.jumpFromGround(); + } + } + } + if(keepLeashTick > 0) {//keepTick-- + self.setKeepLeashTick(keepLeashTick - 1); + } + } + + @Override + public Entity getLeashHolder() { + if (Pl$LeashData == null) return null; + if (Pl$LeashData.leashHolder == null && Pl$LeashData.delayedLeashHolderId != 0 ) { + Pl$LeashData.leashHolder = this.level().getEntity(Pl$LeashData.delayedLeashHolderId); + } + return Pl$LeashData.leashHolder; + } + + /** + * 数据整理 -> 如果Pl$LeashData非null,最终Pl$LeashData的leashHolder将不为null + */ + @Unique + private void Pl$RestoreLeashFormSave() { + assert this.Pl$LeashData != null; + if(!(this.level() instanceof ServerLevel)) { + //非服务器端退出 + return; + } + + if(this.Pl$LeashData.delayedLeashInfo == null) { + //delayedLeashInfo无数据 + if(Pl$LeashData.leashHolder != null) {//且LeashHolder不为null,则直接用它 + setLeashedTo(Pl$LeashData.leashHolder, true); + return; + } + return; + + } + if(this.Pl$LeashData.delayedLeashInfo.left().isPresent()) { + //如果有实体的UUID(一般是LivingEntity),则在服务器其通过UUID来查找实体 + Entity entity = ((ServerLevel) this.level()).getEntity(this.Pl$LeashData.delayedLeashInfo.left().get()); + if(entity != null) { + setLeashedTo(entity, true); + } + } else if(this.Pl$LeashData.delayedLeashInfo.right().isPresent()) { + //如果有实体的坐标(一般就是拴绳结),在服务器端获取拴绳结实体(通过给定坐标和维度获取) + setLeashedTo(LeashFenceKnotEntity.getOrCreateKnot(this.level(), this.Pl$LeashData.delayedLeashInfo.right().get()), true); + } + + } + + @org.jetbrains.annotations.Nullable + @Override + public LeashData getLeashData() { + return Pl$LeashData; + } + @SuppressWarnings("AddedMixinMembersNamePattern") + @Override + @Nullable + public LeashData getLeashDataFromEntityData() { + CompoundTag compoundTag = this.entityData.get(Pl$LEASH_DATA); + return compoundTag.read("Pl$LeashData", Leashable.LeashData.CODEC).orElse(null); + } + @Override + public void setLeashData(@org.jetbrains.annotations.Nullable Leashable.LeashData pLeashData) { + this.Pl$LeashData = pLeashData; + saveLeashData(pLeashData); + } + @SuppressWarnings("AddedMixinMembersNamePattern") + @Unique + private void saveLeashData(@org.jetbrains.annotations.Nullable LeashData pLeashData) { + CompoundTag compoundTag = new CompoundTag(); + this.writeLeashData(compoundTag, pLeashData); + + this.entityData.set(Pl$LEASH_DATA, compoundTag); + } + @SuppressWarnings("AddedMixinMembersNamePattern") + @Override + public boolean canBeLeashedInstantly(Player player) { + return !isLeashed(); + } + @Inject( + method = {"defineSynchedData"}, at = {@At("TAIL")} + ) + //定义Client/Server player 同步数据 + private void defineSyncData (SynchedEntityData.Builder pBuilder, CallbackInfo ci) { + CompoundTag leashCompoundTag = new CompoundTag(); + this.writeLeashData(leashCompoundTag, null); + pBuilder.define(Pl$LEASH_DATA, leashCompoundTag); + pBuilder.define(DATA_ENTITY_LEASH_LENGTH, 5.0F); + } + @Inject( + method = {"addAdditionalSaveData"}, at = {@At("RETURN")} + )//数据保存 + private void addSaveData(CompoundTag pCompound, CallbackInfo ci) { + CompoundTag pLeashTag = new CompoundTag(); + writeLeashData(pLeashTag, Pl$LeashData); + pCompound.put("Pl$LeashData", pLeashTag); + this.entityData.set(Pl$LEASH_DATA, pLeashTag); + pCompound.putFloat("LeashLength", getLeashLength()); + } + @Inject( + method = {"readAdditionalSaveData"}, at = {@At("RETURN")} + )//数据读取 + private void readSaveData(CompoundTag pCompound, CallbackInfo ci) { + Leashable.LeashData leashable$leashdata = pCompound.read("Pl$LeashData", Leashable.LeashData.CODEC).orElse(null); + if (this.getLeashData() != null && leashable$leashdata == null) { + this.removeLeash(); + } + this.setLeashData(leashable$leashdata); + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/mixin/client/MixinClientBootstrap.java b/src/main/java/com/r3944realms/leashedplayer/mixin/client/MixinClientBootstrap.java new file mode 100644 index 0000000..faba909 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/mixin/client/MixinClientBootstrap.java @@ -0,0 +1,20 @@ +package com.r3944realms.leashedplayer.mixin.client; + +import com.r3944realms.leashedplayer.client.renderer.item.properties.conditionalRange.ConditionalRangeItemModelProperties; +import net.minecraft.client.ClientBootstrap; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ClientBootstrap.class) +public class MixinClientBootstrap { + @Inject(method = "bootstrap", at = @At( + value = "INVOKE", + target = "Lnet/minecraft/client/renderer/item/properties/numeric/RangeSelectItemModelProperties;bootstrap()V", + shift = At.Shift.AFTER) + ) + private static void bootstrap(CallbackInfo ci) { + ConditionalRangeItemModelProperties.bootstrap(); + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/mixin/client/MixinLevelRenderer.java b/src/main/java/com/r3944realms/leashedplayer/mixin/client/MixinLevelRenderer.java new file mode 100644 index 0000000..1b2f50a --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/mixin/client/MixinLevelRenderer.java @@ -0,0 +1,45 @@ +package com.r3944realms.leashedplayer.mixin.client; + +import com.mojang.blaze3d.resource.GraphicsResourceAllocator; +import com.mojang.blaze3d.vertex.PoseStack; +import com.r3944realms.leashedplayer.client.renderer.LeashRendererUtil; +import net.minecraft.client.Camera; +import net.minecraft.client.DeltaTracker; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.renderer.GameRenderer; +import net.minecraft.client.renderer.LevelRenderer; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.RenderBuffers; +import org.joml.Matrix4f; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import javax.annotation.Nullable; + + +@Mixin(LevelRenderer.class) +public abstract class MixinLevelRenderer { + @Shadow + @Nullable + private ClientLevel level; + + @Shadow @Final private RenderBuffers renderBuffers; + + @Inject( + method = {"renderLevel"}, + at = @At(value = "HEAD") + ) + private void renderLevel(GraphicsResourceAllocator graphicsResourceAllocator, DeltaTracker deltaTracker, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, Matrix4f frustumMatrix, Matrix4f projectionMatrix, CallbackInfo ci) { + assert this.level != null; + PoseStack poseStack = new PoseStack(); + MultiBufferSource.BufferSource multibuffersource$buffersource = this.renderBuffers.bufferSource(); + LeashRendererUtil.levelRenderLeash(level, camera, poseStack, multibuffersource$buffersource); + } + + +} + diff --git a/src/main/java/com/r3944realms/leashedplayer/mixin/client/MixinPlayerRenderState.java b/src/main/java/com/r3944realms/leashedplayer/mixin/client/MixinPlayerRenderState.java new file mode 100644 index 0000000..f6673a2 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/mixin/client/MixinPlayerRenderState.java @@ -0,0 +1,39 @@ +package com.r3944realms.leashedplayer.mixin.client; + +import com.r3944realms.leashedplayer.client.renderer.PlayerLeashState; +import com.r3944realms.leashedplayer.client.renderer.PlayerSlotItemLayerState; +import com.r3944realms.leashedplayer.modInterface.IPlayerRenderStateExtension; +import net.minecraft.client.renderer.entity.state.PlayerRenderState; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; + +@SuppressWarnings({"AddedMixinMembersNamePattern"}) +@Mixin(PlayerRenderState.class) +public class MixinPlayerRenderState implements IPlayerRenderStateExtension { + @Unique + @Nullable + private PlayerLeashState playerLeashState; + @Unique + private PlayerSlotItemLayerState playerSlotItemLayerState; + @Override + public @Nullable PlayerLeashState getPlayerLeashState() { + return playerLeashState; + } + + @Override + public void setPlayerLeashState(@Nullable PlayerLeashState playerRenderState) { + this.playerLeashState = playerRenderState; + } + + @Override + public PlayerSlotItemLayerState getPlayerSlotItemLayerState() { + return playerSlotItemLayerState; + } + + @Override + public void setPlayerSlotItemLayerState(PlayerSlotItemLayerState playerRenderState) { + this.playerSlotItemLayerState = playerRenderState; + } + +} diff --git a/src/main/java/com/r3944realms/leashedplayer/mixin/client/MixinPlayerRenderer.java b/src/main/java/com/r3944realms/leashedplayer/mixin/client/MixinPlayerRenderer.java new file mode 100644 index 0000000..e263a8b --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/mixin/client/MixinPlayerRenderer.java @@ -0,0 +1,30 @@ +package com.r3944realms.leashedplayer.mixin.client; + +import com.r3944realms.leashedplayer.client.renderer.LeashRendererUtil; +import com.r3944realms.leashedplayer.client.renderer.PlayerSlotItemLayerState; +import com.r3944realms.leashedplayer.modInterface.IPlayerRenderStateExtension; +import com.r3944realms.leashedplayer.modInterface.IPlayerRendererExtension; +import net.minecraft.client.model.PlayerModel; +import net.minecraft.client.player.AbstractClientPlayer; +import net.minecraft.client.renderer.entity.EntityRendererProvider; +import net.minecraft.client.renderer.entity.LivingEntityRenderer; +import net.minecraft.client.renderer.entity.player.PlayerRenderer; +import net.minecraft.client.renderer.entity.state.PlayerRenderState; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(PlayerRenderer.class) +public abstract class MixinPlayerRenderer extends LivingEntityRenderer implements IPlayerRendererExtension { + public MixinPlayerRenderer(EntityRendererProvider.Context pContext, PlayerModel pModel, float pShadowRadius) { + super(pContext, pModel, pShadowRadius); + } + @Inject(method = {"extractRenderState(Lnet/minecraft/client/player/AbstractClientPlayer;Lnet/minecraft/client/renderer/entity/state/PlayerRenderState;F)V"}, at = @At("TAIL")) + public void extractRenderState(AbstractClientPlayer abstractClientPlayer, PlayerRenderState playerRenderState, float partTicks, CallbackInfo ci) { + LeashRendererUtil.createPlayerLeashState(abstractClientPlayer, (IPlayerRenderStateExtension) playerRenderState, partTicks); + ((IPlayerRenderStateExtension)playerRenderState).setPlayerSlotItemLayerState(new PlayerSlotItemLayerState(abstractClientPlayer)); + } + + +} \ No newline at end of file diff --git a/src/main/java/com/r3944realms/leashedplayer/mixin/item/MixinArmorSlotMixin.java b/src/main/java/com/r3944realms/leashedplayer/mixin/item/MixinArmorSlotMixin.java new file mode 100644 index 0000000..ce9d211 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/mixin/item/MixinArmorSlotMixin.java @@ -0,0 +1,16 @@ +package com.r3944realms.leashedplayer.mixin.item; + +import net.minecraft.world.inventory.ArmorSlot; +import net.minecraft.world.item.ItemStack; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(ArmorSlot.class) +public class MixinArmorSlotMixin { + @Inject(method = "mayPlace", at = @At("RETURN"), cancellable = true) + public void allowItemEquipping(ItemStack stack, CallbackInfoReturnable cir) { + cir.setReturnValue(true); + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/mixin/item/MixinLeadItem.java b/src/main/java/com/r3944realms/leashedplayer/mixin/item/MixinLeadItem.java new file mode 100644 index 0000000..7232df3 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/mixin/item/MixinLeadItem.java @@ -0,0 +1,57 @@ +package com.r3944realms.leashedplayer.mixin.item; + +import com.r3944realms.leashedplayer.modInterface.PlayerLeashable; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.InteractionResult; +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.item.ItemStack; +import net.minecraft.world.item.LeadItem; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.gameevent.GameEvent; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.List; + +@Mixin(LeadItem.class) +public class MixinLeadItem { + /** + * 拴住自己的逻辑 + */ + @Inject( + method = {"bindPlayerMobs"}, + at = @At("HEAD"), + cancellable = true) + private static void selfLeash(Player pPlayer, Level pLevel, BlockPos pPos, CallbackInfoReturnable cir) { + List list = LeadItem.leashableInArea(pLevel, pPos, p_353025_ -> p_353025_.getLeashHolder() == pPlayer); + if (list.isEmpty()) { + ItemStack mainHandItem = pPlayer.getMainHandItem(); + if (!(mainHandItem.getItem() instanceof LeadItem )) { + return; + } + //非创造模式减少,防止刷物品 + if(!pPlayer.isCreative()) mainHandItem.shrink(1); + //自己 + Entity leashDataEntity = PlayerLeashable.getLeashDataEntity((ServerPlayer) pPlayer, (ServerLevel) pLevel); + PlayerLeashable playerLeashable = (PlayerLeashable) pPlayer; + if(leashDataEntity != null) { + Leashable.dropLeash((Entity & Leashable) playerLeashable, true, true); + } + //获取拴绳结实体 + LeashFenceKnotEntity leashfenceknotentity = LeashFenceKnotEntity.getOrCreateKnot(pLevel, pPos); + //播放绳结被放置的声音 + leashfenceknotentity.playPlacementSound(); + //将自己与拴绳结绑定LeashData + playerLeashable.setLeashedTo(leashfenceknotentity, true); + pLevel.gameEvent(GameEvent.BLOCK_ATTACH, pPos, GameEvent.Context.of(pPlayer)); + cir.setReturnValue(InteractionResult.SUCCESS); + } + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/mixin/registry/MixinJukeboxSongs.java b/src/main/java/com/r3944realms/leashedplayer/mixin/registry/MixinJukeboxSongs.java new file mode 100644 index 0000000..b49fc35 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/mixin/registry/MixinJukeboxSongs.java @@ -0,0 +1,18 @@ +package com.r3944realms.leashedplayer.mixin.registry; + +import com.r3944realms.leashedplayer.datagen.provider.attributes.ModJukeboxSongs; +import net.minecraft.data.worldgen.BootstrapContext; +import net.minecraft.world.item.JukeboxSong; +import net.minecraft.world.item.JukeboxSongs; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(JukeboxSongs.class) +public interface MixinJukeboxSongs { + @Inject(method = {"bootstrap"}, at = @At("TAIL")) + private static void bootstrap(BootstrapContext pContext, CallbackInfo ci) { + ModJukeboxSongs.JukeboxSongBootstrap(pContext); + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/mixin/registry/MixinPaintingVariants.java b/src/main/java/com/r3944realms/leashedplayer/mixin/registry/MixinPaintingVariants.java new file mode 100644 index 0000000..9eadd23 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/mixin/registry/MixinPaintingVariants.java @@ -0,0 +1,19 @@ +package com.r3944realms.leashedplayer.mixin.registry; + + +import com.r3944realms.leashedplayer.datagen.provider.attributes.ModPaintingVariants; +import net.minecraft.data.worldgen.BootstrapContext; +import net.minecraft.world.entity.decoration.PaintingVariant; +import net.minecraft.world.entity.decoration.PaintingVariants; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(PaintingVariants.class) +public class MixinPaintingVariants { + @Inject(method = {"bootstrap"}, at = @At("TAIL")) + private static void bootstrap(BootstrapContext pContext , CallbackInfo ci) { + ModPaintingVariants.PaintingVariantBootstrap(pContext); + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/mixin/server/MixinServerGamePacketListenerImpl.java b/src/main/java/com/r3944realms/leashedplayer/mixin/server/MixinServerGamePacketListenerImpl.java new file mode 100644 index 0000000..89183dc --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/mixin/server/MixinServerGamePacketListenerImpl.java @@ -0,0 +1,60 @@ +package com.r3944realms.leashedplayer.mixin.server; + +import com.r3944realms.leashedplayer.content.gamerules.GameruleRegistry; +import com.r3944realms.leashedplayer.content.gamerules.Server.TeleportWithLeashedPlayers; +import com.r3944realms.leashedplayer.modInterface.PlayerLeashable; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerGamePacketListenerImpl; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.Leashable; +import net.minecraft.world.entity.PositionMoveRotation; +import net.minecraft.world.entity.Relative; +import net.minecraft.world.phys.Vec3; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.*; +import java.util.stream.Collectors; + +import static com.r3944realms.leashedplayer.utils.Logger.logger; +@Mixin(ServerGamePacketListenerImpl.class) +public class MixinServerGamePacketListenerImpl { + + @Shadow + public ServerPlayer player; + @Unique + private List Pl$LeashPlayers = new ArrayList<>(); + @Inject(method = {"teleport(Lnet/minecraft/world/entity/PositionMoveRotation;Ljava/util/Set;)V"}, at = {@At("HEAD")}) + private void teleportHead(PositionMoveRotation positionMoveRotation, Set pRelativeSet, CallbackInfo ci) { + try { + //獲取Holder + this.Pl$LeashPlayers = ((PlayerLeashable)this.player).getLeashHolder() != null ? Collections.emptyList() : Objects.requireNonNull(this.player.getServer()).getPlayerList().getPlayers().stream().filter(serverPlayer -> (serverPlayer instanceof PlayerLeashable) && ((PlayerLeashable)serverPlayer).getLeashHolder() == this.player && player != serverPlayer).collect(Collectors.toList()); + } catch (Exception e) { + logger.error("Internal Error:",e); + } + } + @Inject(method = {"teleport(Lnet/minecraft/world/entity/PositionMoveRotation;Ljava/util/Set;)V"}, at = {@At("TAIL")}) + private void teleportTail(PositionMoveRotation moveRotation, Set pRelativeSet, CallbackInfo ci) { + if(GameruleRegistry.getGameruleBoolValue(this.player.serverLevel(), TeleportWithLeashedPlayers.ID)) { + for (Entity Pl$LeashPlayer : this.Pl$LeashPlayers) { + if(Pl$LeashPlayer instanceof ServerPlayer) { + if(Pl$LeashPlayer instanceof PlayerLeashable playerLeashable) { + Leashable.dropLeash((Entity & Leashable) playerLeashable, false,false); + if(((ServerPlayer) playerLeashable).serverLevel() == this.player.serverLevel()) { + ((ServerPlayer) playerLeashable).connection.teleport(moveRotation, pRelativeSet); + } else { + Vec3 vec3 = moveRotation.deltaMovement(); + ((ServerPlayer) playerLeashable).teleportTo(this.player.serverLevel(), vec3.x, vec3.y, vec3.z, pRelativeSet, moveRotation.yRot(), moveRotation.yRot(),false); + ((ServerPlayer) playerLeashable).stopRiding(); + } + playerLeashable.setLeashedTo(this.player, true); + } + } + } + } + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/modInterface/ILivingEntityExtension.java b/src/main/java/com/r3944realms/leashedplayer/modInterface/ILivingEntityExtension.java new file mode 100644 index 0000000..32e7165 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/modInterface/ILivingEntityExtension.java @@ -0,0 +1,28 @@ +package com.r3944realms.leashedplayer.modInterface; + +public interface ILivingEntityExtension { + /** + * 获取拴绳的长度 + * @return length 拴绳的长度(Float) + */ + float getLeashLength(); + + /** + * 设置拴绳的长度 + * @param length 拴绳的长度(Float) + */ + void setLeashLength(float length); + + /** + * 设置超出断裂长度后等待时间,如果时间到仍超出则会执行断裂操作 + * @apiNote 该为服务器方法,只能在服务器端调用,切勿在客户端线程调用 + * @param keepTick 等待tick(int) + */ + void setKeepLeashTick(int keepTick); + /** + * 获取超出断裂长度后等待时间,如果时间到仍超出则会执行断裂操作 + * @apiNote 该为服务器方法,只能在服务器端调用,切勿在客户端线程调用 + * @return keepTick 等待tick(int) + */ + int getKeepLeashTick(); +} diff --git a/src/main/java/com/r3944realms/leashedplayer/modInterface/IPlayerRenderStateExtension.java b/src/main/java/com/r3944realms/leashedplayer/modInterface/IPlayerRenderStateExtension.java new file mode 100644 index 0000000..142a8ab --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/modInterface/IPlayerRenderStateExtension.java @@ -0,0 +1,11 @@ +package com.r3944realms.leashedplayer.modInterface; + +import com.r3944realms.leashedplayer.client.renderer.PlayerLeashState; +import com.r3944realms.leashedplayer.client.renderer.PlayerSlotItemLayerState; + +public interface IPlayerRenderStateExtension { + PlayerLeashState getPlayerLeashState(); + void setPlayerLeashState(PlayerLeashState playerRenderState); + PlayerSlotItemLayerState getPlayerSlotItemLayerState(); + void setPlayerSlotItemLayerState(PlayerSlotItemLayerState playerRenderState); +} diff --git a/src/main/java/com/r3944realms/leashedplayer/modInterface/IPlayerRendererExtension.java b/src/main/java/com/r3944realms/leashedplayer/modInterface/IPlayerRendererExtension.java new file mode 100644 index 0000000..c5d50fc --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/modInterface/IPlayerRendererExtension.java @@ -0,0 +1,24 @@ +package com.r3944realms.leashedplayer.modInterface; + +import net.minecraft.client.Camera; +import net.minecraft.world.phys.Vec3; + +public interface IPlayerRendererExtension { + default void renderLeashForCamera( + Camera pCamera, + float pPartialTick, + com.mojang.blaze3d.vertex.PoseStack pPoseStack, + net.minecraft.client.renderer.MultiBufferSource pBufferSource, + E pLeashHolder + ) { + renderLeashForCamera(pCamera, pPartialTick, pPoseStack, pBufferSource, pLeashHolder, Vec3.ZERO); + } + void renderLeashForCamera( + Camera pCamera, + float pPartialTick, + com.mojang.blaze3d.vertex.PoseStack pPoseStack, + net.minecraft.client.renderer.MultiBufferSource pBufferSource, + E pLeashHolder, + Vec3 holderOffset + ); +} diff --git a/src/main/java/com/r3944realms/leashedplayer/modInterface/PlayerLeashable.java b/src/main/java/com/r3944realms/leashedplayer/modInterface/PlayerLeashable.java new file mode 100644 index 0000000..5de681f --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/modInterface/PlayerLeashable.java @@ -0,0 +1,225 @@ +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; + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/network/LeashedPlayerNetwork.java b/src/main/java/com/r3944realms/leashedplayer/network/LeashedPlayerNetwork.java new file mode 100644 index 0000000..00d00fc --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/network/LeashedPlayerNetwork.java @@ -0,0 +1,35 @@ +package com.r3944realms.leashedplayer.network; + +import com.r3944realms.leashedplayer.LeashedPlayer; +import com.r3944realms.leashedplayer.network.client.UpdatePlayerMovement; +import com.r3944realms.leashedplayer.network.server.DecreaseLeashRopeLength; +import com.r3944realms.leashedplayer.network.server.IncreaseLeashRopeLength; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.common.EventBusSubscriber; +import net.neoforged.neoforge.network.event.RegisterPayloadHandlersEvent; +import net.neoforged.neoforge.network.registration.PayloadRegistrar; + +@EventBusSubscriber(modid = LeashedPlayer.MOD_ID, bus = EventBusSubscriber.Bus.MOD) +public class LeashedPlayerNetwork { + + @SubscribeEvent + public static void registerPackets(RegisterPayloadHandlersEvent event) { + PayloadRegistrar registrar = event.registrar(LeashedPlayer.MOD_ID); + registrar.playToClient( + UpdatePlayerMovement.TYPE, + UpdatePlayerMovement.STREAM_CODEC, + UpdatePlayerMovement::handle + ); + registrar.playToServer( + IncreaseLeashRopeLength.TYPE, + IncreaseLeashRopeLength.STREAM_CODEC, + IncreaseLeashRopeLength::handle + ); + registrar.playToServer( + DecreaseLeashRopeLength.TYPE, + DecreaseLeashRopeLength.STREAM_CODEC, + DecreaseLeashRopeLength::handle + ); + } + +} diff --git a/src/main/java/com/r3944realms/leashedplayer/network/client/BooleanGameRuleValueChangeData.java b/src/main/java/com/r3944realms/leashedplayer/network/client/BooleanGameRuleValueChangeData.java new file mode 100644 index 0000000..f180668 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/network/client/BooleanGameRuleValueChangeData.java @@ -0,0 +1,31 @@ +package com.r3944realms.leashedplayer.network.client; + +import com.r3944realms.leashedplayer.LeashedPlayer; +import com.r3944realms.leashedplayer.content.gamerules.GameRules; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.ResourceLocation; +import net.neoforged.neoforge.network.handling.IPayloadContext; +import org.jetbrains.annotations.NotNull; + +public record BooleanGameRuleValueChangeData(String gamerule_id, boolean value) implements IGameRuleValueChangeData { + public static Type TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(LeashedPlayer.MOD_ID,"bool_gamerule_value_change")); + public static final StreamCodec STREAM_CODEC = + StreamCodec.composite( + ByteBufCodecs.STRING_UTF8, BooleanGameRuleValueChangeData::gamerule_id, + ByteBufCodecs.BOOL, BooleanGameRuleValueChangeData::value, + BooleanGameRuleValueChangeData::new + ); + + public void handle(IPayloadContext context) { + context.enqueueWork(() -> { + GameRules.gamerulesBooleanValuesClient.put(gamerule_id, value); + }); + } + @Override + public @NotNull Type type() { + return TYPE; + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/network/client/IGameRuleValueChangeData.java b/src/main/java/com/r3944realms/leashedplayer/network/client/IGameRuleValueChangeData.java new file mode 100644 index 0000000..3911513 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/network/client/IGameRuleValueChangeData.java @@ -0,0 +1,10 @@ +package com.r3944realms.leashedplayer.network.client; + +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import org.jetbrains.annotations.NotNull; + +public interface IGameRuleValueChangeData extends CustomPacketPayload { + @Override + @NotNull + Type type(); +} diff --git a/src/main/java/com/r3944realms/leashedplayer/network/client/UpdatePlayerMovement.java b/src/main/java/com/r3944realms/leashedplayer/network/client/UpdatePlayerMovement.java new file mode 100644 index 0000000..f97fae5 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/network/client/UpdatePlayerMovement.java @@ -0,0 +1,55 @@ +package com.r3944realms.leashedplayer.network.client; + +import com.r3944realms.leashedplayer.LeashedPlayer; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.phys.Vec3; +import net.neoforged.neoforge.network.codec.NeoForgeStreamCodecs; +import net.neoforged.neoforge.network.handling.IPayloadContext; +import org.jetbrains.annotations.NotNull; + +/** + * @author qyl27 + * @param operation + * @param x + * @param y + * @param z + */ +public record UpdatePlayerMovement(Operation operation, double x, double y, double z) implements CustomPacketPayload { + public static final Type TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(LeashedPlayer.MOD_ID,"update_player_movement")); + @Override + public @NotNull Type type() { + return TYPE; + } + public static final StreamCodec STREAM_CODEC = + StreamCodec.composite( + NeoForgeStreamCodecs.enumCodec(Operation.class), UpdatePlayerMovement::operation, + ByteBufCodecs.DOUBLE, UpdatePlayerMovement::x, + ByteBufCodecs.DOUBLE, UpdatePlayerMovement::y, + ByteBufCodecs.DOUBLE, UpdatePlayerMovement::z, + UpdatePlayerMovement::new + ); + public void handle(IPayloadContext context) { + context.enqueueWork(() -> { + Player player = context.player(); + switch (operation) { + case ADD -> player.addDeltaMovement(new Vec3(x, y, z)); + case SET -> player.setDeltaMovement(new Vec3(x, y, z)); + case MULTIPLY -> player.addDeltaMovement(player.getDeltaMovement().multiply(x, y, z)); + } + } + + ); + } + public enum Operation { + SET, + ADD, + MULTIPLY + } +} + + diff --git a/src/main/java/com/r3944realms/leashedplayer/network/server/Code.java b/src/main/java/com/r3944realms/leashedplayer/network/server/Code.java new file mode 100644 index 0000000..00c8560 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/network/server/Code.java @@ -0,0 +1,8 @@ +package com.r3944realms.leashedplayer.network.server; + +public enum Code { + OTHER_ST, + OTHER_WE, + SELF; + public static final String LEASH_LENGTH_FAILED_SET = "leashedplayer.leash_rope.length.failed"; +} diff --git a/src/main/java/com/r3944realms/leashedplayer/network/server/DecreaseLeashRopeLength.java b/src/main/java/com/r3944realms/leashedplayer/network/server/DecreaseLeashRopeLength.java new file mode 100644 index 0000000..d101487 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/network/server/DecreaseLeashRopeLength.java @@ -0,0 +1,61 @@ +package com.r3944realms.leashedplayer.network.server; + +import com.r3944realms.leashedplayer.LeashedPlayer; +import com.r3944realms.leashedplayer.modInterface.ILivingEntityExtension; +import com.r3944realms.leashedplayer.modInterface.PlayerLeashable; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.chat.Component; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.player.Player; +import net.neoforged.neoforge.network.codec.NeoForgeStreamCodecs; +import net.neoforged.neoforge.network.handling.IPayloadContext; +import org.jetbrains.annotations.NotNull; + +import java.util.UUID; + +public record DecreaseLeashRopeLength(Code code, String playerUUID) implements CustomPacketPayload { + public static final Type TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(LeashedPlayer.MOD_ID,"sub_leash_rope_length")); + public static final StreamCodec STREAM_CODEC = + StreamCodec.composite( + NeoForgeStreamCodecs.enumCodec(Code.class), DecreaseLeashRopeLength::code, + ByteBufCodecs.STRING_UTF8, DecreaseLeashRopeLength::playerUUID, + DecreaseLeashRopeLength::new + ); + public static final String DECREASE_LEASH_ROPE_LENGTH = "leashedplayer.leash_rope.length.decrease", + DECREASE_SELF_LEASH_ROPE_LENGTH = DECREASE_LEASH_ROPE_LENGTH + ".self"; + + public void handle(IPayloadContext context) { + context.enqueueWork(() -> { + Player player = context.player(); + ServerLevel level = (ServerLevel) player.level(); + Player playerByUUID = level.getPlayerByUUID(UUID.fromString(playerUUID)); + if (playerByUUID != null) { + ILivingEntityExtension entityExtension = (ILivingEntityExtension) playerByUUID; + Entity leashDataEntity = PlayerLeashable.getLeashDataEntity((ServerPlayer) playerByUUID, level); + if ((leashDataEntity != null && code == Code.OTHER_ST && leashDataEntity == player ) || (leashDataEntity != null && code == Code.OTHER_ST)) { + float newValue = Math.max(Math.min(entityExtension.getLeashLength() - 1, LeashedPlayer.M4()), LeashedPlayer.M3()); + entityExtension.setLeashLength(newValue); + ((ServerPlayer) playerByUUID).sendSystemMessage(Component.translatable(DECREASE_SELF_LEASH_ROPE_LENGTH, newValue), true); + ((ServerPlayer) player).sendSystemMessage(Component.translatable(DECREASE_LEASH_ROPE_LENGTH, playerByUUID.getDisplayName(), newValue), true); + } else if (code == Code.SELF) { + float newValue = Math.max(Math.min(entityExtension.getLeashLength() - 1, LeashedPlayer.M4()), LeashedPlayer.M3()); + entityExtension.setLeashLength(newValue); + ((ServerPlayer) playerByUUID).sendSystemMessage(Component.translatable(DECREASE_SELF_LEASH_ROPE_LENGTH, newValue), true); + } else { + ((ServerPlayer) playerByUUID).sendSystemMessage(Component.translatable(Code.LEASH_LENGTH_FAILED_SET), true); + ((ServerPlayer) player).sendSystemMessage(Component.translatable(Code.LEASH_LENGTH_FAILED_SET), true); + } + } + }); + } + @Override + public @NotNull Type type() { + return TYPE; + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/network/server/IncreaseLeashRopeLength.java b/src/main/java/com/r3944realms/leashedplayer/network/server/IncreaseLeashRopeLength.java new file mode 100644 index 0000000..67191c5 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/network/server/IncreaseLeashRopeLength.java @@ -0,0 +1,63 @@ +package com.r3944realms.leashedplayer.network.server; + +import com.r3944realms.leashedplayer.LeashedPlayer; +import com.r3944realms.leashedplayer.modInterface.ILivingEntityExtension; +import com.r3944realms.leashedplayer.modInterface.PlayerLeashable; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.chat.Component; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.player.Player; +import net.neoforged.neoforge.network.codec.NeoForgeStreamCodecs; +import net.neoforged.neoforge.network.handling.IPayloadContext; +import org.jetbrains.annotations.NotNull; + +import java.util.UUID; + +public record IncreaseLeashRopeLength(Code code, String playerUUID) implements CustomPacketPayload { + public static final Type TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(LeashedPlayer.MOD_ID,"add_leash_rope_length")); + public static final StreamCodec STREAM_CODEC = + StreamCodec.composite( + NeoForgeStreamCodecs.enumCodec(Code.class), IncreaseLeashRopeLength::code, + ByteBufCodecs.STRING_UTF8, IncreaseLeashRopeLength::playerUUID, + IncreaseLeashRopeLength::new + ); + public static final String INCREASE_LEASH_ROPE_LENGTH = "leashedplayer.leash_rope.length.increase", + INCREASE_SELF_LEASH_ROPE_LENGTH = INCREASE_LEASH_ROPE_LENGTH + ".self"; + + + public void handle(IPayloadContext context) { + context.enqueueWork(() -> { + Player player = context.player(); + ServerLevel level = (ServerLevel) player.level(); + Player playerByUUID = level.getPlayerByUUID(UUID.fromString(playerUUID)); + if (playerByUUID != null) { + ILivingEntityExtension entityExtension = (ILivingEntityExtension) playerByUUID; + Entity leashDataEntity = PlayerLeashable.getLeashDataEntity((ServerPlayer) playerByUUID, level); + if ((leashDataEntity != null && code == Code.OTHER_ST && leashDataEntity == player ) || (leashDataEntity != null && code == Code.OTHER_ST)) { + float newValue = Math.max(Math.min(entityExtension.getLeashLength() + 1, LeashedPlayer.M4()), LeashedPlayer.M3()); + entityExtension.setLeashLength(newValue); + ((ServerPlayer) playerByUUID).sendSystemMessage(Component.translatable(INCREASE_SELF_LEASH_ROPE_LENGTH, newValue), true); + ((ServerPlayer) player).sendSystemMessage(Component.translatable(INCREASE_LEASH_ROPE_LENGTH, playerByUUID.getDisplayName(), newValue), true); + } else if (code == Code.SELF) { + float newValue = Math.max(Math.min(entityExtension.getLeashLength() + 1, LeashedPlayer.M4()), LeashedPlayer.M3()); + entityExtension.setLeashLength(newValue); + ((ServerPlayer) playerByUUID).sendSystemMessage(Component.translatable(INCREASE_SELF_LEASH_ROPE_LENGTH, newValue), true); + } else { + ((ServerPlayer) playerByUUID).sendSystemMessage(Component.translatable(Code.LEASH_LENGTH_FAILED_SET), true); + ((ServerPlayer) player).sendSystemMessage(Component.translatable(Code.LEASH_LENGTH_FAILED_SET), true); + + } + } + }); + } + @Override + public @NotNull Type type() { + return TYPE; + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/utils/ColorUtil.java b/src/main/java/com/r3944realms/leashedplayer/utils/ColorUtil.java new file mode 100644 index 0000000..0be7bdc --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/utils/ColorUtil.java @@ -0,0 +1,18 @@ +package com.r3944realms.leashedplayer.utils; + +public class ColorUtil { + // 渐变颜色计算 + public static int lerpColor(float progress) { + int a = (int) (getAlpha(-65536) * (1 - progress) + getAlpha(-16711936) * progress); + int r = (int) (getRed(-65536) * (1 - progress) + getRed(-16711936) * progress); + int g = (int) (getGreen(-65536) * (1 - progress) + getGreen(-16711936) * progress); + int b = (int) (getBlue(-65536) * (1 - progress) + getBlue(-16711936) * progress); + return (a << 24) | (r << 16) | (g << 8) | b; + } + + private static int getAlpha(int color) { return (color >> 24) & 0xFF; } + private static int getRed(int color) { return (color >> 16) & 0xFF; } + private static int getGreen(int color) { return (color >> 8) & 0xFF; } + private static int getBlue(int color) { return color & 0xFF; } +} + diff --git a/src/main/java/com/r3944realms/leashedplayer/utils/Enum/LanguageEnum.java b/src/main/java/com/r3944realms/leashedplayer/utils/Enum/LanguageEnum.java new file mode 100644 index 0000000..5593d0d --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/utils/Enum/LanguageEnum.java @@ -0,0 +1,13 @@ +package com.r3944realms.leashedplayer.utils.Enum; + +public enum LanguageEnum { + English("en_us"), + SimpleChinese("zh_cn"), + TraditionalChinese("zh_tw"), + LiteraryChinese("lzh"), + ; + public final String local; + LanguageEnum(String local) { + this.local = local; + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/utils/Enum/ModPartEnum.java b/src/main/java/com/r3944realms/leashedplayer/utils/Enum/ModPartEnum.java new file mode 100644 index 0000000..362b078 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/utils/Enum/ModPartEnum.java @@ -0,0 +1,21 @@ +package com.r3944realms.leashedplayer.utils.Enum; + +public enum ModPartEnum { + DEFAULT, + ITEM, + BLOCK, + ENCHANTMENT, + ADVANCEMENT, + CREATIVE_TAB, + CONFIG, + ENTITY, + GUI, + AUTHOR, + TITLE, + NAME, + DESCRIPTION, + INFO, + MESSAGE, + COMMAND, + +} diff --git a/src/main/java/com/r3944realms/leashedplayer/utils/Lazy.java b/src/main/java/com/r3944realms/leashedplayer/utils/Lazy.java new file mode 100644 index 0000000..afcda4d --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/utils/Lazy.java @@ -0,0 +1,25 @@ +package com.r3944realms.leashedplayer.utils; + +import java.util.function.Supplier; + +public class Lazy implements Supplier { + + private final Supplier delegate; + private T value; + + public Lazy(Supplier delegate) { + + this.delegate = delegate; + } + + @Override + public T get() { + + if (this.value == null) { + + this.value = this.delegate.get(); + } + + return this.value; + } +} \ No newline at end of file diff --git a/src/main/java/com/r3944realms/leashedplayer/utils/Logger.java b/src/main/java/com/r3944realms/leashedplayer/utils/Logger.java new file mode 100644 index 0000000..da59223 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/utils/Logger.java @@ -0,0 +1,7 @@ +package com.r3944realms.leashedplayer.utils; + +import org.slf4j.LoggerFactory; + +public class Logger { + public static final org.slf4j.Logger logger = LoggerFactory.getLogger(Logger.class); +} diff --git a/src/main/java/com/r3944realms/leashedplayer/utils/Util.java b/src/main/java/com/r3944realms/leashedplayer/utils/Util.java new file mode 100644 index 0000000..eaf3aed --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/utils/Util.java @@ -0,0 +1,136 @@ +package com.r3944realms.leashedplayer.utils; + +import com.r3944realms.leashedplayer.LeashedPlayer; +import com.r3944realms.leashedplayer.content.gamerules.GameRules; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.item.ItemEntity; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.ClipContext; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.*; +import net.neoforged.fml.loading.FMLPaths; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Function; + +import static com.r3944realms.leashedplayer.utils.Logger.logger; + +public class Util { + public static String getGameruleName(Class clazz) { + return GameRules.GAMERULE_PREFIX + clazz.getSimpleName(); + } + public static String getGameruleName(String gamerulesName) { + return GameRules.GAMERULE_PREFIX + gamerulesName; + } + + public static void configFileCreate(String[] children) {//初始化配置文件目录 + File configFile = new File(FMLPaths.CONFIGDIR.get().toFile(), LeashedPlayer.MOD_ID); + if (!configFile.exists()) { + boolean mkdirSuccess = configFile.mkdirs(); + if (!mkdirSuccess) { + logger.error("failed to create config directory for whimsicality"); + throw new RuntimeException("failed to create config directory for " + LeashedPlayer.MOD_ID); + } else { + for (String child : children) { + File file = new File(configFile, child); + if (!file.exists()) { + boolean mkdirChildrenSuccess = file.mkdirs(); + if (!mkdirChildrenSuccess) { + logger.error("failed to create {} directory for +" + LeashedPlayer.MOD_ID, child); + throw new RuntimeException("failed to create " + child + " directory for" +LeashedPlayer.MOD_ID); + } + } + } + } + } + } + public static void throwItemTowardsLook(Entity thrower, Item itemToThrow, float velocity, float inaccuracy) { + ItemEntity thrownItem = new ItemEntity( + thrower.level(), + thrower.getX(), + thrower.getEyeY() - 0.1, + thrower.getZ(), + new ItemStack(itemToThrow) + ); + thrownItem.setPickUpDelay(40); + Vec3 lookDirection = thrower.getLookAngle(); + + thrownItem.setDeltaMovement( + lookDirection.x * velocity + (thrower.level().random.nextGaussian() * 0.0075 * inaccuracy), + lookDirection.y * velocity + (thrower.level().random.nextGaussian() * 0.0075 * inaccuracy), + lookDirection.z * velocity + (thrower.level().random.nextGaussian() * 0.0075 * inaccuracy) + ); + + thrower.level().addFreshEntity(thrownItem); + } + + public static List getRefLookAtEntityHitResult(Entity looker, Level level, float distance, Function condition) { + Vec3 startPosition = looker.getEyePosition(); + Vec3 endPosition = looker.getLookAngle().normalize().scale(distance).add(startPosition); + BlockHitResult clip = level.clip(new ClipContext(startPosition, endPosition, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, looker)); + endPosition = clip.getLocation(); + AABB range = looker.getBoundingBox().expandTowards(endPosition.subtract(startPosition)); + List hits = new ArrayList<>(); + List entities = level.getEntities(looker, range, condition::apply); + for (Entity entity : entities) { + Vec3 vec3 = entity.getBoundingBox().clip(startPosition, endPosition).orElse(null); + if(vec3 != null) { + EntityHitResult entityHitResult = new EntityHitResult(entity, vec3); + hits.add(entityHitResult); + } + } + return hits; + } + + @Nullable + public static Entity getTheNearestEntityFromHitResultList(Entity looker, List hitResults) { + if(!hitResults.isEmpty()) { + hitResults.sort(Comparator.comparingDouble(e -> e.getLocation().distanceTo(looker.getEyePosition()))); + HitResult hitResult = hitResults.getFirst(); + if (hitResult instanceof EntityHitResult entityHitResult) + return entityHitResult.getEntity(); + else return null; + } + else return null; + } + /** + * 刹车( + * @param pEntity 刹车的实体 + * @param pMaxX X方向的最大动量 + * @param pMaxY Y方向的最大动量 + * @param pMaxZ Z方向的最大动量 + */ + public static void MovementBrake(Entity pEntity, double pMaxX, double pMaxY, double pMaxZ) { + Vec3 deltaMovement = pEntity.getDeltaMovement(); + double dX = Math.abs(deltaMovement.x) > pMaxX ? 0 : deltaMovement.x; + double dY = Math.abs(deltaMovement.y) > pMaxY ? 0 : deltaMovement.y; + double dZ = Math.abs(deltaMovement.z) > pMaxZ ? 0 : deltaMovement.z; + pEntity.setDeltaMovement(dX, dY,dZ); + pEntity.hurtMarked = true; + } + /** + * 刹车( + * @param pEntity 刹车的实体 + * @param pOpt 自定义规则 + */ + public static void MovementBrake(Entity pEntity, @javax.annotation.Nullable Consumer pOpt) { + Consumer consumer = pOpt; + if(pOpt == null) { + consumer = entity -> { + Vec3 deltaMovement = entity.getDeltaMovement(); + double dX = Math.abs(deltaMovement.x) > 1 ? 0 : deltaMovement.x; + double dY = Math.abs(deltaMovement.y) > 1 ? 0 : deltaMovement.y; + double dZ = Math.abs(deltaMovement.z) > 1 ? 0 : deltaMovement.z; + entity.setDeltaMovement(dX, dY,dZ); + entity.hurtMarked = true; + }; + } + consumer.accept(pEntity); + } +} diff --git a/src/main/java/com/r3944realms/leashedplayer/utils/dataModel/ItemModelUtils.java b/src/main/java/com/r3944realms/leashedplayer/utils/dataModel/ItemModelUtils.java new file mode 100644 index 0000000..dd8d7f0 --- /dev/null +++ b/src/main/java/com/r3944realms/leashedplayer/utils/dataModel/ItemModelUtils.java @@ -0,0 +1,47 @@ +package com.r3944realms.leashedplayer.utils.dataModel; + +import com.r3944realms.leashedplayer.client.renderer.item.ConditionalRangeItemModel; +import com.r3944realms.leashedplayer.client.renderer.item.properties.conditionalRange.ConditionalRangeItemModelProperty; +import net.minecraft.client.data.models.model.ModelTemplate; +import net.minecraft.client.data.models.model.TextureMapping; +import net.minecraft.client.renderer.item.ItemModel; +import net.minecraft.resources.ResourceLocation; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.api.distmarker.OnlyIn; + +import java.util.List; +import java.util.Optional; + +@OnlyIn(Dist.CLIENT) +public class ItemModelUtils { + public static ItemModel.Unbaked conditionalRange( + ConditionalRangeItemModelProperty property, float scale, ItemModel.Unbaked fallback, ConditionalRangeItemModel.Entry[] onTrue, ConditionalRangeItemModel.Entry[] onFalse + ) { + return new ConditionalRangeItemModel.Unbaked(property, scale, List.of(onTrue), List.of(onFalse), Optional.of(fallback)); + } + public static ItemModel.Unbaked conditionalRange( + ConditionalRangeItemModelProperty property, float scale, ConditionalRangeItemModel.Entry[] onTrue, ConditionalRangeItemModel.Entry[] onFalse + ) { + return new ConditionalRangeItemModel.Unbaked(property, scale, List.of(onTrue), List.of(onFalse), Optional.empty()); + } + public static ItemModel.Unbaked conditionalRange( + ConditionalRangeItemModelProperty property, ConditionalRangeItemModel.Entry[] onTrue, ConditionalRangeItemModel.Entry[] onFalse + ) { + return new ConditionalRangeItemModel.Unbaked(property, 1.0F, List.of(onTrue), List.of(onFalse), Optional.empty()); + } + public static ItemModel.Unbaked conditionalRange( + ConditionalRangeItemModelProperty property, ItemModel.Unbaked fallback, ConditionalRangeItemModel.Entry[] onTrue, ConditionalRangeItemModel.Entry[] onFalse + ) { + return new ConditionalRangeItemModel.Unbaked(property, 1.0F, List.of(onTrue), List.of(onFalse), Optional.of(fallback)); + } + public static ConditionalRangeItemModel.Entry override(ItemModel.Unbaked threshold, float model) { + return new ConditionalRangeItemModel.Entry(model, threshold); + } + public static ResourceLocation createFlatItemModelByPath(String MOD_ID, String path, ModelTemplate modelTemplate, java. util. function. BiConsumer consumer ) { + return modelTemplate.create( + ResourceLocation.fromNamespaceAndPath(MOD_ID, path), + TextureMapping.layer0(ResourceLocation.fromNamespaceAndPath(MOD_ID, path)), + consumer + ); + } +} diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg new file mode 100644 index 0000000..5d21920 --- /dev/null +++ b/src/main/resources/META-INF/accesstransformer.cfg @@ -0,0 +1,25 @@ +#package-private -> public +public net.minecraft.world.entity.Leashable$LeashData (Lnet/minecraft/world/entity/Entity;)V # LeashData +#priavte ->protected +protected net.minecraft.client.renderer.entity.EntityRenderer renderLeash(Lnet/minecraft/world/entity/Entity;FLcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;Lnet/minecraft/world/entity/Entity;)V # renderLeash +#这个方法原包为package-priavet 有时需要限制范围故修改 +public net.minecraft.world.level.GameRules$IntegerValue create(IIILnet/minecraft/world/flag/FeatureFlagSet;Ljava/util/function/BiConsumer;)Lnet/minecraft/world/level/GameRules$Type; # create +#因为'net.minecraft.world.level.GameRules.Type' 中不为 public。无法从外部软件包访问 +public net.minecraft.world.level.GameRules$Type (Ljava/util/function/Supplier;Ljava/util/function/Function;Ljava/util/function/BiConsumer;Lnet/minecraft/world/level/GameRules$VisitorCaller;Ljava/lang/Class;Lnet/minecraft/world/flag/FeatureFlagSet;)V # Type +#因为'net.minecraft.world.level.GameRules.VisitorCaller' 在 'net.minecraft.world.level.GameRules' 中不为 public。无法从外部软件包访问 +public net.minecraft.world.level.GameRules$VisitorCaller #Interface +#private -> public +public net.minecraft.world.entity.Leashable$LeashData delayedLeashHolderId # delayedLeashHolderId +#private -> public +public net.minecraft.world.entity.Leashable dropLeash(Lnet/minecraft/world/entity/Entity;ZZ)V +#priveate -> public +public net.minecraft.world.entity.Leashable readLeashDataInternal(Lnet/minecraft/nbt/CompoundTag;)Lnet/minecraft/world/entity/Leashable$LeashData; +#private -> protect +protected net.minecraft.world.entity.projectile.AbstractArrow life # life +#protect -> public +public net.minecraft.world.effect.MobEffect (Lnet/minecraft/world/effect/MobEffectCategory;I)V # MobEffect +#packge-private -> public +public net.minecraft.world.inventory.ArmorSlot #ArmorSlot +#private -> protected +protected net.minecraft.world.entity.projectile.Projectile cachedOwner # cachedOwner +protected net.minecraft.world.entity.projectile.Projectile ownerUUID # ownerUUID \ No newline at end of file diff --git a/src/main/resources/assets/leashedplayer/items/neoforge.json b/src/main/resources/assets/leashedplayer/items/neoforge.json new file mode 100644 index 0000000..0055b2f --- /dev/null +++ b/src/main/resources/assets/leashedplayer/items/neoforge.json @@ -0,0 +1,6 @@ +{ + "model": { + "type": "minecraft:model", + "model": "leashedplayer:item/neoforge" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/leashedplayer/models/item/neoforge.json b/src/main/resources/assets/leashedplayer/models/item/neoforge.json new file mode 100644 index 0000000..528ab26 --- /dev/null +++ b/src/main/resources/assets/leashedplayer/models/item/neoforge.json @@ -0,0 +1,12 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "leashedplayer:item/neoforge" + }, + "display": { + "head": { + "translation": [0, 0, -7.5], + "scale": [1.25, 1.25, 1.25] + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/leashedplayer/sounds/music/what_does_the_fox_say.ogg b/src/main/resources/assets/leashedplayer/sounds/music/what_does_the_fox_say.ogg new file mode 100644 index 0000000..3029305 Binary files /dev/null and b/src/main/resources/assets/leashedplayer/sounds/music/what_does_the_fox_say.ogg differ diff --git a/src/main/resources/assets/leashedplayer/textures/entity/decorated_pot/lead_rope_pottery_pattern.png b/src/main/resources/assets/leashedplayer/textures/entity/decorated_pot/lead_rope_pottery_pattern.png new file mode 100644 index 0000000..5cc4f7e Binary files /dev/null and b/src/main/resources/assets/leashedplayer/textures/entity/decorated_pot/lead_rope_pottery_pattern.png differ diff --git a/src/main/resources/assets/leashedplayer/textures/entity/projectiles/leash_rope_arrow.png b/src/main/resources/assets/leashedplayer/textures/entity/projectiles/leash_rope_arrow.png new file mode 100644 index 0000000..35b662f Binary files /dev/null and b/src/main/resources/assets/leashedplayer/textures/entity/projectiles/leash_rope_arrow.png differ diff --git a/src/main/resources/assets/leashedplayer/textures/entity/projectiles/spectral_leash_rope_arrow.png b/src/main/resources/assets/leashedplayer/textures/entity/projectiles/spectral_leash_rope_arrow.png new file mode 100644 index 0000000..af45d0e Binary files /dev/null and b/src/main/resources/assets/leashedplayer/textures/entity/projectiles/spectral_leash_rope_arrow.png differ diff --git a/src/main/resources/assets/leashedplayer/textures/entity/projectiles/tipped_leash_rope_arrow.png b/src/main/resources/assets/leashedplayer/textures/entity/projectiles/tipped_leash_rope_arrow.png new file mode 100644 index 0000000..fd1589e Binary files /dev/null and b/src/main/resources/assets/leashedplayer/textures/entity/projectiles/tipped_leash_rope_arrow.png differ diff --git a/src/main/resources/assets/leashedplayer/textures/gui/advancements/backgrounds/leashed_player.png b/src/main/resources/assets/leashedplayer/textures/gui/advancements/backgrounds/leashed_player.png new file mode 100644 index 0000000..43dac53 Binary files /dev/null and b/src/main/resources/assets/leashedplayer/textures/gui/advancements/backgrounds/leashed_player.png differ diff --git a/src/main/resources/assets/leashedplayer/textures/gui/progress_bar.png b/src/main/resources/assets/leashedplayer/textures/gui/progress_bar.png new file mode 100644 index 0000000..af615a1 Binary files /dev/null and b/src/main/resources/assets/leashedplayer/textures/gui/progress_bar.png differ diff --git a/src/main/resources/assets/leashedplayer/textures/item/amethyst_shears.png b/src/main/resources/assets/leashedplayer/textures/item/amethyst_shears.png new file mode 100644 index 0000000..eea8545 Binary files /dev/null and b/src/main/resources/assets/leashedplayer/textures/item/amethyst_shears.png differ diff --git a/src/main/resources/assets/leashedplayer/textures/item/bow_lra_pulling_0.png b/src/main/resources/assets/leashedplayer/textures/item/bow_lra_pulling_0.png new file mode 100644 index 0000000..7cd8732 Binary files /dev/null and b/src/main/resources/assets/leashedplayer/textures/item/bow_lra_pulling_0.png differ diff --git a/src/main/resources/assets/leashedplayer/textures/item/bow_lra_pulling_1.png b/src/main/resources/assets/leashedplayer/textures/item/bow_lra_pulling_1.png new file mode 100644 index 0000000..5f5c393 Binary files /dev/null and b/src/main/resources/assets/leashedplayer/textures/item/bow_lra_pulling_1.png differ diff --git a/src/main/resources/assets/leashedplayer/textures/item/bow_lra_pulling_2.png b/src/main/resources/assets/leashedplayer/textures/item/bow_lra_pulling_2.png new file mode 100644 index 0000000..2b67a06 Binary files /dev/null and b/src/main/resources/assets/leashedplayer/textures/item/bow_lra_pulling_2.png differ diff --git a/src/main/resources/assets/leashedplayer/textures/item/crossbow_leash_rope_arrow.png b/src/main/resources/assets/leashedplayer/textures/item/crossbow_leash_rope_arrow.png new file mode 100644 index 0000000..7517484 Binary files /dev/null and b/src/main/resources/assets/leashedplayer/textures/item/crossbow_leash_rope_arrow.png differ diff --git a/src/main/resources/assets/leashedplayer/textures/item/fabric.png b/src/main/resources/assets/leashedplayer/textures/item/fabric.png new file mode 100644 index 0000000..4ab8370 Binary files /dev/null and b/src/main/resources/assets/leashedplayer/textures/item/fabric.png differ diff --git a/src/main/resources/assets/leashedplayer/textures/item/lead_rope_pottery_sherd.png b/src/main/resources/assets/leashedplayer/textures/item/lead_rope_pottery_sherd.png new file mode 100644 index 0000000..615d02a Binary files /dev/null and b/src/main/resources/assets/leashedplayer/textures/item/lead_rope_pottery_sherd.png differ diff --git a/src/main/resources/assets/leashedplayer/textures/item/leash_rope_arrow.png b/src/main/resources/assets/leashedplayer/textures/item/leash_rope_arrow.png new file mode 100644 index 0000000..83cec87 Binary files /dev/null and b/src/main/resources/assets/leashedplayer/textures/item/leash_rope_arrow.png differ diff --git a/src/main/resources/assets/leashedplayer/textures/item/neoforge.png b/src/main/resources/assets/leashedplayer/textures/item/neoforge.png new file mode 100644 index 0000000..9a58fc6 Binary files /dev/null and b/src/main/resources/assets/leashedplayer/textures/item/neoforge.png differ diff --git a/src/main/resources/assets/leashedplayer/textures/item/spectral_leash_rope_arrow.png b/src/main/resources/assets/leashedplayer/textures/item/spectral_leash_rope_arrow.png new file mode 100644 index 0000000..5b47f66 Binary files /dev/null and b/src/main/resources/assets/leashedplayer/textures/item/spectral_leash_rope_arrow.png differ diff --git a/src/main/resources/assets/leashedplayer/textures/item/tipped_leash_rope_arrow_base.png b/src/main/resources/assets/leashedplayer/textures/item/tipped_leash_rope_arrow_base.png new file mode 100644 index 0000000..374c118 Binary files /dev/null and b/src/main/resources/assets/leashedplayer/textures/item/tipped_leash_rope_arrow_base.png differ diff --git a/src/main/resources/assets/leashedplayer/textures/item/tipped_leash_rope_arrow_head.png b/src/main/resources/assets/leashedplayer/textures/item/tipped_leash_rope_arrow_head.png new file mode 100644 index 0000000..2a7bc78 Binary files /dev/null and b/src/main/resources/assets/leashedplayer/textures/item/tipped_leash_rope_arrow_head.png differ diff --git a/src/main/resources/assets/leashedplayer/textures/mob_effect/no_leash.png b/src/main/resources/assets/leashedplayer/textures/mob_effect/no_leash.png new file mode 100644 index 0000000..e515854 Binary files /dev/null and b/src/main/resources/assets/leashedplayer/textures/mob_effect/no_leash.png differ diff --git a/src/main/resources/assets/leashedplayer/textures/painting/group_photo.png b/src/main/resources/assets/leashedplayer/textures/painting/group_photo.png new file mode 100644 index 0000000..14e9f92 Binary files /dev/null and b/src/main/resources/assets/leashedplayer/textures/painting/group_photo.png differ diff --git a/src/main/resources/leashedplayer.mixins.json b/src/main/resources/leashedplayer.mixins.json new file mode 100644 index 0000000..fd74d16 --- /dev/null +++ b/src/main/resources/leashedplayer.mixins.json @@ -0,0 +1,24 @@ +{ + "package": "com.r3944realms.leashedplayer.mixin", + "mixins": [ + "both.MixinEntity", + "both.MixinLivingEntity", + "both.MixinPlayer", + "item.MixinArmorSlotMixin", + "item.MixinLeadItem", + "registry.MixinJukeboxSongs", + "registry.MixinPaintingVariants", + "server.MixinServerGamePacketListenerImpl" + ], + "client": [ + "client.MixinClientBootstrap", + "client.MixinLevelRenderer", + "client.MixinPlayerRenderer", + "client.MixinPlayerRenderState" + ], + "refmap": "whimsicality.refmap.json", + "required": true, + "minVersion": "0.8", + "compatibilityLevel": "JAVA_17", + "plugin": "com.r3944realms.leashedplayer.mixin.MixinPlugin" +} \ No newline at end of file diff --git a/src/main/resources/leashedplayerlogo.png b/src/main/resources/leashedplayerlogo.png new file mode 100644 index 0000000..7581309 Binary files /dev/null and b/src/main/resources/leashedplayerlogo.png differ