本篇教程由作者设定未经允许禁止转载。

由于并没有在百科找到关于如何利用CRT魔改禁止物品的使用、方块交互、方块破坏,所以我在此讲解一下如何利用CRT做到这些功能。
本文代码分析较为详细,请仔细阅读,阅读时间较长。

如何禁止方块破坏

这是一个很简单的功能,假如我想设定玩家在未达到某个游戏阶段时(由gamestage提供),不能破坏某种方块。

下面的代码是实现生存模式下没有获得“ForestryAge”和“IndustrialAge”的玩家不能破坏对应mod的方块。

import crafttweaker.event.BlockBreakEvent;
import crafttweaker.player.IPlayer;
import crafttweaker.event.IEventCancelable;
import crafttweaker.block.IBlock;//导包

global stage as string[string]={
    ForestryAge:"forestry",
    IndustrialAge:"ic2"
};

events.onBlockBreak(function(event as crafttweaker.event.BlockBreakEvent){ 
var block  as IBlock  = event.block;
var player as IPlayer = event.player;
for key in stage{
//判断条件
if(!player.creative && block.definition.id.contains(stage[key])&& !player.hasGameStage(key)){
event.cancel();//阻止该事件
event.player.sendStatusMessage(format.red("你对它并不熟悉"));//输出提示
}}
});

代码分析:


1、导包就不用说了,用什么event就导对应的包,events的类型可以在CRT的wiki上查找。

[1.12]如何控制模组物品的使用、方块交互、方块破坏-第1张图片

比如我们这里使用的是BlockBreakEvent,它的event interface extensions包括IEventCancelable和IBlockEvent,也就是说它可以直接使用IEventCancelable和IBlockEvent提供的getter、setter、method,或者说它继承了IEventCancelable和IBlockEvent的功能。


2、global stage……这里我声明了一个叫stage的全局变量,它的类型是hashmap(理解成带自定义索引的数组吧),索引类型是string,内容类型也是string。然后我填写了两个实例,ForestryAge:"forestry", IndustrialAge:"ic2"。

这里一步是用来存放后面会用到的gamestage变量的,之所以写成hashmap是为了方便后续的批量操作。


