• 欢迎来到Minecraft插件百科!
  • 对百科编辑一脸懵逼?帮助:快速入门带您快速熟悉百科编辑!
  • 因近日遭受攻击,百科现已限制编辑,有意编辑请加入插件百科企鹅群:223812289

GuillaumeVDN的插件文档/QuestCreator/高级内容

来自Minecraft插件百科
Qsefthuopq留言 | 贡献2020年11月29日 (日) 07:07的版本 →‎功能性任务
(差异) ←上一版本 | 最后版本 (差异) | 下一版本→ (差异)
跳转到导航 跳转到搜索
GuillaumeVDN的插件文档
页面

GuillaumeVDN的插件文档 · 迁移

所有插件都有的常见内容

配置 · 杂项 · 关联

QuestCreator

基础内容 · 示例 · 详细特性 · 高级内容 · 关联

变量

插件有一个可影响任务的内置变量系统。例如,你可以用它来记住一名玩家在任务A中做出的抉择,并因此在任务B中显示不同的抉择选项,而这将存储在用户变量中。

每个变量都是一行文本。变量可以包含数字,并用在解析逻辑或在数学公式中。

变量名对大小写敏感:MyValue不等于myvalue

如果变量没有值,则该变量值会显示为0

用户变量

每名玩家的用户变量都是特有的。如果有一个名为my_variable的变量,那么它对GuillaumeVDN而言的值是1 ,而对Notch而言的值是2

你可以在这里配置变量默认值:/plugins/QuestCreator/default_variables.yml

variable_1: 0
 variable_2: 'default'

模型变量

模型变量用于活跃的任务中,任务结束后这些变量就会被废弃。这些变量可能只在进行任务时有用,而对未来的任务并不重要。

你可以在每个任务模型中配置变量默认值。

服务器变量

这些变量用于服务器。服务器变量值对所有的玩家都一样。

你可以在这里配置服务器变量默认值:/plugins/QuestCreator/server_variables.yml

server_variable_1: 0
 server_variable_2: 'default'

全局变量

全局变量拥有固定值且无法修改。它主要用于避免你在任务中重复编写相同内容。它就相当于宏,你可以在配置内的某个地方使用{gvariable:<variable>}来获取全局变量。

全局变量包含其它变量或占位符。

你可以在这批配置全局变量默认值:/plugins/QuestCreator/global_variables.yml

message_prefix: '&6[QuestCreator] &7'
 sample_variable_placeholderapi: '嘿,%player_name%'
 sample_variable_qc: '{variable:my_variable} is the value'
 sample_spawn_location: 'world,0,64,0'
 sample_player_location_through_papi: '%player_world%,%player_x%,%player_y%,%player_z%'


全局元素

为避免重复,你可以用全局元素来配置几乎全部的元素类型,其位于/global_<element type>/

比如在这个分支里,玩家重连服务器时会获得药水效果:

branches:
 
   MAIN_BRANCH:
     # 任务内容
 
   ANOTHER_BRANCH:
     # 其它任务分支
 
   RECONNECT_EFFECTS:
     starts_directly: true
     starts_at: TRIGGER
     objects:
     TRIGGER:
       type: PLAYER_CONNECT
       goto: OBJECT EFFECT
     EFFECT:
       type: SERVER_PLAYER_EFFECTS_GIVE
       start_notify:
       message: >
         &a欢迎回来,{player}&a!这是你的速度 II效果。
       effects:
         a:
           type: SPEED
           amplifier: 1
           duration: 1 MINUTE
       goto: OBJECT TRIGGER  # 循环触发

你不需要复制粘帖该分支到你的全部任务内,你可以在/global_branches/RECONNECT_EFFECTS.yml内创建一个全局分支:

