Skip to content

Commit

Permalink
Merge pull request #21 from workadventure/dynamic_configuration
Browse files Browse the repository at this point in the history
Adding dynamic configuration
  • Loading branch information
moufmouf authored Sep 3, 2021
2 parents cdbd1d8 + 002bdb3 commit 4b784ac
Show file tree
Hide file tree
Showing 59 changed files with 3,776 additions and 19,458 deletions.
151 changes: 151 additions & 0 deletions docs/automatic-configuration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
{.section-title.accent.text-primary}
# Generating automatically a configuration screen

{.alert.alert-info}
**Important!** To generate a configuration screen automatically, you need to [import the "Scripting API Extra" script in your map](about.md#importing-the-extended-features)

WorkAdventure comes with a ["variables"](https://workadventu.re/map-building/api-state.md) system that can be used
to change a map dynamically. Variables can have an impact on a map through [property bindings](variable-to-property-binding.md),
or through the [Scripting API](https://workadventu.re/map-building/api-state.md).

In order to edit the value of a variable, the *Scripting API Extra* library comes with a way to define configuration
pages automatically.

The configuration page displays a form that is **generated from the variables** present on the map.
Each variable is mapped to one field in the form.

<figure class="figure">
<img class="figure-img img-fluid rounded" src="images/sample_configuration_screen.png" alt="" />
<figcaption class="figure-caption">A sample configuration screen</figcaption>
</figure>

For a variable to appear in the configuration form, it MUST be stored in a layer called `configuration`.

<figure class="figure">
<img class="figure-img img-fluid rounded" src="images/variables_in_configuration_layer.png" alt="" />
<figcaption class="figure-caption">List of variables that will be displayed in the configuration screen</figcaption>
</figure>

## Protecting the configuration screen

By default, the configuration screen will be accessible to anyone. You will probably want to restrict the access of the
configuration screen to users that have a certain *tag*.

To do this, simply add a `tag` property to the configuration layer. The value of the property is the name of the tag
that users must have to access the configuration screen.

<figure class="figure">
<img class="figure-img img-fluid rounded" src="images/configuration_tag.png" alt="" />
<figcaption class="figure-caption">Here, only users with tag "admin" will have access to the configuration screen</figcaption>
</figure>


## Altering the display of a variable

### Changing the label

Each variable is mapped to one field in the form.

By default, the name of the variable is used as the label.

You can add a `label` property on the variable to display a custom label for your variable.

<figure class="figure">
<img class="figure-img img-fluid rounded" src="images/configuration_label.png" alt="" />
<figcaption class="figure-caption">The label property added to a variable</figcaption>
</figure>

<figure class="figure">
<img class="figure-img img-fluid rounded" src="images/configuration_label_screenshot.png" alt="" />
<figcaption class="figure-caption">The label property is used as field label</figcaption>
</figure>

### Changing the type of the field

By default, a variable will be displayed as a text box, unless the `default` property is a boolean, in which case
it will be displayed as a checkbox.

You can alter this type of the field displayed by using the `type` **custom** property.

{.alert.alert-warning}
**Important!** The *type* of the point object that represents the variable must always be `variable`. You should add
a **custom** property whose name is "type" to set the type of the field.

Acceptable values for the "type" property are:

- `text`: displays a text field
- `checkbox`: displays a checkbox
- `select`: displays a select (see `allowed_values`)
- `radio`: displays radio buttons (see `allowed_values`)

### Enumerations (select / radio buttons)

If you want to display a select box or radio buttons, you need to provide the list of possible values.
This can be done through the `allowed_values` property.

The `allowed_values` must be passed a JSON object whose keys are the text displayed, and whose values are the value that
will take the variable if the option is selected.

For instance, if you want to do a simple "Yes/No" radio button, you would write:

`allowed_values: {
"Yes": true,
"No": false
}`

When you use the `allowed_values` property in your variable, do not forget to the the `type` property to `select` or `radio`.

<figure class="figure">
<img class="figure-img img-fluid rounded" src="images/configuration_allowed_values.png" alt="" />
<figcaption class="figure-caption">The "allowed_values" property added to a variable</figcaption>
</figure>

<figure class="figure">
<img class="figure-img img-fluid rounded" src="images/configuration_allowed_values_description.png" alt="" />
<figcaption class="figure-caption">The field is displayed as a "select" because we chose "type = select"</figcaption>
</figure>


### Adding a description / hint

You can add a `description` property on the variable to display a description of the purpose of the field, below
the field.

<figure class="figure">
<img class="figure-img img-fluid rounded" src="images/configuration_description.png" alt="" />
<figcaption class="figure-caption">The description property added to a variable</figcaption>
</figure>

<figure class="figure">
<img class="figure-img img-fluid rounded" src="images/configuration_description_screenshot.png" alt="" />
<figcaption class="figure-caption">The description property is displayed below the field</figcaption>
</figure>

### Field visibility

The configuration page will respect the visibility rights configured on the variable.

- If the `readableBy` property is set on the variable, the variable will appear in the configuration screen only if the current user has the right
to read this variable.
- If the `writableBy` property is set on the variable, the variable will be displayed, but modifiable only if the current user has the right
to write to this variable.

## Creating sub-sections

Do you have many variables on your map? You can organize these variables on different configuration pages.
To do this, simply turn the `configuration` layer into a "group" layer. In this group layer, you can put many object
layers. Each object layer will be rendered in a different page.

<figure class="figure">
<img class="figure-img img-fluid rounded" src="images/configuration_layers_tree.png" alt="" />
<figcaption class="figure-caption">Variables are stored in layers inside the "configuration" group layer</figcaption>
</figure>

<figure class="figure">
<img class="figure-img img-fluid rounded" src="images/configuration_main_page.png" alt="" />
<figcaption class="figure-caption">Each configuration page is accessible from the main page</figcaption>
</figure>

Each configuration page is accessible from the main page using a menu made of buttons.

The label of the buttons can be edited by setting a custom `label` property on each object layer inside the "configuration" layer.
9 changes: 6 additions & 3 deletions docs/doors.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,10 +129,13 @@ Door steps are layers (usually one in front of the door and one behind) that con
</tr>
</table>

Each door step can have a different set of rules. For instance, you can have the back door step that triggers
automatically the opening of the door, while the front door step requires to enter a code to open the door.
{.alert.alert-info}
**One or two door steps?** Do you want to have different rules for opening / closing the door depending on the side
of the door you are standing? If yes, you will need 2 door steps (and therefore, 2 layers). For instance, you can have the back
door step that triggers automatically the opening of the door, while the front door step requires to enter a code to open the door.
One door step layer spanning the 2 sides of the door can be enough if your door behaves the same on both sides.

In order to create a door step, you MUST put the following properties on the doorstep layer:
In order to create a door step, you MUST create an additional layer and put the following properties on it:

- `zone` (string): a unique identifier for your doorstep (the need to define this property will be removed in a future version)
- `doorVariable` (string): the name of the door variable that this door step controls
Expand Down
1 change: 1 addition & 0 deletions docs/functions-properties.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,6 @@ class Properties {
mustGetString(name: string): string; // returns the property as a string (throws an Error if not found)
mustGetNumber(name: string): string; // returns the property as a number (throws an Error if not found)
mustGetBoolean(name: string): string; // returns the property as a boolean (throws an Error if not found)
getType(name: string): string | undefined; // returns the type of property (as defined in the map)
}
```
26 changes: 26 additions & 0 deletions docs/functions-svelte.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{.section-title.accent.text-primary}
# Svelte utility functions

{.alert.alert-info}
**Important!** To use these functions, you need to [import the "Scripting API Extra"](utils.md#importing-the-utility-functions) library.

If you happen to use the [Svelte framework](https://svelte.dev/), the Scripting API Extra package provide some
utility functions to easily bind your Svelte components to your WorkAdventure map.

## Mapping a WorkAdventure variable to a Svelte store

Use `createStoreFromVariable` to create a Svelte store this is bound to a WorkAdventure variable.

```typescript
const myVariableStore = createStoreFromVariable('my_variable');
```

`createStoreFromVariable` returns a `writable` store.

If you already have a Svelte store and you want to bind it to a WorkAdventure variable, use `mapVariableToStore`.

```typescript
// Maps a WorkAdventure variable to an existing store.
// The "myVariableStore" must be a Svelte store with a "set" function.
mapVariableToStore('my_variable', myVariableStore);
```
4 changes: 4 additions & 0 deletions docs/functions-variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,9 @@ class VariableDescriptor {
// The position of the variable
x: number
y: number
// True if the variable can be read by the current player
isReadable: boolean
// True if the variable can be written by the current player
isWritable: boolean
}
```
Binary file added docs/images/configuration_allowed_values.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/configuration_description.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/configuration_label.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/configuration_label_screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/configuration_layers_tree.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/configuration_main_page.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/configuration_tag.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/sample_configuration_screen.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/templated_property.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/variable.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/variables_in_configuration_layer.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/visible_property.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions docs/menu.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,14 @@
'url' => '/map-building-extra/generic-action-zones.md',
'markdown' => 'scripting_api_extra_doc.generic-action-zones'
],
[
'title' => 'Binding variables to properties',
'url' => '/map-building-extra/variable-to-property-binding.md',
'markdown' => 'scripting_api_extra_doc.variable-to-property-binding'
],[
'title' => 'Configuration screen',
'url' => '/map-building-extra/automatic-configuration.md',
'markdown' => 'scripting_api_extra_doc.automatic-configuration'
],
],
];
5 changes: 5 additions & 0 deletions docs/menu_functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,10 @@
'url' => '/map-building-extra/functions-layers.md',
'markdown' => 'scripting_api_extra_doc.functions-layers'
],
[
'title' => 'Svelte bindings',
'url' => '/map-building-extra/functions-svelte.md',
'markdown' => 'scripting_api_extra_doc.functions-svelte'
],
],
];
1 change: 1 addition & 0 deletions docs/utils.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ It provides a complete development environment ready with Typescript enabled.
- [Property related functions](functions-properties.md)
- [Variable related functions](functions-variables.md)
- [Layer related functions](functions-layers.md)
- [Svelte bindings](functions-svelte.md)
82 changes: 82 additions & 0 deletions docs/variable-to-property-binding.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
{.section-title.accent.text-primary}
# Binding variables to properties

