Reference - MikuSnow/BetonQuest GitHub Wiki

Reference

This chapter describes all aspects of BetonQuest in one place. You should read it at least once to know what you're dealing with and where to search for information if you ever have any problems.

Conversations

Each conversation must define name of the NPC (some conversations can be not bound to any NPC, so it’s important to specify it even though an NPC will have a name) and his initial options.

quester: Name
first: option1, option2
stop: 'true'
final_events: event1, event2
NPC_options:
  option1:
    text: Some text in default language
    events: event3, event4
    conditions: condition1, !condition2
    pointers: reply1, reply2
  option2:
    text: '&3This ends the conversation'
player_options:
  reply1:
    text:
      en: Text in English
      pl: Tekst po polsku
    event: event5
    condition: '!condition3'
    pointer: option2
  reply2:
    text: 'Text containing '' character'

Note 1: Configuration files use YAML syntax. Google it if you don't know anything about it. Main rule is that you must use two spaces instead of tabs when going deeper into the hierarchy tree. If you want to write ' character, you must double it and surround the whole text with another ' characters. When writing true or false it also needs to be surrounded with '. If you want to start the line with & character, the whole line needs to be surrounded with '. You can check if the file is correct using this tool.

  • quester is name of NPC. It should be the same as name of NPC this conversation is assigned to for greater immersion, but it's your call.
  • first are pointers to options the NPC will use at the beginning of the conversation. He will choose the first one that meets all conditions. You define these options in npc_options branch.
  • final_events are events that will fire on conversation end, no matter how it ends (so you can create e.g. guards attacking the player if he tries to run). You can leave this option out if you don't need any final events.
  • stop determines if player can move away from an NPC while in this conversation (false) or if he's stopped every time he tries to (true). If enabled, it will also suspend the conversation when the player quits, and resume it after he joins back in. This way he will have to finish his conversation no matter what. It needs to be in ''! You can modify the distance at which the conversation is ended / player is moved back with max_npc_distance option in the config.yml.
  • NPC_options is a branch with texts said by the NPC.
  • player_options is a branch with options the player can choose.
  • text defines what will display on screen. If you don't want to set any events/conditions/pointers to the option, just skip them. Only text is always required.
  • conditions are names of conditions which must be met for this option to display, separated by commas.
  • events is a list of events that will fire when an option is chosen (either by NPC or a player), defined similarly to conditions.
  • pointer is list of pointers to the opposite branch (from NPC branch it will point to options player can choose from when answering, and from player branch it will point to different NPC reactions).

When an NPC wants to say something he will check conditions for the first option (in this case option1). If they are met, he will choose it. Otherwise, he will skip to next option (note: conversation ends when there are no options left to choose). After choosing an option NPC will say it, the events will fire and then the player will see options defined in player_options branch to which pointers setting points, in this case reply1 and reply2. If the conditions for the player option are not met, the option is simply not displayed, similar to texts from NPC. Player will choose option he wants, and it will point back to other NPC text, which points to next player options and so on.

If there are no possible options for player or NPC (either from not meeting any conditions or being not defined) the conversations ends. If the conversation ends unexpectedly, check the console - it could be an error in the configuration.

This can and will be a little confusing, so you should name your options, conditions and events in a way which you will understand in the future. Don't worry though, if you make some mistake in configuration, the plugin will tell you this in console when testing a conversation. Also, study the default conversation included with the plugin to fully understand how powerful this system can be.

Cross-conversation pointers

If you want to create a conversation with multiple NPCs at once or split a huge conversation into smaller, more focused files, you can point to NPC options in other conversation. Just type the pointer as conversation.npc_option.

Keep in mind that you can only cross-point to NPC options. It means that you can use those pointers only in first starting options and in all player options. Using them in NPC options will throw errors.

Conversation variables