starts_directly: true
 starts_at: TRIGGER
 objects:
   TRIGGER:
     type: PLAYER_CONNECT
     goto: OBJECT EFFECT
   EFFECT:
     type: SERVER_PLAYER_EFFECTS_GIVE
     start_notify:
       message: >
         &a欢迎回来,{player}&a!这是你的速度 II效果。
     effects:
       a:
         type: SPEED
         amplifier: 1
         duration: 1 MINUTE
     goto: OBJECT TRIGGER  # 循环触发

只需要一行文本,你就可以在你的实际任务模型里面引用这个分支。

你可以通过将元素键设置为global@<全局元素id>和一个任意值来引用全局元素,如下示例:

branches:
 
   MAIN_BRANCH:
     # 任务内容
 
   ANOTHER_BRANCH:
     # 其它任务分支
 
   global@RECONNECT_EFFECTS: /
 
   global@ANOTHER_EASY_GENERIC_BRANCH: /

全局任务目标

全局任务目标只有一点不同:你仍可以配置goto:

objects:
 
   SOME_OBJECT:
     # 普通本地目标
 
   global@COOL_GLOBAL_OBJECT:
     goto: OBJECT NEXT_COOL_GLOBAL_OBJECT
 
   global@NEXT_COOL_GLOBAL_OBJECT:
     goto: OBJECT ANOTHER_OBJECT
 
   ANOTHER_OBJECT:
     # 其它普通本地目标

任务进度检测流程

以下是插件检查一个任务进度时的流程。这适用于常规任务模型。

任务进度会在玩家触发事件(比如破坏方块)或每X刻时进行检测。第二张方式称为“定时器调用”:这种方式是更新服务器目标进度最常使用的。

