Reference - MikuSnow/BetonQuest GitHub Wiki
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.
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 innpc_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 withmax_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. Onlytext
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.
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.
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 ("").
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.
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 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.
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 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.
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
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)
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 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 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".
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 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 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.
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.
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:
-
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. -
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 isenchants:DAMAGE_ALL:3,KNOCKBACK:2
. - If the item is a written book you can also add
title:
,author:
andtext:
. 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) andY
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 exampleowner:Notch
. This won't work if the head is notdata: 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:
- Obtain a desired book (for example by writing it)
- Save it with
/q item book
(or any other name, it doesn't matter for this example) - Get it by an event (
give book
) - Save the book you've just got again under the same name
- Get the book again with the same event as before
- That book will be identical as the one in configuration
I am very sorry for the inconvenience, but removing those §0
s 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.
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.
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).