3、events.onBlockBreak(function(event as crafttweaker.event.BlockBreakEvent){};这是CRT通用的函数cast方法,声明了这个函数中的event指的是crafttweaker.event.BlockBreakEvent。

不理解也行,反正这么写就行,对于不同的事件,更换里面的名称就行。


4、var block  as IBlock  = event.block;
var player as IPlayer = event.player;

这里其实就是声明了两个变量,block和player,它们的类型分别是IBlock和IPlayer,内容指的是event.block和event.player。

(这里的event在 3 中我们已经定义过了,其实是crafttweaker.event.BlockBreakEvent)

3和4中的声明其实都是为了缩写,方便后面,省略这些步骤也可以。比如在省略了3、4之后,后续代码中的block就要写作crafttweaker.event.BlockBreakEvent.blcok。但是为了美观和提高可读性,我们还是这样做吧。


5、for key in stage{}这里是对stage里面的元素进行了遍历,也就是把两个阶段都应用于后面的判断语句。


6、if(!player.creative && block.definition.id.contains(stage[key])&& !player.hasGameStage(key))这里的判断条件是:

非创造模式&&事件目标方块的名字中含有”stage[key]“中的元素(即forestryic2)&&玩家不具有stage索引(即ForestryAgeIndustrialAge,注意是一 一对应的)。

如果你并不想通过mod_id来控制方块的破坏,你可以改成任何你想要的条件,只需要查询对应的event提供的class就行了,注意条件来源必须是你所选的事件提供的或者它继承的。


7、为什么不能直接检测物品的mod_id呢?

我也想啊,但是crt并没有提供检测物品属于哪个mod的方法,因此只能检测物品的definition.id中是否包含某些mod_id中的关键字,也就contains函数。注意不是item.name而是item.definition.id详细的区别可以自己去print一下看看区别。

如何禁止物品使用

import ……
events.onPlayerRightClickItem(function(event as crafttweaker.event.PlayerRightClickItemEvent){ 
var item   as IItemStack = event.item;
var player as IPlayer = event.player;
for key in stage{
if(!player.creative && !isNull(item) && item.definition.id.contains(stage[key])&& !player.hasGameStage(key)){
event.cancel();
event.player.sendStatusMessage(format.red("你对它并不熟悉"));
}}
});

如何禁止物品和方块交互

events.onPlayerInteractBlock(function(event as crafttweaker.event.PlayerInteractBlockEvent){ 
var block  as IBlock  = event.block;
var player as IPlayer = event.player;
var item   as IItemStack = event.item;

for key in stage{
if(!player.creative && block.definition.id.contains(stage[key]) && !player.hasGameStage(key)){
event.cancel();
event.player.sendStatusMessage(format.red("你对它并不熟悉"));
}}
for key in stage{
if(!player.creative && !isNull(item) && player.currentItem.definition.id.contains(stage[key]) && !player.hasGameStage(key)){
event.cancel();
event.player.sendStatusMessage(format.red("你对它并不熟悉"));
}}
});

代码分析:

1、这段代码值得注意的是:第一个判断语句检测的是事件目标方块的名字,第二个判断语句检测的是玩家手持的物品;[1.12]如何控制模组物品的使用、方块交互、方块破坏-第2张图片

PlayerInteractBlockEvent并没有给我们提供事件目标方块或者手持物品,但是它继承的PlayerInteract和IPlayerEvent类给我提供了对应的返回类:[1.12]如何控制模组物品的使用、方块交互、方块破坏-第3张图片

playerinteractevent提供的类

[1.12]如何控制模组物品的使用、方块交互、方块破坏-第4张图片IPlayer提供的类(IPlayerEvent继承自它,差不多相当于PlayerInteractBlockEvent的爷爷之一


2、q:为什么不能把两个判断合起来,同时判断手持物品or目标方块(event.item||event.block)?

      a:交互事件和方块破坏事件存在一个区别,方块破坏事件只在服务端判断一次,而交互事件要在客户端和服务端之间通讯两次,合计四次判断(此处涉及mc的底层逻辑,这里是作者自己的猜测,如果错误请及时联系作者更改),因此在第一次判断后event会被cancel,导致后续的第二次判断时,事件认为事件中的物品是不存在的(事件本身已经被取消了,当然也不会有事件物品),这会导致pointernull报错,代码功能仍然能够实现,但是会反复出现报错。因此将!isNull(item)加入第二个判断语句,也就是如果item是null就不用再去判断事件物品了,免得事件物品为空报错


3、q:为什么不直接阻止PlayerInteract事件呢?

      a:我们看看官方wiki[1.12]如何控制模组物品的使用、方块交互、方块破坏-第5张图片

确实,PlayerInteractEvent(作为PlayerInteractBlockEvent的父类)也提供item、block等getter,如果调用的话,也可以阻止玩家交互。

但是,官方文档其实存在一个错误(原因未知)

官方文档中显示PlayerInteractEvent继承了IEventCancelable的功能,也就是event.cancel()的功能,让我们可以调用它。

但实际上PlayerInteractEvent并没有继承IEventCancelable,如下图:[1.12]如何控制模组物品的使用、方块交互、方块破坏-第6张图片

显然PlayerInteractEvent只继承了IPlayerEvent和IEventPositionable,而且这两个类也没有继承IEventCancelable,因此我们不能在PlayerInteractEvent中调用event.cancel();乌鱼子


下面放出我自己整合包中的一段代码,大家可以试着按照前面的方法去分析理解,学会如何使用crt控制这些事件。

#priority 500
#loader crafttweaker reloadableevents
import crafttweaker.item.IItemStack;
import crafttweaker.item.IItemDefinition;
import crafttweaker.event.PlayerInteractEvent;
import crafttweaker.event.PlayerInteractBlockEvent;
import crafttweaker.event.IEventCancelable;
import crafttweaker.event.BlockBreakEvent;
import crafttweaker.player.IPlayer;
import crafttweaker.block.IBlock;
import crafttweaker.event.PlayerRightClickItemEvent;
import mods.recipestages.Recipes;


global stage as string[string] = {
        Rustic : "rustic",
        Harvest : "harvestcraft",
        Tropic : "tropicraft",
        HAC : "dcs_climate",
        Animania : "animania",
        Magic:  "magic",
        Tech:  "refinedstorage",
        Betweenlands:  "thebetweenlands",
        Rats:  "rats",
        Forestry:  "forestry",
        Plants:  "pmp",
        Tofu:  "tofucraft"
    };
//stagedevent
events.onPlayerRightClickItem(function(event as crafttweaker.event.PlayerRightClickItemEvent){ 
var item   as IItemStack = event.item;
var player as IPlayer = event.player;
for key in stage{
if(!player.creative && !isNull(item) && item.definition.id.contains(stage[key])&& !player.hasGameStage(key)){

event.cancel();
event.player.sendStatusMessage(format.red("你对它并不熟悉"));
}}
});

events.onBlockBreak(function(event as crafttweaker.event.BlockBreakEvent){ 
var block  as IBlock  = event.block;
var player as IPlayer = event.player;
for key in stage{
if(!player.creative && block.definition.id.contains(stage[key])&& !player.hasGameStage(key)){
print(block.definition.id);
print(stage[key]);
print(player.name);
print(block.definition.id.contains(key));
print(!player.hasGameStage(key));
event.cancel();
event.player.sendStatusMessage(format.red("你对它并不熟悉"));
}}
});

events.onPlayerInteractBlock(function(event as crafttweaker.event.PlayerInteractBlockEvent){ 
var block  as IBlock  = event.block;
var player as IPlayer = event.player;
var item   as IItemStack = event.item;

for key in stage{
if(!player.creative && block.definition.id.contains(stage[key]) && !player.hasGameStage(key)){

event.cancel();
event.player.sendStatusMessage(format.red("你对它并不熟悉"));
}}
for key in stage{
if(!player.creative && !isNull(item) && player.currentItem.definition.id.contains(stage[key]) && !player.hasGameStage(key)){

event.cancel();
event.player.sendStatusMessage(format.red("你对它并不熟悉"));
}}
});

for key in stage{
setRecipeStage(key,stage[key],key!="Magic");
}


关于gamestage的添加不属于本教程范围,请参考其它教程。