对每个任务进行检测(所有玩家进行中的任务或所有调用定时器的活跃任务):

  1. 确保世界允许

  2. 如果调用定时器,则使符合条件的任务回到检查点。

  3. 尝试检测每个分支/独立分支的进度:

    1. 检查是否还必须等待一定的时间才能开始该分支的当前目标。

      • 如须等待,则不检测该分支

      • 如无须等待,则发送开始该目标的提醒

    2. 检测当前目标是否超时

      • 如果超时,则应用失败goto

      • 如果未超时,则发送剩余时间提醒

    3. 检测该分支的当前目标,如果尚未完成,

      (a) 如果是事件调用:

      1. 独立检测分支是否与事件玩家匹配。
      
      2. 检查身份限制是否与事件中的玩家匹配
      
      3. 检测目标的特定设置是否匹配事件(比如方块类型是否正确,是否是玩家放置的方块等
      
      4. 确认匹配位置设置(大多数情况下是玩家位置,少数情况下是方块位置)
      
      5. 确认事件玩家是否有所需物品
      
      6. 确认匹配进度条件是否匹配
         > 如果不匹配,则应用失败goto
      
      7. 计算进度成功率
         > 如果失败,则应用失败goto
      
      8. (如果设置则)取消事件
      
      9. 计算并应用进度
      
      10. 从事件玩家处获取进度条件
      
      11. 从事件玩家处拿取所需物品

      (b) 如果是定时器调用:

      1. 检测定时器调用刻是否匹配目标刻
      
      2. (如果设置了‘wait_after_match_fail’则)在检测该目标前等待
      
      3. 检测目标是否取得进展
      
      4. 确认匹配身份的玩家拥有所需物品
      
      5. 确认匹配进度条件(检测匹配身份的玩家)
         > 如果不匹配,则应用失败goto
      
      6. 计算进度成功率
         > 如果失败,则应用失败goto
      
      7. 计算并应用进度
      
      8. 从匹配身份的玩家处获取进度条件
      
      9.  从匹配身份的玩家处拿取所需物品

      之后:

      (a) 如果调用完成目标且该目标是委托目标,则发送完成提醒。

      (b) 如果调用影响目标(取得进展),则发送进度提醒

      另外,如果必须按顺序完成委托目标的话,或许可以停止检查目标。(GROUP目标)

      (c) 如果调用完全不影响目标:

      1. 启用“position_stay”时,确认玩家是否在指定位置
      
      > 如果不在,则发送警告提醒
      
      > 如果玩家长时间未留在指定位置,则执行失败goto
      
      1. 如果玩家离上次任务更新太久未取得进展则发送更新提醒
      
      2. 如果玩家太久未取得进展则重新发送分支目标抉择选项
  4. 如果任务受到影响:更新显示 (actionbar/显示/计分板/……)

功能性任务

功能性任务是基本没有用户显示和限制的任务。它对于创建一些特性很有用,你可以用QuestCreator的任务分支/目标轻松地创建功能性任务。你可以用功能性任务做任何事情:当玩家进入一片区域,与一个方块交互,杀死一个实体等时,执行一些效果。你可以使用所有的玩家和服务器目标类型(不包括特殊类型)。

功能性任务实际上并不会被保存,它们在玩家连接时自动启动(根据条件自动启动),并在玩家断开连接时被丢弃。它们无法拥有激活器或结束目标。

它拥有大部分常规任务模型的设置,但没有大部分的用户显示:它没有提醒、显示名、描述、信息、GUI物品等。它也没有一些时间设置,如模型时间限制或冷却。它们不能在多人任务中运行,因为它们是针对每个玩家的,且没有玩家相关的设置。

它位于/quest_models_functional/

开发者API

插件包含了一些事件,但由于一切都可以轻易地完成,插件并没有特定的API。我的插件并没有混淆代码。

如果你想要关联QuestCreator而不想购买这款插件,你可以联系我。

确保在你的项目路径中同时包含QuestCreator和GCore,这样你就可以看到所有的自动补全选项。

如有疑问,请勿犹豫,请联系我。

事件

事件无法取消,它只会告诉你有事情发生。它主要用于统计数据或增加额外效果

  • QuestStartEvent:在任务开始时调用
  • QuestStopEvent:在任务完成时调用
  • BranchStartEvent:在分支开始时调用
  • BranchStopEvent:在分支停止时调用
  • ObjectCompleteEvent:在任务目标完成时调用
  • QuestAppliedGotoEvent:在应用got时调用
  • QueueChangePositionEvent:在排队中玩家更改队列位置时调用

在这些事件中,当你调用 getQuest()方法时,你会得到 AbstractQuest。 如果是进行中的任务Quest,你将可以获得更多关于它的信息(启动/停止任务的原因等)。

即使功能性任务会触发事件,它也不值得检查(FunctionalQuest)。

数据管理

你可以用UserQC访问用户数据。

// 检索缓存用户
UserQC user = UserQC.cachedOrNull(player);

// 如果用户不在缓存中,则获取用户,然后做一些事。
// 如果用户不在缓存中且数据被修改,则保存数据,并在回调后丢弃。
UserQC.process(uuid, user -> {
    // user was loaded
});

// 如果用户不在缓存中,则获取用户,然后做一些事。
// 如果用户不在缓存中且数据被修改,则保存数据,并在回调后丢弃。
UserQC.processWithQuests(uuid, user -> {
    // user was loaded
});

// 你也可以用“UserQC”访问任务数据
user.getCachedActiveQuests().forEach(quest -> {
    // process quest
});

您也可以直接从任务板中获取缓存任务。不建议通过任务板获取某个玩家的任务,请使用UserQC来获取。

// get all cached active quests on the server
BoardQuests.inst().getCacheValues().forEach(quest -> {
    // process quest
});

自定义目标类型

为了创建自定义元素类型,我们以一个新的任务目标类型作为示例。我建议您查看进度流程,以了解任务调用的大致情况。

首先你需要扩展类型类。以事件目标为例 :

public class TypePlayerBlockFarmCustom extends QuestObjectTypeEventGoalCustom<BlockPlaceEvent> {

    public static final QuestObjectEvent EVENT = QuestObjectEvent.register(BlockPlaceEvent.class, "getPlayer");

    public TypePlayerBlockFarm(String id) {
        super(id, CommonMats.DIRT, PositionNeed.OPTIONAL, AllowItemsNeeded.YES, AllowProgressConditions.YES, EVENT);
    }

    // call
    @Override
    protected boolean match(AbstractElementQuestObject object, QuestCallBranchProgress call, ObjectProgression progression, BlockPlaceEvent event, Player eventPlayer) {
        if (!event.getBlock().getType().equals(Material.FARMLAND)) {
            return false;
        }
        Mat mat = Mat.fromItem(eventPlayer.getItemInHand()).orNull();
        if (mat == null || !mat.getData().getDataName().toUpperCase().contains("_HOE")) {
            WorkerQC.inst().logSpam(call.getQuest(), event, eventPlayer, ">>>> found item " + mat + " isn't a hoe");
            return false;
        }
        return true;
    }

    @Override
    protected Location findLocationToMatch(AbstractElementQuestObject object, QuestCallBranchProgress call, BlockPlaceEvent event, Player eventPlayer) {
        return event.getBlock().getLocation();
    }

}

QuestObjectTypeEventGoalCustom 自动添加在目标设置内,但你也许想要为该目标添加自定义设置。你可以在fill方法内这么做。比如:

// 添加额外几率设置
@Override
protected void doFillSettingsElements(AbstractElementQuestObject object) {
    super.doFillSettingsElements(object); // fill other type settings
    object.addChancePercentage("extra_chance", Need.OPTIONAL, 0d, new TextElement(CollectionUtils.asList("&7这是我们为该目标添加的额外几率 (editor description)")));
}

// 在匹配方法中,让我们在能够正常继续之前先确保匹配额外的设置。
@Override
protected boolean match(AbstractElementQuestObject object, QuestCallBranchProgress call, ObjectProgression progression, BlockPlaceEvent event, Player eventPlayer) {
    // ... other stuff described above
    Double configuredExtraChance = object.parseElementAsOrNull("extra_chance", call.getParser()); // retrieve the current value of the setting
    if (configuredExtraChance != null && NumberUtils.random(0d, 100d) > configuredExtraChance) {
        WorkerQC.inst().logSpam(call.getQuest(), event, eventPlayer, ">>>> we are very unlucky and can't progress the object for this event");
        return false;
    }
    return true;
}

可用于目标的类型类:

  • QuestObjectTypeEvent:默认事件类型(你必须手动建立目标)
  • QuestObjectTypeEventGoalDefault:目标数为1的事件类型
  • QuestObjectTypeEventGoalCustom:用户可配置的带目标事件类型
  • QuestObjectTypeTimer:默认定时器类型(你必须手动建立目标)
  • QuestObjectTypeTimerGoalDefault:目标数为1的定时器类型;在调用后会立即进行
  • QuestObjectTypeTimerAction:目标数为1的定时器类型;在调用后会立即进行并调用performInstantEffects()方法

之后,你需要在任务目标类型中注册你的目标。

QuestCreator.inst().getQuestObjectTypes().register(new TypePlayerBlockFarmCustom("PLAYER_BLOCK_FARM_CUSTOM"));
TypePlayerBlockFarmCustom.EVENT.registerListener(); // 在示例中,我们必须也要注册事件的监听器

别忘了在插件卸载时取消注册。

QuestCreator.inst().getQuestObjectTypes().unregister("PLAYER_BLOCK_FARM_CUSTOM");
TypePlayerBlockFarmCustom.EVENT.unregisterListener(); // 在示例中,我们必须也要注册事件的监听器

其它自定义类型

你可以注册几乎全部新内容:

  • 激活器类型必须扩展ActivatorType(或 ActivatorTypePhysical)并用QuestCreator.inst().getActivatorTypes()注册/取消注册
  • 条件类型必须拓展ConditionType并用QuestCreator.inst().getConditionTypes()注册/取消注册
  • 分支类型必须拓展BranchType并用QuestCreator.inst().getBranchTypes()注册/取消注册

……