Skip to content

Latest commit

 

History

History
309 lines (194 loc) · 8.85 KB

USAGE.md

File metadata and controls

309 lines (194 loc) · 8.85 KB

Clyde Interpreter Usage

For details about Clyde and how to write dialogues, check Clyde/LANGUAGE.md

Interpreter's interface

This plugin exposes the interpreter as ClydeDialogue.

This is ClydeDialogue's interface:

extends Node

signal variable_changed(variable_name: String, value; Variant, previous_vale: Variant)
signal event_triggered(event_name; String)

# Load dialogue file
# file_name: path to the dialogue file.
#            i.e 'my_dialogue', 'res://my_dialogue.clyde', res://my_dialogue.json
# block: block name to run. This allows keeping
#        multiple dialogues in the same file.
func load_dialogue(file_name: String, block: String = "") -> Void


# Start or restart dialogue. Variables are not reset.
func start(block_name: String = "") -> void


# Get next dialogue content.
# The content may be a line, options or null.
# If null, it means the dialogue reached an end.
func get_content() -> Dictionary


# Choose one of the available options.
# option_index: index starting in 0.
func choose(option_index: int) -> void


# Set variable to be used in the dialogue
# name: variable name
# value: variable value
func set_variable(name: String, value: Variant) -> Variant


# Get current value of a variable inside the dialogue.
# name: variable name
func get_variable(name: String) -> Variant


# Set callback to be used when requesting external variables
func on_external_variable_fetch(callback: Callable) -> void


# Set callback to be used when an external variable is updated in the dialogue
func on_external_variable_update(callback: Callable) -> void

# Return all variables and internal variables. Useful for persisting the dialogue's internal
# data, such as options already choosen and random variations states.
func get_data(): Dictionary


# Load internal data
func load_data(data: Dictionary) -> void


# Clear all internal data
func clear_data() -> void


# Set optional settings for current interpreter. [br]
# Options:
#   include_hidden_options (boolean, default false): Returns conditional options event when check resulted in false.
#
func configure(options: Dictionary) -> void


## Checks if a block with the given name exists.
func has_block(block_name: String) -> bool

Creating an object

You need to instantiate a ClydeDialogue object.

var dialogue = ClydeDialogue.new()

Loading files

The interpreter supports loading parsed JSON files, as well as .clyde files imported in the project.

When only the file name is provided, the interpreter will look into the default folder defined on Project > Project Settings > Dialogue > Source Folder.

dialogue.load_dialogue('my_dialogue')
# or
dialogue.load_dialogue('res://dialogues/my_dialogue.clyde')
# or
dialogue.load_dialogue('res://dialogues/my_dialogue.json')

As you can have more than one dialogue defined in a file through blocks, you can provide the block name to be used.

dialogue.load_dialogue('level_001', 'first_dialogue')

Starting / Restarting a dialogue

You can use dialogue.start() at any time to restart a dialogue or start a different block.

# starts default dialogue
dialogue.start()

# starts a different block
dialogue.start('block_name')

Restarting a dialogue won't reset the variables already set.

Getting next content

You should use dialogue.get_content() to get the next available content.

This method may return one of the following values:

Line

A dialogue line (Dictionary).

{
  "type": "line",
  "text": "Ahoy!",
  "speaker": "Captain", # optional
  "id": "123", # optional
  "tags": ["happy"] # optional
}

Options

Options list with options/topics the player may choose from (Dictionary).

{
  "type": "options",
  "name": "What do you want to talk about?", # optional
  "speaker": "NPC", # optional
  "options": [
    {
    "label": "option display text",
    "speaker": "NPC", # optional
    "id": "abc", # optional
    "tags": [ "some_tag" ], # optional
    },
    ...
  ]
}

End

Returned when the dialogue reached its end. Any new subsequent call will return an end object.

{ "type": "end" }

Listening to variable changes

You can listen to variable changes by observing the variable_changed signal.

  # ...

  dialogue.connect('variable_changed', self, '_on_variable_changed')


func _on_variable_changed(variable_name, value, previous_vale):
  if variable_name == 'hp' and value < previous_value:
    print('damage taken')

Listening to events

You can listen to events triggered by the dialogue by observing the event_triggered signal.

  # ...

  dialogue.connect('event_triggered', self, '_on_event_triggered')


func _on_event_triggered(event_name):
  if event_name == 'self_destruction_activated':
    _shake_screen()
    _play_explosion()

Data persistence

To be able to use variations, single-use options and internal variables properly, you need to persist the dialogue data after each execution.

If you create a new ClydeDialogue without doing it so, the interpreter will show the dialogue as if it was the first time it was run.

You can use dialogue.get_data() to retrieve all internal data, and then later use dialogue.load_data(data) to re-populate the internal memory.

Here is a simplified implementation:

var _dialogue_filename = 'first_dialogue'
var _dialogue

func _ready():
    _dialogue = ClydeDialogue.new()
    _dialogue.load_dialogue(_dialogue_filename)
    _dialogue.load_data(persistence.dialogues[_dialogue_filename]) # load data


func _get_next_content():
    var content = _dialogue.get_content()

    # ...

    if content == null:
        _dialogue_ended()


func _dialogue_ended():
    persistence.dialogues[_dialogue_filename] = _dialogue.get_data() # retrieve data for persistence

The example above assumes there is a global object called persistence, which is persisted every time the game is saved.

When starting a new dialogue execution, the internal data is loaded from the persistence object. When the dialogue ends, we update said object with the new values.

Note that the data is saved in in the dictionary under the dialogue filename key. The internal data should be used only in the same dialogue it was extracted from.

You should not change this object manually. If you want't to change a variable used in the previous execution, you should use dialogue.set_variable(name, value).

    # ...
    _dialogue = ClydeDialogue.new()
    _dialogue.load_dialogue(_dialogue_filename)
    _dialogue.load_data(persistence.dialogues[_dialogue_filename])

    _dialogue.set_variable("health", character.health)

Variables set via set_variable are included in the dialogue data object. This might not be ideal in cases where the data does not belong to the dialogue. For instance, if you pass a health variable to the dialogue, all dialogues will contain a version of that value, which will increase your save file size. You can workaround this by using external variables.

External variables

External variables are accessed using the @ prefix. i.e. @health. You need to define two callbacks to allow the dialogue to access and modify external variables:

# ...

_dialogue.on_external_variable_fetch(func (variable_name: String):
  return persistence[variable_name]
)

_dialogue.on_external_variable_update(func (variable_name: String, value):
  persistence[variable_name] = value
)

on_external_variable_fetch will be called any time the dialogue tries to access an external variable. i.e. { @health > 10 }.

on_external_variable_update will be called any time the dialogue tries to update an external variable. i.e { set @health = 100 }

External variables are just a proxy to the game variables. No external data is stored as part of the dialogue.

Translations / Localisation

Godot already comes with a localisation solution built-in.

The interpreter leverages this solution to translate its dialogues. Any dialogue line which contains an id defined will be translated to the current locale if a translation is available.

In case there is no translation for the id provided, the interpreter will return the default line.

Dialogue folder and organisation

By default, the interpreter will look for files under res://dialogues/. In case you want to specify a different default folder, you need to change the configuration in Project > Project Settings > Dialogue > Source Folder.

Alternatively, you can use the full path when loading dialogues:

var dialogue = ClydeDialogue.new()

dialogue.load_dialogue("res://samples/banana.clyde")

Examples

You can find usage examples on /example/ folder.