4.2 方块状态
方块状态是什么我们在前面已经解释过了。
我们现在就要为我们的水箱添加“水位”这一状态,在这里,可以去回顾下Blockstates文件的讲解
初始化方块状态
这一部分内容也是可以从原版中找到参考的,可以参考按钮相关的内容。
我们为方块类里定义一个Property,简单来讲就是定义了一个状态:
public static final IntegerProperty WATER_LEVEL = IntegerProperty.create("level", 0, 4);
其中三个参数分别对应了:
- 状态的id,我们将会在修改blockstates文件的时候用到它
 - 后面两个参数根据不同的Property的类型有所不同(数量也会不同),对于IntegerProperty来说是最小值和最大值
 
你完全可以通过实现Property<?>来自定义Property,这里提供几个原版给出的实现
- IntegerProperty,实现了Property
 - BooleanProperty,实现了Property
 - EnumProperty
>,实现了Property  - DirectionProperty,是EnumProperty
的便利实现。  
然后我们将这个Property添加到这个方块的每个方块状态上:
@Override
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> pBuilder) {
    pBuilder.add(WATER_LEVEL);
}
为它提供一个默认值:
    @Nullable
    @Override
    public BlockState getStateForPlacement(@NotNull BlockPlaceContext pContext) {
        return this.defaultBlockState().setValue(WATER_LEVEL, 0);
    }
至此初始化的内容就完成了。
右键升高水位
首先我要说明的是,这里用的是一个已经被弃用的方法(但是它仍然能用,不过我目前尚且不知道怎么让它能够应用左手右键)。
这里提供两种思路来解决这个问题(但是教程中没有完成这件事):
- 参考稍后的左键取水的方式进行逻辑的编写
 - 将这件事情做在
Item里面,如果Item的代码过长了,可以尝试使用Block和Function的映射关系缩减代码 
那么还是一样的,找到我们需要用的方法,这里使用的方法是use。
@Override
public @NotNull InteractionResult use(@NotNull BlockState blockState, @NotNull Level level, @NotNull BlockPos blockPos, @NotNull Player player, @NotNull InteractionHand hand, @NotNull BlockHitResult result)
然后我们来为它添加逻辑。
在这个逻辑中,我们首先需要确定我们手上的是不是水壶。
如果是水壶,那么取水壶剩余的水量和水箱里面还剩余的空间的最小值,然后水壶里面减少这部分水量,水箱中增加这部分水量。
很自然的,我们就有了这段代码:
@Override
public @NotNull InteractionResult use(@NotNull BlockState blockState, @NotNull Level level, @NotNull BlockPos blockPos, @NotNull Player player, @NotNull InteractionHand hand, @NotNull BlockHitResult result) {
    ItemStack itemStack = player.getItemInHand(hand);
    if (itemStack.is(KETTLE.get())) {
        int kettleCurrentDuration = itemStack.getMaxDamage() - itemStack.getDamageValue();
        int currentCisternLevel = blockState.getValue(WATER_LEVEL);
        int cisternLeavingLevel = 4 - currentCisternLevel;
        int couldInputLevel = Math.min(kettleCurrentDuration, cisternLeavingLevel);
        itemStack.setDamageValue(itemStack.getDamageValue() + couldInputLevel);
        if (itemStack.getDamageValue() == itemStack.getMaxDamage()) {
            itemStack.shrink(1);
            if (!level.isClientSide) {
                player.addItem(new ItemStack(EMPTY_KETTLE.get()));
            }
        }
        blockState.setValue(WATER_LEVEL, currentCisternLevel + couldInputLevel); // ?
    }
    return InteractionResult.SUCCESS;
}
修改Blockstates文件
在本章开始之前,我让读者回顾了下Blockstates相关的内容,没错,我们的level可以用到Blockstates里面,我们可以把Blockstates改成这样:
{
  "variants": {
    "level=0": [{
      "model": "thirst:block/cistern"
    }],
    "level=1": [{
      "model": "thirst:block/cistern1"
    }],
    "level=2": [{
      "model": "thirst:block/cistern2"
    }],
    "level=3": [{
      "model": "thairst:block/cistern3"
    }],
    "level=4": [{
      "model": "thirst:block/cistern4"
    }]
  }
}
这样,每个不同的水位就会有不同的模型了,代码已经在GitHub仓库上了,就不在这里放模型代码和材质图片了(太长了)
好的,到这里位置,思路清晰,逻辑通顺,一气呵成。
打开游戏,拿起水壶,右键水箱……?
对,对吗?
不可变的方块状态
那找找原因吧——要么是方块状态错误,要么是渲染错误。
很简单,再拿一个水壶右键它,然后发现,水壶里的水又没了。
那很明确了,Property错误。
检查一遍逻辑,没啥问题。
那这一切就指向了一件事情:Property没有正确变化。
事实上,在Forge中,当一个方块被放置时,它的Property就已经被确定了,是不可更改的。
我们如果想要修改一个方块的Property,那么必须重新放置那个方块。
现在,我们把我们之前的代码改一下:
    @Override
    public @NotNull InteractionResult use(@NotNull BlockState blockState, @NotNull Level level, @NotNull BlockPos blockPos, @NotNull Player player, @NotNull InteractionHand hand, @NotNull BlockHitResult result) {
        ItemStack itemStack = player.getItemInHand(hand);
        if (itemStack.is(KETTLE.get())) {
            int kettleCurrentDuration = itemStack.getMaxDamage() - itemStack.getDamageValue();
            int currentCisternLevel = blockState.getValue(WATER_LEVEL);
            int cisternLeavingLevel = 4 - currentCisternLevel;
            int couldInputLevel = Math.min(kettleCurrentDuration, cisternLeavingLevel);
            itemStack.setDamageValue(itemStack.getDamageValue() + couldInputLevel);
            if (itemStack.getDamageValue() == itemStack.getMaxDamage()) {
                itemStack.shrink(1);
                if (!level.isClientSide) {
                    player.addItem(new ItemStack(EMPTY_KETTLE.get()));
                }
            }
            level.setBlock(blockPos, blockState.setValue(WATER_LEVEL, currentCisternLevel + couldInputLevel), 0b0011);
        }
        return InteractionResult.SUCCESS;
    }
此时再进入游戏就会发现,一切都正常了。