(Added gibber, clientActions, and background information)
m (line breaks would help here)
 
Line 118: Line 118:


- <code>value</code>: The unique value of the dialogue option. This is not displayed to the player and is instead used to reference this option from scripts.
- <code>value</code>: The unique value of the dialogue option. This is not displayed to the player and is instead used to reference this option from scripts.
- <code>text</code>The display text for the option. This can be a simple string or can be an object
- <code>text</code>The display text for the option. This can be a simple string or can be an object
- <code>action</code>: The MoLang expression to run when the user chooses the option.
- <code>action</code>: The MoLang expression to run when the user chooses the option.
- <code>isVisible</code>: A MoLang expression which must return either true or false. If false, the option will not be visible for the player. Defaults to true.
- <code>isVisible</code>: A MoLang expression which must return either true or false. If false, the option will not be visible for the player. Defaults to true.
- <code>isSelectable</code>: A MoLang expression which must return either true or false. If false, the option will be greyed out (but only if it is visible). Defaults to true.
- <code>isSelectable</code>: A MoLang expression which must return either true or false. If false, the option will be greyed out (but only if it is visible). Defaults to true.



Latest revision as of 10:04, 1 November 2025

Dialogues are a feature that can be used from API or from datapacks to play conversations in a GUI. They display entities, view pages of text, and handle various types of input.

A dialogue that has been registered through a datapack can be opened using the /opendialogue command.

Every case of a MoLang expression can be either a single text property or can be an array of text to support running multiple expressions to produce a result.

Dialogue Intro.png

Dialogue

A Dialogue JSON file consists of the following main elements:

- escapeAction: A string or array that defines the action to be taken when the dialogue is escaped. This can be a MoLang expression or a sequence of MoLang expressions.

- speakers: An object that defines the speakers in the dialogue. Each speaker is represented by a key-value pair, where the key is the speaker's identifier and the value is an object that describes what the speaker looks like and their name.

- pages: An array of objects, each representing a page in the dialogue.

Speakers

Each speaker is represented by an object with the following properties:

- name: A string or object that defines the speaker's name. This is usually just text. If it's an object, it must have a type property set to "expression" and an expression property that contains a MoLang expression. You would use an expression if you wanted the name to be dynamic.

- face: An object or MoLang expression that defines the speaker's face. This will be rendered in a frame of the GUI. For objects, it must have a type property set to the type of face. The only object type support currently is "standard". The others are done using MoLang expressions rather than an object.

- gibber: An object defining the gibber behaviour. This can be defined as simply {} to use the default settings, or as a full object with any of the properties that you want to set. See the section on gibber further below to see the properties that are available.

Faces

The speaker faces can be defined in multiple ways. If it is an object it is going to be a "standard" face type.

"standard"

The standard face type is for when the face displayed is not connected to a specific entity in the world but does follow Cobblemon's format of identifier and aspect list. The object must have the following properties:

- modelType: One of 'pokemon', 'npc'

- identifier: The identifier of the Pokémon or NPC, such as cobblemon:bulbasaur.

- aspects: An array of aspects that will be applied to the model.

For example:

 "face": {
   "type": "artificial",
   "modelType": "pokemon",
   "identifier": "cobblemon:pikachu",
   "aspects": [
     "shiny"
   ]
 }
 

player

By using q.player.face() you can make the player's model render for this face.

For example:

 "face": "q.player.face();"

npc

By using q.npc.face() you can show a direct reference to the NPC you're having the dialogue with, if this was dialogue started with an NPC entity.

For example:

 "face": "q.npc.face();"

Gibber

An additional feature of dialogues is having the text appear gradually and with a simplistic set of tones that mimic speech, referred to here as gibbering. Gibbering has many options and all have sensible defaults. The properties available are as follows:

- graduallyShowText: A boolean which, when true, will cause the text in the GUI to gradually be typed out. When false, gibbering sound effects will play but all of the text will be visible from the beginning. Defaults to true.

