Квест на убийство (ЧАСТИЧНО)

TODO: предупреждение о том, какие разделы нужно прочитать перед началом.

TODO: пошаговое руководство по написанию квеста на убийство с NPC.

Историческая версия

Старое обучающее видео всё ещё можно посмотреть, но учтите, что часть приёмов в нём устарели — в первую очередь теги флагов. Если решите смотреть видео, пройдитесь по списку «Изменения после выхода обучающих видео».

Пример скрипта

Это руководство ещё не написано, но ниже — скрипт с поясняющими комментариями, который показывает одну из возможных реализаций квеста на убийство с NPC.

Этот пример подразумевает, что у вас современный конфиг-файл Denizen. То есть ваш plugins/Denizen/config.yml был сгенерирован после июня 2021 года (релиз 1743 или новее). Если у вас более старый конфиг, но вы хотите использовать этот скрипт, можно либо

  • A: удалить config.yml и перезапустить сервер, чтобы Denizen сгенерировал новый.

  • либо B: открыть config.yml в текстовом редакторе, найти строку Queue speed: 0.5s под Interact: и заменить 0.5s на instant, после чего выполнить /denizen reload config

Скрипт устроен так, что вы можете скопировать его, загрузить на сервер и привязать к NPC через /npc assign --set npc_killquest или /ex assignment set npc_killquest — и всё заработает. Дальше его можно кастомизировать или использовать как образец для обучения.

В редакторе скриптов его читать удобнее, чем на этой странице.

# Core assignment script to be given to the NPC
npc_killquest:
    type: assignment
    actions:
        on assignment:
        # Enable both triggers that will be used
        - trigger name:click state:true
        - trigger name:chat state:true
    interact scripts:
    # And link the interact script below
    - npc_killquest_interact

# Alternate chat format for the NPC if you don't like default 'chat'
# if you make multiple NPCs with this script as a template, remember that you only need the format scripts *once* (unless you have it different per NPC)
cchat:
    type: format
    format: <&b><npc.name> <&f>to you<&co> <&2><[text]>

# A clean but simple format for instructions given to the player by the script rather than by the NPC
# (The Out-Of-Character clarification of what the NPC said In-Character).
instruction_format:
    type: format
    # Light Gray + Italic
    # Can also be written like <gray><italic>
    format: <&7><&o>[<[text]><&7><&o>]

npc_killquest_interact:
    type: interact
    steps:
        # Default step: waiting for player to interact
        waiting*:
            # The player's initial interaction with this NPC is always just right clicking it
            click trigger:
                script:
                # Example of doing engage per-player with a flag
                # Engaging is a way to prevent players from spamming interactions with an NPC and glitching the scripts out as a result.
                # It just forces the player to wait on their next interaction until the current one is finished.
                # It works by using a has_flag check at the start of each interaction to stop early, and adding/removing before/after slow interactions like the narrates with waits below.
                - if <player.has_flag[npc_engaged]>:
                    - stop
                # Check for a cooldown before dealing with engage so we don't have to disengage inside the if
                - if <player.has_flag[kill_zombie_quest_cooldown]>:
                    # While a cooldown *could* be a step or a `cooldown` command, having it as a flag allows you to track and display the cooldown timer clearly, which players will appreciate.
                    - narrate format:instruction_format "You can repeat this quest in <player.flag_expiration[kill_zombie_quest_cooldown].from_now.formatted>."
                    # If we put the script *after* an engage, the disengage would go here - just before any 'stop' command.
                    - stop
                # Add the engage flag for one minute - either it will be cleared via the flag command, or if something bugs, it will clear on its own after a minute
                - flag player npc_engaged expire:1m
                # Alternately, instead of the player flags, you can use engage commands to engage per-NPC,
                # which is simpler to do (it's all in the one short command) but gets in the way of other players often
                #- engage
                - narrate format:cchat "Hello there. Would you care for a special prize?"
                - wait 5t
                - narrate format:cchat "If so, you can kill 5 zombies for me."
                - wait 5t
                - narrate format:cchat "Will you accept this request?"
                - wait 5t
                # The player can type 'yes' or 'no' into chat normally, OR click the word to automatically say it
                - narrate format:instruction_format "Type <&b><&o><element[Yes].click_chat[yes]><&7><&o> or <&b><&o><element[No].click_chat[no]>"
                # Zap to the step that contains the chat trigger. Add a five minute limit so if the player runs away and comes back, the NPC isn't still expecting a response.
                - zap accept_question 5m
                - flag player npc_engaged:!
                #- disengage
        # Second step: only active while waiting for a response to the question given in the click trigger
        accept_question:
            chat trigger:
                1:
                    # Simple main chat trigger: if the player says yes, they start the quest
                    trigger: "/Yes/ I accept the quest"
                    script:
                    - if <player.has_flag[npc_engaged]>:
                        - stop
                    - flag player npc_engaged expire:1m
                    #- engage
                    - narrate format:cchat "Okay great!"
                    - wait 5t
                    - narrate format:instruction_format "Kill 5 zombies!"
                    # Start a counter flag at zero (no zombies killed yet).
                    - flag player kill_zombie_quest_count:0
                    # Jump to the step for finishing the quest and lock it in (no timeout).
                    - zap finish_quest
                    - flag player npc_engaged:!
                    #- disengage
                2:
                    # Even though it's not really needed, add a 'no' response that just zaps back
                    trigger: "/No/ I don't"
                    script:
                    - if <player.has_flag[npc_engaged]>:
                        - stop
                    # More frequently shown messages can get annoying to players who have to see them constantly.
                    # Adding randomness to some messages is a little touch that makes scripts just that little bit nicer for players
                    - random:
                        - narrate format:cchat "Okay screw off!"
                        - narrate format:cchat "Okay then."
                        - narrate format:cchat "Screw you then!"
                    # They refused, so hop back to the default step and wait.
                    - zap *
        # 3rd step: only allowed interaction is click, they're stuck here til they finish the quest
        finish_quest:
            click trigger:
                script:
                # This step doesn't have delayed interactions, so no engage needed.
                # If the quest is marked as completed, give rewards.
                - if <player.has_flag[kill_zombie_quest_complete]>:
                    - narrate format:cchat "Great work! Here's your reward!"
                    - give diamond
                    # Then remove the 'complete' flag and set the cooldown
                    - flag player kill_zombie_quest_complete:!
                    - flag player kill_zombie_quest_cooldown expire:24h
                    # And zap back to the default step so they can retry the quest after the cooldown is done.
                    - zap *
                - else:
                    # Otherwise, just give idle waiting chatter.
                    - random:
                        - narrate format:cchat "You killed those zombies yet?"
                        - narrate format:cchat "I'm still waiting on the zombies."
                        - narrate format:cchat "Are you gonna kill those zombies or what?"

killquest_zombie_world:
    type: world
    events:
        # Listen to the event of a player killing zombies
        # Only listen when the counter flag is present
        after player kills zombie flagged:kill_zombie_quest_count:
        # After the kill, bump the player's kill counter
        - flag player kill_zombie_quest_count:++
        # When it hits five, you're done!
        - if <player.flag[kill_zombie_quest_count]> == 5:
            - narrate format:instruction_format "Zombie Quest Complete: Return to the NPC"
            # So remove the counter (to avoid it continuing to count up) and add a new flag indicating the quest is complete
            - flag player kill_zombie_quest_count:!
            # Alternatively to the flag, using '- zap npc_killquest_interact quest_completed' and a corresponding new step would also work.
            # The flag + if/else was chosen mostly for the sake of having examples of different methodologies.
            - flag player kill_zombie_quest_complete