祭坛合成

本章涉及内容:自定义结构、配方检测、配方注册、JEI自定义注册、物品实体 涉及模组及版本:

  1. jei-1.20.1-forge-15.3.0.4

  2. rhino-forge-2001.2.2-build.18

  3. architectury-9.2.14-forge

  4. kubejsadditions-forge-4.3.2

  5. kubejs-forge-2001.6.5-build.14

  6. probejs-7.0.1-forge

注册方块和结构检测

注:该代码应在startup_scripts

const { $InteractionHand } = require("packages/net/minecraft/world/$InteractionHand")
const { $EntityType } = require("packages/net/minecraft/world/entity/$EntityType")
const { $ItemEntity } = require("packages/net/minecraft/world/entity/item/$ItemEntity")
const { $BlockPos } = require("packages/net/minecraft/core/$BlockPos");

StartupEvents.registry("block", event => {
    event.create("meng:test_block", "basic")
        .hardness(1)
        .defaultCutout()
        .box(1,0,1,15,3,15,true)
        .box(4,3,4,12,21,12,true)
        .box(6,21,6,10,21.2,10,true)
        .blockEntity(e => {
            e.inventory(1, 1)
            e.initialData({ item: "minecraft:air" })
        }).rightClick(e => {
            if (e.getHand() != $InteractionHand.MAIN_HAND) return
            let block = e.getBlock()
            let blockPos = block.getPos()
            let player = e.getPlayer()
            let world = player.getLevel()
            let mainHandItem = player.getMainHandItem().copy()
            let data = block.getEntityData().get("data")
            let dataInItem = data.get("item")
            if (player.isShiftKeyDown()) {
                if (getBlockStructure(world, blockPos)) {
                    let output = global.recipeStructure(dataInItem, getStructureItems(world, blockPos))
                    if (output != null) {
                        removeFloatItem(world, dataInItem, blockPos)
                        itemFloat(world, Item.of(output), block)
                        removeStructureItems(world, blockPos)
                    }
                }
                return
            }
            if (dataInItem != "minecraft:air") {
                removeFloatItem(world, dataInItem, blockPos)
                player.give(dataInItem)
            }

            itemFloat(world, mainHandItem, block, blockPos)
            player.getMainHandItem().count--
        })
})

/**
 * 删除悬浮的物品
 * @param {$Level} world 
 * @param {String} itemId 
 * @param {$BlockPos} pos 
 */
function removeFloatItem(world,itemId,pos){
    const {x,y,z} = pos
    world.getEntities($EntityType.ITEM, (value) => {
        // value type is $ItemEntity
        if (value.item.getId() == itemId) {
            if (value.x == x + 0.5 && value.y == y + 1.42 && value.z == z + 0.5) {
                // 删除物品的方式是丢弃(discarded)
                value.remove("discarded")
                return true
            }
        }
        return false
    });
}

/**
 * 使物品悬浮
 * @param {$Level} world 
 * @param {$ItemStack_} item 
 * @param {$BlockContainerJS} block 
 */
function itemFloat(world,item,block){
    let {x,y,z} = block.getPos()
    /**
     * @type {$ItemEntity}
     */
    let itemEntity = world.createEntity("item")
    itemEntity.setPos(x + 0.5, y + 1.42, z + 0.5)
    itemEntity.item = item
    itemEntity.age = -32768
    itemEntity.setPickUpDelay(-1)
    itemEntity.setNoGravity(true)
    itemEntity.item.count = 1
    itemEntity.spawn()
    block.setEntityData({ data: { item: itemEntity.item.id } })
}
/**
 * 获取结构的方块
 * @param {$Level} world 
 * @param {$BlockPos} blockPos 
 * @returns 
 */
function structureBlocksPos(world,blockPos){
    let { x, y, z } = blockPos
    let posList = [
        [x + 3, y, z],
        [x - 3, y, z],
        [x, y, z + 3],
        [x, y, z - 3],
        [x + 2, y, z + 2],
        [x + 2, y, z - 2],
        [x - 2, y, z + 2],
        [x - 2, y, z - 2]
    ]
    let blocks = []
    for (const pos of posList){
        blocks.push(world.getBlock(pos[0], pos[1], pos[2]))
    }
    return blocks
}

/**
 * 检测结构完整性
 * @param {$Level} world
 * @param {$BlockPos} blockPos 
 */
function getBlockStructure(world, blockPos) {
    let blocks = structureBlocksPos(world, blockPos)
    for (const block of blocks) if (block != "meng:test_block") return false
    return true
}

/**
 * 获取结构内的物品
 * @param {$Level} world
 * @param {$BlockPos} blockPos 
 */
function getStructureItems(world, blockPos) {
    let blocks = structureBlocksPos(world, blockPos)
    let itemList = []
    for (const block of blocks){
        let item = block.getEntityData().get("data").get("item")
        if (item == "minecraft:air") continue
        itemList.push(item.getAsString())
    }
    return itemList
}

/**
 * 删除结构内的物品
 * @param {$Level} world
 * @param {$BlockPos} blockPos 
 */
function removeStructureItems(world,blockPos){
    let blocks = structureBlocksPos(world, blockPos)
    for (const block of blocks){
        let itemId = block.getEntityData().get("data").get("item")
        removeFloatItem(world,itemId,block.getPos())
        itemFloat(world,Item.of("minecraft:air"),block)
    }
}

方块模型材质

注:该代码应在blockstates

注:该代码应在models\block

祭坛使用配方和注册配方

注:该代码应在server_scripts

祭坛破坏掉落里面的物品

注:该代码应在server_scripts

JEI配方注册显示

注:该代码应在client_scripts里,并且需要添加模组KubeJS Additions

一些注意事项

  1. 该项目的配方检测全是字符串,并不是ItemStack所以无法检测nbt,如需请自行更改

  2. 该项目只是作为示例,很多地方并不是最优解,可自行进行解决

  3. 如果对该项目代码部分不满可以将修改好的代码上传至gitee项目仓库

  4. 项目完整代码在这里

  5. 因为物品渲染使用的物品掉落物实体,所以会被kill @e[type="item"] 给清理掉,所以得注意一下(可以考虑写一个方块tick事件去检测解决)

Last updated