/*
* Super Lead rope mod
* Copyright (C) 2025 R3944Realms
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package top.r3944realms.superleadrope.content.item;
import net.minecraft.core.BlockPos;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.InteractionResultHolder;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.TieredItem;
import net.minecraft.world.item.context.UseOnContext;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.gameevent.GameEvent;
import net.minecraftforge.common.extensions.IForgeItem;
import org.jetbrains.annotations.NotNull;
import top.r3944realms.superleadrope.api.SuperLeadRopeApi;
import top.r3944realms.superleadrope.api.type.capabilty.ILeashData;
import top.r3944realms.superleadrope.content.SLPToolTier;
import top.r3944realms.superleadrope.content.entity.SuperLeashKnotEntity;
import top.r3944realms.superleadrope.core.register.SLPSoundEvents;
import top.r3944realms.superleadrope.util.capability.LeashDataInnerAPI;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* The type Super lead rope item.
*/
//TODO:
// 1. 可抛掷使用 ***(下个版本实现)
// - 可转移拴绳
// 2. 多绑定(一个生物可以被多个加强拴绳绑定,产生共同作用效果)
// 3. 支持更多支撑方块
// 4. 可配置的生物栓效果(通过标签系统可配置哪些额外实体可被拴住)
// 5. 可自定义拴绳渲染位置(以匹配不同的生物模型)
// 6. 不可消耗的(但是有耐久,需要修复, 通过消耗原版拴绳恢复,每次多重绑定就会消耗1点耐久)
// 实现拴生物,在生物的interact方法里去写相关逻辑
// (尝试0 mixin 实现 加强拴绳逻辑)
public class SuperLeadRopeItem extends TieredItem implements IForgeItem {
// 配置常量
// 【手动调节,可以通过附魔获取更远抛掷和抛掷距离 - x1.3】//TODO:将可抛掷实现留到下次编写
// 可以做个大于一定距离时远距离使用时抛出拴绳的实体,击中生物才栓中的
private static final double SHORT_RANGE = 5.0D;
private static final float THROW_SPEED = 1.5F;
/**
* Instantiates a new Super lead rope item.
*
* @param pProperties the p properties
*/
public SuperLeadRopeItem(@NotNull Properties pProperties) {
super(SLPToolTier.STRING,
pProperties
.durability(1024)
.setNoRepair()
);
}
//通过按键 可抛掷启用/关闭实现(不会影响use逻辑)
@Override
public @NotNull InteractionResultHolder use(@NotNull Level pLevel, @NotNull Player pPlayer, @NotNull InteractionHand pUsedHand) {
ItemStack lead = pPlayer.getItemInHand(pUsedHand);
if(!pLevel.isClientSide) {
if (canUse(lead)) return InteractionResultHolder.pass(lead);
return super.use(pLevel, pPlayer, pUsedHand);
}
return InteractionResultHolder.success(lead);
}
/**
* Can use boolean.
*
* @param itemStack the item stack
* @return the boolean
*/
public static boolean canUse(@NotNull ItemStack itemStack) {
return itemStack.getDamageValue() < 1000;
}
@Override
public @NotNull InteractionResult useOn(@NotNull UseOnContext context) {
Level level = context.getLevel();
BlockPos pos = context.getClickedPos();
BlockState state = level.getBlockState(pos);
ItemStack itemStack = context.getItemInHand();
if (canUse(itemStack)) return InteractionResult.SUCCESS;
if(SuperLeashKnotEntity.isSupportBlock(state)) {
Player player = context.getPlayer();
if(!level.isClientSide && player != null) {
return bindToBlock(player, level, pos, itemStack, false) ? InteractionResult.CONSUME : InteractionResult.SUCCESS;
}
}
return InteractionResult.SUCCESS;
}
/**
* 右键蹲下绑定到另一实体上
*
* @param newHolder 新实体
* @param player 明确持有玩家
* @param level 维度世界
* @return 是否成功 boolean
*/
public static boolean bindToEntity(Entity newHolder, Player player, Level level) {
return bindToEntity(newHolder, player, level, player.getOnPos());
}
/**
* 右键蹲下绑定到另一实体上
*
* @param newHolder 新实体
* @param player 明确持有玩家
* @param level 维度世界
* @param pos 坐标(一般是明确持有玩家的位置)
* @return 是否成功 boolean
*/
public static boolean bindToEntity(Entity newHolder, Player player, Level level, BlockPos pos) {
boolean isSuccess = false;
// 查找当前玩家持有的可拴生物
List list = SuperLeadRopeApi.leashableInArea(
level, pos.getCenter(),
entity -> SuperLeadRopeApi.isLeashHolder(entity, player.getUUID())
);
for (Entity e : list) {
Optional leashDataOpt = LeashDataInnerAPI.getLeashData(e);
if (leashDataOpt.map(i -> i.canBeAttachedTo(newHolder)).orElse(false)) {
leashDataOpt.ifPresent(i -> i.transferLeash(player.getUUID(), newHolder));
isSuccess = true;
}
}
if (!isSuccess) {
return false;
} else {
level.gameEvent(GameEvent.ENTITY_INTERACT, pos, GameEvent.Context.of(player));
level.playSound(null, newHolder.getOnPos(), SLPSoundEvents.LEAD_UNTIED.get(), SoundSource.PLAYERS);
return true;
}
}
/**
* 右键蹲下绑定到支持方块上
*
* @param player 明确持有玩家
* @param level 维度世界
* @param leashStack 拴绳物品实例
* @param shouldBindSelf 是否应该触发拴自己逻辑检查
* @return 是否成功 boolean
*/
public static boolean bindToBlock(Player player, Level level, ItemStack leashStack, boolean shouldBindSelf) {
return bindToBlock(player, level, player.getOnPos(), leashStack, shouldBindSelf);
}
/**
* 右键蹲下绑定到支持方块上
*
* @param player 明确持有玩家
* @param level 维度世界
* @param pos 坐标(一般是明确持有玩家的位置)
* @param leashStack 拴绳物品实例
* @param shouldBindSelf 是否应该触发拴自己逻辑检查
* @return 是否成功 boolean
*/
public static boolean bindToBlock(Player player, Level level, BlockPos pos, ItemStack leashStack, boolean shouldBindSelf) {
SuperLeashKnotEntity knot = null;
AtomicBoolean isSuccess = new AtomicBoolean(false);
UUID uuid = player.getUUID();
List list = SuperLeadRopeApi.leashableInArea(level, pos.getCenter(),
entity -> SuperLeadRopeApi.isLeashHolder(entity, uuid));
// 情况一:拴自己到新 knot
if (shouldBindSelf && list.isEmpty()) {
if (leashStack.isEmpty() || !canUse(leashStack)) {
return false;
}
knot = SuperLeashKnotEntity.getOrCreateKnot(level, pos);
knot.playPlacementSound();
SuperLeashKnotEntity finalKnot = knot;
LeashDataInnerAPI.getLeashData(player).ifPresent(i -> {
if (i.canBeAttachedTo(finalKnot)) {
if (!level.isClientSide) i.addLeash(finalKnot);
isSuccess.set(true);
}
});
}
// 情况二:把已有生物拴到 knot
else if (!list.isEmpty()) {
for (Entity e : list) {
if (knot == null) {
knot = SuperLeashKnotEntity.getOrCreateKnot(level, pos);
knot.playPlacementSound();
}
SuperLeashKnotEntity finalKnot = knot;
LeashDataInnerAPI.getLeashData(e).ifPresent(i -> {
if (i.canBeAttachedTo(finalKnot)) {
if (!level.isClientSide) i.transferLeash(uuid, finalKnot);
isSuccess.set(true);
}
});
}
}
if (isSuccess.get()) {
level.gameEvent(GameEvent.BLOCK_ATTACH, pos, GameEvent.Context.of(player));
return true;
}
return false;
}
}