- allowSkip: Whether or not clicking in the dialogue box will skip the gibbering and display all of the text at once. If false, the player must wait until the entirety of the gibbering has occurred before they can continue to the next page.

step: The number of characters between each gibber. A gibber will play for the current character index and then, after some time interval, this step will be used to move to a later character in the text to play the next gibber. When gradually revealing text is on, a larger step will mean that text will appear in larger chunks. Defaults to 4 characters.

interval: The time (in seconds) between each gibber sound. A smaller value will make the sounds come more rapidly, and the text will appear more quickly if gradually showing text. Defaults to 0.1 seconds.

minPitch: The minimum pitch multiplier that could be used when randomly selecting a tone. Defaults to 0.8.

maxPitch: The maximum pitch multiplier that could be used when randomly selecting a tone. Defaults to 1.2.

minVolume: The minimum volume multiplier that could be used when randomly selecting a tone. Defaults to 0.75.

maxVolume: The maximum volume multiplier that could be used when randomly selecting a tone. Defaults to 1.1.

sounds: A list of resource locations indicating which sound events should be chosen from when playing a gibber. It can be any number of sounds. Defaults to three sounds: "cobblemon:entity.villager.npc1", "cobblemon:entity.villager.npc2", and "cobblemon:entity.villager.npc1".

Pages

Each page is represented by an object with the following properties:

- id: A string that uniquely identifies the page. This is needed for instances where you might want to jump to a specific page. Page numbers could be used, but you should use page IDs.

- speaker: A string that specifies the speaker's identifier. This depends upon the speaker by this identifier existing in the speakers list.

- lines: An array of strings or objects, each representing a line of text. If it's an object, it must have a type property set to "expression" and an expression property that contains a MoLang expression that evaluates to a string. You would use an expression if you want to include variables to make dynamic text.

- input: An object or string that defines the input prompt. If it's an object, it must have a type property set to "option", "text", "auto-continue", or "none", and other properties depending on the type. By default, the input value will be none.

- gibber: A page-specific gibber configuration. See above for more information about gibber objects. By default this is null, and will therefore fallback to the gibber that is on the speaker for this page, if any is.

- clientActions: A list of MoLang expressions that will run on the client after the first frame of the page has been rendered on the client. This can be used to play sounds with "q.sound(...)", or even play animations on the face being rendered (if it is a Bedrock animated entity) by doing, for example, "q.face.play_animation('win');".

- escapeAction: A string or array that defines the action to be taken when the page is escaped. This can be a MoLang expression or a sequence of MoLang expressions.

- background: A resource location to use as the background texture for the dialogue box. By default this is null, which will result in Cobblemon's internal texture to be used.

Input Types

The input property in a page object can have different structures depending on the type.

For all inputs that specify an action property which is set to a MoLang expression, you can access the time it took for them to make that input using v.seconds_taken_to_input.

"option"

The input is a set of options. The object must have an options property that is an array of objects, each representing an option. It can also specify whether the options should be displayed vertically or horizontally by whether or not the vertical property is true or false.

Each option object can have the following properties:

- value: The unique value of the dialogue option. This is not displayed to the player and is instead used to reference this option from scripts.

- textThe display text for the option. This can be a simple string or can be an object

- action: The MoLang expression to run when the user chooses the option.

- isVisible: A MoLang expression which must return either true or false. If false, the option will not be visible for the player. Defaults to true.

- isSelectable: A MoLang expression which must return either true or false. If false, the option will be greyed out (but only if it is visible). Defaults to true.

Dialogue Options.png

"text"

The input is a text field. The action property is a MoLang expression that will run when the player hits enter with text typed in. As specified earlier, you can check the text that they input using v.selected_option inside of the MoLang expression.

Dialogue Input.png

"auto-continue"

The dialogue will automatically progress after some period of time. There are several optional properties for this.

- delay: The delay in seconds until it progresses. By default this is 5 seconds.

- allowSkip: Whether the user is able to skip the delay by clicking. By default this is true. If false, the player will be forced to wait for the delay.

- showTimer: Whether a bar will be displayed to indicate how much more time the player has until it auto-continues. Defaults to false.