{.alert.alert-info}
**Important!** To use variables to properties binding, you need to [import the "Scripting API Extra" script in your map](about.md#importing-the-extended-features)

In WorkAdventure maps, ["variables"](https://workadventu.re/map-building/api-state.md) are used to share a state between
players.

Using the Scripting API Extra library, you can bind your variables values directly into properties on your map.

In a property of your map, use the `{{{ variableName }}}` to refer to the name of a property.

**Sample**

Let's imagine you want to dynamically change the URL of a co-website based on a variable value.
You can create a new variable named "myWebsiteUrl" and bind it to the `openWebsite` property of your co-website layer.

<figure class="figure">
<img class="figure-img img-fluid rounded" src="images/variable.png" alt="" />
<figcaption class="figure-caption">The variable</figcaption>
</figure>

<figure class="figure">
<img class="figure-img img-fluid rounded" src="images/templated_property.png" alt="" />
<figcaption class="figure-caption">The property referring to the variable</figcaption>
</figure>

## Context

You can use template properties in:

- any property of any "tile" layer

## Configuration

Binding variables to properties can make your map reactive to variable changes, but you still need to find a way
to modify the values of variables. There are plenty of ways to do this, including:

- [Using the scripting API](https://workadventu.re/map-building/api-state.md)
- [Using auto-generated configuration screen]() // TODO
- [Using generic action zones](generic-action-zones.md)

## About bindings

Use `{{{ variableName }}}` to refer to a variable name.

Behind the scene the [Mustache templating engine](https://en.wikipedia.org/wiki/Mustache_(template_system)) is used.
This means you can use all the features of Mustache like conditional:

`openWebsite: {{#enableWebsite}}https://example.com{{/enableWebsite}}`

The website above will be displayed only if the `enableWebsite` variable is set to `true`.

{.alert.alert-warning}
Be sure to use `{{{ variableName }}}` for binding variable and NOT `{{ variableName }}`. The version with a double
curly-braces will work most of the time, but it escapes HTML characters (which is not needed in properties of a map)
and this might cause weird behaviours (like breaking URLs)


## The special "visible" property

You can control the visibility of a layer with the `visible` **custom** property.

If this custom property is set, it will override the "Visible" property of the layer.

If you bind it to a variable, you can display or hide a layer based on the value of a variable.

<figure class="figure">
<img class="figure-img img-fluid rounded" src="images/visible_property.png" alt="" />
<figcaption class="figure-caption">Usage of the `visible` property</figcaption>
</figure>

### Inverting a boolean variable

In the example above, the `holeInWall` property is a boolean variable. If it is set to `true`, we want to hide the
layer. So we are setting a `visible` property on the layer. If we put `visible: {{{ holeInWall }}}` the layer
would be visible when `holeInWall` is `true`. But here, we want the opposite: the layer must be displayed when
`holeInWall` variable is `false`. To do this, we can use Mustache's "inverted sections" (delimited by `{{^variable}}...{{/variable}}`).
This section will be displayed if the variable is false or empty.

Therefore, `{{^holeInWall}}1{{/holeInWall}}` will return "1" when the value of `holeInWall` is false, and will be empty otherwise.
4 changes: 2 additions & 2 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ module.exports = {
collectCoverageFrom: ['<rootDir>/src/**/*.ts', '!**/node_modules/**', '!**/vendor/**'],
coverageDirectory: '<rootDir>/coverage',
coverageReporters: [['lcov', { projectRoot: './' }], 'text'],
coverageThreshold: {
/*coverageThreshold: {
global: {
branches: 50,
functions: 80,
lines: 80,
statements: -35,
},
},
},*/
}
Loading

0 comments on commit 4b784ac

Please sign in to comment.