You can use variables in the conversations. They will be resolved and displayed to the player when he starts a conversation. A variable generally looks like that: %type.optional.arguments%. Type is a mandatory argument, it defines what kind of variable it is. Optional arguments depend on the type of the variable, i.e. %npc% does not have any additional arguments, but %player% can also have display (it will look like that: %player.display%). You can find a list of all available variable types in the "Variables List" chapter.

If you use a variable incorrectly (for example trying to get a property of an objective which isn't active for the player, or using %npc% in message event), the variable will be replaced with empty string ("").

Translations

As you can see in default conversation, there are additional messages in other languages. That's because you can translate your conversations into multiple languages. The players will be albe to choose their preferred one with /questlang command. You can translate every NPC/player option and quester's name. You do this like this:

quester:
  en: Innkeeper
  pl: Karczmarz
  de: Gastwirt

As said before, the same rule applies to all options and quester's name. The player can choose only from languages present in messages.yml, and if there will be no translation to this language in the conversation, the plugin will fall back to the default language, as defined in config.yml. If that one is not defined, there will be an error.

You can also translate journal entries, quest cancelers and message events, more about that later.

Conversation displaying

By default BetonQuest uses the most native and safe way of displaying a conversation, which is the Minecraft chat. You choose the option by typing their number in. You can however change it with default_conversation_IO option in config.yml file. Default value is simple. By changing it to tellraw you will add a possibility to click on options. Keep in mind that if the chat is quickly flowing, players will sometimes "miss" an option and click another one. There is a display type that doesn't suffer from this problem at all, it's called chest. It will display the conversation in an inventory GUI, where the NPC's text and options will be shown as item lore.

You can control the colors of conversation elements in the config.yml file, in conversation_colors section. Here you must use names of the colors.

If you're using the chest display method you can change the option's item to something else than Ender Pearl by adding a prefix to that option's text. The prefix is a name of the material (like in items.yml) inside curly brackets. Example of such option text: {diamond_sword}I want to start a quest!


Conditions, events and objectives are defined with an "instruction string". It's a piece of text, formatted in a specific way, containing the instruction for the condition/event/objective. Thanks to this string they know what should they do. To define the instruction string you will need a reference, few pages below. It describes how something behaves and how it should be created. All instruction strings are defined in appropriate files, for example all conditions are in conditions.yml config. The syntax used to define them looks like this: name: 'the instruction string containing the data'. Apostrophes are optional in most cases, you can find out when to use them by looking up "YAML syntax" in Google.

Conditions

Conditions are the most versatile and useful tools in creating advanced quests. They allow you to control what options are available to player in conversations, how the NPC responds or if the objective will be completed. The reference of all possible conditions is down below.

You can negate the condition (revert its output) by adding an exclamation mark (!) at the beginning of it's name (in the place you use it, i.e. in conversations, not in the conditions.yml file).

You can use conversation variables instead of numeric arguments in conditions. If the variable fails to resolve (i.e. it will return an empty string) BetonQuest will use 0 instead.

Events

In certain moments you will want something to happen. Updating the journal, setting tags, giving rewards, all these are done using events. You define them just like conditions, by specifying a name and instruction string. You can find instruction strings to all events in the event reference. At the end of the instruction string you can add conditions: or condition: (with or without s at the end) attribute followed by a list of condition names separated by commas, like conditions:angry,!quest_started. This will make an event fire only when these conditions are met.

You can use conversation variables instead of numeric arguments in events. If the variable fails to resolve (i.e. it will return an empty string) BetonQuest will use 0 instead.

Objectives

Objectives are the main things you will use when creating complex quests. You start them with a special event, objective. You define them in the objectives.yml file, just as you would conditions or events. At the end of the instruction string you can add conditions and events for the objective. Conditions will limit when the objective can be completed (e.g. killing zombies only at given location in quest for defending city gates), and events will fire when the objective is completed (e.g. giving a reward, or setting a tag which will enable collecting a reward from an NPC). You define these like that: conditions:con1,con2 events:event1,event2 at the end of instruction string . Separate them by commas and never use spaces! You can also use singular forms of these arguments: condition: and event:.

If you want to start an objective right after it was completed (for example die objective: when you die, teleport you to a special spawnpoint and start die objective again), you can add persistent argument at the end of an instruction string. It will prevent the objective from being completed, although it will run all its events. To cancel such objective you will need to use objective delete event.

Objectives are loaded at start-up, but they do not consume resources without player actually having them active. This means that if you have 100 objectives defined, and 20 players doing one objective, 20 another players doing second objective, and the rest objectives are inactive (no one does them), then only 2 objectives will be consuming your server resources, not 100, not 40.


Packages

All the content you create will be organized into packages. Each package contains the main.yml file with package-specific settings, conversations directory and all other files like events.yml and conditions.yml. The default package is called simply "default". It is always present, and if you delete it, it will be regenerated with a sample quest.

If you want, you can simply ignore the existence of packages and write all your quests in the default one. You will however come to a point, when your files contain hundreds of lines and it gets a little bit confusing. That's why it's better to split your quests into multiple packages, for example "main" for the quests in the main city, "dungeon" for some interesting dungeon story etc.

Don't worry, you can reference things from other packages just by prefixing their names with packages. If you're writing a conversation in package village and you want to fire an event reward from package beton, you simply name the event as beton.reward. The plugin will search for reward in beton package instead of the one in which the conversation is defined. The same goes for "folder" event and "and"/"or" conditions.

There are however things you cannot reference from another packages - journal entries and quest items. For this you need to define an event/condition inside that package and reference this event.

All packages must be defined in the config.yml file, under the packages section. If your package is inside another directory, you should add the directory name separated with a dash. This directory tree:

BetonQuest/
  default
  quests/
    village1/
      quest1
      quest2
    village2/
      quest1

should be defined in config.yml as:

packages:
- default
- quests-village1-quest1
- quests-village1-quest2
- quests-village2-quest1

Global variables

You can insert a global variable in any instruction string. It looks like this: $beton$ (and this one would be called "beton"). When the plugin loads that instruction string it will replace those variables with values assigned to them in main.yml file. This will be useful for example when downloading a package from the internet containing a WorldEdit schematic of the quest building. Instead of going through the whole code to set those locations, names or texts you will only have to specify a few variables (that is, of course, only if the author of the package used those variables properly in the code).

Note that these variables are something entirely different than conversation variables. Global ones use $ characters and conversation ones use % characters. You also can't use instructions in global variables.

There is a special type of variable - location modifiers. They follow a specific syntax, which looks like this: $beton$->(12;0;-20). It means "take variable named beton (which is a valid location, like that above) and add a vector (10;0;-10) to it". In the case below it would translate derived variable to 112;200;280;world, because 100 + 12 = 112, zero does nothing and 300 + (-20) = 280.

variables:
  beton: 100;200;300;world
  derived: $beton$->(12,0,-20)

Canceling quests

If you want to let your players cancel their quest there is a function for that. In main.yml file there is cancel branch. You can specify there quests, which can be canceled, as well as actions that need to be done to actually cancel them. You can find an example in the default package. The arguments you can specify are:

  • name - this will be the name displayed to the player. All _ characters will be converted to spaces. If you want to include other languages you can add here additional options (en for English etc.)
  • conditions - this is a list of conditions separated by commas. The player needs to meet all those conditions to be able to cancel this quest. Place there the ones which detect that the player has started the quest, but he has not finished it yet.
  • objectives - list of all objectives used in this quest. They will be canceled without firing their events.
  • tags - this is a list of tags that will be deleted. Place here all tags that you use during the quest.
  • points - list of all categories that will be entirely deleted.
  • journal - these journal entries will be deleted when canceling the quest.
  • events - if you want to do something else when canceling the quest (like punishing the player), list the events here.
  • loc - this is a location to which the player will be teleported when canceling the quest (defined as in teleport event);

To cancel the quest you need to open your backpack and select a "cancel" button (by default a bone, can be changes by naming an item "cancel_button" inside default package). There will be a list of quests which can be canceled. Just select the one that interests you and it will be canceled.

Global locations

Global locations are locations at which events can fire. They are defined as normal location objectives in objectives.yml and will fire when any player is in range of that location. Global locations are active for all players, even if they don't have an active objective. They can be used for starting location specific quest (such as encountering a dungeon and checking it out), teleporting players to arenas, this kind of stuff. They use tags to tell which player has been at certain location and which hasn't. These tags follow syntax global_<tag>, where <tag> is location objective's tag, defined at the end of instruction string (eg. global_conversation_start, where conversation_start is a tag of location objective). You can specify which events will be global locations in the main.yml file inside a package, separating them with commas:

global_locations: dungeon_entrance,trap,something_else

Static events

Static events are events that will fire at the specified time of the day. They are not tied to a specific player, so not all of event types can be used as static. (Which player should receive a tag or objective? From which one should the items be taken?) Also, static events cannot have conditions defined (event-conditions: argument), as the plugin cannot check any condition without the player. Events, that can be used as static are flagges with static keyword in this documentation. You can define your static events in main.yml file under static section, as such:

static:
  '09:00': beton
  '23:59': lightning_strike
  '11:23': some_command

The hour must be in '' to avoid problems, it needs leading zero if less than 10. beton, lightnint_strike etc. are IDs of events. There can only be one event specified, but it can be of type "folder".

Journal

The journal is a book in which all your adventures are described. You can obtain it by typing /j command or /b and selecting it from backpack. You cannot put it into any chests, item frames and so on. If you ever feel the need to get rid of your journal, just drop it - it will return to your backpack. The journal is updated with the journal event, and the text inside is defined in journal.yml config file. If you update these texts and reload the plugin, all players' journals will reflect changes. Colors in the journal can be altered in config.yml. The entries can use color codes, but the color will be lost between pages.

The journal by default appears in the last slot of the hotbar. If you want to change that use default_journal_slot option in config.yml, experiment with different settings until you're ok with it.

If you want to translate the entry do the same thing as with conversation option - go to new line, add language ID and the journal text for every language you want to include.

You can control behavior of the journal in config.yml file, in journal section. chars_per_page specifies how many characters will be placed on a single page. If you set it too high, the text will overflow outside of the page, too low, there will be too much pages. one_entry_per_page allows you to place every entry on a single page. The chars_per_page setting is in this case ignored, BetonQuest will put entire entry on that page. reversed_order allows you to reverse order of entries and hide_date lets you remove the date from journal entries.

You can control colors in the journal in journal_colors section in config.yml: date is a color of date of every entry, line is a color of lines separating entries and text is just a color of a text. You need to use standard color codes without & (eg. '4' for dark red).

You can also add a main page to the journal. It's a list of strings, which will show only if specified conditions are met. You can specify them in the main.yml file, in the journal_main_page section. Each string can have text in different languages, list of conditions separated by commas (these must be met for the string to show in the journal) and priority, which controls the order of strings. You can use conversation variables in the texts, but they will only be updated when the player gets his journal with the /journal command. Color codes are supported.

Tags

Tags are little pieces of text you can assign to player and then check if he has them. They are particularly useful to determine if player has started or completed quest. They are given with tag event and checked with tag condition. All tags are bound to a package, so if you add beton tag from within the default package, the tag will look like default.beton. If you're checking for beton tag from within default package, you're actually checking for default.beton. If you want to check a tag from another package, then you just need to prefix it's name with that package, for example quest.beton.

Points

Points are like tags, but with amount. You can earn them for doing quest, talking with NPC’s, basically for everything you want. You can also take the points away, even to negative numbers. Points can be divided to categories, so the ones from beton category won’t mix with points from quests group. Of course then you can check if player has (or doesn’t have) certain amount and do something based on this condition. They can be used as counter for specific number of quest done, as a reputation system in villages and even NPC’s attitude to player.

NPCs

Conversations can be assigned to NPCs. You do it in the main.yml file inside a package, in "npcs" section:

npcs:
  '0': innkeeper
  'Innkeeper': innkeeper

The first string is the name of the NPC, second one is the corresponding conversation name. In case you use Citizens, name is the ID of an NPC (don't try to put Citizens NPC's name here, it must be the ID). To acquire it just select the NPC and type /npc. If you don't want to use Citizens, you can also build NPCs as any other building in Minecraft:

Place somewhere a block of stained clay, no matter the color. Then place a head on top of it (type doesn't matter, it must be head). Now place a sign on the side of the clay block (it can be on it's back) and type in the first line [NPC], and on the second line the ID of the NPC (in case of the above code example, the ID would be Innkeeper). You need to have permission betonquest.createnpc for that. Congratulations, you have created the NPC. Now you can add levers (hands) to it and maybe even a fence gate (legs). Conversation is started by right clicking it's head.

Items

If you want to use items in Give and Take events or Item and Hand conditions you must define them in items.yml file. This assures you that they will be exactly the same items. You can add them to this file in two ways:

  1. Using the command /q item <pack>.<itemID>. It will take the item you are holding in hand and put it into items.yml inside package under <itemID> name. Easy and fast.

  2. Manually. Add a new line to items.yml and type there item's ID followed by a colon and space. Now the first thing you need to do is insert item's type. You can find available types here. That's all you need for an item. Now there are some arguments you can add to make it better:

    • data: is the data value (eg. for different wool colors). It should be followed by an integer. Default value is 0, so in most cases you can omit it.
    • name is item's display name. All spaces should be replaced by _.
    • lore: is item's lore. Spaces follow the same rules as in name, and new lines are added by a semicolon.
    • enchants: is a list of enchants separated by command. Each enchantment has two values separated by a colon: name and level. All names can be found here. Example of two enchantments is enchants:DAMAGE_ALL:3,KNOCKBACK:2.
    • If the item is a written book you can also add title:, author: and text:. Spaces should be replaced by _, as always. The text can be divided to pages with | character and you can add new lines with \n.
    • If the item is a potion then you can add effects:, where all effects are separated by commas and are build like that: EFFECT_NAME:X:Y where list of available effect names is here, X is potion's power (1 is 1, not 2) and Y is duration in seconds (not ticks).
    • If the item is a leather armor part, you can specify color of it by adding color: with an integer. The integer can be easily generated with this tool. If you need to get the color of a dye, just save a dyed armor to config via /q item name and see the exact result.
    • If the item is a player's head, you can specify it's owner by adding owner: followed by owner's name, for example owner:Notch. This won't work if the head is not data: 3!
    • If the item is an enchanted book just specify normal enchantments, they will be automatically applied to it as stored enchantments.

Sometimes you will want to target everything with your item, for example check if the player has any diamond sword (you don't want to let him go on a dangerous quest without proper equipment). Sometimes however you will want to target some specific simple item. Imagine a player has to bring a standard diamond sword to someone, so it can be enchanted with powerful magic. But he also carries his own, private Diamond Sword of Beton and Destruction. You don't want to just take any diamond sword from him, as it could potentially remove his "private" sword. This is when none tag becomes useful.

If you want to target only item without any enchantments, or without a name (or potion without effects etc.), you should define it as enchants:none or name:none etc. The item will behave as usual, but targeting it with conditions and events will have special behavior.

Items cannot be translated, because it's not possible to track your every item and change it's metadata everytime you change the language. Sorry.

Important note about books in 1.8 version of Minecraft

It seems that some servers are adding §0 at the end of every line when plugins generate books. It's usually no big deal, as this color code just resets the formating back to black color, but it can break item conditions (the item saved to configuration isn't the same as the one you've got with an event). Because of that I suggest you do the following to ensure that both books (the one in your hand and the other one in configuration) are identical:

  1. Obtain a desired book (for example by writing it)
  2. Save it with /q item book (or any other name, it doesn't matter for this example)
  3. Get it by an event (give book)
  4. Save the book you've just got again under the same name
  5. Get the book again with the same event as before
  6. That book will be identical as the one in configuration

I am very sorry for the inconvenience, but removing those §0s automaticly could cause problems with formatting done by the user. I'm not going to risk that.

Examples:

sword: 'DIAMOND_SWORD data:0 name:Slasher lore:Very_powerful;Forged_by_Endermen
  enchants:DAMAGE_ALL:5,KNOCKBACK:2'
potion: 'POTION name:Poison lore:Will_kill_you_in_10_secods_flat effects:HARM:10:0'
book: 'WRITTEN_BOOK title:Book_about_everything author:Notch
  text:The_text_goes_here.|This_is_on_the_new_page.\nAnd_this_is_one_line_below.'

Note that YAML allows line breaking.

Backpack

Sometimes you'll want some items to be persistent over death. If the player has lost them the quest would be broken. You can add a apecific line to item's lore to make it persistent (&2Quest_Item by default, _ is a space in item's definition). Note that this must be a whole new line in the lore! Such item wouldn't be dropped on death, instead it would be placed in player's backpack. Example: important_sword: 'DIAMOND_SWORD name:Sword_for_destroying__The_Concrete lore:Made_of_pure_Mithril;&2Quest_Item'

To open your backpack just type /j command. The inventory window will open, displaying your stored items. The first slot is always the journal, and if you get it, the slot will stay empty. You can transfer quest items back and forth between inventories by clicking on them. Left click will transfer just one item, right click will try to transfer all items. Normal items cannot be stored into the backpack, so it's not an infinite inventory.

If you will ever have more than one page of quest items, the buttons will appear. You can customize those buttons by creating previous_button and next_button items in items.yml file. Their name will be overwritten with the one defined in messages.yml.

Quest items cannot be dropped in any way other than using them. This way you can create a quest for eating cookies by giving the player a stack of cookies flagged as quest items and not continuing until there are no more cookies in his inventory/backpack. The player cannot drop the cookies, so he must eat every one of them to complete the quest.

Don't worry if the item-dropping filter isn't working for your items when you're in creative mode - it's not a bug. It's a feature. Creative-mode players should be able to easily put quest items in containers like TreasureChests.

Party

Parties are very simple. So simple, that they are hard to understand if you already know some other party system. Basically, they don't even have to be created before using them. Parties are defined directly in conditions/events (party event, party conditions, check them out in the reference lists below). In such instruction strings the first argument is a number - range. It defines the radius where the party members will be looked for. Second is a list of conditions. Only the players that meet those conditions will be considered as members of the party. It's most intuitive for players, as they don't have to do anything to be in a party - no commands, no GUIs, just starting the same quest or having the same item - you choose what and when makes the party.

To understand better how it works I will show you an example of party event. Let's say that every player has an objective of pressing a button. When one of them presses it, this event is fired:

party_reward: party 50 quest_started cancel_button,teleport_to_dungeon

Now, it means that all players that: are in radius of 50 blocks around the player who pressed the button AND meet quest_started condition will receive cancel_button and teleport_to_dungeon events. The first one will cancel the quest for pressing the button for the others (it's no longer needed), the second one will teleport them somewhere. Now, imagine there is a player on the other side of the world who also meets quest_started condition - he won't be teleported into the dungeon, because he was not with the other players (not in 50 blocks range). Now, there were a bunch of other players running around the button, but they didn't meet the quest_started condition. They also won't be teleported (they didn't start this quest).

⚠️ **GitHub.com Fallback** ⚠️