Flandre923
924 words
5 minutes
10 可以抛掷的斧头

10 可以抛掷的斧头#

先添加一个投掷物实体.和之前的雪球类似,不过我们需要修改一个内容实现投掷的斧头特有的功能.

我们复用了之前的STONE_PROJECTILE的entitytype,由于STONE_PROJECTILE是使用的物品作为投掷物渲染,所以这里直接复用了.


public class FuProjectileEntity extends ThrowableItemProjectile {
    /**
     * 构造函数,初始化FuProjectileEntity实例。
     * 
     * @param entityType 实体类型
     * @param level 所属的世界实例
     */
    public FuProjectileEntity(EntityType<? extends ThrowableItemProjectile> entityType, Level level) {
        super(entityType, level);
    }

    /**
     * 另一个构造函数,用于从LivingEntity(如玩家或怪物)投掷FuProjectileEntity。
     * 
     * @param livingEntity 投掷实体
     * @param world 所属的世界实例
     */
    public FuProjectileEntity(LivingEntity livingEntity, Level world) {
        super(ModEntities.STONE_PROJECTILE.get(), livingEntity, world);
    }

    /**
     * 覆盖ThrowableItemProjectile类的getDefaultItem方法,返回此投掷物的默认物品。
     * 
     * @return 返回的默认物品
     */
    @Override
    protected Item getDefaultItem() {
        return ModItems.STONE_BALL.get();
    }

    /**
     * 当投掷物击中方块时调用的方法。
     * 如果服务器端执行并且击中的是木头方块,则会移除方块并生成相应的物品实体。
     * 无论击中什么方块,都会生成投掷物本身的物品实体并丢弃这个实体。
     * 
     * @param result 击中结果,包含击中的方块位置等信息
     */
    @Override
    protected void onHitBlock(BlockHitResult result) {
        Level level = level();
        BlockPos blockPos = result.getBlockPos();
        BlockState blockState = level.getBlockState(blockPos);
        boolean isWoodenBlock = blockState.is(BlockTags.LOGS);

        if (!this.level().isClientSide) {
            level.broadcastEntityEvent(this, (byte) 3);
            if (isWoodenBlock) {
                level.removeBlock(blockPos, false);
                spawnAtLocation(blockState.getBlock());
            }
            ItemStack itemStack = this.getItem();
            ItemEntity itemEntity = new ItemEntity(this.level(), this.getX(), this.getY(), this.getZ(), itemStack);
            itemEntity.setThrower(getOwner());
            this.level().addFreshEntity(itemEntity);
        }
        this.discard();
        super.onHitBlock(result);
    }

    /**
     * 当投掷物击中实体时调用的方法。
     * 如果服务器端执行,则会对该实体造成伤害,并生成投掷物本身的物品实体。
     * 然后丢弃这个投掷物实体。
     * 
     * @param result 击中结果,包含击中的实体信息
     */
    @Override
    protected void onHitEntity(EntityHitResult result) {
        if (!this.level().isClientSide) {
            Entity entity = result.getEntity();
            Entity owner = this.getOwner();
            entity.hurt(this.damageSources().thrown(this, owner), 4.0f);
            ItemEntity itemEntity = new ItemEntity(this.level(), this.getX(), this.getY(), this.getZ(), this.getItem());
            itemEntity.setThrower(getOwner());
            this.level().addFreshEntity(itemEntity);
            this.discard();
        }
    }
}

好了我们需要让斧头右键使用的时候可以投掷出去,这里直接用了mixin,mixin了斧头的use方法.


/**
 * 该类使用Mixin技术扩展了AxeItem的行为,添加了投掷斧头的功能。
 */
@Mixin(AxeItem.class)
public class AxeItemMixin extends DiggerItem {

    /**
     * 构造函数,初始化AxeItemMixin实例。
     * 
     * @param tier 斧头的材质等级
     * @param blocks 斧头可以挖掘的方块类型
     * @param properties 斧头的其他属性
     */
    public AxeItemMixin(Tier tier, TagKey<Block> blocks, Properties properties) {
        super(tier, blocks, properties);
    }

    /**
     * 当玩家使用斧头时调用的方法,覆盖了DiggerItem类的use方法。
     * 如果配置允许,玩家可以像投掷雪球一样投掷斧头。
     * 
     * @param level 玩家所在的世界实例
     * @param player 使用斧头的玩家
     * @param usedHand 使用斧头的手(主手或副手)
     * @return InteractionResultHolder<ItemStack> 表示交互结果和使用后的物品栈
     */
    @Override
    public InteractionResultHolder<ItemStack> use(Level level, Player player, InteractionHand usedHand) {
        boolean CFG_isFuThrowable = Config.isFuThrowable(); // 从配置文件获取是否允许投掷斧头
        ItemStack itemStack = player.getItemInHand(usedHand); // 获取玩家手中的斧头

        if (CFG_isFuThrowable) {
            level.playSound(null, player.getX(), player.getY(), player.getZ(),
                    SoundEvents.SNOWBALL_THROW, SoundSource.NEUTRAL, 0.5f, 0.4f / (level.getRandom().nextFloat() * 0.4f + 0.8f)); // 播放投掷声音
            player.awardStat(Stats.ITEM_USED.get(this)); // 给玩家添加使用此物品的统计

            if (!level.isClientSide) {
                player.awardStat(Stats.ITEM_USED.get(this)); // 在服务器端给玩家添加统计
                if (!player.getAbilities().invulnerable) {
                    itemStack.hurtAndBreak(1, (ServerLevel) level, player, p -> {
                        // 斧头损坏时调用
                    });
                    player.getInventory().removeItem(itemStack); // 从玩家的背包中移除损坏的斧头
                }

                FuProjectileEntity fuProjectileEntity = new FuProjectileEntity(player, level); // 创建投掷物实体
                fuProjectileEntity.setItem(itemStack); // 设置投掷物实体的物品栈
                fuProjectileEntity.shootFromRotation(player, player.getXRot(), player.getYRot(), 0.0f, 1.5f, 1.0f); // 设置投掷物的发射角度和速度
                level.addFreshEntity(fuProjectileEntity); // 将投掷物实体添加到世界中
            }
            itemStack.consume(1, player); // 消耗一个斧头
            return InteractionResultHolder.success(itemStack); // 返回交互成功
        }
        return InteractionResultHolder.fail(itemStack); // 如果不允许投掷,返回交互失败
    }
}

实现总成

10 可以抛掷的斧头
https://fuwari.vercel.app/posts/minecraft1_21_0/10_可以抛掷的斧头/
Author
Flandre923
Published at
2024-08-24