@datamoc/mw_games 0.7.6 → 0.7.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +270 -0
- package/dist/actors/Inventory.d.ts +7 -1
- package/dist/actors/Inventory.d.ts.map +1 -1
- package/dist/actors/Inventory.js.map +1 -1
- package/dist/actors/craft.d.ts +35 -14
- package/dist/actors/craft.d.ts.map +1 -1
- package/dist/actors/craft.js +56 -24
- package/dist/actors/craft.js.map +1 -1
- package/dist/actors/index.d.ts +1 -1
- package/dist/actors/index.d.ts.map +1 -1
- package/dist/ai/Heuristics.d.ts +232 -0
- package/dist/ai/Heuristics.d.ts.map +1 -0
- package/dist/ai/Heuristics.js +298 -0
- package/dist/ai/Heuristics.js.map +1 -0
- package/dist/ai/index.d.ts +4 -0
- package/dist/ai/index.d.ts.map +1 -1
- package/dist/ai/index.js +2 -0
- package/dist/ai/index.js.map +1 -1
- package/dist/ai/score.d.ts +126 -0
- package/dist/ai/score.d.ts.map +1 -0
- package/dist/ai/score.js +121 -0
- package/dist/ai/score.js.map +1 -0
- package/dist/assets/loader.d.ts +18 -4
- package/dist/assets/loader.d.ts.map +1 -1
- package/dist/assets/loader.js +40 -11
- package/dist/assets/loader.js.map +1 -1
- package/dist/audio/Positional.d.ts +125 -0
- package/dist/audio/Positional.d.ts.map +1 -0
- package/dist/audio/Positional.js +135 -0
- package/dist/audio/Positional.js.map +1 -0
- package/dist/audio/Sound.d.ts +7 -1
- package/dist/audio/Sound.d.ts.map +1 -1
- package/dist/audio/Sound.js +8 -2
- package/dist/audio/Sound.js.map +1 -1
- package/dist/audio/index.d.ts +2 -0
- package/dist/audio/index.d.ts.map +1 -1
- package/dist/audio/index.js +1 -0
- package/dist/audio/index.js.map +1 -1
- package/dist/battle/AttackDialog.d.ts +75 -0
- package/dist/battle/AttackDialog.d.ts.map +1 -0
- package/dist/battle/AttackDialog.js +115 -0
- package/dist/battle/AttackDialog.js.map +1 -0
- package/dist/battle/AttackPreview.d.ts +70 -0
- package/dist/battle/AttackPreview.d.ts.map +1 -0
- package/dist/battle/AttackPreview.js +78 -0
- package/dist/battle/AttackPreview.js.map +1 -0
- package/dist/battle/BattleStats.d.ts +45 -0
- package/dist/battle/BattleStats.d.ts.map +1 -0
- package/dist/battle/BattleStats.js +63 -0
- package/dist/battle/BattleStats.js.map +1 -0
- package/dist/battle/UnitSelector.d.ts +72 -0
- package/dist/battle/UnitSelector.d.ts.map +1 -0
- package/dist/battle/UnitSelector.js +117 -0
- package/dist/battle/UnitSelector.js.map +1 -0
- package/dist/battle/Whiteboard.d.ts +50 -0
- package/dist/battle/Whiteboard.d.ts.map +1 -0
- package/dist/battle/Whiteboard.js +94 -0
- package/dist/battle/Whiteboard.js.map +1 -0
- package/dist/battle/index.d.ts +10 -0
- package/dist/battle/index.d.ts.map +1 -1
- package/dist/battle/index.js +5 -0
- package/dist/battle/index.js.map +1 -1
- package/dist/board/FogOfWar.d.ts +32 -2
- package/dist/board/FogOfWar.d.ts.map +1 -1
- package/dist/board/FogOfWar.js +70 -6
- package/dist/board/FogOfWar.js.map +1 -1
- package/dist/core/Achievements.d.ts +40 -7
- package/dist/core/Achievements.d.ts.map +1 -1
- package/dist/core/Achievements.js +51 -7
- package/dist/core/Achievements.js.map +1 -1
- package/dist/core/Hex.d.ts +26 -6
- package/dist/core/Hex.d.ts.map +1 -1
- package/dist/core/Hex.js +40 -13
- package/dist/core/Hex.js.map +1 -1
- package/dist/core/Input.d.ts +36 -0
- package/dist/core/Input.d.ts.map +1 -1
- package/dist/core/Input.js +63 -1
- package/dist/core/Input.js.map +1 -1
- package/dist/core/Random.d.ts +93 -0
- package/dist/core/Random.d.ts.map +1 -1
- package/dist/core/Random.js +177 -0
- package/dist/core/Random.js.map +1 -1
- package/dist/core/SyncGuard.d.ts +57 -0
- package/dist/core/SyncGuard.d.ts.map +1 -0
- package/dist/core/SyncGuard.js +116 -0
- package/dist/core/SyncGuard.js.map +1 -0
- package/dist/core/index.d.ts +3 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +2 -1
- package/dist/core/index.js.map +1 -1
- package/dist/i18n/Po.d.ts +51 -0
- package/dist/i18n/Po.d.ts.map +1 -0
- package/dist/i18n/Po.js +176 -0
- package/dist/i18n/Po.js.map +1 -0
- package/dist/i18n/index.d.ts +2 -0
- package/dist/i18n/index.d.ts.map +1 -1
- package/dist/i18n/index.js +1 -0
- package/dist/i18n/index.js.map +1 -1
- package/dist/mw_games.global.js +104 -98
- package/dist/mwl/MapFile.d.ts +41 -0
- package/dist/mwl/MapFile.d.ts.map +1 -0
- package/dist/mwl/MapFile.js +43 -0
- package/dist/mwl/MapFile.js.map +1 -0
- package/dist/mwl/campaign.d.ts +51 -0
- package/dist/mwl/campaign.d.ts.map +1 -0
- package/dist/mwl/campaign.js +58 -0
- package/dist/mwl/campaign.js.map +1 -0
- package/dist/mwl/carryover.d.ts +79 -0
- package/dist/mwl/carryover.d.ts.map +1 -0
- package/dist/mwl/carryover.js +63 -0
- package/dist/mwl/carryover.js.map +1 -0
- package/dist/mwl/content.d.ts +10 -0
- package/dist/mwl/content.d.ts.map +1 -1
- package/dist/mwl/content.js +8 -0
- package/dist/mwl/content.js.map +1 -1
- package/dist/mwl/hooks.d.ts +6 -5
- package/dist/mwl/hooks.d.ts.map +1 -1
- package/dist/mwl/hooks.js.map +1 -1
- package/dist/mwl/index.d.ts +8 -2
- package/dist/mwl/index.d.ts.map +1 -1
- package/dist/mwl/index.js +4 -1
- package/dist/mwl/index.js.map +1 -1
- package/dist/mwl/runtime.d.ts +140 -7
- package/dist/mwl/runtime.d.ts.map +1 -1
- package/dist/mwl/runtime.js +427 -33
- package/dist/mwl/runtime.js.map +1 -1
- package/dist/mwl/schema.d.ts.map +1 -1
- package/dist/mwl/schema.js +97 -18
- package/dist/mwl/schema.js.map +1 -1
- package/dist/roguelike/MultiTurnBeam.d.ts +50 -12
- package/dist/roguelike/MultiTurnBeam.d.ts.map +1 -1
- package/dist/roguelike/MultiTurnBeam.js +100 -53
- package/dist/roguelike/MultiTurnBeam.js.map +1 -1
- package/dist/roguelike/Targeting.d.ts +94 -0
- package/dist/roguelike/Targeting.d.ts.map +1 -1
- package/dist/roguelike/Targeting.js +123 -0
- package/dist/roguelike/Targeting.js.map +1 -1
- package/dist/roguelike/index.d.ts +3 -1
- package/dist/roguelike/index.d.ts.map +1 -1
- package/dist/roguelike/index.js +1 -0
- package/dist/roguelike/index.js.map +1 -1
- package/dist/simulation/CampaignSave.d.ts +68 -0
- package/dist/simulation/CampaignSave.d.ts.map +1 -0
- package/dist/simulation/CampaignSave.js +54 -0
- package/dist/simulation/CampaignSave.js.map +1 -0
- package/dist/simulation/EventPresentation.d.ts +111 -0
- package/dist/simulation/EventPresentation.d.ts.map +1 -0
- package/dist/simulation/EventPresentation.js +120 -0
- package/dist/simulation/EventPresentation.js.map +1 -0
- package/dist/simulation/index.d.ts +4 -0
- package/dist/simulation/index.d.ts.map +1 -1
- package/dist/simulation/index.js +2 -0
- package/dist/simulation/index.js.map +1 -1
- package/dist/two-d/pixi-interop.d.ts +18 -6
- package/dist/two-d/pixi-interop.d.ts.map +1 -1
- package/dist/two-d/pixi-interop.js +18 -6
- package/dist/two-d/pixi-interop.js.map +1 -1
- package/dist/two-d/render/AnimatedSprite.d.ts +74 -7
- package/dist/two-d/render/AnimatedSprite.d.ts.map +1 -1
- package/dist/two-d/render/AnimatedSprite.js +88 -26
- package/dist/two-d/render/AnimatedSprite.js.map +1 -1
- package/dist/two-d/render/Camera.d.ts +64 -4
- package/dist/two-d/render/Camera.d.ts.map +1 -1
- package/dist/two-d/render/Camera.js +132 -11
- package/dist/two-d/render/Camera.js.map +1 -1
- package/dist/two-d/render/Halo.d.ts +58 -0
- package/dist/two-d/render/Halo.d.ts.map +1 -0
- package/dist/two-d/render/Halo.js +48 -0
- package/dist/two-d/render/Halo.js.map +1 -0
- package/dist/two-d/render/ImageModifiers.d.ts +174 -0
- package/dist/two-d/render/ImageModifiers.d.ts.map +1 -1
- package/dist/two-d/render/ImageModifiers.js +438 -1
- package/dist/two-d/render/ImageModifiers.js.map +1 -1
- package/dist/two-d/render/LightningArc.d.ts +57 -0
- package/dist/two-d/render/LightningArc.d.ts.map +1 -0
- package/dist/two-d/render/LightningArc.js +91 -0
- package/dist/two-d/render/LightningArc.js.map +1 -0
- package/dist/two-d/render/PaletteRemap.d.ts +135 -0
- package/dist/two-d/render/PaletteRemap.d.ts.map +1 -0
- package/dist/two-d/render/PaletteRemap.js +181 -0
- package/dist/two-d/render/PaletteRemap.js.map +1 -0
- package/dist/two-d/render/Particles.d.ts +26 -0
- package/dist/two-d/render/Particles.d.ts.map +1 -1
- package/dist/two-d/render/Particles.js +23 -2
- package/dist/two-d/render/Particles.js.map +1 -1
- package/dist/two-d/render/SpriteAttachment.d.ts +55 -0
- package/dist/two-d/render/SpriteAttachment.d.ts.map +1 -0
- package/dist/two-d/render/SpriteAttachment.js +60 -0
- package/dist/two-d/render/SpriteAttachment.js.map +1 -0
- package/dist/two-d/render/TerrainGraphics.d.ts +142 -0
- package/dist/two-d/render/TerrainGraphics.d.ts.map +1 -0
- package/dist/two-d/render/TerrainGraphics.js +161 -0
- package/dist/two-d/render/TerrainGraphics.js.map +1 -0
- package/dist/two-d/render/index.d.ts +13 -3
- package/dist/two-d/render/index.d.ts.map +1 -1
- package/dist/two-d/render/index.js +6 -1
- package/dist/two-d/render/index.js.map +1 -1
- package/dist/two-d/stage/StoryScreen.d.ts +110 -0
- package/dist/two-d/stage/StoryScreen.d.ts.map +1 -0
- package/dist/two-d/stage/StoryScreen.js +178 -0
- package/dist/two-d/stage/StoryScreen.js.map +1 -0
- package/dist/two-d/stage/index.d.ts +2 -0
- package/dist/two-d/stage/index.d.ts.map +1 -1
- package/dist/two-d/stage/index.js +1 -0
- package/dist/two-d/stage/index.js.map +1 -1
- package/dist/two-d/ui/Checkbox.d.ts +44 -0
- package/dist/two-d/ui/Checkbox.d.ts.map +1 -0
- package/dist/two-d/ui/Checkbox.js +89 -0
- package/dist/two-d/ui/Checkbox.js.map +1 -0
- package/dist/two-d/ui/DataTable.d.ts +79 -0
- package/dist/two-d/ui/DataTable.d.ts.map +1 -0
- package/dist/two-d/ui/DataTable.js +164 -0
- package/dist/two-d/ui/DataTable.js.map +1 -0
- package/dist/two-d/ui/Dropdown.d.ts +72 -0
- package/dist/two-d/ui/Dropdown.d.ts.map +1 -0
- package/dist/two-d/ui/Dropdown.js +126 -0
- package/dist/two-d/ui/Dropdown.js.map +1 -0
- package/dist/two-d/ui/FloatingText.d.ts +43 -4
- package/dist/two-d/ui/FloatingText.d.ts.map +1 -1
- package/dist/two-d/ui/FloatingText.js +52 -5
- package/dist/two-d/ui/FloatingText.js.map +1 -1
- package/dist/two-d/ui/FloatingTextStack.d.ts +86 -13
- package/dist/two-d/ui/FloatingTextStack.d.ts.map +1 -1
- package/dist/two-d/ui/FloatingTextStack.js +89 -18
- package/dist/two-d/ui/FloatingTextStack.js.map +1 -1
- package/dist/two-d/ui/Layout.d.ts +105 -0
- package/dist/two-d/ui/Layout.d.ts.map +1 -0
- package/dist/two-d/ui/Layout.js +174 -0
- package/dist/two-d/ui/Layout.js.map +1 -0
- package/dist/two-d/ui/ScrollBox.d.ts +67 -0
- package/dist/two-d/ui/ScrollBox.d.ts.map +1 -0
- package/dist/two-d/ui/ScrollBox.js +127 -0
- package/dist/two-d/ui/ScrollBox.js.map +1 -0
- package/dist/two-d/ui/Skins.d.ts +59 -0
- package/dist/two-d/ui/Skins.d.ts.map +1 -0
- package/dist/two-d/ui/Skins.js +77 -0
- package/dist/two-d/ui/Skins.js.map +1 -0
- package/dist/two-d/ui/Slider.d.ts +91 -0
- package/dist/two-d/ui/Slider.d.ts.map +1 -0
- package/dist/two-d/ui/Slider.js +170 -0
- package/dist/two-d/ui/Slider.js.map +1 -0
- package/dist/two-d/ui/Spinner.d.ts +67 -0
- package/dist/two-d/ui/Spinner.d.ts.map +1 -0
- package/dist/two-d/ui/Spinner.js +137 -0
- package/dist/two-d/ui/Spinner.js.map +1 -0
- package/dist/two-d/ui/TabbedList.d.ts +118 -0
- package/dist/two-d/ui/TabbedList.d.ts.map +1 -0
- package/dist/two-d/ui/TabbedList.js +221 -0
- package/dist/two-d/ui/TabbedList.js.map +1 -0
- package/dist/two-d/ui/TextModel.d.ts +65 -0
- package/dist/two-d/ui/TextModel.d.ts.map +1 -0
- package/dist/two-d/ui/TextModel.js +126 -0
- package/dist/two-d/ui/TextModel.js.map +1 -0
- package/dist/two-d/ui/TreeView.d.ts +68 -0
- package/dist/two-d/ui/TreeView.d.ts.map +1 -0
- package/dist/two-d/ui/TreeView.js +133 -0
- package/dist/two-d/ui/TreeView.js.map +1 -0
- package/dist/two-d/ui/Window.d.ts +13 -0
- package/dist/two-d/ui/Window.d.ts.map +1 -1
- package/dist/two-d/ui/Window.js +23 -0
- package/dist/two-d/ui/Window.js.map +1 -1
- package/dist/two-d/ui/WindowStack.d.ts.map +1 -1
- package/dist/two-d/ui/WindowStack.js +2 -0
- package/dist/two-d/ui/WindowStack.js.map +1 -1
- package/dist/two-d/ui/index.d.ts +27 -3
- package/dist/two-d/ui/index.d.ts.map +1 -1
- package/dist/two-d/ui/index.js +14 -2
- package/dist/two-d/ui/index.js.map +1 -1
- package/dist/two-d/ui/markup.d.ts +127 -0
- package/dist/two-d/ui/markup.d.ts.map +1 -0
- package/dist/two-d/ui/markup.js +322 -0
- package/dist/two-d/ui/markup.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/world/SideTurns.d.ts +115 -0
- package/dist/world/SideTurns.d.ts.map +1 -0
- package/dist/world/SideTurns.js +138 -0
- package/dist/world/SideTurns.js.map +1 -0
- package/dist/world/index.d.ts +2 -0
- package/dist/world/index.d.ts.map +1 -1
- package/dist/world/index.js +1 -0
- package/dist/world/index.js.map +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
/** the tags this parser knows; anything else is literal text, not a tag */
|
|
2
|
+
const KNOWN_TAGS = new Set(['b', 'i', 'span', 'br', 'img']);
|
|
3
|
+
/**
|
|
4
|
+
* Reads `source` as inline markup and returns one span per styled run.
|
|
5
|
+
*
|
|
6
|
+
* Known tags are `<b>`, `<i>`, `<span color='…' size='…'>`, `<br/>` (or `<br>`, which becomes a
|
|
7
|
+
* newline in the text) and `<img>path</img>` (or `<img src='path'/>`). Nesting works the way it
|
|
8
|
+
* reads: a `<b>` inside an `<i>` is both, and a closing tag restores exactly the style it opened
|
|
9
|
+
* over. `<`, `>`, `&`, `"` and `'` are the escapes for the characters that would
|
|
10
|
+
* otherwise be syntax.
|
|
11
|
+
*
|
|
12
|
+
* Two rules are deliberate, because content written by hand meets both. An **unknown or malformed**
|
|
13
|
+
* tag - `<blink>`, a `</b>` that closes nothing, an `<img>` with no `</img>` - is kept literally in
|
|
14
|
+
* the text rather than dropped or guessed at, so the worst case is text that reads oddly instead of
|
|
15
|
+
* text that lost a piece. And a `$name` that nothing supplies is kept literally too, for the same
|
|
16
|
+
* reason.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* import { escapeHtml, markupToHtml, parseMarkup, stripMarkup } from '@datamoc/mw_games/two-d/ui';
|
|
21
|
+
*
|
|
22
|
+
* const spans = parseMarkup("Hail, <b>$name</b>! <span color='#c0ffee'>Four</span> coins<br/>And an<img>gold.png</img>", {
|
|
23
|
+
* variables: { name: 'Kalenz' },
|
|
24
|
+
* });
|
|
25
|
+
*
|
|
26
|
+
* console.log(spans[1]); // { text: 'Kalenz', bold: true, italic: false }
|
|
27
|
+
* console.log(spans[2]); // { text: 'Four', bold: false, italic: false, color: '#c0ffee' }
|
|
28
|
+
* console.log(spans[3].text); // '\nAnd an'
|
|
29
|
+
* console.log(spans[4].image); // 'gold.png'
|
|
30
|
+
* console.log(stripMarkup('<b>Bold</b> and <i>italic</i>')); // 'Bold and italic'
|
|
31
|
+
* console.log(markupToHtml(spans)); // escaped text with <b>/<i> around the emphasis
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export function parseMarkup(source, options = {}) {
|
|
35
|
+
const variables = options.variables ?? {};
|
|
36
|
+
const spans = [];
|
|
37
|
+
const stack = [];
|
|
38
|
+
let style = { bold: false, italic: false };
|
|
39
|
+
let text = '';
|
|
40
|
+
const flush = () => {
|
|
41
|
+
if (!text)
|
|
42
|
+
return;
|
|
43
|
+
const span = { text, bold: style.bold, italic: style.italic };
|
|
44
|
+
if (style.color !== undefined)
|
|
45
|
+
span.color = style.color;
|
|
46
|
+
if (style.size !== undefined)
|
|
47
|
+
span.size = style.size;
|
|
48
|
+
spans.push(span);
|
|
49
|
+
text = '';
|
|
50
|
+
};
|
|
51
|
+
for (let index = 0; index < source.length; index++) {
|
|
52
|
+
const char = source[index];
|
|
53
|
+
if (char === '&') {
|
|
54
|
+
const entity = readEntity(source, index);
|
|
55
|
+
if (entity) {
|
|
56
|
+
text += entity.text;
|
|
57
|
+
index = entity.end;
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (char === '$') {
|
|
62
|
+
const variable = readVariable(source, index);
|
|
63
|
+
const value = variable ? variables[variable.name] : undefined;
|
|
64
|
+
if (variable && value !== undefined) {
|
|
65
|
+
text += value;
|
|
66
|
+
index = variable.end;
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (char === '<') {
|
|
71
|
+
const tag = readTag(source, index);
|
|
72
|
+
if (tag && KNOWN_TAGS.has(tag.name)) {
|
|
73
|
+
if (tag.name === 'img') {
|
|
74
|
+
const image = readImage(source, tag);
|
|
75
|
+
if (image) {
|
|
76
|
+
flush();
|
|
77
|
+
spans.push({ text: '', bold: false, italic: false, image: image.src });
|
|
78
|
+
index = image.end;
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
else if (tag.name === 'br') {
|
|
83
|
+
text += '\n';
|
|
84
|
+
index = tag.end;
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
else if (!tag.closing) {
|
|
88
|
+
//flush before the style moves: the text so far belongs to the style it was written
|
|
89
|
+
//in, and accumulating across a tag would give all of it the last style seen
|
|
90
|
+
flush();
|
|
91
|
+
stack.push({ tag: tag.name, style });
|
|
92
|
+
style = opened(style, tag.name, tag.body);
|
|
93
|
+
index = tag.end;
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
const top = stack[stack.length - 1];
|
|
98
|
+
if (top?.tag === tag.name) {
|
|
99
|
+
flush();
|
|
100
|
+
style = top.style;
|
|
101
|
+
stack.pop();
|
|
102
|
+
index = tag.end;
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
text += char;
|
|
109
|
+
}
|
|
110
|
+
flush();
|
|
111
|
+
return spans;
|
|
112
|
+
}
|
|
113
|
+
/** The same text with the markup taken out, for a place that cannot render any of it. */
|
|
114
|
+
export function stripMarkup(source, options = {}) {
|
|
115
|
+
return parseMarkup(source, options)
|
|
116
|
+
.map((span) => span.image ?? span.text)
|
|
117
|
+
.join('');
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Spans as an HTML fragment for a renderer that speaks HTML text, which is what the ui's `RichLabel`
|
|
121
|
+
* is. Emphasis, colour and size all render - `HTMLText`'s dialect is real CSS, so a `color`/`size`
|
|
122
|
+
* span becomes a `<span style="...">` wrapper - and the text is escaped, so `a < b` can never turn
|
|
123
|
+
* into a tag. An image span renders as nothing here: `HTMLText` has no inline-image primitive, so a
|
|
124
|
+
* caller that wants the image drawn positions a sprite itself, at the position `layoutMarkupLines`
|
|
125
|
+
* reports for that span.
|
|
126
|
+
*/
|
|
127
|
+
export function markupToHtml(spans) {
|
|
128
|
+
return spans
|
|
129
|
+
.map((span) => {
|
|
130
|
+
if (span.image !== undefined)
|
|
131
|
+
return '';
|
|
132
|
+
let text = escapeHtml(span.text);
|
|
133
|
+
if (span.italic)
|
|
134
|
+
text = `<i>${text}</i>`;
|
|
135
|
+
if (span.bold)
|
|
136
|
+
text = `<b>${text}</b>`;
|
|
137
|
+
const style = [
|
|
138
|
+
span.color !== undefined ? `color:${span.color}` : undefined,
|
|
139
|
+
span.size !== undefined ? `font-size:${span.size}px` : undefined,
|
|
140
|
+
].filter((declaration) => declaration !== undefined);
|
|
141
|
+
if (style.length > 0)
|
|
142
|
+
text = `<span style="${style.join(';')}">${text}</span>`;
|
|
143
|
+
return text;
|
|
144
|
+
})
|
|
145
|
+
.join('');
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* The plain-text projection for a place that cannot render any markup at all - a screen reader, a
|
|
149
|
+
* log line, a tooltip that only takes a string. Unlike `stripMarkup` (which keeps an image span's
|
|
150
|
+
* raw path, useful for round-tripping but not for reading aloud), an image becomes `describeImage`'s
|
|
151
|
+
* result, defaulting to `[image]` so an image is at least announced as present rather than silently
|
|
152
|
+
* dropped or read as a file path.
|
|
153
|
+
*
|
|
154
|
+
* @example
|
|
155
|
+
* ```ts
|
|
156
|
+
* import { markupAccessibilityText, parseMarkup } from '@datamoc/mw_games/two-d/ui';
|
|
157
|
+
*
|
|
158
|
+
* const spans = parseMarkup("Pay <b>10</b> gold<img>coin.png</img>");
|
|
159
|
+
* console.log(markupAccessibilityText(spans)); // 'Pay 10 gold[image]'
|
|
160
|
+
* console.log(markupAccessibilityText(spans, { describeImage: () => ' (coin icon)' }));
|
|
161
|
+
* // 'Pay 10 gold (coin icon)'
|
|
162
|
+
* ```
|
|
163
|
+
*/
|
|
164
|
+
export function markupAccessibilityText(spans, options = {}) {
|
|
165
|
+
const describeImage = options.describeImage ?? (() => '[image]');
|
|
166
|
+
return spans.map((span) => (span.image !== undefined ? describeImage(span.image) : span.text)).join('');
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Wraps already-parsed spans into lines, word by word, each word measured under its own span's
|
|
170
|
+
* style - which is the acceptance this item was missing: wrapping computed *after* styling, not
|
|
171
|
+
* before it, so a bold or larger run wraps where its own wider glyphs actually land rather than
|
|
172
|
+
* where the plain text would have. An explicit line break (`<br/>`, which `parseMarkup` turns into
|
|
173
|
+
* a literal `\n`) always starts a new line; otherwise a word moves to the next line only once it
|
|
174
|
+
* would overflow `maxWidth`, so a single word wider than `maxWidth` still renders (not clipped) as
|
|
175
|
+
* ever a caller's original design.
|
|
176
|
+
*
|
|
177
|
+
* Backend-neutral by construction: the same spans, the same `measure` function and the same
|
|
178
|
+
* `maxWidth` given to `layoutMarkupLines` decide the exact same line breaks whether the caller then
|
|
179
|
+
* draws each line through `markupToHtml`/`HTMLText` or through a canvas `Text2D` per run - which is
|
|
180
|
+
* what makes the two backends render "equivalent runs" rather than each doing its own wrapping.
|
|
181
|
+
*
|
|
182
|
+
* @example
|
|
183
|
+
* ```ts
|
|
184
|
+
* import { layoutMarkupLines, parseMarkup } from '@datamoc/mw_games/two-d/ui';
|
|
185
|
+
*
|
|
186
|
+
* const spans = parseMarkup('Take <b>two small</b> coins');
|
|
187
|
+
* const lines = layoutMarkupLines(spans, (piece) => piece.text.length * 8, 100);
|
|
188
|
+
* console.log(lines.map((line) => line.spans.map((span) => span.text).join('')));
|
|
189
|
+
* ```
|
|
190
|
+
*/
|
|
191
|
+
export function layoutMarkupLines(spans, measure, maxWidth) {
|
|
192
|
+
const pieces = [];
|
|
193
|
+
for (const span of spans) {
|
|
194
|
+
if (span.image !== undefined) {
|
|
195
|
+
pieces.push({ ...span });
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
const parts = span.text.split('\n');
|
|
199
|
+
parts.forEach((part, index) => {
|
|
200
|
+
if (index > 0)
|
|
201
|
+
pieces.push({ text: '', bold: span.bold, italic: span.italic, hardBreak: true });
|
|
202
|
+
for (const word of part.match(/\S+|\s+/g) ?? []) {
|
|
203
|
+
pieces.push({ text: word, bold: span.bold, italic: span.italic, color: span.color, size: span.size });
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
const lines = [];
|
|
208
|
+
let current = [];
|
|
209
|
+
let width = 0;
|
|
210
|
+
const flushLine = () => {
|
|
211
|
+
//trailing whitespace never counts toward a line's own width or survives into it
|
|
212
|
+
while (current.length > 0 && current[current.length - 1].text.trim() === '' && !current[current.length - 1].image) {
|
|
213
|
+
width -= measure(current[current.length - 1]);
|
|
214
|
+
current.pop();
|
|
215
|
+
}
|
|
216
|
+
lines.push({ spans: mergeMarkupSpans(current), width: Math.max(0, width) });
|
|
217
|
+
current = [];
|
|
218
|
+
width = 0;
|
|
219
|
+
};
|
|
220
|
+
for (const piece of pieces) {
|
|
221
|
+
if (piece.hardBreak) {
|
|
222
|
+
flushLine();
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
const pieceWidth = measure(piece);
|
|
226
|
+
const isSpace = piece.image === undefined && piece.text.trim() === '';
|
|
227
|
+
if (width > 0 && !isSpace && width + pieceWidth > maxWidth)
|
|
228
|
+
flushLine();
|
|
229
|
+
current.push(piece);
|
|
230
|
+
width += pieceWidth;
|
|
231
|
+
}
|
|
232
|
+
if (current.length > 0 || lines.length === 0)
|
|
233
|
+
flushLine();
|
|
234
|
+
return lines;
|
|
235
|
+
}
|
|
236
|
+
function mergeMarkupSpans(pieces) {
|
|
237
|
+
const merged = [];
|
|
238
|
+
for (const piece of pieces) {
|
|
239
|
+
const last = merged[merged.length - 1];
|
|
240
|
+
if (last &&
|
|
241
|
+
piece.image === undefined &&
|
|
242
|
+
last.image === undefined &&
|
|
243
|
+
last.bold === piece.bold &&
|
|
244
|
+
last.italic === piece.italic &&
|
|
245
|
+
last.color === piece.color &&
|
|
246
|
+
last.size === piece.size) {
|
|
247
|
+
last.text += piece.text;
|
|
248
|
+
}
|
|
249
|
+
else {
|
|
250
|
+
merged.push({ ...piece });
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
return merged;
|
|
254
|
+
}
|
|
255
|
+
/** escapes the five characters that would otherwise read as syntax */
|
|
256
|
+
export function escapeHtml(text) {
|
|
257
|
+
return text
|
|
258
|
+
.replaceAll('&', '&')
|
|
259
|
+
.replaceAll('<', '<')
|
|
260
|
+
.replaceAll('>', '>')
|
|
261
|
+
.replaceAll('"', '"')
|
|
262
|
+
.replaceAll("'", ''');
|
|
263
|
+
}
|
|
264
|
+
function opened(style, name, body) {
|
|
265
|
+
if (name === 'b')
|
|
266
|
+
return { ...style, bold: true };
|
|
267
|
+
if (name === 'i')
|
|
268
|
+
return { ...style, italic: true };
|
|
269
|
+
const color = attribute(body, 'color');
|
|
270
|
+
const size = Number(attribute(body, 'size') ?? '');
|
|
271
|
+
const next = { ...style };
|
|
272
|
+
if (color)
|
|
273
|
+
next.color = color;
|
|
274
|
+
if (Number.isFinite(size) && size > 0)
|
|
275
|
+
next.size = size;
|
|
276
|
+
return next;
|
|
277
|
+
}
|
|
278
|
+
/** the value of one attribute, quoted or bare; `undefined` when it is not there */
|
|
279
|
+
function attribute(body, name) {
|
|
280
|
+
const match = new RegExp(`(?:^|\\s)${name}\\s*=\\s*(?:"([^"]*)"|'([^']*)'|([^\\s"'>]+))`).exec(body);
|
|
281
|
+
return match ? (match[1] ?? match[2] ?? match[3]) : undefined;
|
|
282
|
+
}
|
|
283
|
+
function readTag(source, at) {
|
|
284
|
+
const match = /^<(\/?)([a-zA-Z]+)([^>]*)>/.exec(source.slice(at));
|
|
285
|
+
if (!match)
|
|
286
|
+
return null;
|
|
287
|
+
const body = match[3].trimEnd();
|
|
288
|
+
return {
|
|
289
|
+
name: match[2].toLowerCase(),
|
|
290
|
+
closing: match[1] === '/',
|
|
291
|
+
selfClosing: body.endsWith('/') && match[1] === '',
|
|
292
|
+
body: body.replace(/\/$/, '').trim(),
|
|
293
|
+
end: at + match[0].length - 1,
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
/** `<img>path</img>` or `<img src='path'/>`, whichever shape was written; null when neither is */
|
|
297
|
+
function readImage(source, tag) {
|
|
298
|
+
if (tag.selfClosing) {
|
|
299
|
+
const src = attribute(tag.body, 'src');
|
|
300
|
+
return src ? { src, end: tag.end } : null;
|
|
301
|
+
}
|
|
302
|
+
const close = source.indexOf('</img>', tag.end + 1);
|
|
303
|
+
if (close < 0)
|
|
304
|
+
return null;
|
|
305
|
+
const src = source.slice(tag.end + 1, close).trim();
|
|
306
|
+
return src ? { src, end: close + '</img>'.length - 1 } : null;
|
|
307
|
+
}
|
|
308
|
+
function readEntity(source, at) {
|
|
309
|
+
const named = { lt: '<', gt: '>', amp: '&', quot: '"', apos: "'" };
|
|
310
|
+
const match = /^&(lt|gt|amp|quot|apos);/.exec(source.slice(at));
|
|
311
|
+
if (match)
|
|
312
|
+
return { text: named[match[1]], end: at + match[0].length - 1 };
|
|
313
|
+
const numeric = /^&#(\d+);/.exec(source.slice(at));
|
|
314
|
+
if (numeric)
|
|
315
|
+
return { text: String.fromCodePoint(Number(numeric[1])), end: at + numeric[0].length - 1 };
|
|
316
|
+
return null;
|
|
317
|
+
}
|
|
318
|
+
function readVariable(source, at) {
|
|
319
|
+
const match = /^\$([A-Za-z_][A-Za-z0-9_]*)/.exec(source.slice(at));
|
|
320
|
+
return match ? { name: match[1], end: at + match[0].length - 1 } : null;
|
|
321
|
+
}
|
|
322
|
+
//# sourceMappingURL=markup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markup.js","sourceRoot":"","sources":["../../../src/two-d/ui/markup.ts"],"names":[],"mappings":"AA4BA,2EAA2E;AAC3E,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;AAmB5D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAM,UAAU,WAAW,CAAC,MAAc,EAAE,OAAO,GAAkB,EAAE;IACtE,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,EAAE,CAAC;IAC1C,MAAM,KAAK,GAAiB,EAAE,CAAC;IAC/B,MAAM,KAAK,GAAoC,EAAE,CAAC;IAClD,IAAI,KAAK,GAAU,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IAClD,IAAI,IAAI,GAAG,EAAE,CAAC;IAEd,MAAM,KAAK,GAAG,GAAS,EAAE;QACxB,IAAI,CAAC,IAAI;YAAE,OAAO;QAClB,MAAM,IAAI,GAAe,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC;QAC1E,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS;YAAE,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;QACxD,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS;YAAE,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;QACrD,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjB,IAAI,GAAG,EAAE,CAAC;IACX,CAAC,CAAC;IAEF,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;QACpD,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QAE3B,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;YAClB,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;YACzC,IAAI,MAAM,EAAE,CAAC;gBACZ,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC;gBACpB,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC;gBACnB,SAAS;YACV,CAAC;QACF,CAAC;QAED,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;YAClB,MAAM,QAAQ,GAAG,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;YAC7C,MAAM,KAAK,GAAG,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YAC9D,IAAI,QAAQ,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;gBACrC,IAAI,IAAI,KAAK,CAAC;gBACd,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC;gBACrB,SAAS;YACV,CAAC;QACF,CAAC;QAED,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;YAClB,MAAM,GAAG,GAAG,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;YACnC,IAAI,GAAG,IAAI,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;gBACrC,IAAI,GAAG,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;oBACxB,MAAM,KAAK,GAAG,SAAS,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;oBACrC,IAAI,KAAK,EAAE,CAAC;wBACX,KAAK,EAAE,CAAC;wBACR,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC;wBACvE,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC;wBAClB,SAAS;oBACV,CAAC;gBACF,CAAC;qBAAM,IAAI,GAAG,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;oBAC9B,IAAI,IAAI,IAAI,CAAC;oBACb,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC;oBAChB,SAAS;gBACV,CAAC;qBAAM,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;oBACzB,mFAAmF;oBACnF,4EAA4E;oBAC5E,KAAK,EAAE,CAAC;oBACR,KAAK,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;oBACrC,KAAK,GAAG,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;oBAC1C,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC;oBAChB,SAAS;gBACV,CAAC;qBAAM,CAAC;oBACP,MAAM,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;oBACpC,IAAI,GAAG,EAAE,GAAG,KAAK,GAAG,CAAC,IAAI,EAAE,CAAC;wBAC3B,KAAK,EAAE,CAAC;wBACR,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC;wBAClB,KAAK,CAAC,GAAG,EAAE,CAAC;wBACZ,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC;wBAChB,SAAS;oBACV,CAAC;gBACF,CAAC;YACF,CAAC;QACF,CAAC;QAED,IAAI,IAAI,IAAI,CAAC;IACd,CAAC;IAED,KAAK,EAAE,CAAC;IACR,OAAO,KAAK,CAAC;AACd,CAAC;AAED,yFAAyF;AACzF,MAAM,UAAU,WAAW,CAAC,MAAc,EAAE,OAAO,GAAkB,EAAE;IACtE,OAAO,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC;SACjC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,IAAI,CAAC;SACtC,IAAI,CAAC,EAAE,CAAC,CAAC;AACZ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAAC,KAA4B;IACxD,OAAO,KAAK;SACV,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACb,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS;YAAE,OAAO,EAAE,CAAC;QACxC,IAAI,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjC,IAAI,IAAI,CAAC,MAAM;YAAE,IAAI,GAAG,MAAM,IAAI,MAAM,CAAC;QACzC,IAAI,IAAI,CAAC,IAAI;YAAE,IAAI,GAAG,MAAM,IAAI,MAAM,CAAC;QACvC,MAAM,KAAK,GAAG;YACb,IAAI,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS;YAC5D,IAAI,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,aAAa,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS;SAChE,CAAC,MAAM,CAAC,CAAC,WAAW,EAAyB,EAAE,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC;QAC5E,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;YAAE,IAAI,GAAG,gBAAgB,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,IAAI,SAAS,CAAC;QAC/E,OAAO,IAAI,CAAC;IACb,CAAC,CAAC;SACD,IAAI,CAAC,EAAE,CAAC,CAAC;AACZ,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,uBAAuB,CACtC,KAA4B,EAC5B,OAAO,GAAiD,EAAE;IAE1D,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,IAAI,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IACjE,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACzG,CAAC;AAgBD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAA4B,EAAE,OAAsB,EAAE,QAAgB;IAEvG,MAAM,MAAM,GAAY,EAAE,CAAC;IAC3B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QAC1B,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;YAC9B,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,IAAI,EAAE,CAAC,CAAC;YACzB,SAAS;QACV,CAAC;QACD,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACpC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;YAC7B,IAAI,KAAK,GAAG,CAAC;gBAAE,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAChG,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,CAAC;gBACjD,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;YACvG,CAAC;QACF,CAAC,CAAC,CAAC;IACJ,CAAC;IAED,MAAM,KAAK,GAAiB,EAAE,CAAC;IAC/B,IAAI,OAAO,GAAY,EAAE,CAAC;IAC1B,IAAI,KAAK,GAAG,CAAC,CAAC;IAEd,MAAM,SAAS,GAAG,GAAS,EAAE;QAC5B,gFAAgF;QAChF,OAAO,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC;YACnH,KAAK,IAAI,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;YAC9C,OAAO,CAAC,GAAG,EAAE,CAAC;QACf,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,gBAAgB,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;QAC5E,OAAO,GAAG,EAAE,CAAC;QACb,KAAK,GAAG,CAAC,CAAC;IACX,CAAC,CAAC;IAEF,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC5B,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;YACrB,SAAS,EAAE,CAAC;YACZ,SAAS;QACV,CAAC;QACD,MAAM,UAAU,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;QAClC,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;QACtE,IAAI,KAAK,GAAG,CAAC,IAAI,CAAC,OAAO,IAAI,KAAK,GAAG,UAAU,GAAG,QAAQ;YAAE,SAAS,EAAE,CAAC;QACxE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACpB,KAAK,IAAI,UAAU,CAAC;IACrB,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,SAAS,EAAE,CAAC;IAE1D,OAAO,KAAK,CAAC;AACd,CAAC;AAED,SAAS,gBAAgB,CAAC,MAA6B;IACtD,MAAM,MAAM,GAAiB,EAAE,CAAC;IAChC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACvC,IACC,IAAI;YACJ,KAAK,CAAC,KAAK,KAAK,SAAS;YACzB,IAAI,CAAC,KAAK,KAAK,SAAS;YACxB,IAAI,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI;YACxB,IAAI,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM;YAC5B,IAAI,CAAC,KAAK,KAAK,KAAK,CAAC,KAAK;YAC1B,IAAI,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,EACvB,CAAC;YACF,IAAI,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC;QACzB,CAAC;aAAM,CAAC;YACP,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC;QAC3B,CAAC;IACF,CAAC;IACD,OAAO,MAAM,CAAC;AACf,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,UAAU,CAAC,IAAY;IACtC,OAAO,IAAI;SACT,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC;SACxB,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC;SACvB,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC;SACvB,UAAU,CAAC,GAAG,EAAE,QAAQ,CAAC;SACzB,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;AAC5B,CAAC;AAED,SAAS,MAAM,CAAC,KAAY,EAAE,IAAY,EAAE,IAAY;IACvD,IAAI,IAAI,KAAK,GAAG;QAAE,OAAO,EAAE,GAAG,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IAClD,IAAI,IAAI,KAAK,GAAG;QAAE,OAAO,EAAE,GAAG,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAEpD,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACvC,MAAM,IAAI,GAAG,MAAM,CAAC,SAAS,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;IACnD,MAAM,IAAI,GAAU,EAAE,GAAG,KAAK,EAAE,CAAC;IACjC,IAAI,KAAK;QAAE,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IAC9B,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,CAAC;QAAE,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACxD,OAAO,IAAI,CAAC;AACb,CAAC;AAED,mFAAmF;AACnF,SAAS,SAAS,CAAC,IAAY,EAAE,IAAY;IAC5C,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,YAAY,IAAI,+CAA+C,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACrG,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAC/D,CAAC;AAED,SAAS,OAAO,CAAC,MAAc,EAAE,EAAU;IAC1C,MAAM,KAAK,GAAG,4BAA4B,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;IAClE,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;IAChC,OAAO;QACN,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE;QAC5B,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,KAAK,GAAG;QACzB,WAAW,EAAE,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,EAAE;QAClD,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE;QACpC,GAAG,EAAE,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC;KAC7B,CAAC;AACH,CAAC;AAED,kGAAkG;AAClG,SAAS,SAAS,CAAC,MAAc,EAAE,GAAQ;IAC1C,IAAI,GAAG,CAAC,WAAW,EAAE,CAAC;QACrB,MAAM,GAAG,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACvC,OAAO,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IAC3C,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;IACpD,IAAI,KAAK,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAC3B,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;IACpD,OAAO,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAC/D,CAAC;AAED,SAAS,UAAU,CAAC,MAAc,EAAE,EAAU;IAC7C,MAAM,KAAK,GAA2B,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC;IAC3F,MAAM,KAAK,GAAG,0BAA0B,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;IAChE,IAAI,KAAK;QAAE,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;IAE3E,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;IACnD,IAAI,OAAO;QAAE,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;IACxG,OAAO,IAAI,CAAC;AACb,CAAC;AAED,SAAS,YAAY,CAAC,MAAc,EAAE,EAAU;IAC/C,MAAM,KAAK,GAAG,6BAA6B,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;IACnE,OAAO,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AACzE,CAAC"}
|
package/dist/version.d.ts
CHANGED
|
@@ -9,5 +9,5 @@
|
|
|
9
9
|
* this from `package.json` automatically, chosen because this project's own build already
|
|
10
10
|
* has no step before `tsc`/`vite` that this would need to run ahead of.
|
|
11
11
|
*/
|
|
12
|
-
export declare const version = "0.7.
|
|
12
|
+
export declare const version = "0.7.7";
|
|
13
13
|
//# sourceMappingURL=version.d.ts.map
|
package/dist/version.js
CHANGED
|
@@ -9,5 +9,5 @@
|
|
|
9
9
|
* this from `package.json` automatically, chosen because this project's own build already
|
|
10
10
|
* has no step before `tsc`/`vite` that this would need to run ahead of.
|
|
11
11
|
*/
|
|
12
|
-
export const version = '0.7.
|
|
12
|
+
export const version = '0.7.7';
|
|
13
13
|
//# sourceMappingURL=version.js.map
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The turn model a strategy game counts by: sides take turns one after another, a round is
|
|
3
|
+
* every side taking one, and the time of day steps on once per round. This is deliberately
|
|
4
|
+
* distinct from `TurnClock` (timed effects on a single actor) and `simulation.Scheduler`
|
|
5
|
+
* (energy-cost turn order within a side): it is the outer loop a scenario runs on.
|
|
6
|
+
*
|
|
7
|
+
* The time schedule carries a `lawfulBonus` per entry, in percent, the number the reference
|
|
8
|
+
* game's `[time] lawful_bonus=` writes (day `25`, night `-25`, dawn/dusk `0`). A unit's
|
|
9
|
+
* alignment turns that into its own bonus, and a `TimeArea` can override the schedule for
|
|
10
|
+
* part of the map, which is what makes the bonus per-hex rather than global.
|
|
11
|
+
*/
|
|
12
|
+
/** how a unit's damage answers the time of day, the reference's `align=` values */
|
|
13
|
+
export type Alignment = 'lawful' | 'neutral' | 'chaotic' | 'liminal';
|
|
14
|
+
export interface TimeOfDay {
|
|
15
|
+
/** the time's own name: a game's content (`'morning'`, `'first_watch'`) */
|
|
16
|
+
id: string;
|
|
17
|
+
/** the schedule's own percentage a lawful unit gains and a chaotic one loses */
|
|
18
|
+
lawfulBonus: number;
|
|
19
|
+
}
|
|
20
|
+
/** A part of the map on its own schedule, indexed in parallel with the global one. */
|
|
21
|
+
export interface TimeArea {
|
|
22
|
+
/** the schedule this area's cells use instead of the global one */
|
|
23
|
+
times: readonly TimeOfDay[];
|
|
24
|
+
contains(x: number, y: number): boolean;
|
|
25
|
+
}
|
|
26
|
+
export interface SideTurnsOptions {
|
|
27
|
+
/** the sides in the order they take turns within a round */
|
|
28
|
+
sides: readonly string[];
|
|
29
|
+
/** the time-of-day schedule, one entry per round, cycling */
|
|
30
|
+
schedule: readonly TimeOfDay[];
|
|
31
|
+
/** which side takes the first turn; 0 by default */
|
|
32
|
+
sideIndex?: number;
|
|
33
|
+
/** the first round, 1 by default (rounds are 1-based, as the reference's `turn` is) */
|
|
34
|
+
round?: number;
|
|
35
|
+
/** the schedule entry the first round uses; 0 by default */
|
|
36
|
+
timeIndex?: number;
|
|
37
|
+
/** per-cell schedule overrides; the first area containing a cell wins */
|
|
38
|
+
areas?: readonly TimeArea[];
|
|
39
|
+
}
|
|
40
|
+
export interface SideTurn {
|
|
41
|
+
side: string;
|
|
42
|
+
round: number;
|
|
43
|
+
/** true on the turn that opened a new round and advanced the time of day */
|
|
44
|
+
newRound: boolean;
|
|
45
|
+
}
|
|
46
|
+
/** what `SideTurns.toJSON` writes and `fromJSON` resumes from */
|
|
47
|
+
export interface SideTurnState {
|
|
48
|
+
round: number;
|
|
49
|
+
sideIndex: number;
|
|
50
|
+
timeIndex: number;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Which side is taking its turn, which round it is, and what time of day applies - the
|
|
54
|
+
* outer scenario loop. `advance()` moves to the next side; wrapping past the last side opens
|
|
55
|
+
* a new round and steps the schedule on one entry, cycling. `timeOfDayAt(x, y)` and
|
|
56
|
+
* `lawfulBonusAt(...)` read the schedule for a cell, so a time area can give one region a
|
|
57
|
+
* different bonus without a second turn counter.
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```ts
|
|
61
|
+
* import { SideTurns } from '@datamoc/mw_games/world';
|
|
62
|
+
*
|
|
63
|
+
* const turns = new SideTurns({
|
|
64
|
+
* sides: ['rebels', 'undead'],
|
|
65
|
+
* schedule: [
|
|
66
|
+
* { id: 'day', lawfulBonus: 25 },
|
|
67
|
+
* { id: 'night', lawfulBonus: -25 },
|
|
68
|
+
* ],
|
|
69
|
+
* });
|
|
70
|
+
*
|
|
71
|
+
* console.log(turns.side, turns.round, turns.timeOfDay.id); // 'rebels' 1 'day'
|
|
72
|
+
* console.log(turns.lawfulBonusAt('lawful', 4, 4)); // 25 - a lawful unit in the day
|
|
73
|
+
* turns.advance(); // 'undead', still round 1
|
|
74
|
+
* turns.advance(); // 'rebels', round 2, timeOfDay is now 'night'
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
export declare class SideTurns {
|
|
78
|
+
readonly sides: readonly string[];
|
|
79
|
+
readonly schedule: readonly TimeOfDay[];
|
|
80
|
+
private readonly areas;
|
|
81
|
+
private state;
|
|
82
|
+
constructor(options: SideTurnsOptions);
|
|
83
|
+
get round(): number;
|
|
84
|
+
/** the side whose turn it is */
|
|
85
|
+
get side(): string;
|
|
86
|
+
get timeIndex(): number;
|
|
87
|
+
/** the global time of day, before any time area overrides it */
|
|
88
|
+
get timeOfDay(): TimeOfDay;
|
|
89
|
+
get snapshot(): SideTurnState;
|
|
90
|
+
/** The time of day at a cell: the first containing time area's entry, else the global one. */
|
|
91
|
+
timeOfDayAt(x: number, y: number): TimeOfDay;
|
|
92
|
+
/** The percentage bonus `alignment` gets at a cell, through that cell's own time of day. */
|
|
93
|
+
lawfulBonusAt(alignment: Alignment, x: number, y: number): number;
|
|
94
|
+
/** Moves to the next side, opening a new round and stepping the schedule on a wrap. */
|
|
95
|
+
advance(): SideTurn;
|
|
96
|
+
toJSON(): SideTurnState;
|
|
97
|
+
/** Resumes from `toJSON`, with the same sides/schedule/areas the game was created with. */
|
|
98
|
+
static fromJSON(options: SideTurnsOptions, state: SideTurnState): SideTurns;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* What one alignment gets out of the time of day's own `lawfulBonus`, in percent: a lawful
|
|
102
|
+
* unit takes it, a chaotic one takes its negation, and neutral and liminal units ignore the
|
|
103
|
+
* time entirely (the reference's own four `align=` values).
|
|
104
|
+
*
|
|
105
|
+
* @example
|
|
106
|
+
* ```ts
|
|
107
|
+
* import { alignmentBonus } from '@datamoc/mw_games/world';
|
|
108
|
+
*
|
|
109
|
+
* alignmentBonus('lawful', 25); // 25 - day favours a lawful unit
|
|
110
|
+
* alignmentBonus('chaotic', 25); // -25 - the same day punishes a chaotic one
|
|
111
|
+
* alignmentBonus('neutral', 25); // 0 - neutral ignores the time of day
|
|
112
|
+
* ```
|
|
113
|
+
*/
|
|
114
|
+
export declare function alignmentBonus(alignment: Alignment, lawfulBonus: number): number;
|
|
115
|
+
//# sourceMappingURL=SideTurns.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SideTurns.d.ts","sourceRoot":"","sources":["../../src/world/SideTurns.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,mFAAmF;AACnF,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;AAErE,MAAM,WAAW,SAAS;IACzB,2EAA2E;IAC3E,EAAE,EAAE,MAAM,CAAC;IAEX,gFAAgF;IAChF,WAAW,EAAE,MAAM,CAAC;CACpB;AAED,sFAAsF;AACtF,MAAM,WAAW,QAAQ;IACxB,mEAAmE;IACnE,KAAK,EAAE,SAAS,SAAS,EAAE,CAAC;IAE5B,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACxC;AAED,MAAM,WAAW,gBAAgB;IAChC,4DAA4D;IAC5D,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAEzB,6DAA6D;IAC7D,QAAQ,EAAE,SAAS,SAAS,EAAE,CAAC;IAE/B,oDAAoD;IACpD,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,uFAAuF;IACvF,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,4DAA4D;IAC5D,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,yEAAyE;IACzE,KAAK,CAAC,EAAE,SAAS,QAAQ,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,QAAQ;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IAEd,4EAA4E;IAC5E,QAAQ,EAAE,OAAO,CAAC;CAClB;AAED,iEAAiE;AACjE,MAAM,WAAW,aAAa;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,qBAAa,SAAS;IACrB,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,QAAQ,EAAE,SAAS,SAAS,EAAE,CAAC;IAExC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAsB;IAC5C,OAAO,CAAC,KAAK,CAAgB;IAE7B,YAAY,OAAO,EAAE,gBAAgB,EAmBpC;IAED,IAAI,KAAK,IAAI,MAAM,CAElB;IAED,gCAAgC;IAChC,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED,gEAAgE;IAChE,IAAI,SAAS,IAAI,SAAS,CAEzB;IAED,IAAI,QAAQ,IAAI,aAAa,CAE5B;IAED,8FAA8F;IAC9F,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,SAAS,CAM3C;IAED,4FAA4F;IAC5F,aAAa,CAAC,SAAS,EAAE,SAAS,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAEhE;IAED,uFAAuF;IACvF,OAAO,IAAI,QAAQ,CAUlB;IAED,MAAM,IAAI,aAAa,CAEtB;IAED,2FAA2F;IAC3F,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,gBAAgB,EAAE,KAAK,EAAE,aAAa,GAAG,SAAS,CAO1E;CACD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAShF"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The turn model a strategy game counts by: sides take turns one after another, a round is
|
|
3
|
+
* every side taking one, and the time of day steps on once per round. This is deliberately
|
|
4
|
+
* distinct from `TurnClock` (timed effects on a single actor) and `simulation.Scheduler`
|
|
5
|
+
* (energy-cost turn order within a side): it is the outer loop a scenario runs on.
|
|
6
|
+
*
|
|
7
|
+
* The time schedule carries a `lawfulBonus` per entry, in percent, the number the reference
|
|
8
|
+
* game's `[time] lawful_bonus=` writes (day `25`, night `-25`, dawn/dusk `0`). A unit's
|
|
9
|
+
* alignment turns that into its own bonus, and a `TimeArea` can override the schedule for
|
|
10
|
+
* part of the map, which is what makes the bonus per-hex rather than global.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Which side is taking its turn, which round it is, and what time of day applies - the
|
|
14
|
+
* outer scenario loop. `advance()` moves to the next side; wrapping past the last side opens
|
|
15
|
+
* a new round and steps the schedule on one entry, cycling. `timeOfDayAt(x, y)` and
|
|
16
|
+
* `lawfulBonusAt(...)` read the schedule for a cell, so a time area can give one region a
|
|
17
|
+
* different bonus without a second turn counter.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* import { SideTurns } from '@datamoc/mw_games/world';
|
|
22
|
+
*
|
|
23
|
+
* const turns = new SideTurns({
|
|
24
|
+
* sides: ['rebels', 'undead'],
|
|
25
|
+
* schedule: [
|
|
26
|
+
* { id: 'day', lawfulBonus: 25 },
|
|
27
|
+
* { id: 'night', lawfulBonus: -25 },
|
|
28
|
+
* ],
|
|
29
|
+
* });
|
|
30
|
+
*
|
|
31
|
+
* console.log(turns.side, turns.round, turns.timeOfDay.id); // 'rebels' 1 'day'
|
|
32
|
+
* console.log(turns.lawfulBonusAt('lawful', 4, 4)); // 25 - a lawful unit in the day
|
|
33
|
+
* turns.advance(); // 'undead', still round 1
|
|
34
|
+
* turns.advance(); // 'rebels', round 2, timeOfDay is now 'night'
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export class SideTurns {
|
|
38
|
+
constructor(options) {
|
|
39
|
+
if (options.sides.length === 0)
|
|
40
|
+
throw new Error('SideTurns needs at least one side');
|
|
41
|
+
if (options.schedule.length === 0)
|
|
42
|
+
throw new Error('SideTurns needs a non-empty schedule');
|
|
43
|
+
const round = options.round ?? 1;
|
|
44
|
+
const sideIndex = options.sideIndex ?? 0;
|
|
45
|
+
const timeIndex = options.timeIndex ?? 0;
|
|
46
|
+
if (!Number.isInteger(round) || round < 1)
|
|
47
|
+
throw new Error('SideTurns round must be a positive integer');
|
|
48
|
+
if (sideIndex < 0 || sideIndex >= options.sides.length) {
|
|
49
|
+
throw new Error(`SideTurns side index ${sideIndex} is outside ${options.sides.length} sides`);
|
|
50
|
+
}
|
|
51
|
+
if (timeIndex < 0 || timeIndex >= options.schedule.length) {
|
|
52
|
+
throw new Error(`SideTurns time index ${timeIndex} is outside ${options.schedule.length} times`);
|
|
53
|
+
}
|
|
54
|
+
this.sides = [...options.sides];
|
|
55
|
+
this.schedule = [...options.schedule];
|
|
56
|
+
this.areas = options.areas ?? [];
|
|
57
|
+
this.state = { round, sideIndex, timeIndex };
|
|
58
|
+
}
|
|
59
|
+
get round() {
|
|
60
|
+
return this.state.round;
|
|
61
|
+
}
|
|
62
|
+
/** the side whose turn it is */
|
|
63
|
+
get side() {
|
|
64
|
+
return this.sides[this.state.sideIndex];
|
|
65
|
+
}
|
|
66
|
+
get timeIndex() {
|
|
67
|
+
return this.state.timeIndex;
|
|
68
|
+
}
|
|
69
|
+
/** the global time of day, before any time area overrides it */
|
|
70
|
+
get timeOfDay() {
|
|
71
|
+
return this.schedule[this.state.timeIndex];
|
|
72
|
+
}
|
|
73
|
+
get snapshot() {
|
|
74
|
+
return { ...this.state };
|
|
75
|
+
}
|
|
76
|
+
/** The time of day at a cell: the first containing time area's entry, else the global one. */
|
|
77
|
+
timeOfDayAt(x, y) {
|
|
78
|
+
for (const area of this.areas) {
|
|
79
|
+
if (!area.contains(x, y))
|
|
80
|
+
continue;
|
|
81
|
+
return area.times[this.state.timeIndex] ?? this.timeOfDay;
|
|
82
|
+
}
|
|
83
|
+
return this.timeOfDay;
|
|
84
|
+
}
|
|
85
|
+
/** The percentage bonus `alignment` gets at a cell, through that cell's own time of day. */
|
|
86
|
+
lawfulBonusAt(alignment, x, y) {
|
|
87
|
+
return alignmentBonus(alignment, this.timeOfDayAt(x, y).lawfulBonus);
|
|
88
|
+
}
|
|
89
|
+
/** Moves to the next side, opening a new round and stepping the schedule on a wrap. */
|
|
90
|
+
advance() {
|
|
91
|
+
let newRound = false;
|
|
92
|
+
this.state.sideIndex++;
|
|
93
|
+
if (this.state.sideIndex >= this.sides.length) {
|
|
94
|
+
this.state.sideIndex = 0;
|
|
95
|
+
this.state.round++;
|
|
96
|
+
this.state.timeIndex = (this.state.timeIndex + 1) % this.schedule.length;
|
|
97
|
+
newRound = true;
|
|
98
|
+
}
|
|
99
|
+
return { side: this.side, round: this.state.round, newRound };
|
|
100
|
+
}
|
|
101
|
+
toJSON() {
|
|
102
|
+
return this.snapshot;
|
|
103
|
+
}
|
|
104
|
+
/** Resumes from `toJSON`, with the same sides/schedule/areas the game was created with. */
|
|
105
|
+
static fromJSON(options, state) {
|
|
106
|
+
return new SideTurns({
|
|
107
|
+
...options,
|
|
108
|
+
round: state.round,
|
|
109
|
+
sideIndex: state.sideIndex,
|
|
110
|
+
timeIndex: state.timeIndex,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* What one alignment gets out of the time of day's own `lawfulBonus`, in percent: a lawful
|
|
116
|
+
* unit takes it, a chaotic one takes its negation, and neutral and liminal units ignore the
|
|
117
|
+
* time entirely (the reference's own four `align=` values).
|
|
118
|
+
*
|
|
119
|
+
* @example
|
|
120
|
+
* ```ts
|
|
121
|
+
* import { alignmentBonus } from '@datamoc/mw_games/world';
|
|
122
|
+
*
|
|
123
|
+
* alignmentBonus('lawful', 25); // 25 - day favours a lawful unit
|
|
124
|
+
* alignmentBonus('chaotic', 25); // -25 - the same day punishes a chaotic one
|
|
125
|
+
* alignmentBonus('neutral', 25); // 0 - neutral ignores the time of day
|
|
126
|
+
* ```
|
|
127
|
+
*/
|
|
128
|
+
export function alignmentBonus(alignment, lawfulBonus) {
|
|
129
|
+
switch (alignment) {
|
|
130
|
+
case 'lawful':
|
|
131
|
+
return lawfulBonus;
|
|
132
|
+
case 'chaotic':
|
|
133
|
+
return -lawfulBonus;
|
|
134
|
+
default:
|
|
135
|
+
return 0;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=SideTurns.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SideTurns.js","sourceRoot":"","sources":["../../src/world/SideTurns.ts"],"names":[],"mappings":"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"}
|
package/dist/world/index.d.ts
CHANGED
|
@@ -7,4 +7,6 @@ export { rollEncounter } from './Encounters.ts';
|
|
|
7
7
|
export type { EncounterEntry, EncounterTable } from './Encounters.ts';
|
|
8
8
|
export { EnvironmentClock } from './Environment.ts';
|
|
9
9
|
export type { DayPhase, EnvironmentSnapshot, EnvironmentOptions } from './Environment.ts';
|
|
10
|
+
export { SideTurns, alignmentBonus } from './SideTurns.ts';
|
|
11
|
+
export type { Alignment, TimeOfDay, TimeArea, SideTurnsOptions, SideTurn, SideTurnState } from './SideTurns.ts';
|
|
10
12
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/world/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,YAAY,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,YAAY,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtE,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,YAAY,EAAE,QAAQ,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/world/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,YAAY,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,YAAY,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtE,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,YAAY,EAAE,QAAQ,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAC1F,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAC3D,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,gBAAgB,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC"}
|
package/dist/world/index.js
CHANGED
|
@@ -3,4 +3,5 @@ export { Overworld } from './Overworld.js';
|
|
|
3
3
|
export { TurnClock } from './TurnClock.js';
|
|
4
4
|
export { rollEncounter } from './Encounters.js';
|
|
5
5
|
export { EnvironmentClock } from './Environment.js';
|
|
6
|
+
export { SideTurns, alignmentBonus } from './SideTurns.js';
|
|
6
7
|
//# sourceMappingURL=index.js.map
|
package/dist/world/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/world/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/world/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEpD,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC"}
|