- action: A MoLang expression that runs when either the player clicks (only when allowSkip is true) or the delay finishes. By default this moves to the next page.

"none"

There is no input except that it waits until the user clicks to continue. The object can have an action property that defines the MoLang expression to run when the player clicks. By default, it moves to the next page.

Timeouts

All input types except for "none" and "autocontinue" can have a 'timeout' object which allows you to apply time constraints to the player viewing the page.

The timeout object can have 3 properties:

- duration: The time, in seconds, that the player will have to complete the input. By default this is 10 seconds.

- showTimer: Whether or not a bar will be shown to show the player how much time they have left. By default this is true.

- action: A MoLang expression to run if they do not enter the input in time. By default this will close the dialogue.

Dialogue Timer.png

Example

The following example is built-in to the mod, labelled "example".

 {
   "escapeAction": "q.dialogue.set_page('quitter')",
   "speakers": {
     "pikachu": {
       "name": "The Voice of Hiro",
       "face": {
         "type": "artificial",
         "modelType": "pokemon",
         "identifier": "cobblemon:pikachu",
         "aspects": [
           "shiny"
         ]
       },
       "gibber": {
         "step": 2,
         "interval": 0.07,
         "allowSkip": false
       }
     },
     "player": {
       "face": "q.player.face();",
       "name": {
         "type": "expression",
         "expression": "q.player.username"
       }
     }
   },
   "pages": [
     {
       "id": "player-chat",
       "speaker": "player",
       "lines": [
         {
           "type": "expression",
           "expression": "'Hello, I\\'m ' + q.player.username + '!'"
         }
       ]
     },
     {
       "speaker": "pikachu",
       "lines": [
         {
           "type": "expression",
           "expression": "'Nice to meet you, ' + q.player.username + '! Welcome to the world of dialogues!'"
         }
       ]
     },
     {
       "id": "intro",
       "speaker": "pikachu",
       "lines": [ "Do you want to learn more?"],
       "input": {
         "type": "option",
         "vertical": false,
         "options": [
           {
             "text": "Yes",
             "value": "yes",
             "action": [
               "v.player_response = 'That sounds great!';",
               "v.next_page = 'page1';",
               "q.dialogue.set_page('player-surrogate');"
             ]
           },
           {
             "text": "No",
             "value": "no",
             "action": [
               "v.player_response = 'No, I really don\\'t care.';",
               "v.next_page = 'quitter';",
               "q.dialogue.set_page('player-surrogate');"
             ]
           },
           {
             "text": "Sword!",
             "value": "sword",
             "isVisible": "q.player.main_held_item.is_of('minecraft:iron_sword')",
             "isSelectable": "q.player.data.scared_npc != true;",
             "action": [
               "v.player_response = 'How about you die! Muahahaha!';",
               "v.next_page = 'sword';",
               "q.dialogue.set_page('player-surrogate');"
             ]
           },
           {
             "text": "Sword again!",
             "value": "sword2",
             "isVisible": "q.player.main_held_item.is_of('minecraft:iron_sword')",
             "isSelectable": "q.player.data.scared_npc == true;",
             "action": [
               "v.player_response = 'How about you die... again! Muahahaha!';",
               "v.next_page = 'sword-again';",
               "q.dialogue.set_page('player-surrogate');"
             ]
           }
         ]
       }
     },
     {
       "id": "player-surrogate",
       "speaker": "player",
       "gibber": {},
       "lines": [
         {
           "type": "expression",
           "expression": "v.player_response;"
         }
       ],
       "input": "q.dialogue.set_page(v.next_page);"
     },
     {
       "id": "page1",
       "speaker": "pikachu",
       "lines": [
         "Well you see, this is a dialogue.",
         "You can have multiple pages, and each page can have multiple lines."
       ]
     },
     {
       "id": "page2",
       "speaker": "pikachu",
       "lines": ["On an unrelated note, did you know that I'm deeply afraid of iron swords? Anyway, cya!"],
       "input": {
         "type": "auto-continue",
         "delay": 5,
         "allowSkip": true,
         "action": "q.dialogue.close();"
       }
     },
     {
       "id": "quitter",
       "speaker": "pikachu",
       "lines": ["Fine. Be that way."],
       "input": "q.dialogue.close();",
       "escapeAction": "q.dialogue.close();"
     },
     {
       "id": "sword",
       "speaker": "pikachu",
       "lines": ["Oh god no, please don't hurt me!"],
       "escapeAction": "q.dialogue.input();",
       "input": [
         "t.data = q.player.data();",
         "t.data.scared_npc = true;",
         "q.player.save_data();",
         "q.dialogue.close();"
       ]
     },
     {
       "id": "sword-again",
       "speaker": "pikachu",
       "lines": ["I'm ready for you this time. YOU will die today!"],
       "escapeAction": "0",
       "input": {
         "type": "option",
         "vertical": true,
         "timeout": {
           "delay": 6,
           "action": "q.dialogue.input('stand');"
         },
         "options": [
           {
             "text": "Parry his attack.",
             "value": "parry",
             "action": [
               "v.reaction = 'parry';",
               "q.dialogue.set_page('sword-again-decided');"
             ]
           },
           {
             "text": "Stand perfectly still.",
             "value": "stand",
             "action": [
               "v.reaction = 'stand still';",
               "q.dialogue.set_page('sword-again-decided');"
             ]
           },
           {
             "text": "Drop your sword.",
             "value": "drop",
             "action": [
               "v.reaction = 'drop your sword';",
               "q.dialogue.set_page('sword-again-decided');"
             ]
           }
         ]
       }
     },
     {
       "id": "sword-again-decided",
       "lines": [
         {
           "type": "expression",
           "expression": "v.reaction == 'parry' ? 'You parry his attack and kill him.' : (v.reaction == 'stand still' ? 'He kills you.' : 'You drop your sword and he kills you.')"
         }
       ],
       "input": {
         "type": "auto-continue",
         "delay": 3,
         "allowSkip": false,
         "showTimer": false,
         "action": "q.dialogue.set_page('sword-again-reaction');"
       }
     },
     {
       "id": "sword-again-reaction",
       "speaker": "pikachu",
       "lines": [
         {
           "type": "expression",
           "expression": "'Nah, I\\'m just kidding. Interesting that you chose to ' + v.reaction + ' though.'"
         }
       ]
     },
     {
       "id": "name-question",
       "speaker": "pikachu",
       "lines": [ "Here's a puzzle though... What is my name? Lowercase only."],
       "input": {
         "type": "text",
         "timeout": {
           "delay": 10,
           "action": "q.dialogue.set_page('too-slow');"
         },
         "action": [
           "q.dialogue.set_page('name-speak');",
           "t.data = q.player.data();"
         ]
       }
     },
     {
       "id": "name-speak",
       "speaker": "player",
       "lines": [
         {
           "type": "expression",
           "expression": "'Your name is ' + v.selected_option + ', right?'"
         }
       ],
       "input": "q.dialogue.set_page('name-guess');"
     },
     {
       "id": "name-guess",
       "speaker": "pikachu",
       "lines": [
         {
           "type": "expression",
           "expression": "v.selected_option == 'hiroku' ? 'Correct! You know things that you shouldn\\'t...' : ('Wrong! I\\'m not \"' + v.selected_option + '\". Whoever that is.')"
         }
       ],
       "input": {
         "type": "auto-continue",
         "delay": 5,
         "allowSkip": true,
         "action": "q.dialogue.set_page('farewell');"
       }
     },
     {
       "id": "farewell",
       "speaker": "pikachu",
       "lines": ["Anyway, I'm done with this dialogue. Bye!"],
       "input": "q.dialogue.close();"
     },
     {
       "id": "too-slow",
       "speaker": "pikachu",
       "lines": ["Don't know? Sad. Well I don't know your name either, so I guess we're even."],
       "input": "q.dialogue.close()"
     }
   ]
 }