questmark 0.0.36 → 0.1.0
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/.github/workflows/docs.yml +56 -0
- package/bin/questmark +1 -1
- package/dist/browser/bundle.js +28365 -0
- package/dist/node/QuestVM.d.ts +1 -2
- package/dist/node/QuestVM.js +81 -83
- package/dist/node/QuestVM.js.map +1 -1
- package/dist/node/cli.js +33 -49
- package/dist/node/cli.js.map +1 -1
- package/dist/node/index.d.ts +2 -2
- package/dist/node/index.js +2 -14
- package/dist/node/index.js.map +1 -1
- package/dist/node/parseMarkdown.d.ts +18 -7
- package/dist/node/parseMarkdown.js +81 -77
- package/dist/node/parseMarkdown.js.map +1 -1
- package/docs/explanation/how-questmark-works.md +123 -0
- package/docs/explanation/why-questmark.md +72 -0
- package/docs/how-to/compile-to-bytecode.md +62 -0
- package/docs/how-to/play-a-document.md +57 -0
- package/docs/how-to/use-the-library.md +98 -0
- package/docs/index.md +61 -0
- package/docs/reference/api.md +135 -0
- package/docs/reference/cli.md +66 -0
- package/docs/reference/language.md +213 -0
- package/docs/tutorials/first-conversation.md +219 -0
- package/package.json +33 -31
- package/readme.md +55 -80
- package/tsconfig.json +8 -3
- package/tsconfig.webpack.json +9 -0
- package/webpack.config.cjs +49 -0
- package/dist/browser/QuestVM.d.ts +0 -13
- package/dist/browser/QuestVM.js +0 -89
- package/dist/browser/QuestVM.js.map +0 -1
- package/dist/browser/cli.d.ts +0 -2
- package/dist/browser/cli.js +0 -80
- package/dist/browser/cli.js.map +0 -1
- package/dist/browser/index.d.ts +0 -2
- package/dist/browser/index.js +0 -15
- package/dist/browser/index.js.map +0 -1
- package/dist/browser/parseMarkdown.d.ts +0 -25
- package/dist/browser/parseMarkdown.js +0 -413
- package/dist/browser/parseMarkdown.js.map +0 -1
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
# Write and play your first conversation
|
|
2
|
+
|
|
3
|
+
In this tutorial you'll write a small piece of interactive fiction from
|
|
4
|
+
scratch, and play it. Along the way you'll meet the three ideas everything in
|
|
5
|
+
Questmark is built on: **states**, **options**, and **context**.
|
|
6
|
+
|
|
7
|
+
You don't need any programming experience, but you do need to be comfortable
|
|
8
|
+
with Markdown — which is exactly the point. Everything you write here is
|
|
9
|
+
ordinary Markdown plus a few backticked additions.
|
|
10
|
+
|
|
11
|
+
This tutorial takes about ten minutes. If you follow it to the end, you'll
|
|
12
|
+
have a working, playable conversation.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 1. Get set up
|
|
17
|
+
|
|
18
|
+
Clone the repository and install its dependencies:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
git clone https://github.com/jorisvddonk/questmark.git
|
|
22
|
+
cd questmark
|
|
23
|
+
npm install
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The Questmark command line is available from the project directory as
|
|
27
|
+
`npm start`, and the published CLI is also available via
|
|
28
|
+
`npx questmark` if you'd rather use that.
|
|
29
|
+
|
|
30
|
+
## 2. Write your first document
|
|
31
|
+
|
|
32
|
+
Create a file called `tavern.md` with the following contents:
|
|
33
|
+
|
|
34
|
+
```markdown
|
|
35
|
+
# QUESTMARK-OPTIONS-HEADER
|
|
36
|
+
|
|
37
|
+
{
|
|
38
|
+
"questmark-spec": "1.0",
|
|
39
|
+
"initial-state": "bar"
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
# bar
|
|
43
|
+
|
|
44
|
+
The barkeep eyes you from behind the counter.
|
|
45
|
+
* [Order a drink](#drink)
|
|
46
|
+
* [Leave](#leave)
|
|
47
|
+
|
|
48
|
+
# drink
|
|
49
|
+
|
|
50
|
+
A frothing mug of ale slides across the counter.
|
|
51
|
+
* [Back to the bar](#bar)
|
|
52
|
+
|
|
53
|
+
# leave
|
|
54
|
+
|
|
55
|
+
"Safe travels, stranger."
|
|
56
|
+
`exit`
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Let's take it apart.
|
|
60
|
+
|
|
61
|
+
Every Questmark document starts with a **`QUESTMARK-OPTIONS-HEADER`** section.
|
|
62
|
+
Its indented code block is JSON that tells the interpreter where to start
|
|
63
|
+
(`initial-state`) and, later, what variables to set up (`initial-context`).
|
|
64
|
+
|
|
65
|
+
Each `# heading` in the document defines a **state**. A state is one "room" of
|
|
66
|
+
your fiction — the thing the player sees at one moment in the story. The text
|
|
67
|
+
under a heading is shown to the player when they enter that state.
|
|
68
|
+
|
|
69
|
+
Each `*` list item is an **option**: one thing the player can choose to do.
|
|
70
|
+
The Markdown link in `[Order a drink](#drink)` means "when the player picks
|
|
71
|
+
this, jump to the `drink` state."
|
|
72
|
+
|
|
73
|
+
The last line, `` `exit` ``, is a piece of code that ends the conversation.
|
|
74
|
+
|
|
75
|
+
## 3. Play it
|
|
76
|
+
|
|
77
|
+
Run the document:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
npm start -- --input tavern.md
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
You should see the barkeep's text, and then a menu with two choices. Pick
|
|
84
|
+
"Order a drink", then "Back to the bar", then "Leave". Each time you make a
|
|
85
|
+
choice, the story moves to the state that choice pointed at.
|
|
86
|
+
|
|
87
|
+
Play it once more and make a different choice. The story branches — that's the
|
|
88
|
+
whole trick behind conversation trees.
|
|
89
|
+
|
|
90
|
+
## 4. Remember something the player did
|
|
91
|
+
|
|
92
|
+
Right now the barkeep can't tell whether you've already had a drink. Let's add
|
|
93
|
+
some memory. Questmark tracks what the player has done in a bag of variables
|
|
94
|
+
called the **context**.
|
|
95
|
+
|
|
96
|
+
Update the header to initialize a variable, and set it when the player orders
|
|
97
|
+
a drink:
|
|
98
|
+
|
|
99
|
+
```markdown
|
|
100
|
+
# QUESTMARK-OPTIONS-HEADER
|
|
101
|
+
|
|
102
|
+
{
|
|
103
|
+
"questmark-spec": "1.0",
|
|
104
|
+
"initial-state": "bar",
|
|
105
|
+
"initial-context": { "had_drink": 0 }
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
# bar
|
|
109
|
+
|
|
110
|
+
The barkeep eyes you from behind the counter.
|
|
111
|
+
* [Order a drink](#drink)
|
|
112
|
+
* [Leave](#leave)
|
|
113
|
+
|
|
114
|
+
# drink
|
|
115
|
+
|
|
116
|
+
A frothing mug of ale slides across the counter.
|
|
117
|
+
`1 "had_drink" setContext`
|
|
118
|
+
* [Back to the bar](#bar)
|
|
119
|
+
|
|
120
|
+
# leave
|
|
121
|
+
|
|
122
|
+
"Safe travels, stranger."
|
|
123
|
+
`exit`
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
The new line `` `1 "had_drink" setContext` `` means "push the number `1`, then
|
|
127
|
+
store it into the context under the name `had_drink`." Code inside backticks is
|
|
128
|
+
Tzo bytecode, written in a reverse-Polish style: the values come first, then
|
|
129
|
+
the operation.
|
|
130
|
+
|
|
131
|
+
## 5. Make an option conditional
|
|
132
|
+
|
|
133
|
+
Now let's use that memory. A new option should appear only once the player has
|
|
134
|
+
had a drink. Backticks *before* an option's text are a **precondition**: the
|
|
135
|
+
option is only offered when the code leaves a positive number on the stack.
|
|
136
|
+
|
|
137
|
+
Add the option to the `bar` state:
|
|
138
|
+
|
|
139
|
+
```markdown
|
|
140
|
+
# bar
|
|
141
|
+
|
|
142
|
+
The barkeep eyes you from behind the counter.
|
|
143
|
+
* [Order a drink](#drink)
|
|
144
|
+
* `"had_drink" getContext` [Ask about the storm](#storm)
|
|
145
|
+
* [Leave](#leave)
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
The precondition `` `"had_drink" getContext` `` means "look up `had_drink` and
|
|
149
|
+
push its value." On your first visit the value is `0`, so the option is hidden.
|
|
150
|
+
After you've ordered a drink the value is `1`, so the option appears.
|
|
151
|
+
|
|
152
|
+
Now add the `storm` state the option links to:
|
|
153
|
+
|
|
154
|
+
```markdown
|
|
155
|
+
# storm
|
|
156
|
+
|
|
157
|
+
"Storm's coming. Mark my words."
|
|
158
|
+
`exit`
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## 6. Try the finished conversation
|
|
162
|
+
|
|
163
|
+
Here is the complete document:
|
|
164
|
+
|
|
165
|
+
```markdown
|
|
166
|
+
# QUESTMARK-OPTIONS-HEADER
|
|
167
|
+
|
|
168
|
+
{
|
|
169
|
+
"questmark-spec": "1.0",
|
|
170
|
+
"initial-state": "bar",
|
|
171
|
+
"initial-context": { "had_drink": 0 }
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
# bar
|
|
175
|
+
|
|
176
|
+
The barkeep eyes you from behind the counter.
|
|
177
|
+
* [Order a drink](#drink)
|
|
178
|
+
* `"had_drink" getContext` [Ask about the storm](#storm)
|
|
179
|
+
* [Leave](#leave)
|
|
180
|
+
|
|
181
|
+
# drink
|
|
182
|
+
|
|
183
|
+
A frothing mug of ale slides across the counter.
|
|
184
|
+
`1 "had_drink" setContext`
|
|
185
|
+
* [Back to the bar](#bar)
|
|
186
|
+
|
|
187
|
+
# storm
|
|
188
|
+
|
|
189
|
+
"Storm's coming. Mark my words."
|
|
190
|
+
`exit`
|
|
191
|
+
|
|
192
|
+
# leave
|
|
193
|
+
|
|
194
|
+
"Safe travels, stranger."
|
|
195
|
+
`exit`
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Play it and notice the difference:
|
|
199
|
+
|
|
200
|
+
1. On your first visit to the bar, "Ask about the storm" is **not** in the
|
|
201
|
+
menu.
|
|
202
|
+
2. Order a drink, return to the bar, and the option is **now** there.
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
npm start -- --input tavern.md
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## 7. Where to go next
|
|
209
|
+
|
|
210
|
+
You've used states, options, links, context, and a precondition. Those are the
|
|
211
|
+
core of the language.
|
|
212
|
+
|
|
213
|
+
- Read the [how-to guide on playing documents](../how-to/play-a-document.md)
|
|
214
|
+
for more ways to run a document.
|
|
215
|
+
- Read the [language reference](../reference/language.md) for the complete set
|
|
216
|
+
of language features, including the `@once` directive and effects.
|
|
217
|
+
- Browse the [examples](https://github.com/jorisvddonk/questmark/tree/master/examples)
|
|
218
|
+
in the repository — `space_alien.md` is a longer conversation tree that puts
|
|
219
|
+
everything together.
|
package/package.json
CHANGED
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "questmark",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"description": "",
|
|
5
|
+
"type": "module",
|
|
5
6
|
"main": "dist/node/index.js",
|
|
6
7
|
"types": "dist/node/index.d.js",
|
|
7
8
|
"exports": {
|
|
8
|
-
".":
|
|
9
|
-
"import": "./dist/node/index.js",
|
|
10
|
-
"require": "./dist/browser/index.js"
|
|
11
|
-
}
|
|
9
|
+
".": "./dist/node/index.js"
|
|
12
10
|
},
|
|
13
11
|
"scripts": {
|
|
14
12
|
"prepublishOnly": "npm run build",
|
|
15
|
-
"build": "npx cross-env rm -rf dist/ && tsc -p tsconfig.json &&
|
|
16
|
-
"
|
|
17
|
-
"
|
|
13
|
+
"build": "npx cross-env rm -rf dist/ && tsc -p tsconfig.json && npm run wp_build",
|
|
14
|
+
"wp_build": "webpack build --config webpack.config.cjs",
|
|
15
|
+
"test": "npm run build && node --test \"test/**/*.test.cjs\"",
|
|
16
|
+
"start": "tsx ./src/cli.ts",
|
|
17
|
+
"docs:dev": "vitepress dev docs",
|
|
18
|
+
"docs:build": "vitepress build docs",
|
|
19
|
+
"docs:preview": "vitepress preview docs"
|
|
18
20
|
},
|
|
19
21
|
"bin": {
|
|
20
22
|
"questmark": "./bin/questmark.mjs"
|
|
@@ -23,32 +25,32 @@
|
|
|
23
25
|
"license": "MIT",
|
|
24
26
|
"dependencies": {
|
|
25
27
|
"array-flat-polyfill": "^1.0.1",
|
|
26
|
-
"commander": "^
|
|
27
|
-
"inquirer": "^
|
|
28
|
+
"commander": "^15.0.0",
|
|
29
|
+
"inquirer": "^14.2.2",
|
|
28
30
|
"mdast": "^3.0.0",
|
|
29
|
-
"mdast-util-from-markdown": "^0.
|
|
30
|
-
"node-fetch": "^2.6.5",
|
|
31
|
+
"mdast-util-from-markdown": "^2.0.3",
|
|
31
32
|
"tzo": "^1.0.19",
|
|
32
|
-
"unist-builder": "^
|
|
33
|
-
"unist-util-filter": "^
|
|
34
|
-
"unist-util-find-after": "^
|
|
35
|
-
"unist-util-find-all-after": "^
|
|
36
|
-
"unist-util-find-all-before": "^
|
|
37
|
-
"unist-util-
|
|
38
|
-
"unist-util-
|
|
39
|
-
"unist-util-
|
|
40
|
-
"unist-util-
|
|
41
|
-
"unist-util-visit": "^
|
|
42
|
-
"unist-util-visit-parents": "^3.1.0"
|
|
33
|
+
"unist-builder": "^4.0.0",
|
|
34
|
+
"unist-util-filter": "^5.0.1",
|
|
35
|
+
"unist-util-find-after": "^5.0.0",
|
|
36
|
+
"unist-util-find-all-after": "^5.0.0",
|
|
37
|
+
"unist-util-find-all-before": "^5.0.0",
|
|
38
|
+
"unist-util-flat-filter": "^2.0.0",
|
|
39
|
+
"unist-util-map": "^4.0.0",
|
|
40
|
+
"unist-util-remove-position": "^5.0.0",
|
|
41
|
+
"unist-util-visit": "^5.1.0",
|
|
42
|
+
"unist-util-visit-parents": "^6.0.2"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"@types/
|
|
46
|
-
"@types/
|
|
47
|
-
"@types/
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
45
|
+
"@types/mdast": "^4.0.4",
|
|
46
|
+
"@types/node": "^26.6.3",
|
|
47
|
+
"@types/unist": "^3.0.3",
|
|
48
|
+
"node-polyfill-webpack-plugin": "^4.1.0",
|
|
49
|
+
"ts-loader": "^9.2.6",
|
|
50
|
+
"tsx": "^4.23.15",
|
|
51
|
+
"typescript": "^5.9.3",
|
|
52
|
+
"vitepress": "^1.6.4",
|
|
53
|
+
"webpack": "^5.54.0",
|
|
54
|
+
"webpack-cli": "^7.2.3"
|
|
53
55
|
}
|
|
54
56
|
}
|
package/readme.md
CHANGED
|
@@ -1,102 +1,77 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Questmark
|
|
2
2
|
|
|
3
|
-
Questmark is a [hypertext fiction](https://en.wikipedia.org/wiki/Hypertext_fiction)
|
|
3
|
+
Questmark is a [hypertext fiction](https://en.wikipedia.org/wiki/Hypertext_fiction)
|
|
4
|
+
and [conversation tree](https://en.wikipedia.org/wiki/Dialogue_tree) language,
|
|
5
|
+
compiler, interpreter, and TypeScript library. It supports both conversation
|
|
6
|
+
trees (like in games such as Star Control 2) and quests, hypertext fiction,
|
|
7
|
+
text adventures, and visual novels (like in games such as Space Rangers 2).
|
|
4
8
|
|
|
5
|
-
|
|
9
|
+
Questmark is based heavily on Markdown, and compiles to
|
|
10
|
+
[Tzo](https://github.com/jorisvddonk/tzo) bytecode. If you can write Markdown,
|
|
11
|
+
you can write Questmark: every properly-structured Markdown document with
|
|
12
|
+
headers and in-document links is already a Questmark document. Behaviour and
|
|
13
|
+
side-effects are added with small, backticked code snippets where needed.
|
|
6
14
|
|
|
7
|
-
|
|
15
|
+
## Documentation
|
|
8
16
|
|
|
9
|
-
|
|
17
|
+
The docs follow the [Diátaxis](https://diataxis.fr/) framework and are also
|
|
18
|
+
rendered as a [hosted website](https://jorisvddonk.github.io/questmark/):
|
|
10
19
|
|
|
11
|
-
|
|
20
|
+
- **[Tutorials](docs/tutorials/)** — start here if you're new. Write and play
|
|
21
|
+
your first conversation in about ten minutes.
|
|
22
|
+
- **[How-to guides](docs/how-to/)** — play a document, compile it to bytecode,
|
|
23
|
+
and embed Questmark in your own application.
|
|
24
|
+
- **[Reference](docs/reference/)** — the language, the CLI, and the API,
|
|
25
|
+
described precisely.
|
|
26
|
+
- **[Explanation](docs/explanation/)** — why Questmark exists and how it works
|
|
27
|
+
under the hood.
|
|
12
28
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
This means that, if you come up with [an advanced scripting language with associated editor](https://github.com/jorisvddonk/p6014-dialogue-scripting-tool), you're likely to only attract programmers. Your dialogue prose authors will either be programmers (which may be terrible at writing dialogue prose!), or your dialogue prose authors will just continue to work inside Word documents...
|
|
16
|
-
|
|
17
|
-
Maybe your dialogue prose authors are writing using a different, proprietary system instead. That might actually work out OK, or it might not, for instance if these proprietary systems use binary file formats that are difficult/impossible to merge.
|
|
18
|
-
|
|
19
|
-
# The solution!
|
|
20
|
-
|
|
21
|
-
Both authors and programmers, however, are likely to know Markdown or any of its modern dialects. This knowledge shared between authors and programmers is what Questmark makes use of.
|
|
22
|
-
|
|
23
|
-
Questmark is a natural dialect of Markdown, allowing for the creation of dialogue graphs with in-state and state-transition side-effects.
|
|
24
|
-
|
|
25
|
-
Every Markdown document with properly structured headers and in-document links is a Questmark document. Questmark-specific features and code can then be added to add further interactivity and side-effects to a dialogue system.
|
|
26
|
-
|
|
27
|
-
Questmark aims to make the following workflow viable:
|
|
28
|
-
|
|
29
|
-
1. Authors write dialogue trees in Markdown, which can be demoed interactively, either by interpreting Markdown and compiling it as HTML, by interpreting Questmark in a simple Questmark runner, or by interpreting Questmark inside of a modern game engine. Note that the Markdown->HTML approach only works for simple documents without scripting.
|
|
30
|
-
2. Authors, project managers, and other reviewers can add comments to provide feedback or clarification on dialogue without affecting interpreted Questmark output. These comments will *not* be visible anywhere when the dialogue tree definition is interpreted as Questmark.
|
|
31
|
-
3. Gameplay programmers can add interactivity where needed.
|
|
32
|
-
4. Authors can modify dialogue prose easily without affecting dialogue interactivity, if needed.
|
|
33
|
-
5. Game can be shipped!
|
|
34
|
-
|
|
35
|
-
Ideally, your dialogue tree Questmark files would be saved under revision control (e.g. git).
|
|
36
|
-
|
|
37
|
-
# Current status of Questmark
|
|
38
|
-
|
|
39
|
-
The spec is currently not written down properly, the supported featureset has not been decided on, and there is no formal test library (though [Tzo](https://github.com/jorisvddonk/tzo) *does* have a [testsuite](https://github.com/jorisvddonk/tzo/tree/master/src/tests)). This Github repository, however, contains a proof of concept implemented in TypeScript!
|
|
40
|
-
|
|
41
|
-
You should look in the [examples](https://github.com/jorisvddonk/questmark/blob/master/examples) folder for examples. Particularly, [self-describing.md](https://github.com/jorisvddonk/questmark/blob/master/examples/self-describing.md) is a text adventure written in Questmark that explains how Questmark works to you!
|
|
42
|
-
|
|
43
|
-
There are a few things that are still a bit "up in the air" and need to be thought about or implemented properly:
|
|
44
|
-
|
|
45
|
-
* How should inline HTML be treated?
|
|
46
|
-
* How can the Questmark compiler be modified to add custom directives and macros?
|
|
47
|
-
* How can a Questmark document be translated to another language, whilst keeping the scripting logic intact and easy to modify?
|
|
48
|
-
|
|
49
|
-
# Questmark cli usage
|
|
50
|
-
|
|
51
|
-
Questmark contains a simple CLI to compile and optionally interpret Questmark documents that don't contain any foreign opcodes that aren't implemented by the standard reference inplementation.
|
|
52
|
-
|
|
53
|
-
## Compiling a Questmark document to Tzo bytecode
|
|
29
|
+
## Quick start
|
|
54
30
|
|
|
55
31
|
```bash
|
|
56
|
-
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
## Interpreting a Questmark document
|
|
60
|
-
|
|
61
|
-
```bash
|
|
62
|
-
npx questmark --input path_to_questmark_document.md
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
# Questmark library usage
|
|
32
|
+
npm install
|
|
66
33
|
|
|
67
|
-
|
|
34
|
+
# Play an example conversation
|
|
35
|
+
npm start -- --input examples/space_alien.md
|
|
68
36
|
|
|
69
|
-
|
|
37
|
+
# Compile a document to Tzo bytecode without running it
|
|
38
|
+
npm start -- --input examples/space_alien.md --output vmstate.json --no-run
|
|
70
39
|
|
|
71
|
-
#
|
|
72
|
-
|
|
73
|
-
|
|
40
|
+
# Run the test suite
|
|
41
|
+
npm test
|
|
42
|
+
```
|
|
74
43
|
|
|
75
|
-
|
|
44
|
+
You can also use the published CLI with `npx questmark` (see the
|
|
45
|
+
[how-to guide](docs/how-to/play-a-document.md)).
|
|
76
46
|
|
|
77
|
-
|
|
78
|
-
npx questmark --help
|
|
79
|
-
```
|
|
47
|
+
## Examples
|
|
80
48
|
|
|
81
|
-
|
|
49
|
+
The [examples](examples) folder contains playable documents. Notably,
|
|
50
|
+
[`self-describing.md`](examples/self-describing.md) is a text adventure written
|
|
51
|
+
in Questmark that explains how the language works, and
|
|
52
|
+
[`space_alien.md`](examples/space_alien.md) demonstrates preconditions,
|
|
53
|
+
`@once` options, and context-driven branching.
|
|
82
54
|
|
|
83
|
-
|
|
84
|
-
npx questmark --input <path_to_file.md>
|
|
85
|
-
```
|
|
55
|
+
## Current status
|
|
86
56
|
|
|
87
|
-
|
|
57
|
+
Questmark is a working proof of concept implemented in TypeScript, with a
|
|
58
|
+
compiler, an interpreter, a CLI, and a [test suite](test). The spec is still
|
|
59
|
+
informal and the supported feature set is still evolving.
|
|
88
60
|
|
|
89
|
-
|
|
90
|
-
npx questmark --input https://ghcdn.rawgit.org/jorisvddonk/questmark/master/examples/self-describing.md
|
|
91
|
-
```
|
|
61
|
+
Open questions being worked on:
|
|
92
62
|
|
|
93
|
-
|
|
63
|
+
- How should inline HTML be treated?
|
|
64
|
+
- How can the compiler be modified to add custom directives and macros?
|
|
65
|
+
- How can a document be translated to another language, whilst keeping the
|
|
66
|
+
scripting logic intact and easy to modify?
|
|
94
67
|
|
|
95
|
-
|
|
96
|
-
npx questmark --input <path_to_file.md> --output <path_to_output_VMState.json> --no-run
|
|
97
|
-
```
|
|
68
|
+
## Other tools / libraries
|
|
98
69
|
|
|
99
|
-
|
|
70
|
+
- [Tzo](https://github.com/jorisvddonk/tzo) — the bytecode and VM Questmark
|
|
71
|
+
compiles to.
|
|
72
|
+
- [questmark-webrenderer](https://github.com/jorisvddonk/questmark-webrenderer) —
|
|
73
|
+
write and play Questmark documents in your web browser.
|
|
100
74
|
|
|
101
|
-
|
|
75
|
+
## License
|
|
102
76
|
|
|
77
|
+
MIT
|
package/tsconfig.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"compilerOptions": {
|
|
3
|
-
"moduleResolution": "
|
|
4
|
-
"target": "
|
|
5
|
-
"module": "
|
|
3
|
+
"moduleResolution": "nodenext",
|
|
4
|
+
"target": "es2022",
|
|
5
|
+
"module": "nodenext",
|
|
6
6
|
"lib": [
|
|
7
7
|
"esnext"
|
|
8
8
|
],
|
|
@@ -19,8 +19,13 @@
|
|
|
19
19
|
"rootDir": "./src",
|
|
20
20
|
"typeRoots": [
|
|
21
21
|
"node_modules/@types"
|
|
22
|
+
],
|
|
23
|
+
"types": [
|
|
24
|
+
"node",
|
|
25
|
+
"mdast"
|
|
22
26
|
]
|
|
23
27
|
},
|
|
28
|
+
"include": ["src/**/*.ts"],
|
|
24
29
|
"exclude": [
|
|
25
30
|
"dist",
|
|
26
31
|
"node_modules"
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const NodePolyfillPlugin = require("node-polyfill-webpack-plugin");
|
|
3
|
+
|
|
4
|
+
module.exports = {
|
|
5
|
+
mode: "production",
|
|
6
|
+
entry: './src/index.ts',
|
|
7
|
+
devtool: "inline-source-map",
|
|
8
|
+
output: {
|
|
9
|
+
filename: 'bundle.js',
|
|
10
|
+
path: path.resolve(__dirname, 'dist/browser'),
|
|
11
|
+
library: {
|
|
12
|
+
name: "Questmark",
|
|
13
|
+
type: "window"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
module: {
|
|
17
|
+
rules: [
|
|
18
|
+
{
|
|
19
|
+
test: /\.tsx?$/,
|
|
20
|
+
use: [{
|
|
21
|
+
loader: 'ts-loader',
|
|
22
|
+
options: {
|
|
23
|
+
configFile: "tsconfig.webpack.json"
|
|
24
|
+
}
|
|
25
|
+
}],
|
|
26
|
+
exclude: /node_modules/,
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
},
|
|
30
|
+
resolve: {
|
|
31
|
+
extensions: [".ts", ".tsx", ".js"],
|
|
32
|
+
extensionAlias: {
|
|
33
|
+
".js": [".ts", ".js"]
|
|
34
|
+
},
|
|
35
|
+
fallback: { fs: false }
|
|
36
|
+
},
|
|
37
|
+
optimization: {
|
|
38
|
+
minimize: false,
|
|
39
|
+
mangleExports: false,
|
|
40
|
+
},
|
|
41
|
+
plugins: [
|
|
42
|
+
new NodePolyfillPlugin()
|
|
43
|
+
/*new CopyPlugin({
|
|
44
|
+
patterns: [
|
|
45
|
+
{ from: "webRender-basic.js", to: "webRender-basic.js" }
|
|
46
|
+
],
|
|
47
|
+
}),*/
|
|
48
|
+
],
|
|
49
|
+
};
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/// <reference types="node" />
|
|
2
|
-
import { EventEmitter } from "events";
|
|
3
|
-
import { Functions } from "tzo";
|
|
4
|
-
import { VM } from "tzo";
|
|
5
|
-
export interface Choice {
|
|
6
|
-
title: string;
|
|
7
|
-
id: number;
|
|
8
|
-
}
|
|
9
|
-
export declare class QuestVM extends VM {
|
|
10
|
-
responses: any[];
|
|
11
|
-
eventBus: EventEmitter;
|
|
12
|
-
constructor(emitFunction: (body: string | number) => void, getResponseFunction: (choices: Choice[]) => Promise<number>, additionalFunctions?: Functions);
|
|
13
|
-
}
|
package/dist/browser/QuestVM.js
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.QuestVM = void 0;
|
|
4
|
-
const events_1 = require("events");
|
|
5
|
-
const tzo_1 = require("tzo");
|
|
6
|
-
class QuestVM extends tzo_1.VM {
|
|
7
|
-
constructor(emitFunction, getResponseFunction, additionalFunctions) {
|
|
8
|
-
super({}, Object.assign(Object.assign({}, (additionalFunctions || {})), {
|
|
9
|
-
"emit": (stack) => {
|
|
10
|
-
const [str1] = tzo_1.getStackParams("emit", ["string | number"], stack);
|
|
11
|
-
this.eventBus.emit('emit', str1);
|
|
12
|
-
emitFunction(str1);
|
|
13
|
-
},
|
|
14
|
-
"response": (stack, context, vm) => {
|
|
15
|
-
const [num1, str1] = tzo_1.getStackParams("response", ["number", "string"], stack);
|
|
16
|
-
const response = { response: str1, pc: num1 };
|
|
17
|
-
this.eventBus.emit('response', response);
|
|
18
|
-
this.responses.push(response);
|
|
19
|
-
},
|
|
20
|
-
"getResponse": (stack, context, vm) => {
|
|
21
|
-
vm.suspend();
|
|
22
|
-
const choices = this.responses.map((resp, index) => { return { title: resp.response, programCounter: resp.pc, id: index }; });
|
|
23
|
-
const sChoices = choices.map(c => ({ title: c.title, id: c.id }));
|
|
24
|
-
while (this.responses.length > 0) { // clear responses, so that current responses won't appear again next time
|
|
25
|
-
this.responses.pop();
|
|
26
|
-
}
|
|
27
|
-
getResponseFunction(sChoices).then(choiceID => {
|
|
28
|
-
const choice = choices.find(c => c.id === choiceID);
|
|
29
|
-
if (choice === undefined) {
|
|
30
|
-
throw new Error(`Unknown choice ID: ${choiceID}!`);
|
|
31
|
-
}
|
|
32
|
-
vm.stack.push(choice.programCounter);
|
|
33
|
-
vm.run();
|
|
34
|
-
}).catch(e => {
|
|
35
|
-
this.eventBus.emit('error', e);
|
|
36
|
-
this.quit();
|
|
37
|
-
});
|
|
38
|
-
},
|
|
39
|
-
"optionEnabled": (stack, context, vm) => {
|
|
40
|
-
// DEPRECATED - do not use!
|
|
41
|
-
console.warn("optionEnabled opcode is deprecated! - do not use!");
|
|
42
|
-
const [str1] = tzo_1.getStackParams("optionEnabled", ["string"], stack);
|
|
43
|
-
const optstr = `optionEnabled_${str1}`;
|
|
44
|
-
let retval = null;
|
|
45
|
-
if (vm.context[optstr] === undefined) {
|
|
46
|
-
vm.context[optstr] = 1;
|
|
47
|
-
retval = 1;
|
|
48
|
-
}
|
|
49
|
-
else {
|
|
50
|
-
retval = vm.context[optstr];
|
|
51
|
-
}
|
|
52
|
-
stack.push(retval);
|
|
53
|
-
},
|
|
54
|
-
"optionDisabled": (stack, context, vm) => {
|
|
55
|
-
// DEPRECATED - do not use!
|
|
56
|
-
console.warn("optionDisabled opcode is deprecated! - do not use!");
|
|
57
|
-
const [str1] = tzo_1.getStackParams("optionDisabled", ["string"], stack);
|
|
58
|
-
const optstr = `optionEnabled_${str1}`;
|
|
59
|
-
let retval = null;
|
|
60
|
-
if (vm.context[optstr] === undefined) {
|
|
61
|
-
vm.context[optstr] = 1;
|
|
62
|
-
retval = 0;
|
|
63
|
-
}
|
|
64
|
-
else {
|
|
65
|
-
retval = vm.context[optstr] === 1 ? 0 : 1;
|
|
66
|
-
}
|
|
67
|
-
stack.push(retval);
|
|
68
|
-
},
|
|
69
|
-
"disableOption": (stack, context, vm) => {
|
|
70
|
-
// DEPRECATED - do not use!
|
|
71
|
-
console.warn("disableOption opcode is deprecated! - do not use!");
|
|
72
|
-
const [str1] = tzo_1.getStackParams("disableOption", ["string"], stack);
|
|
73
|
-
const optstr = `optionEnabled_${str1}`;
|
|
74
|
-
vm.context[optstr] = 0;
|
|
75
|
-
},
|
|
76
|
-
"enableOption": (stack, context, vm) => {
|
|
77
|
-
// DEPRECATED - do not use!
|
|
78
|
-
console.warn("enableOption opcode is deprecated! - do not use!");
|
|
79
|
-
const [str1] = tzo_1.getStackParams("enableOption", ["string"], stack);
|
|
80
|
-
const optstr = `optionEnabled_${str1}`;
|
|
81
|
-
vm.context[optstr] = 1;
|
|
82
|
-
},
|
|
83
|
-
}));
|
|
84
|
-
this.responses = [];
|
|
85
|
-
this.eventBus = new events_1.EventEmitter();
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
exports.QuestVM = QuestVM;
|
|
89
|
-
//# sourceMappingURL=QuestVM.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"QuestVM.js","sourceRoot":"","sources":["../../src/QuestVM.ts"],"names":[],"mappings":"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"}
|
package/dist/browser/cli.d.ts
DELETED