You've already forked MenuGraphr
mirror of
https://github.com/FullScreenShenanigans/MenuGraphr.git
synced 2026-04-28 13:02:13 -07:00
Added detailed docs on schemas, dialogs, lists, and text (#53)
* Added detailed docs on schemas, dialogs, lists, and text * TSLint fixups * Finished README.md * Trailing whitespace...
This commit is contained in:
@@ -7,6 +7,156 @@
|
||||
In-game menu and dialog creation and management for GameStartr.
|
||||
<!-- {{/Top}} -->
|
||||
|
||||
MenuGraphr automates creating in-game menus containing paragraphs or scrolling lists of text.
|
||||
Each menu has a unique name by which its globally identified as well as a rectangular position relative to its parent.
|
||||
Menus can be positioned as children of the root game's MapScreenr viewport or of each other.
|
||||
|
||||
## Usage
|
||||
|
||||
MenuGraphr instances take in, at the very least, a GameStartr game to create Things within.
|
||||
The game should have have a `"Menu"` Thing defined.
|
||||
|
||||
### Constructor
|
||||
|
||||
```typescript
|
||||
const gameStarter = new GameStartr({ ... });
|
||||
const menuGrapher = new MenuGraphr({ gameStarter });
|
||||
```
|
||||
|
||||
#### `gameStarter`
|
||||
|
||||
The parent GameStartr managing Things.
|
||||
This is the only mandatory settings field.
|
||||
|
||||
#### `aliases`
|
||||
|
||||
Alternate Thing titles for characters, such as `" "` for `"Space"`.
|
||||
Normally, Things used as menu text have titles equal to `"Text"` plus the name of the character.
|
||||
These will replace the name of the character in that computation.
|
||||
|
||||
```typescript
|
||||
// Uses "TextSpace" instead of "Text "
|
||||
new MenuGraphr({
|
||||
aliases: {
|
||||
" ": "Space",
|
||||
},
|
||||
gameStarter,
|
||||
});
|
||||
```
|
||||
|
||||
#### `sounds`
|
||||
|
||||
Sounds that should be played for certain menu actions.
|
||||
So far, this is only `onInteraction`, which is whenever a menu is interacted with
|
||||
(usually off the A or B buttons being pressed).
|
||||
These are played with the GameStartr's AudioPlayr.
|
||||
|
||||
```typescript
|
||||
new MenuGraphr({
|
||||
gameStarter,
|
||||
sounds: {
|
||||
onInteraction: "Bloop",
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
#### `replacements`
|
||||
|
||||
Programmatic replacements for deliniated words.
|
||||
Allows texts in menus to contain dynamic values using predetermined strings.
|
||||
|
||||
These can be hardcoded strings or functions to generate them.
|
||||
|
||||
```typescript
|
||||
new MenuGraphr({
|
||||
gameStarter,
|
||||
replacements: {
|
||||
"DYNAMIC": () => gameStarter.itemsHolder.get("dynamic-value"),
|
||||
"STATIC": "My name here!",
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
Menu dialogs and lists will directly replace the values of replacements between the menu's `replacerKey` (see below):
|
||||
|
||||
```typescript
|
||||
menuGrapher.addMenuDialog("GeneralText", [
|
||||
// Inserts the value of gameStarter.itemsHolder.get("dynamic-value")
|
||||
"Dynamic value: %%%%%%%DYNAMIC%%%%%%%",
|
||||
|
||||
// Inserts "My name here!"
|
||||
"Static value: %%%%%%%STATIC%%%%%%%",
|
||||
]);
|
||||
```
|
||||
|
||||
#### `replacerKey`
|
||||
|
||||
Separator for words to replace using `replacements`.
|
||||
Defaults to `"%%%%%%%"`.
|
||||
|
||||
```typescript
|
||||
new MenuGraphr({
|
||||
gameStarter,
|
||||
replacements: {
|
||||
"STATIC": "My name here!",
|
||||
},
|
||||
replacerKey: "|",
|
||||
});
|
||||
```
|
||||
|
||||
```typescript
|
||||
menuGrapher.addMenuDialog("GeneralText", [
|
||||
// Inserts "My name here!"
|
||||
"Static value: |STATIC|",
|
||||
]);
|
||||
```
|
||||
|
||||
#### `schemas`
|
||||
|
||||
Known menu schemas, keyed by name.
|
||||
Those properties are defined on `IMenuSchema`.
|
||||
See [`docs/schemas.md`](./docs/schemas.md).
|
||||
|
||||
```typescript
|
||||
new MenuGraphr({
|
||||
gameStarter,
|
||||
schemas: {
|
||||
GeneralText: {
|
||||
size: {
|
||||
height: 96,
|
||||
width: 320,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### `createMenu`
|
||||
|
||||
Menus are created with `createMenu`, which takes in the string name of the menu and any additional properties.
|
||||
|
||||
```typescript
|
||||
menuGrapher.createMenu("GeneralText", { /* ... */ });
|
||||
```
|
||||
|
||||
Each menu is identified by a unique string name.
|
||||
When `createMenu` creates a menu, any existing menu under that name is disposed of.
|
||||
|
||||
### `setActiveMenu`
|
||||
|
||||
Sets a menu to appear to have user focus.
|
||||
For dialogs, this allows the user to "A" through them.
|
||||
For lists, this visualizes the selected index with an "Arrow" Thing.
|
||||
|
||||
Only one menu may be active at any time.
|
||||
There does not need to be an active menu, and menus are not active by default.
|
||||
|
||||
```typescript
|
||||
menuGrapher.createMenu("GeneralText");
|
||||
menuGrapher.addMenuDialog("GeneralText", "Hello world!");
|
||||
menuGrapher.setActiveMenu("GeneralText");
|
||||
```
|
||||
|
||||
<!-- {{Development}} -->
|
||||
## Development
|
||||
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
# Dialogs
|
||||
|
||||
Menu dialogs refer to any amount of text on a menu.
|
||||
Some dialogs are static, and just used to display text Things on top of a menu.
|
||||
Others are dynamic and can advance through formatted lines of interactive text.
|
||||
|
||||
## `addMenuDialog`
|
||||
|
||||
Adds dialog-style text to a menu.
|
||||
If the text would overflow the menu's size, excess horizontal lines are delayed.
|
||||
The user can advance through the menu with "A" button presses.
|
||||
|
||||
Parameters:
|
||||
|
||||
* `menuName`: Name of the menu.
|
||||
* `dialog`: Raw dialog to add to the menu, as strings, arrays of strings, or complex placement commands.
|
||||
* `onCompletion`: Optional callback for when the text is done.
|
||||
|
||||
### Dialogs
|
||||
|
||||
The actual type for menu dialogs is weirdly flexible:
|
||||
|
||||
```typescript
|
||||
type IMenuDialogRaw = string | (string | string[] | (string | string[])[] | IMenuWordCommandBase)[];
|
||||
```
|
||||
|
||||
#### String Dialogs
|
||||
|
||||
The simplest dialogs will typically just contain strings:
|
||||
When provided as a raw string, the dialog is split across whitespace to generate words.
|
||||
This forces the dialog to not wrap words across lines.
|
||||
|
||||
```typescript
|
||||
// Creates a new GeneralText menu, deleting any existing one
|
||||
menuGrapher.createMenu("GeneralText");
|
||||
|
||||
// Adds the dialog to GeneralText
|
||||
menuGrapher.addMenuDialog("GeneralText", "Hello world!");
|
||||
|
||||
// Sets GeneralText as the active, input-receiving menu
|
||||
menuGrapher.setActiveMenu("GeneralText");
|
||||
```
|
||||
|
||||
#### Array Dialogs
|
||||
|
||||
Dialogs will show as many consecutive lines on "A" press as possible by default.
|
||||
You can force a "break" in the sections by providing an array of dialog strings.
|
||||
The dialog will clear any lines on the screen when moving across a break.
|
||||
|
||||
```typescript
|
||||
menuGrapher.addMenuDialog("GeneralText", ["Hello world!", "Me again!"]);
|
||||
```
|
||||
|
||||
### Advanced Commands
|
||||
|
||||
It's allowed to provide advanced "commands" to dialogs along with words in the dialogs.
|
||||
These commands can insert "floating" text or change position offsets or alignments of the dialog text.
|
||||
|
||||
Alas, these command types aren't well fleshed out in `IMenuGraphr.ts` and not recommended until documentation is solidified.
|
||||
See [#52](https://github.com/FullScreenShenanigans/MenuGraphr/issues/52).
|
||||
|
||||
## `IMenuSchema`
|
||||
|
||||
These options are available on `IMenuSchema` generally, but only useful once the menu is given a dialog.
|
||||
|
||||
### `deleteOnFinish`
|
||||
|
||||
Whether the menu should be deleted when its dialog finishes.
|
||||
|
||||
```typescript
|
||||
{
|
||||
deleteOnFinish: true,
|
||||
}
|
||||
```
|
||||
|
||||
### `finishAutomatically`
|
||||
|
||||
Whether the dialog should finish when the last word is displayed,
|
||||
instead of waiting for user input.
|
||||
|
||||
```typescript
|
||||
{
|
||||
finishAutomatically: true,
|
||||
},
|
||||
```
|
||||
|
||||
### `finishAutomaticSpeed`
|
||||
|
||||
How many game ticks to delay completion by when `finishAutomatically` is true.
|
||||
Defaults to `0`.
|
||||
|
||||
```typescript
|
||||
{
|
||||
finishAutomaticSpeed: 100,
|
||||
}
|
||||
```
|
||||
|
||||
See [`lists.md`](./lists.md) for examples of lists intermixed with dialogs.
|
||||
+216
@@ -0,0 +1,216 @@
|
||||
# Lists
|
||||
|
||||
Menus can have scrollable lists of selectable text options in them.
|
||||
Users can use direction inputs to scroll through the items.
|
||||
|
||||
Lists can be one-dimensional or two-dimensional.
|
||||
This is determined by the computed height of list options within the menu's height.
|
||||
If enough options are added to a list to pass the bottom, unless the menu specifies `singleColumnList`, they will overflow to a column to the right.
|
||||
|
||||
## `IListMenuSchema`
|
||||
|
||||
When creating or declaring schemas for list menus, there are some additional properties you can apply to them.
|
||||
These are all optional.
|
||||
|
||||
### `saveIndex`
|
||||
|
||||
Whether the last selected index should be saved.
|
||||
When `true`, the parent GameStartr's ItemsHoldr will save the selected index of the menu under the menu's name.
|
||||
Recreating the menu will read from that stored index if available.
|
||||
|
||||
```typescript
|
||||
{
|
||||
saveIndex: true,
|
||||
},
|
||||
```
|
||||
|
||||
### `clearedIndicesOnDeletion`
|
||||
|
||||
Names of menus whose whose selected indices that should be cleared when this menu is deleted.
|
||||
Use this when there are multiple related list menus open at once, and finishing one clears another.
|
||||
|
||||
```typescript
|
||||
{
|
||||
clearedIndicesOnDeletion: [
|
||||
"KeyboardKeys",
|
||||
"NameCollection",
|
||||
],
|
||||
},
|
||||
```
|
||||
|
||||
### `scrollingItems`
|
||||
|
||||
How many scrolling items should be visible within the menu vertically.
|
||||
List menus will by default show all the items at once, which is bad if there are many options and not enough menu height to display them all.
|
||||
Specify a `scrollingItems` number to hardcode a maximum to display at once.
|
||||
|
||||
If the user shifts their selected index to below the lowest displayed item or above the highest displayed item,
|
||||
the menu will "scroll" items vertically.
|
||||
It does this by shifting their Things vertically and setting `hidden` on items not allowed to be seen.
|
||||
|
||||
```typescript
|
||||
{
|
||||
scrollingItems: 10,
|
||||
},
|
||||
```
|
||||
|
||||
> There is no equivalent for horizontal items.
|
||||
|
||||
### `scrollingItemsComputed`
|
||||
|
||||
As an alternative to `scrollingItems`, you can have the maximum displayed number of items computed as a function of menu height and expected height per list option.
|
||||
This will set the `scrollingItems` member of the menu on list creation.
|
||||
|
||||
```typescript
|
||||
{
|
||||
scrollingItemsComputed: true,
|
||||
},
|
||||
```
|
||||
|
||||
### `singleColumnList`
|
||||
|
||||
Whether the list should always be a single column, rather than auto-flow.
|
||||
|
||||
```typescript
|
||||
{
|
||||
singleColumnList: true,
|
||||
},
|
||||
```
|
||||
|
||||
## `addMenuList`
|
||||
|
||||
Adds a list of text options to a menu.
|
||||
|
||||
Parameters:
|
||||
|
||||
* `menuName`: Name of the menu.
|
||||
* `settings`: Settings for the list, particularly its options.
|
||||
|
||||
### `IListMenuOption`
|
||||
|
||||
Individual option within a list.
|
||||
Only `text` is required.
|
||||
|
||||
#### `callback`
|
||||
|
||||
Callback for when the option is triggered.
|
||||
Receives just the menu name.
|
||||
|
||||
```typescript
|
||||
{
|
||||
options: [
|
||||
{
|
||||
callback: () => console.log("First!"),
|
||||
text: "First",
|
||||
},
|
||||
],
|
||||
},
|
||||
```
|
||||
|
||||
#### `position`
|
||||
|
||||
Position offsets to shift the option by, allowing top, right, bottom, left.
|
||||
|
||||
```typescript
|
||||
{
|
||||
options: [
|
||||
{
|
||||
position: {
|
||||
top: 30,
|
||||
right: -20,
|
||||
},
|
||||
text: "First",
|
||||
},
|
||||
],
|
||||
},
|
||||
```
|
||||
|
||||
#### `text`
|
||||
|
||||
Text displayed as the option.
|
||||
This text will be rendered all in one row, similar to dialogs.
|
||||
|
||||
|
||||
### `IListMenuOptions`
|
||||
|
||||
Settings to create a new list menu.
|
||||
Only
|
||||
|
||||
#### `bottom`
|
||||
|
||||
A bottom option to display underneath displayed options.
|
||||
If the list is two-dimensional, this will span across all rows.
|
||||
|
||||
```typescript
|
||||
{
|
||||
bottom: {
|
||||
callback: () => console.log("Cancelled."),
|
||||
text: "Cancel",
|
||||
},
|
||||
},
|
||||
```
|
||||
|
||||
#### `options`
|
||||
|
||||
Options within the menu, or a function to generate them.
|
||||
This is a flat list of options regarldess of whether the menu is one- or two-dimensional.
|
||||
|
||||
```typescript
|
||||
{
|
||||
options: [
|
||||
{
|
||||
callback: () => console.log("First!"),
|
||||
text: "First",
|
||||
},
|
||||
],
|
||||
},
|
||||
```
|
||||
|
||||
See `IListMenuOption` above.
|
||||
|
||||
#### `selectedIndex`
|
||||
|
||||
Each list contains a `selectedIndex: [number, number]` of the position of the currently selected index.
|
||||
This option overrides the starting selected index.
|
||||
It defaults to `[0, 0]`.
|
||||
|
||||
## Examples
|
||||
|
||||
Showing a simple "Yes/No" menu after a general text dialog that stays alive:
|
||||
|
||||
```typescript
|
||||
const finalize = (choice) => {
|
||||
console.log("Choice:", choice);
|
||||
menuGrapher.deleteMenu("Yes/No");
|
||||
};
|
||||
|
||||
const createOptions = () => {
|
||||
menuGrapher.createMenu("Yes/No", {
|
||||
killOnB: ["GeneralText"],
|
||||
});
|
||||
|
||||
menuGrapher.addMenuList("Yes/No", {
|
||||
options: [
|
||||
{
|
||||
text: "YES",
|
||||
callback: () => finalize(true),
|
||||
},
|
||||
{
|
||||
text: "NO",
|
||||
callback: () => finalize(false),
|
||||
}
|
||||
],
|
||||
});
|
||||
|
||||
menuGrapher.setActiveMenu("Yes/No");
|
||||
};
|
||||
|
||||
menuGrapher.createMenu("GeneralText", {
|
||||
finishAutomatically: true,
|
||||
keepOnBack: true,
|
||||
});
|
||||
menuGrapher.addMenuDialog("GeneralText", "Are you sure?", createOptions);
|
||||
menuGrapher.setActiveMenu("GeneralText");
|
||||
```
|
||||
|
||||
Creating a phone-style keyboard with 0-9, *, and #:
|
||||
+337
@@ -0,0 +1,337 @@
|
||||
# Menu Schemas
|
||||
|
||||
## `IMenuSchema`
|
||||
|
||||
Attributes describing menu appearance and behavior.
|
||||
These may be specified in the default schemas on a MenuGraphr instance or overriden with `createMenu`.
|
||||
|
||||
All properties are optional.
|
||||
|
||||
> See [`dialogs.md`](./dialogs.md) for properties specific to menu dialogs.
|
||||
|
||||
> See [`lists.md`](./lists.md) for properties specific to menu lists.
|
||||
|
||||
> See [`text.md`](./text.md) for how properties around displaying text in dialog and list menus.
|
||||
|
||||
### `backMenu`
|
||||
|
||||
Name of a menu to set as active when this one is deleted.
|
||||
|
||||
```typescript
|
||||
{
|
||||
backMenu: "GeneralText",
|
||||
},
|
||||
```
|
||||
|
||||
### `callback`
|
||||
|
||||
Callback for when this menu is set as active.
|
||||
Called with the menu name.
|
||||
|
||||
```typescript
|
||||
{
|
||||
callback: (menuName) => {
|
||||
console.log("Set", menuName, "as active.");
|
||||
},
|
||||
},
|
||||
```
|
||||
|
||||
### `childrenSchemas`
|
||||
|
||||
Schemas of children to add on creation.
|
||||
These will be directly passed to `createMenuChild`, which will call `createMenu`, `createMenuWord`, or `createMenuThing` as per the child type.
|
||||
As with regular menu schemas, these allow all properties as overrides.
|
||||
|
||||
```typescript
|
||||
{
|
||||
childrenSchemas: [
|
||||
{
|
||||
type: "text",
|
||||
words: ["Hello", "world!"],
|
||||
},
|
||||
{
|
||||
type: "thing",
|
||||
thing: "PlayerPortrait",
|
||||
position: {
|
||||
horizontal: "right",
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "menu",
|
||||
name: "PlayerStats",
|
||||
},
|
||||
],
|
||||
},
|
||||
```
|
||||
|
||||
> See `IMenuChildSchema`.
|
||||
|
||||
### `container`
|
||||
|
||||
Name of a containing menu to position within.
|
||||
If not provided, this defaults to the entire game canvas.
|
||||
|
||||
```typescript
|
||||
{
|
||||
container: "GeneralText",
|
||||
},
|
||||
```
|
||||
|
||||
### `height`
|
||||
|
||||
How tall the menu should be, as Thing height.
|
||||
This will also set the general Thing height of the menu.
|
||||
|
||||
```typescript
|
||||
{
|
||||
height: 80,
|
||||
},
|
||||
```
|
||||
|
||||
### `ignoreA`
|
||||
|
||||
Whether user selection events should be ignored.
|
||||
These "A" events normally advance menu dialogs forward or trigger selected items in lists.
|
||||
|
||||
```typescript
|
||||
{
|
||||
ignoreA: true,
|
||||
},
|
||||
```
|
||||
|
||||
### `ignoreB`
|
||||
|
||||
Whether user deselection events should be ignored.
|
||||
These "B" events normally exit out of menus.
|
||||
|
||||
```typescript
|
||||
{
|
||||
ignoreB: true,
|
||||
},
|
||||
```
|
||||
|
||||
### `ignoreProgressB`
|
||||
|
||||
Whether deselection events should count as selection during dialogs.
|
||||
Menus with "progress" are in the middle of dialog or list creation.
|
||||
Pressing B during progress would normally advance the menu forward.
|
||||
|
||||
```typescript
|
||||
{
|
||||
ignoreProgressB: true,
|
||||
},
|
||||
```
|
||||
|
||||
### `keepOnBack`
|
||||
|
||||
Whether this should be kept alive when deselected.
|
||||
Useful for switching active state between multiple menus on B.
|
||||
|
||||
```typescript
|
||||
{
|
||||
keepOnBack: true,
|
||||
},
|
||||
```
|
||||
|
||||
### `killOnB`
|
||||
|
||||
Other menus to kill when this is deselected.
|
||||
Commonly used with "Yes/No"-style dialogs that appear along with text descriptions in other menus.
|
||||
|
||||
```typescript
|
||||
{
|
||||
killOnB: ["GeneralText", "OtherDecorations"],
|
||||
},
|
||||
```
|
||||
|
||||
### `onActive`
|
||||
|
||||
Callback for when the menu becomes active.
|
||||
Receives just the menu name.
|
||||
|
||||
```typescript
|
||||
{
|
||||
onActive: (menuName) => {
|
||||
console.log("Menu", menuName, "is now active.");
|
||||
},
|
||||
},
|
||||
```
|
||||
|
||||
### `onBPress`
|
||||
|
||||
Callback for when the "B" button is pressed while the menu is active.
|
||||
Receives just the menu name.
|
||||
|
||||
Does not fire if `ignoreB` is true.
|
||||
Also does not fire if the menu is mid-progress and `ignoreProgressB` is not true, as that simulates an "A" press.
|
||||
|
||||
```typescript
|
||||
{
|
||||
onBPress: (menuName) => {
|
||||
console.log("Menu", menuName, "received a B press.");
|
||||
},
|
||||
},
|
||||
```
|
||||
|
||||
### `onDown`
|
||||
|
||||
Callback for when the "down" button is pressed.
|
||||
Receives just the menu name.
|
||||
|
||||
```typescript
|
||||
{
|
||||
onDown: (menuName) => {
|
||||
console.log("Menu", menuName, "received a down event.");
|
||||
},
|
||||
},
|
||||
```
|
||||
|
||||
### `onInactive`
|
||||
|
||||
Callback for when the menu becomes inactive.
|
||||
Receives just the menu name.
|
||||
|
||||
```typescript
|
||||
{
|
||||
onActive: (menuName) => {
|
||||
console.log("Menu", menuName, "is now active.");
|
||||
},
|
||||
},
|
||||
```
|
||||
|
||||
### `onLeft`
|
||||
|
||||
Callback for when the "left" button is pressed.
|
||||
Receives just the menu name.
|
||||
|
||||
```typescript
|
||||
{
|
||||
onLeft: (menuName) => {
|
||||
console.log("Menu", menuName, "received a left event.");
|
||||
},
|
||||
},
|
||||
```
|
||||
|
||||
### `onMenuDelete`
|
||||
|
||||
Callback for when the menu is deleted.
|
||||
Receives just the menu name.
|
||||
|
||||
This is called _after_ the menu is deleted, but _before_ menu children are deleted.
|
||||
|
||||
```typescript
|
||||
{
|
||||
onMenuDelete: (menuName) => {
|
||||
console.log("Menu", menuName, "was deleted.");
|
||||
},
|
||||
},
|
||||
```
|
||||
|
||||
### `onRight`
|
||||
|
||||
Callback for when the "right" button is pressed.
|
||||
Receives just the menu name.
|
||||
|
||||
```typescript
|
||||
{
|
||||
onRight: (menuName) => {
|
||||
console.log("Menu", menuName, "received a right event.");
|
||||
},
|
||||
},
|
||||
```
|
||||
|
||||
### `onUp`
|
||||
|
||||
Callback for when the "up" button is pressed.
|
||||
Receives just the menu name.
|
||||
|
||||
```typescript
|
||||
{
|
||||
onUp: (menuName) => {
|
||||
console.log("Menu", menuName, "received an up event.");
|
||||
},
|
||||
},
|
||||
```
|
||||
|
||||
### `size`
|
||||
|
||||
Sizing description, including `height` and `width`.
|
||||
This will override the native Thing `height` and `width` on the Menu.
|
||||
|
||||
```typescript
|
||||
{
|
||||
size: {
|
||||
height: 80,
|
||||
width: 40,
|
||||
},
|
||||
},
|
||||
```
|
||||
|
||||
```typescript
|
||||
{
|
||||
|
||||
},
|
||||
```
|
||||
|
||||
> This allows menus to act as containers at a different size from their visual Things.
|
||||
|
||||
### `width`
|
||||
|
||||
How wide the menu should be, as Thing width.
|
||||
This will also set the general Thing width of the menu.
|
||||
|
||||
```typescript
|
||||
{
|
||||
width: 40,
|
||||
},
|
||||
```
|
||||
|
||||
### `position`
|
||||
|
||||
How the menu should be positioned within its container.
|
||||
Defaults to the menu aligning itself to the top-left corner of its container and no width or height.
|
||||
|
||||
#### `horizontal`
|
||||
|
||||
Modifies how the schema lays itself out horizontally.
|
||||
|
||||
* If `"center"`, aligns to the horizontal midpoint of its container.
|
||||
* If `"right"`, its right aligns with its container's right.
|
||||
* If `"stretch"`, stretches to fit its container horizontally.
|
||||
|
||||
```typescript
|
||||
position: {
|
||||
horizontal: "right",
|
||||
},
|
||||
```
|
||||
|
||||
#### `offset`
|
||||
|
||||
Horizontal and vertical offsets to shift the menu by.
|
||||
These are allowed to be negative numbers, are calculated relative to the menu's container, and each reduce the menu's size horizontally or vertically.
|
||||
|
||||
```typescript
|
||||
position: {
|
||||
offset: {
|
||||
top: -1,
|
||||
right: 2,
|
||||
bottom: 3,
|
||||
left: -4,
|
||||
},
|
||||
},
|
||||
```
|
||||
|
||||
#### `vertical`
|
||||
|
||||
Modifies how the schema lays itself out vertically.
|
||||
|
||||
* If `"center"`, aligns to the vertical midpoint of its container.
|
||||
* If `"bottom"`, its bottom aligns with its container's bottom.
|
||||
* If `"stretch"`, stretches to fit its container vertically.
|
||||
|
||||
```typescript
|
||||
position: {
|
||||
vertical: "bottom",
|
||||
},
|
||||
```
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
# Text
|
||||
|
||||
Text within menu dialogs and lists can be displayed with granular controls over paddings and spacing.
|
||||
|
||||
## `IMenuSchema`
|
||||
|
||||
These options will be used when the menu has a dialog or list added.
|
||||
|
||||
### `textPaddingRight`
|
||||
|
||||
How much padding there is between the right of the text and the right side of the box.
|
||||
Text in dialogs will return to the next line instead of crossing the menu's `right` minus `textPaddingRight`.
|
||||
Defaults to `0` (none) if not provided.
|
||||
Allowed to be negative.
|
||||
|
||||
```typescript
|
||||
{
|
||||
textPaddingRight: 8,
|
||||
},
|
||||
```
|
||||
|
||||
### `textPaddingX`
|
||||
|
||||
How much horizontal padding should be between characters.
|
||||
Defaults to the `"Text"` Thing prototype's `paddingX` if it exists, or `0` otherwise.
|
||||
Characters placed next to each other in dialogs and lists will be placed this number of game pixels to the right each subsequent character.
|
||||
|
||||
```typescript
|
||||
{
|
||||
textPaddingX: 4,
|
||||
},
|
||||
```
|
||||
|
||||
### `textPaddingY`
|
||||
|
||||
How much vertical padding should be between lines of text.
|
||||
Defaults to the `"Text"` Thing prototype's `paddingY` if it exists, or `0` otherwise.
|
||||
Lines of text in dialogs and lists will start this number of game pixels lower each line.
|
||||
|
||||
```typescript
|
||||
{
|
||||
textPaddingY: 12,
|
||||
},
|
||||
```
|
||||
|
||||
### `textSpeed`
|
||||
|
||||
How long to delay between placing characters and words.
|
||||
If `0` or not provided, characters and words will be placed immediately.
|
||||
|
||||
Otherwise, each character will wait a `textSpeed` delay before displaying.
|
||||
Words will wait a `textSpeed` delay before appearing after each other as well, which gives the illusion of spaces between words also adhering to the delay.
|
||||
|
||||
### `textXOffset`
|
||||
|
||||
Horizontal offset for the text placement area.
|
||||
All text will be offset horizontally by this amount.
|
||||
|
||||
```typescript
|
||||
{
|
||||
textXOffset: 2,
|
||||
},
|
||||
```
|
||||
|
||||
### `textYOffset`
|
||||
|
||||
Vertical offset for the text placement area.
|
||||
All text will be offset vertically by this amount.
|
||||
|
||||
```typescript
|
||||
{
|
||||
textYOffset: -2,
|
||||
},
|
||||
```
|
||||
+164
-131
File diff suppressed because it is too large
Load Diff
+46
-65
@@ -1,8 +1,8 @@
|
||||
import { GameStartr, IThing } from "gamestartr";
|
||||
|
||||
import {
|
||||
IAliases, IGridCell, IListMenu, IListMenuOptions, IListMenuProgress,
|
||||
IMenu, IMenuBase, IMenuChildMenuSchema, IMenuChildSchema,
|
||||
IAliases, IGridCell, IListMenu, IListMenuOption, IListMenuOptions,
|
||||
IListMenuProgress, IMenu, IMenuBase, IMenuChildSchema,
|
||||
IMenuDialogRaw, IMenuGraphr, IMenuGraphrSettings, IMenuSchema,
|
||||
IMenuSchemaPosition, IMenuSchemaPositionOffset, IMenuSchemas,
|
||||
IMenuSchemaSize, IMenusContainer, IMenuThingSchema, IMenuWordCommand,
|
||||
@@ -41,7 +41,7 @@ export class MenuGraphr implements IMenuGraphr {
|
||||
private readonly schemas: IMenuSchemas;
|
||||
|
||||
/**
|
||||
* A list of sounds that should be played for certain menu actions
|
||||
* Sounds that should be played for certain menu actions.
|
||||
*/
|
||||
private readonly sounds: ISoundNames;
|
||||
|
||||
@@ -55,6 +55,11 @@ export class MenuGraphr implements IMenuGraphr {
|
||||
*/
|
||||
private readonly replacements: IReplacements;
|
||||
|
||||
/**
|
||||
* Separator for words to replace using replacements.
|
||||
*/
|
||||
private readonly replacerKey: string;
|
||||
|
||||
/**
|
||||
* The currently "active" (user-selected) menu.
|
||||
*/
|
||||
@@ -66,18 +71,12 @@ export class MenuGraphr implements IMenuGraphr {
|
||||
* @param settings Settings to be used for initialization.
|
||||
*/
|
||||
public constructor(settings: IMenuGraphrSettings) {
|
||||
if (!settings) {
|
||||
throw new Error("No settings object given to MenuGraphr.");
|
||||
}
|
||||
if (!settings.gameStarter) {
|
||||
throw new Error("No GameStartr given to MenuGraphr.");
|
||||
}
|
||||
|
||||
this.gameStarter = settings.gameStarter;
|
||||
|
||||
this.schemas = settings.schemas || {};
|
||||
this.aliases = settings.aliases || {};
|
||||
this.replacements = settings.replacements || {};
|
||||
this.replacerKey = settings.replacerKey || "%%%%%%%";
|
||||
this.sounds = settings.sounds || {};
|
||||
|
||||
this.menus = {};
|
||||
@@ -205,16 +204,16 @@ export class MenuGraphr implements IMenuGraphr {
|
||||
public createMenuChild(name: string, schema: IMenuChildSchema): IThing | IThing[] {
|
||||
switch (schema.type) {
|
||||
case "menu":
|
||||
return this.createMenu((schema as IMenuChildMenuSchema).name, (schema as IMenuChildMenuSchema).attributes);
|
||||
return this.createMenu(schema.name, schema.attributes);
|
||||
|
||||
case "text":
|
||||
return this.createMenuWord(name, schema as IMenuWordSchema);
|
||||
return this.createMenuWord(name, schema);
|
||||
|
||||
case "thing":
|
||||
return this.createMenuThing(name, schema as IMenuThingSchema);
|
||||
return this.createMenuThing(name, schema);
|
||||
|
||||
default:
|
||||
throw new Error(`Unknown schema type: '${schema.type}'.`);
|
||||
throw new Error(`Unknown schema type: '${(schema as IMenuChildSchema).type}'.`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -312,21 +311,21 @@ export class MenuGraphr implements IMenuGraphr {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds dialog-style text to a menu. If the text overflows,
|
||||
* Adds dialog-style text to a menu.
|
||||
*
|
||||
* @param name The name of the menu.
|
||||
* @param menuName Name of the menu.
|
||||
* @param dialog Raw dialog to add to the menu.
|
||||
* @param onCompletion An optional callback for when the text is done.
|
||||
*/
|
||||
public addMenuDialog(name: string, dialog: IMenuDialogRaw, onCompletion?: () => any): void {
|
||||
public addMenuDialog(menuName: string, dialog: IMenuDialogRaw, onCompletion?: () => any): void {
|
||||
const dialogParsed: (string[] | IMenuWordCommand)[][] = this.parseRawDialog(dialog);
|
||||
let currentLine = 1;
|
||||
|
||||
const callback: () => void = (): void => {
|
||||
// If all dialog has been exhausted, delete the menu and finish
|
||||
if (currentLine >= dialogParsed.length) {
|
||||
if (this.menus[name].deleteOnFinish) {
|
||||
this.deleteMenu(name);
|
||||
if (this.menus[menuName].deleteOnFinish) {
|
||||
this.deleteMenu(menuName);
|
||||
}
|
||||
if (onCompletion) {
|
||||
onCompletion();
|
||||
@@ -338,16 +337,15 @@ export class MenuGraphr implements IMenuGraphr {
|
||||
|
||||
// Delete any previous texts. This is only done if continuing
|
||||
// So that when the dialog is finished, the last text remains
|
||||
this.deleteMenuChildren(name);
|
||||
this.deleteMenuChildren(menuName);
|
||||
|
||||
// This continues the dialog with the next iteration (word)
|
||||
this.addMenuText(name, dialogParsed[currentLine - 1], callback);
|
||||
this.addMenuText(menuName, dialogParsed[currentLine - 1], callback);
|
||||
};
|
||||
|
||||
// This first call to addMenuText shouldn't be the callback, because if
|
||||
// Being called from a childrenSchema of type "text", it shouldn't delete
|
||||
// Any other menu children from childrenSchemas.
|
||||
this.addMenuText(name, dialogParsed[0], callback);
|
||||
// This first call to addMenuText shouldn't be the callback.
|
||||
// If called from a childrenSchema of type "text", it shouldn't delete any other menu children from childrenSchemas.
|
||||
this.addMenuText(menuName, dialogParsed[0], callback);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -395,13 +393,12 @@ export class MenuGraphr implements IMenuGraphr {
|
||||
* Adds a list of text options to a menu.
|
||||
*
|
||||
* @param name The name of the menu.
|
||||
* @param settings Settings for the list, particularly its options, starting
|
||||
* index, and optional floating bottom.
|
||||
* @param settings Settings for the list, particularly its options.
|
||||
*/
|
||||
public addMenuList(name: string, settings: IListMenuOptions): void {
|
||||
const menu: IListMenu = this.getExistingMenu(name) as IListMenu;
|
||||
const options: any[] = settings.options.constructor === Function
|
||||
? (settings.options as any)()
|
||||
const options: IListMenuOption[] = typeof settings.options === "function"
|
||||
? settings.options()
|
||||
: settings.options;
|
||||
let left: number = menu.left + (menu.textXOffset || 0);
|
||||
const top: number = menu.top + (menu.textYOffset || 0);
|
||||
@@ -424,7 +421,7 @@ export class MenuGraphr implements IMenuGraphr {
|
||||
let j: number;
|
||||
let k: number;
|
||||
|
||||
menu.options = options;
|
||||
menu.options = options as any[];
|
||||
menu.optionChildren = optionChildren;
|
||||
|
||||
menu.callback = this.triggerMenuListOption.bind(this);
|
||||
@@ -467,7 +464,7 @@ export class MenuGraphr implements IMenuGraphr {
|
||||
menu.children.push(character);
|
||||
optionChild.things.push(character);
|
||||
|
||||
if (!schema.position || !schema.position.relative) {
|
||||
if (!schema.position) {
|
||||
this.gameStarter.physics.shiftVert(character, y - menu.top);
|
||||
}
|
||||
}
|
||||
@@ -778,17 +775,17 @@ export class MenuGraphr implements IMenuGraphr {
|
||||
* Reacts to a user event directing down.
|
||||
*/
|
||||
public registerDown(): void {
|
||||
const menu: IListMenu = this.activeMenu as IListMenu;
|
||||
const menu = this.activeMenu;
|
||||
if (!menu) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (menu.selectedIndex) {
|
||||
if ((menu as IListMenu).selectedIndex) {
|
||||
this.shiftSelectedIndex(menu.name, 0, 1);
|
||||
}
|
||||
|
||||
if (menu.onDown) {
|
||||
menu.onDown(this.gameStarter);
|
||||
menu.onDown(menu.name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -806,7 +803,7 @@ export class MenuGraphr implements IMenuGraphr {
|
||||
}
|
||||
|
||||
if (menu.onLeft) {
|
||||
menu.onLeft(this.gameStarter);
|
||||
menu.onLeft(menu.name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -861,20 +858,6 @@ export class MenuGraphr implements IMenuGraphr {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reacts to a user event from pressing a start key.
|
||||
*/
|
||||
public registerStart(): void {
|
||||
const menu: IListMenu = this.activeMenu as IListMenu;
|
||||
if (!menu) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (menu.startMenu) {
|
||||
this.setActiveMenu(menu.startMenu);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a series of words to a menu.
|
||||
*
|
||||
@@ -939,7 +922,6 @@ export class MenuGraphr implements IMenuGraphr {
|
||||
let textPaddingX: number;
|
||||
let textPaddingY: number;
|
||||
let textSpeed: number;
|
||||
let textWidthMultiplier: number;
|
||||
let character: IText;
|
||||
let j: number;
|
||||
|
||||
@@ -960,16 +942,15 @@ export class MenuGraphr implements IMenuGraphr {
|
||||
textSpeed = typeof menu.textSpeed === undefined ? 1 : menu.textSpeed || 0;
|
||||
textWidth = menu.textWidth || textProperties.width;
|
||||
textPaddingRight = menu.textPaddingRight || 0;
|
||||
textPaddingX = menu.textPaddingX || textProperties.paddingX;
|
||||
textPaddingY = menu.textPaddingY || textProperties.paddingY;
|
||||
textWidthMultiplier = menu.textWidthMultiplier || 1;
|
||||
textPaddingX = menu.textPaddingX || textProperties.paddingX || 0;
|
||||
textPaddingY = menu.textPaddingY || textProperties.paddingY || 0;
|
||||
|
||||
// For each character in the word, schedule it appearing in the menu
|
||||
for (j = 0; j < word.length; j += 1) {
|
||||
// For non-whitespace characters, add them and move to the right
|
||||
if (/\S/.test(word[j])) {
|
||||
character = this.addMenuCharacter(name, word[j], x, y, j * textSpeed);
|
||||
x += textWidthMultiplier * (character.width + textPaddingX);
|
||||
x += character.width + textPaddingX;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -979,7 +960,7 @@ export class MenuGraphr implements IMenuGraphr {
|
||||
x = menu.textX!;
|
||||
y += textPaddingY;
|
||||
} else if (word[j] !== " " || x !== menu.textX) {
|
||||
x += textWidth * textWidthMultiplier;
|
||||
x += textWidth;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1261,13 +1242,13 @@ export class MenuGraphr implements IMenuGraphr {
|
||||
/**
|
||||
* Runs the callback for a menu's selected list option.
|
||||
*
|
||||
* @param name The name of the menu.
|
||||
* @param menuName Name of the containing menu.
|
||||
*/
|
||||
private triggerMenuListOption(name: string): void {
|
||||
const selected: IGridCell = this.getMenuSelectedOption(name);
|
||||
private triggerMenuListOption(menuName: string): void {
|
||||
const selected: IGridCell = this.getMenuSelectedOption(menuName);
|
||||
|
||||
if (selected.callback) {
|
||||
selected.callback.call(this, name);
|
||||
selected.callback.call(this, menuName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1337,7 +1318,7 @@ export class MenuGraphr implements IMenuGraphr {
|
||||
* character itself otherwise.
|
||||
*/
|
||||
private getCharacterEquivalent(character: string): string {
|
||||
if (this.aliases.hasOwnProperty(character)) {
|
||||
if ({}.hasOwnProperty.call(this.aliases, character)) {
|
||||
return this.aliases[character];
|
||||
}
|
||||
|
||||
@@ -1435,11 +1416,11 @@ export class MenuGraphr implements IMenuGraphr {
|
||||
let end: number;
|
||||
let inside: string | string[];
|
||||
|
||||
start = word.indexOf("%%%%%%%", 0);
|
||||
end = word.indexOf("%%%%%%%", start + 1);
|
||||
start = word.indexOf(this.replacerKey, 0);
|
||||
end = word.indexOf(this.replacerKey, start + 1);
|
||||
|
||||
if (start !== -1 && end !== -1) {
|
||||
inside = this.getReplacement(word.substring(start + "%%%%%%%".length, end));
|
||||
inside = this.getReplacement(word.substring(start + this.replacerKey.length, end));
|
||||
if (inside.constructor === Number) {
|
||||
inside = inside.toString().split("");
|
||||
} else if (inside.constructor === String) {
|
||||
@@ -1448,7 +1429,7 @@ export class MenuGraphr implements IMenuGraphr {
|
||||
|
||||
output.push(...word.substring(0, start).split(""));
|
||||
output.push(...(inside as string[]));
|
||||
output.push(...this.filterWord(word.substring(end + "%%%%%%%".length)));
|
||||
output.push(...this.filterWord(word.substring(end + this.replacerKey.length)));
|
||||
|
||||
return output;
|
||||
}
|
||||
@@ -1462,7 +1443,7 @@ export class MenuGraphr implements IMenuGraphr {
|
||||
* @param words The words to filter, as Strings or command Objects.
|
||||
* @returns The words, with all Strings filtered.
|
||||
*/
|
||||
private filterMenuWords(words: (string | IMenuWordCommand)[]): (string[] | IMenuWordCommand)[] {
|
||||
private filterMenuWords(words: (string | string[] | IMenuWordCommand)[]): (string[] | IMenuWordCommand)[] {
|
||||
const output: (string[] | IMenuWordCommand)[] = [];
|
||||
|
||||
for (const word of words) {
|
||||
|
||||
Reference in New Issue
Block a user