silver-tongue 0.4.0 → 0.5.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/README.md +4 -0
- package/dist/courses/zh-china-en/course.json +1 -1
- package/dist/silver-tongue.mjs +132 -65
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,6 +6,10 @@ A language-learning life game. Your phone is dead, your wallet is gone, and you'
|
|
|
6
6
|
|
|
7
7
|
The first course is Mandarin (HSK 1), in a Chinese city, explained in English.
|
|
8
8
|
|
|
9
|
+
## Play in the browser
|
|
10
|
+
|
|
11
|
+
https://jamil314.github.io/silver-tongue/ — the same game, with an on-screen key bar on phones. Games save in the browser; Export and Import move a game between the browser and the terminal.
|
|
12
|
+
|
|
9
13
|
## Play in the terminal
|
|
10
14
|
|
|
11
15
|
```sh
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"id":"zh-china-en","typing":false,"words":{"w0003":{"id":"w0003","w":"三","lv":"1","gloss":"three; 3","pron":"sān"},"w0009":{"id":"w0009","w":"不","lv":"1","gloss":"no; not","pron":"bù"},"w0010":{"id":"w0010","w":"不客气","lv":"1","gloss":"you're welcome; don't mention it","pron":"bùkèqi"},"w0020":{"id":"w0020","w":"五","lv":"1","gloss":"five; 5","pron":"wǔ"},"w0028":{"id":"w0028","w":"你","lv":"1","gloss":"you (informal)","pron":"nǐ"},"w0034":{"id":"w0034","w":"再见","lv":"1","gloss":"goodbye","pron":"zàijiàn"},"w0037":{"id":"w0037","w":"几","lv":"1","gloss":"how much; how many","pron":"jǐ"},"w0051":{"id":"w0051","w":"吗","lv":"1","gloss":"(question particle for \"yes-no\" questions)","pron":"ma"},"w0060":{"id":"w0060","w":"四","lv":"1","gloss":"four; 4","pron":"sì"},"w0072":{"id":"w0072","w":"好","lv":"1","gloss":"good; appropriate; proper","pron":"hǎo"},"w0079":{"id":"w0079","w":"对不起","lv":"1","gloss":"I'm sorry; excuse me; I beg your pardon","pron":"duìbuqǐ"},"w0084":{"id":"w0084","w":"工作","lv":"1","gloss":"to work; (of a machine) to operate","pron":"gōngzuò"},"w0099":{"id":"w0099","w":"是","lv":"1","gloss":"to be (followed by substantives only); correct; right; true","pron":"shì"},"w0108":{"id":"w0108","w":"水","lv":"1","gloss":"water; river","pron":"shuǐ"},"w0112":{"id":"w0112","w":"没关系","lv":"1","gloss":"it doesn't matter","pron":"méiguānxi"},"w0133":{"id":"w0133","w":"茶","lv":"1","gloss":"tea; tea plant","pron":"chá"},"w0141":{"id":"w0141","w":"谢谢","lv":"1","gloss":"to thank; thanks","pron":"xièxie"},"x0001":{"id":"x0001","w":"杯","lv":"1","gloss":"cup; glass (measure word for drinks)","pron":"bēi","bonus":true}},"concepts":{"tea":["w0133"],"water":["w0108"],"three":["w0003"],"four":["w0060"],"five":["w0020"],"hello":["w0028","w0072"],"thanks":["w0141"],"sorry":["w0079"],"bye":["w0034"]},"groups":{"courtesies":["hello","thanks","sorry"],"drinks":["tea","water"],"numbers_3_5":["three","four","five"]},"world":{"start":"street","currency":"¥","slotsPerDay":4,"startWallet":20,"foodPerDay":5,"rentPerWeek":50,"places":{"street":{"links":["noodle_shop"]},"noodle_shop":{"links":["street"]}},"npcs":{"wang":{"place":"street"},"cook":{"place":"noodle_shop"}},"mentor":{"npc":"wang","after":"street-hello"}},"scenes":[{"id":"noodle-intro","place":"noodle_shop","npc":"cook","stage":1,"after":["street-hello"],"requires":{},"repeatable":false,"trustGain":1,"exchanges":[{"id":"greet","slots":{},"expect":{"action":"greet"},"hinges":[],"pay":0,"missCost":0,"variants":{"":{"npc":{"text":"你好!","tokens":[{"start":0,"end":1,"word":"w0028"},{"start":1,"end":2,"word":"w0072"}],"meaning":"Hello!"},"reply":{"text":"你好!","tokens":[{"start":0,"end":1,"word":"w0028"},{"start":1,"end":2,"word":"w0072"}],"meaning":"Hello!"}}}},{"id":"job","slots":{},"expect":{"action":"accept"},"hinges":[],"pay":0,"missCost":0,"variants":{"":{"npc":{"text":"工作,好吗?","tokens":[{"start":0,"end":2,"word":"w0084"},{"start":3,"end":4,"word":"w0072"},{"start":4,"end":5,"word":"w0051"}],"meaning":"Want to work here?"},"reply":{"text":"好。","tokens":[{"start":0,"end":1,"word":"w0072"}],"meaning":"OK."}}}}]},{"id":"noodle-shift","place":"noodle_shop","npc":"cook","stage":1,"after":["noodle-intro"],"requires":{"trust":{"cook":1}},"repeatable":true,"trustGain":1,"exchanges":[{"id":"drink","slots":{"item":"drinks"},"expect":{"action":"fetch","item":"$item"},"hinges":["$item"],"pay":1,"missCost":1,"variants":{"item=tea":{"npc":{"text":"茶。","tokens":[{"start":0,"end":1,"word":"w0133"}],"meaning":"Tea."},"reply":{"text":"好,茶。","tokens":[{"start":0,"end":1,"word":"w0072"},{"start":2,"end":3,"word":"w0133"}],"meaning":"OK, tea."}},"item=water":{"npc":{"text":"水。","tokens":[{"start":0,"end":1,"word":"w0108"}],"meaning":"Water."},"reply":{"text":"好,水。","tokens":[{"start":0,"end":1,"word":"w0072"},{"start":2,"end":3,"word":"w0108"}],"meaning":"OK, water."}}}},{"id":"order","slots":{"item":"drinks","count":"numbers_3_5"},"expect":{"action":"serve","item":"$item","count":"$count"},"hinges":["$item","$count"],"pay":3,"missCost":2,"variants":{"count=three|item=tea":{"npc":{"text":"三杯茶。","tokens":[{"start":0,"end":1,"word":"w0003"},{"start":1,"end":2,"word":"x0001"},{"start":2,"end":3,"word":"w0133"}],"meaning":"Three cups of tea."},"reply":{"text":"好,三杯茶。","tokens":[{"start":0,"end":1,"word":"w0072"},{"start":2,"end":3,"word":"w0003"},{"start":3,"end":4,"word":"x0001"},{"start":4,"end":5,"word":"w0133"}],"meaning":"OK, three cups of tea."},"rephrase":{"text":"茶。三杯。","tokens":[{"start":0,"end":1,"word":"w0133"},{"start":2,"end":3,"word":"w0003"},{"start":3,"end":4,"word":"x0001"}],"meaning":"Tea. Three cups."}},"count=three|item=water":{"npc":{"text":"三杯水。","tokens":[{"start":0,"end":1,"word":"w0003"},{"start":1,"end":2,"word":"x0001"},{"start":2,"end":3,"word":"w0108"}],"meaning":"Three cups of water."},"reply":{"text":"好,三杯水。","tokens":[{"start":0,"end":1,"word":"w0072"},{"start":2,"end":3,"word":"w0003"},{"start":3,"end":4,"word":"x0001"},{"start":4,"end":5,"word":"w0108"}],"meaning":"OK, three cups of water."},"rephrase":{"text":"水。三杯。","tokens":[{"start":0,"end":1,"word":"w0108"},{"start":2,"end":3,"word":"w0003"},{"start":3,"end":4,"word":"x0001"}],"meaning":"Water. Three cups."}},"count=four|item=tea":{"npc":{"text":"四杯茶。","tokens":[{"start":0,"end":1,"word":"w0060"},{"start":1,"end":2,"word":"x0001"},{"start":2,"end":3,"word":"w0133"}],"meaning":"Four cups of tea."},"reply":{"text":"好,四杯茶。","tokens":[{"start":0,"end":1,"word":"w0072"},{"start":2,"end":3,"word":"w0060"},{"start":3,"end":4,"word":"x0001"},{"start":4,"end":5,"word":"w0133"}],"meaning":"OK, four cups of tea."},"rephrase":{"text":"茶。四杯。","tokens":[{"start":0,"end":1,"word":"w0133"},{"start":2,"end":3,"word":"w0060"},{"start":3,"end":4,"word":"x0001"}],"meaning":"Tea. Four cups."}},"count=four|item=water":{"npc":{"text":"四杯水。","tokens":[{"start":0,"end":1,"word":"w0060"},{"start":1,"end":2,"word":"x0001"},{"start":2,"end":3,"word":"w0108"}],"meaning":"Four cups of water."},"reply":{"text":"好,四杯水。","tokens":[{"start":0,"end":1,"word":"w0072"},{"start":2,"end":3,"word":"w0060"},{"start":3,"end":4,"word":"x0001"},{"start":4,"end":5,"word":"w0108"}],"meaning":"OK, four cups of water."},"rephrase":{"text":"水。四杯。","tokens":[{"start":0,"end":1,"word":"w0108"},{"start":2,"end":3,"word":"w0060"},{"start":3,"end":4,"word":"x0001"}],"meaning":"Water. Four cups."}},"count=five|item=tea":{"npc":{"text":"五杯茶。","tokens":[{"start":0,"end":1,"word":"w0020"},{"start":1,"end":2,"word":"x0001"},{"start":2,"end":3,"word":"w0133"}],"meaning":"Five cups of tea."},"reply":{"text":"好,五杯茶。","tokens":[{"start":0,"end":1,"word":"w0072"},{"start":2,"end":3,"word":"w0020"},{"start":3,"end":4,"word":"x0001"},{"start":4,"end":5,"word":"w0133"}],"meaning":"OK, five cups of tea."},"rephrase":{"text":"茶。五杯。","tokens":[{"start":0,"end":1,"word":"w0133"},{"start":2,"end":3,"word":"w0020"},{"start":3,"end":4,"word":"x0001"}],"meaning":"Tea. Five cups."}},"count=five|item=water":{"npc":{"text":"五杯水。","tokens":[{"start":0,"end":1,"word":"w0020"},{"start":1,"end":2,"word":"x0001"},{"start":2,"end":3,"word":"w0108"}],"meaning":"Five cups of water."},"reply":{"text":"好,五杯水。","tokens":[{"start":0,"end":1,"word":"w0072"},{"start":2,"end":3,"word":"w0020"},{"start":3,"end":4,"word":"x0001"},{"start":4,"end":5,"word":"w0108"}],"meaning":"OK, five cups of water."},"rephrase":{"text":"水。五杯。","tokens":[{"start":0,"end":1,"word":"w0108"},{"start":2,"end":3,"word":"w0020"},{"start":3,"end":4,"word":"x0001"}],"meaning":"Water. Five cups."}}}}]},{"id":"street-hello","place":"street","npc":"wang","stage":1,"after":[],"requires":{},"repeatable":false,"trustGain":1,"exchanges":[{"id":"hello","slots":{},"expect":{"action":"greet"},"hinges":["hello"],"pay":0,"missCost":0,"variants":{"":{"npc":{"text":"你好!","tokens":[{"start":0,"end":1,"word":"w0028"},{"start":1,"end":2,"word":"w0072"}],"meaning":"Hello!"},"reply":{"text":"你好!","tokens":[{"start":0,"end":1,"word":"w0028"},{"start":1,"end":2,"word":"w0072"}],"meaning":"Hello!"}}}},{"id":"polite","slots":{"said":"courtesies"},"expect":{"action":"answer","said":"$said"},"hinges":["$said"],"pay":0,"missCost":0,"variants":{"said=hello":{"npc":{"text":"你好!","tokens":[{"start":0,"end":1,"word":"w0028"},{"start":1,"end":2,"word":"w0072"}],"meaning":"Hello!"},"reply":{"text":"你好!","tokens":[{"start":0,"end":1,"word":"w0028"},{"start":1,"end":2,"word":"w0072"}],"meaning":"Hello!"}},"said=thanks":{"npc":{"text":"谢谢!","tokens":[{"start":0,"end":2,"word":"w0141"}],"meaning":"Thank you!"},"reply":{"text":"不客气!","tokens":[{"start":0,"end":3,"word":"w0010"}],"meaning":"You're welcome!"}},"said=sorry":{"npc":{"text":"对不起!","tokens":[{"start":0,"end":3,"word":"w0079"}],"meaning":"Sorry!"},"reply":{"text":"没关系!","tokens":[{"start":0,"end":3,"word":"w0112"}],"meaning":"That's all right!"}}}},{"id":"polite-again","slots":{"said":"courtesies"},"expect":{"action":"answer","said":"$said"},"hinges":["$said"],"pay":0,"missCost":0,"variants":{"said=hello":{"npc":{"text":"你好!","tokens":[{"start":0,"end":1,"word":"w0028"},{"start":1,"end":2,"word":"w0072"}],"meaning":"Hello!"},"reply":{"text":"你好!","tokens":[{"start":0,"end":1,"word":"w0028"},{"start":1,"end":2,"word":"w0072"}],"meaning":"Hello!"}},"said=thanks":{"npc":{"text":"谢谢!","tokens":[{"start":0,"end":2,"word":"w0141"}],"meaning":"Thank you!"},"reply":{"text":"不客气!","tokens":[{"start":0,"end":3,"word":"w0010"}],"meaning":"You're welcome!"}},"said=sorry":{"npc":{"text":"对不起!","tokens":[{"start":0,"end":3,"word":"w0079"}],"meaning":"Sorry!"},"reply":{"text":"没关系!","tokens":[{"start":0,"end":3,"word":"w0112"}],"meaning":"That's all right!"}}}},{"id":"bye","slots":{},"expect":{"action":"farewell"},"hinges":["bye"],"pay":0,"missCost":0,"variants":{"":{"npc":{"text":"再见!","tokens":[{"start":0,"end":2,"word":"w0034"}],"meaning":"Goodbye!"},"reply":{"text":"再见!","tokens":[{"start":0,"end":2,"word":"w0034"}],"meaning":"Goodbye!"}}}}]},{"id":"street-practice","place":"street","npc":"wang","stage":1,"after":["street-hello"],"requires":{},"repeatable":true,"trustGain":1,"exchanges":[{"id":"hello","slots":{},"expect":{"action":"greet"},"hinges":["hello"],"pay":0,"missCost":0,"variants":{"":{"npc":{"text":"你好!","tokens":[{"start":0,"end":1,"word":"w0028"},{"start":1,"end":2,"word":"w0072"}],"meaning":"Hello!"},"reply":{"text":"你好!","tokens":[{"start":0,"end":1,"word":"w0028"},{"start":1,"end":2,"word":"w0072"}],"meaning":"Hello!"}}}},{"id":"polite-1","slots":{"said":"courtesies"},"expect":{"action":"answer","said":"$said"},"hinges":["$said"],"pay":0,"missCost":0,"variants":{"said=hello":{"npc":{"text":"你好!","tokens":[{"start":0,"end":1,"word":"w0028"},{"start":1,"end":2,"word":"w0072"}],"meaning":"Hello!"},"reply":{"text":"你好!","tokens":[{"start":0,"end":1,"word":"w0028"},{"start":1,"end":2,"word":"w0072"}],"meaning":"Hello!"}},"said=thanks":{"npc":{"text":"谢谢!","tokens":[{"start":0,"end":2,"word":"w0141"}],"meaning":"Thank you!"},"reply":{"text":"不客气!","tokens":[{"start":0,"end":3,"word":"w0010"}],"meaning":"You're welcome!"}},"said=sorry":{"npc":{"text":"对不起!","tokens":[{"start":0,"end":3,"word":"w0079"}],"meaning":"Sorry!"},"reply":{"text":"没关系!","tokens":[{"start":0,"end":3,"word":"w0112"}],"meaning":"That's all right!"}}}},{"id":"polite-2","slots":{"said":"courtesies"},"expect":{"action":"answer","said":"$said"},"hinges":["$said"],"pay":0,"missCost":0,"variants":{"said=hello":{"npc":{"text":"你好!","tokens":[{"start":0,"end":1,"word":"w0028"},{"start":1,"end":2,"word":"w0072"}],"meaning":"Hello!"},"reply":{"text":"你好!","tokens":[{"start":0,"end":1,"word":"w0028"},{"start":1,"end":2,"word":"w0072"}],"meaning":"Hello!"}},"said=thanks":{"npc":{"text":"谢谢!","tokens":[{"start":0,"end":2,"word":"w0141"}],"meaning":"Thank you!"},"reply":{"text":"不客气!","tokens":[{"start":0,"end":3,"word":"w0010"}],"meaning":"You're welcome!"}},"said=sorry":{"npc":{"text":"对不起!","tokens":[{"start":0,"end":3,"word":"w0079"}],"meaning":"Sorry!"},"reply":{"text":"没关系!","tokens":[{"start":0,"end":3,"word":"w0112"}],"meaning":"That's all right!"}}}},{"id":"bye","slots":{},"expect":{"action":"farewell"},"hinges":["bye"],"pay":0,"missCost":0,"variants":{"":{"npc":{"text":"再见!","tokens":[{"start":0,"end":2,"word":"w0034"}],"meaning":"Goodbye!"},"reply":{"text":"再见!","tokens":[{"start":0,"end":2,"word":"w0034"}],"meaning":"Goodbye!"}}}}]}],"reactions":{"wrong-generic":{"text":"不是。","tokens":[{"start":0,"end":1,"word":"w0009"},{"start":1,"end":2,"word":"w0099"}],"meaning":"No."},"wrong-count":{"text":"几杯?","tokens":[{"start":0,"end":1,"word":"w0037"},{"start":1,"end":2,"word":"x0001"}],"meaning":"How many cups?"}},"learnerFtl":"# Text-game UI. Every id here is required by packages/tui (UI_KEYS).\n\nhud = Day { $day } · slot { $slot }/{ $slots } · { $currency }{ $wallet } · { $rank }{ $rentLate ->\n [yes] { \" · rent due\" }\n *[no] {\"\"}\n}\n\nrank-0 = Pidgin\nrank-1 = Getting By\nrank-2 = Conversational\nrank-3 = Fluent\nrank-4 = Silver Tongue\n\nmenu-title = What now?\nmenu-talk = Talk to { $npc }: { $scene }\nmenu-go = Go to { $place }\nmenu-mentor = Ask { $npc } about the language\ncost-slot = { \" · 1 slot\" }\nmenu-sleep = Sleep (end the day)\nmenu-quit = Save and quit\n\nkeys-explore = [{ $keys }] choose · [n] notebook · [q] quit\nkeys-pick = [{ $keys }] reply · [w] word help · [n] notebook\nkeys-tiles = [{ $keys }] add tile · [⌫] undo · [enter] say it · [w] word help\nkeys-help = [{ $keys }] look up · [esc] back\nkeys-help-sentence = [{ $keys }] look up · [s] whole sentence · [esc] back\n\nhelp-title = Which word?\nhelp-sentence = The whole sentence\nhelp-in-replies = In the replies:\nreply-title = Your reply:\ntiles-answer = You say:\nyou = You\n\nmismatch = That's not what they asked for.\nrephrased = (slower)\nwallet-change = { $sign }{ $currency }{ $amount } ({ $reason })\nreason-wages = wages\nreason-mixup = mix-up\nreason-food = food\nreason-rent = rent\ntrust-up = { $npc } trusts you a little more ({ $trust }).\nscene-done = { $earned ->\n [0] Done.\n *[other] Done. You earned { $currency }{ $earned }.\n}\nunlocked = New: { $scene }\nrank-up = You're now: { $rank }\nday-ended = Day { $day } is over. You sleep.\nreject-unknown-scene = There's nobody here for that.\nreject-in-scene = Finish the conversation first.\nreject-wrong-place = They're not here.\nreject-locked = They're not ready to talk about that yet.\nreject-no-slots = You're out of time today. Sleep first.\nreject-stale-run = That conversation can't continue. Start it again.\nreject-no-pick = Choose a reply with the number keys.\nreject-bad-choice = There's no reply with that number.\nreject-no-tiles = Build your reply from the tiles.\nreject-bad-tile = There's no tile with that number.\nreject-not-linked = You can't get there from here.\nreject-unknown-word = That word isn't in the dictionary.\nnotice-read-only = Your progress can't be saved on this computer, so this session won't be kept.\nnotice-bad-save = Your save couldn't be read. It was kept as a backup and a new game started.\n\nresume-title = Your games:\nresume-item = Day { $day } · { $place } · { $currency }{ $wallet } · { $done ->\n [one] 1 scene done\n *[other] { $done } scenes done\n} · last played { $date }\nresume-ask = Which one? (number, or enter to cancel)\nresume-none = No saved games yet, so here's a new one.\nreject-no-mentor = There's nobody here to explain things.\nnote-hint = { $npc } seems to have something to tell you.\nmentor-nothing = { $npc } has nothing new to explain today.\n\nkeys-notebook = [↑↓] scroll · [esc] back\nnotebook-progress = Stage { $stage }: { $known } of { $total } words known · { $heard } heard\nnotebook-empty = Nothing yet. Words you hear are written down here.\nnotebook-elsewhere = Heard elsewhere\nnotebook-notes = Notes\nexport-none = There's no saved game to export yet.\nimport-bad = That line isn't a saved game for this course ({ $reason }).\nimport-done = Added: { $game }. Run the game to continue it.\n\n# The story. intro-1, intro-2, … open a new game ($currency, $wallet, $rent are available).\nintro-1 = Your phone is dead, your wallet is gone, and the bus that brought you here left an hour ago.\nintro-2 = You're standing on a street in a Chinese city with { $currency }{ $wallet } in your pocket. You know nobody, and nobody here speaks your language.\nintro-3 = Food costs money. Rent is { $currency }{ $rent }, due at the end of the week. To earn anything you'll need work, and for work you'll need words.\nintro-4 = On a bench by the road, an old man is watching you with open curiosity. Maybe start there.\n\nplace-street = Main Street\nplace-street-desc = Bikes, steam and shouting. An old man sits on a bench by the road. Across it, a noodle shop glows.\nplace-noodle_shop = Noodle Shop\nplace-noodle_shop-desc = Steam everywhere. The cook is shouting orders at nobody in particular.\n\nnpc-wang = Old Wang\nnpc-cook = Cook\n\n# Scene names for the menu; scene-<id>-start and scene-<id>-end are optional narration.\nscene-street-hello = Say hello\nscene-street-hello-start = The old man pats the bench beside him. \"Wang,\" he says, pointing at himself. He seems to have decided you need lessons: he says something, you answer. Use [w] when you're stuck.\nscene-street-hello-end = Old Wang beams. He points across the road at the noodle shop, mimes slurping noodles, then rubs his fingers together. Work. Money.\n\nscene-street-practice = Practise greetings\nscene-street-practice-start = Old Wang shuffles over to make room. Another lesson, then.\nscene-street-practice-end = Old Wang nods, satisfied, and goes back to watching the street.\n\nscene-noodle-intro = Ask about work\nscene-noodle-intro-start = The cook looks you up and down, wiping her hands on her apron.\nscene-noodle-intro-end = She throws you an apron. You have a job, sort of.\n\nscene-noodle-shift = Work a shift\nscene-noodle-shift-start = Orders fly across the counter. Keep up.\nscene-noodle-shift-end = The rush dies down. The cook counts coins into your hand.\n\n# What a reply does (action-<name>), and on a mix-up what was asked (asked-<name>).\n# Concept values arrive as learner names: $item = \"tea\", $count = \"three\".\naction-fetch = You bring { $item }.\nasked-fetch = They wanted { $item }.\naction-serve = You set down { $count } cups of { $item }.\nasked-serve = They wanted { $count } cups of { $item }.\n\n# Usage notes Old Wang explains (languages/zh/notes.json). note-<id>-title and note-<id>.\n\nnote-bukeqi-title = 不客气, \"you're welcome\"\nnote-bukeqi = 不客气 (bùkèqi) literally means \"don't be polite\": no need to stand on ceremony. It's the usual answer to 谢谢. Between close friends, people often skip 谢谢 altogether; thanking a friend too much can sound distant.\n\nnote-hao-ma-title = 好 and 吗\nnote-hao-ma = 好 (hǎo) means \"good\", and on its own it's how you agree: \"OK\". Put 吗 (ma) at the end of a sentence and it becomes a yes-or-no question: 工作,好吗? is \"Work, OK?\" To say yes, answer with the key word: 好。\n\nnote-bei-title = 杯, a measure word\nnote-bei = Chinese doesn't say \"three teas\". A measure word goes between a number and a thing: 三杯茶 is \"three cups of tea\". 杯 (bēi) is the one for cups and glasses. There are many measure words; 个 (gè) is the everyday all-purpose one.\n","conceptNames":{"tea":"tea","water":"water","three":"three","four":"four","five":"five","hello":"Hello","thanks":"Thank you","sorry":"Sorry","bye":"Goodbye"},"stageWords":{"1":["w0001","w0002","w0003","w0004","w0005","w0006","w0007","w0008","w0009","w0010","w0011","w0012","w0013","w0014","w0015","w0016","w0017","w0018","w0019","w0020","w0021","w0022","w0023","w0024","w0025","w0026","w0027","w0028","w0029","w0030","w0031","w0032","w0033","w0034","w0035","w0036","w0037","w0038","w0039","w0040","w0041","w0042","w0043","w0044","w0045","w0046","w0047","w0048","w0049","w0050","w0051","w0052","w0053","w0054","w0055","w0056","w0057","w0058","w0059","w0060","w0061","w0062","w0063","w0064","w0065","w0066","w0067","w0068","w0069","w0070","w0071","w0072","w0073","w0074","w0075","w0076","w0077","w0078","w0079","w0080","w0081","w0082","w0083","w0084","w0085","w0086","w0087","w0088","w0089","w0090","w0091","w0092","w0093","w0094","w0095","w0096","w0097","w0098","w0099","w0100","w0101","w0102","w0103","w0104","w0105","w0106","w0107","w0108","w0109","w0110","w0111","w0112","w0113","w0114","w0115","w0116","w0117","w0118","w0119","w0120","w0121","w0122","w0123","w0124","w0125","w0126","w0127","w0128","w0129","w0130","w0131","w0132","w0133","w0134","w0135","w0136","w0137","w0138","w0139","w0140","w0141","w0142","w0143","w0144","w0145","w0146","w0147","w0148","w0149","w0150"],"2":["w0151","w0152","w0153","w0154","w0155","w0156","w0157","w0158","w0159","w0160","w0161","w0162","w0163","w0164","w0165","w0166","w0167","w0168","w0169","w0170","w0171","w0172","w0173","w0174","w0175","w0176","w0177","w0178","w0179","w0180","w0181","w0182","w0183","w0184","w0185","w0186","w0187","w0188","w0189","w0190","w0191","w0192","w0193","w0194","w0195","w0196","w0197","w0198","w0199","w0200","w0201","w0202","w0203","w0204","w0205","w0206","w0207","w0208","w0209","w0210","w0211","w0212","w0213","w0214","w0215","w0216","w0217","w0218","w0219","w0220","w0221","w0222","w0223","w0224","w0225","w0226","w0227","w0228","w0229","w0230","w0231","w0232","w0233","w0234","w0235","w0236","w0237","w0238","w0239","w0240","w0241","w0242","w0243","w0244","w0245","w0246","w0247","w0248","w0249","w0250","w0251","w0252","w0253","w0254","w0255","w0256","w0257","w0258","w0259","w0260","w0261","w0262","w0263","w0264","w0265","w0266","w0267","w0268","w0269","w0270","w0271","w0272","w0273","w0274","w0275","w0276","w0277","w0278","w0279","w0280","w0281","w0282","w0283","w0284","w0285","w0286","w0287","w0288","w0289","w0290","w0291","w0292","w0293","w0294","w0295","w0296","w0297"],"3":["w0298","w0299","w0300","w0301","w0302","w0303","w0304","w0305","w0306","w0307","w0308","w0309","w0310","w0311","w0312","w0313","w0314","w0315","w0316","w0317","w0318","w0319","w0320","w0321","w0322","w0323","w0324","w0325","w0326","w0327","w0328","w0329","w0330","w0331","w0332","w0333","w0334","w0335","w0336","w0337","w0338","w0339","w0340","w0341","w0342","w0343","w0344","w0345","w0346","w0347","w0348","w0349","w0350","w0351","w0352","w0353","w0354","w0355","w0356","w0357","w0358","w0359","w0360","w0361","w0362","w0363","w0364","w0365","w0366","w0367","w0368","w0369","w0370","w0371","w0372","w0373","w0374","w0375","w0376","w0377","w0378","w0379","w0380","w0381","w0382","w0383","w0384","w0385","w0386","w0387","w0388","w0389","w0390","w0391","w0392","w0393","w0394","w0395","w0396","w0397","w0398","w0399","w0400","w0401","w0402","w0403","w0404","w0405","w0406","w0407","w0408","w0409","w0410","w0411","w0412","w0413","w0414","w0415","w0416","w0417","w0418","w0419","w0420","w0421","w0422","w0423","w0424","w0425","w0426","w0427","w0428","w0429","w0430","w0431","w0432","w0433","w0434","w0435","w0436","w0437","w0438","w0439","w0440","w0441","w0442","w0443","w0444","w0445","w0446","w0447","w0448","w0449","w0450","w0451","w0452","w0453","w0454","w0455","w0456","w0457","w0458","w0459","w0460","w0461","w0462","w0463","w0464","w0465","w0466","w0467","w0468","w0469","w0470","w0471","w0472","w0473","w0474","w0475","w0476","w0477","w0478","w0479","w0480","w0481","w0482","w0483","w0484","w0485","w0486","w0487","w0488","w0489","w0490","w0491","w0492","w0493","w0494","w0495","w0496","w0497","w0498","w0499","w0500","w0501","w0502","w0503","w0504","w0505","w0506","w0507","w0508","w0509","w0510","w0511","w0512","w0513","w0514","w0515","w0516","w0517","w0518","w0519","w0520","w0521","w0522","w0523","w0524","w0525","w0526","w0527","w0528","w0529","w0530","w0531","w0532","w0533","w0534","w0535","w0536","w0537","w0538","w0539","w0540","w0541","w0542","w0543","w0544","w0545","w0546","w0547","w0548","w0549","w0550","w0551","w0552","w0553","w0554","w0555","w0556","w0557","w0558","w0559","w0560","w0561","w0562","w0563","w0564","w0565","w0566","w0567","w0568","w0569","w0570","w0571","w0572","w0573","w0574","w0575","w0576","w0577","w0578","w0579","w0580","w0581","w0582","w0583","w0584","w0585","w0586","w0587","w0588","w0589","w0590","w0591","w0592","w0593","w0594","w0595"],"4":["w0596","w0597","w0598","w0599","w0600","w0601","w0602","w0603","w0604","w0605","w0606","w0607","w0608","w0609","w0610","w0611","w0612","w0613","w0614","w0615","w0616","w0617","w0618","w0619","w0620","w0621","w0622","w0623","w0624","w0625","w0626","w0627","w0628","w0629","w0630","w0631","w0632","w0633","w0634","w0635","w0636","w0637","w0638","w0639","w0640","w0641","w0642","w0643","w0644","w0645","w0646","w0647","w0648","w0649","w0650","w0651","w0652","w0653","w0654","w0655","w0656","w0657","w0658","w0659","w0660","w0661","w0662","w0663","w0664","w0665","w0666","w0667","w0668","w0669","w0670","w0671","w0672","w0673","w0674","w0675","w0676","w0677","w0678","w0679","w0680","w0681","w0682","w0683","w0684","w0685","w0686","w0687","w0688","w0689","w0690","w0691","w0692","w0693","w0694","w0695","w0696","w0697","w0698","w0699","w0700","w0701","w0702","w0703","w0704","w0705","w0706","w0707","w0708","w0709","w0710","w0711","w0712","w0713","w0714","w0715","w0716","w0717","w0718","w0719","w0720","w0721","w0722","w0723","w0724","w0725","w0726","w0727","w0728","w0729","w0730","w0731","w0732","w0733","w0734","w0735","w0736","w0737","w0738","w0739","w0740","w0741","w0742","w0743","w0744","w0745","w0746","w0747","w0748","w0749","w0750","w0751","w0752","w0753","w0754","w0755","w0756","w0757","w0758","w0759","w0760","w0761","w0762","w0763","w0764","w0765","w0766","w0767","w0768","w0769","w0770","w0771","w0772","w0773","w0774","w0775","w0776","w0777","w0778","w0779","w0780","w0781","w0782","w0783","w0784","w0785","w0786","w0787","w0788","w0789","w0790","w0791","w0792","w0793","w0794","w0795","w0796","w0797","w0798","w0799","w0800","w0801","w0802","w0803","w0804","w0805","w0806","w0807","w0808","w0809","w0810","w0811","w0812","w0813","w0814","w0815","w0816","w0817","w0818","w0819","w0820","w0821","w0822","w0823","w0824","w0825","w0826","w0827","w0828","w0829","w0830","w0831","w0832","w0833","w0834","w0835","w0836","w0837","w0838","w0839","w0840","w0841","w0842","w0843","w0844","w0845","w0846","w0847","w0848","w0849","w0850","w0851","w0852","w0853","w0854","w0855","w0856","w0857","w0858","w0859","w0860","w0861","w0862","w0863","w0864","w0865","w0866","w0867","w0868","w0869","w0870","w0871","w0872","w0873","w0874","w0875","w0876","w0877","w0878","w0879","w0880","w0881","w0882","w0883","w0884","w0885","w0886","w0887","w0888","w0889","w0890","w0891","w0892","w0893","w0894","w0895","w0896","w0897","w0898","w0899","w0900","w0901","w0902","w0903","w0904","w0905","w0906","w0907","w0908","w0909","w0910","w0911","w0912","w0913","w0914","w0915","w0916","w0917","w0918","w0919","w0920","w0921","w0922","w0923","w0924","w0925","w0926","w0927","w0928","w0929","w0930","w0931","w0932","w0933","w0934","w0935","w0936","w0937","w0938","w0939","w0940","w0941","w0942","w0943","w0944","w0945","w0946","w0947","w0948","w0949","w0950","w0951","w0952","w0953","w0954","w0955","w0956","w0957","w0958","w0959","w0960","w0961","w0962","w0963","w0964","w0965","w0966","w0967","w0968","w0969","w0970","w0971","w0972","w0973","w0974","w0975","w0976","w0977","w0978","w0979","w0980","w0981","w0982","w0983","w0984","w0985","w0986","w0987","w0988","w0989","w0990","w0991","w0992","w0993","w0994","w0995","w0996","w0997","w0998","w0999","w1000","w1001","w1002","w1003","w1004","w1005","w1006","w1007","w1008","w1009","w1010","w1011","w1012","w1013","w1014","w1015","w1016","w1017","w1018","w1019","w1020","w1021","w1022","w1023","w1024","w1025","w1026","w1027","w1028","w1029","w1030","w1031","w1032","w1033","w1034","w1035","w1036","w1037","w1038","w1039","w1040","w1041","w1042","w1043","w1044","w1045","w1046","w1047","w1048","w1049","w1050","w1051","w1052","w1053","w1054","w1055","w1056","w1057","w1058","w1059","w1060","w1061","w1062","w1063","w1064","w1065","w1066","w1067","w1068","w1069","w1070","w1071","w1072","w1073","w1074","w1075","w1076","w1077","w1078","w1079","w1080","w1081","w1082","w1083","w1084","w1085","w1086","w1087","w1088","w1089","w1090","w1091","w1092","w1093","w1094","w1095","w1096","w1097","w1098","w1099","w1100","w1101","w1102","w1103","w1104","w1105","w1106","w1107","w1108","w1109","w1110","w1111","w1112","w1113","w1114","w1115","w1116","w1117","w1118","w1119","w1120","w1121","w1122","w1123","w1124","w1125","w1126","w1127","w1128","w1129","w1130","w1131","w1132","w1133","w1134","w1135","w1136","w1137","w1138","w1139","w1140","w1141","w1142","w1143","w1144","w1145","w1146","w1147","w1148","w1149","w1150","w1151","w1152","w1153","w1154","w1155","w1156","w1157","w1158","w1159","w1160","w1161","w1162","w1163","w1164","w1165","w1166","w1167","w1168","w1169","w1170","w1171","w1172","w1173","w1174","w1175","w1176","w1177","w1178","w1179","w1180","w1181","w1182","w1183","w1184","w1185","w1186","w1187","w1188","w1189","w1190","w1191","w1192","w1193"]},"notes":[{"id":"bukeqi","trigger":{"scene":"street-hello"}},{"id":"hao-ma","trigger":{"scene":"noodle-intro"}},{"id":"bei","trigger":{"word":"x0001"}}]}
|
|
1
|
+
{"id":"zh-china-en","typing":false,"words":{"w0003":{"id":"w0003","w":"三","lv":"1","gloss":"three; 3","pron":"sān"},"w0009":{"id":"w0009","w":"不","lv":"1","gloss":"no; not","pron":"bù"},"w0010":{"id":"w0010","w":"不客气","lv":"1","gloss":"you're welcome; don't mention it","pron":"bùkèqi"},"w0018":{"id":"w0018","w":"了","lv":"1","gloss":"(completed action marker)","pron":"le"},"w0020":{"id":"w0020","w":"五","lv":"1","gloss":"five; 5","pron":"wǔ"},"w0023":{"id":"w0023","w":"什么","lv":"1","gloss":"what?; something","pron":"shénme"},"w0028":{"id":"w0028","w":"你","lv":"1","gloss":"you (informal)","pron":"nǐ"},"w0034":{"id":"w0034","w":"再见","lv":"1","gloss":"goodbye","pron":"zàijiàn"},"w0037":{"id":"w0037","w":"几","lv":"1","gloss":"how much; how many","pron":"jǐ"},"w0046":{"id":"w0046","w":"叫","lv":"1","gloss":"to shout; to call","pron":"jiào"},"w0047":{"id":"w0047","w":"吃","lv":"1","gloss":"to eat; to consume; to eat at (a cafeteria etc)","pron":"chī"},"w0049":{"id":"w0049","w":"名字","lv":"1","gloss":"name (of a person or thing)","pron":"míngzi"},"w0051":{"id":"w0051","w":"吗","lv":"1","gloss":"(question particle for \"yes-no\" questions)","pron":"ma"},"w0053":{"id":"w0053","w":"呢","lv":"1","gloss":"particle indicating that a previously asked question is…","pron":"ne"},"w0058":{"id":"w0058","w":"喜欢","lv":"1","gloss":"to like; to be fond of","pron":"xǐhuan"},"w0060":{"id":"w0060","w":"四","lv":"1","gloss":"four; 4","pron":"sì"},"w0072":{"id":"w0072","w":"好","lv":"1","gloss":"good; appropriate; proper","pron":"hǎo"},"w0079":{"id":"w0079","w":"对不起","lv":"1","gloss":"I'm sorry; excuse me; I beg your pardon","pron":"duìbuqǐ"},"w0084":{"id":"w0084","w":"工作","lv":"1","gloss":"to work; (of a machine) to operate","pron":"gōngzuò"},"w0087":{"id":"w0087","w":"很","lv":"1","gloss":"(adverb of degree); quite","pron":"hěn"},"w0091":{"id":"w0091","w":"我","lv":"1","gloss":"I; me; my","pron":"wǒ"},"w0099":{"id":"w0099","w":"是","lv":"1","gloss":"to be (followed by substantives only); correct; right; true","pron":"shì"},"w0101":{"id":"w0101","w":"有","lv":"1","gloss":"to have; there is","pron":"yǒu"},"w0102":{"id":"w0102","w":"朋友","lv":"1","gloss":"friend","pron":"péngyou"},"w0108":{"id":"w0108","w":"水","lv":"1","gloss":"water; river","pron":"shuǐ"},"w0112":{"id":"w0112","w":"没关系","lv":"1","gloss":"it doesn't matter","pron":"méiguānxi"},"w0133":{"id":"w0133","w":"茶","lv":"1","gloss":"tea; tea plant","pron":"chá"},"w0141":{"id":"w0141","w":"谢谢","lv":"1","gloss":"to thank; thanks","pron":"xièxie"},"w0143":{"id":"w0143","w":"那","lv":"1","gloss":"(specifier) that; the; those (colloquial pr. [nèi])","pron":"nà"},"w0145":{"id":"w0145","w":"里","lv":"1","gloss":"lining; interior","pron":"lǐ"},"w0149":{"id":"w0149","w":"饭馆","lv":"1","gloss":"restaurant","pron":"fànguǎn"},"w0583":{"id":"w0583","w":"面条","lv":"3","gloss":"noodles","pron":"miàntiáo","bonus":true},"w0587":{"id":"w0587","w":"饿","lv":"3","gloss":"to be hungry; hungry","pron":"è","bonus":true},"x0001":{"id":"x0001","w":"杯","lv":"1","gloss":"cup; glass (measure word for drinks)","pron":"bēi","bonus":true},"x0002":{"id":"x0002","w":"老王","lv":"1","gloss":"Old Wang (a name: 老 \"old\" + the surname Wang)","pron":"Lǎo Wáng","bonus":true},"x0003":{"id":"x0003","w":"小马","lv":"1","gloss":"Xiao Ma, your Chinese name (小 \"little\" + 马 \"horse\")","pron":"Xiǎo Mǎ","bonus":true},"x0004":{"id":"x0004","w":"小张","lv":"1","gloss":"Xiao Zhang, the cook (小 \"little\" + the surname Zhang)","pron":"Xiǎo Zhāng","bonus":true}},"concepts":{"tea":["w0133"],"water":["w0108"],"three":["w0003"],"four":["w0060"],"five":["w0020"],"hello":["w0028","w0072"],"thanks":["w0141"],"sorry":["w0079"],"bye":["w0034"]},"groups":{"courtesies":["hello","thanks","sorry"],"drinks":["tea","water"],"numbers_3_5":["three","four","five"]},"world":{"start":"street","currency":"¥","slotsPerDay":4,"startWallet":20,"foodPerDay":5,"rentPerWeek":50,"places":{"street":{"links":["noodle_shop"]},"noodle_shop":{"links":["street"]}},"npcs":{"wang":{"place":"street"},"cook":{"place":"noodle_shop"}},"mentor":{"npc":"wang","after":"street-hello"}},"scenes":[{"id":"noodle-intro","place":"noodle_shop","npc":"cook","stage":1,"after":["street-hungry"],"requires":{},"repeatable":false,"trustGain":1,"exchanges":[{"id":"greet","slots":{},"expect":{"action":"greet"},"hinges":[],"pay":0,"missCost":0,"variants":{"":{"npc":{"text":"你好!","tokens":[{"start":0,"end":1,"word":"w0028"},{"start":1,"end":2,"word":"w0072"}],"meaning":"Hello!"},"reply":{"text":"你好!你叫什么名字?","tokens":[{"start":0,"end":1,"word":"w0028"},{"start":1,"end":2,"word":"w0072"},{"start":3,"end":4,"word":"w0028"},{"start":4,"end":5,"word":"w0046"},{"start":5,"end":7,"word":"w0023"},{"start":7,"end":9,"word":"w0049"}],"meaning":"Hello! What's your name?"},"alts":[{"text":"再见!","tokens":[{"start":0,"end":2,"word":"w0034"}],"meaning":"Goodbye!"},{"text":"你叫小马。","tokens":[{"start":0,"end":1,"word":"w0028"},{"start":1,"end":2,"word":"w0046"},{"start":2,"end":4,"word":"x0003"}],"meaning":"Your name is Xiao Ma."}]}}},{"id":"names","slots":{},"expect":{"action":"names"},"hinges":[],"pay":0,"missCost":0,"variants":{"":{"npc":{"text":"我叫小张。你呢?","tokens":[{"start":0,"end":1,"word":"w0091"},{"start":1,"end":2,"word":"w0046"},{"start":2,"end":4,"word":"x0004"},{"start":5,"end":6,"word":"w0028"},{"start":6,"end":7,"word":"w0053"}],"meaning":"My name is Xiao Zhang. And you?"},"reply":{"text":"我叫小马。老王是我朋友。","tokens":[{"start":0,"end":1,"word":"w0091"},{"start":1,"end":2,"word":"w0046"},{"start":2,"end":4,"word":"x0003"},{"start":5,"end":7,"word":"x0002"},{"start":7,"end":8,"word":"w0099"},{"start":8,"end":9,"word":"w0091"},{"start":9,"end":11,"word":"w0102"}],"meaning":"My name is Xiao Ma. Old Wang is my friend."},"alts":[{"text":"我叫小张。","tokens":[{"start":0,"end":1,"word":"w0091"},{"start":1,"end":2,"word":"w0046"},{"start":2,"end":4,"word":"x0004"}],"meaning":"My name is Xiao Zhang."},{"text":"你好!","tokens":[{"start":0,"end":1,"word":"w0028"},{"start":1,"end":2,"word":"w0072"}],"meaning":"Hello!"}]}}},{"id":"job","slots":{},"expect":{"action":"job"},"hinges":[],"pay":0,"missCost":0,"variants":{"":{"npc":{"text":"工作,好吗?","tokens":[{"start":0,"end":2,"word":"w0084"},{"start":3,"end":4,"word":"w0072"},{"start":4,"end":5,"word":"w0051"}],"meaning":"Want to work here?"},"reply":{"text":"好。","tokens":[{"start":0,"end":1,"word":"w0072"}],"meaning":"OK."},"alts":[{"text":"再见!","tokens":[{"start":0,"end":2,"word":"w0034"}],"meaning":"Goodbye!"},{"text":"我叫小马。","tokens":[{"start":0,"end":1,"word":"w0091"},{"start":1,"end":2,"word":"w0046"},{"start":2,"end":4,"word":"x0003"}],"meaning":"My name is Xiao Ma."}]}}}]},{"id":"noodle-shift","place":"noodle_shop","npc":"cook","stage":1,"after":["noodle-intro"],"requires":{"trust":{"cook":1}},"repeatable":true,"trustGain":1,"exchanges":[{"id":"drink","slots":{"item":"drinks"},"expect":{"action":"fetch","item":"$item"},"hinges":["$item"],"pay":1,"missCost":1,"variants":{"item=tea":{"npc":{"text":"茶。","tokens":[{"start":0,"end":1,"word":"w0133"}],"meaning":"Tea."},"reply":{"text":"好,茶。","tokens":[{"start":0,"end":1,"word":"w0072"},{"start":2,"end":3,"word":"w0133"}],"meaning":"OK, tea."}},"item=water":{"npc":{"text":"水。","tokens":[{"start":0,"end":1,"word":"w0108"}],"meaning":"Water."},"reply":{"text":"好,水。","tokens":[{"start":0,"end":1,"word":"w0072"},{"start":2,"end":3,"word":"w0108"}],"meaning":"OK, water."}}}},{"id":"order","slots":{"item":"drinks","count":"numbers_3_5"},"expect":{"action":"serve","item":"$item","count":"$count"},"hinges":["$item","$count"],"pay":3,"missCost":2,"variants":{"count=three|item=tea":{"npc":{"text":"三杯茶。","tokens":[{"start":0,"end":1,"word":"w0003"},{"start":1,"end":2,"word":"x0001"},{"start":2,"end":3,"word":"w0133"}],"meaning":"Three cups of tea."},"reply":{"text":"好,三杯茶。","tokens":[{"start":0,"end":1,"word":"w0072"},{"start":2,"end":3,"word":"w0003"},{"start":3,"end":4,"word":"x0001"},{"start":4,"end":5,"word":"w0133"}],"meaning":"OK, three cups of tea."},"rephrase":{"text":"茶。三杯。","tokens":[{"start":0,"end":1,"word":"w0133"},{"start":2,"end":3,"word":"w0003"},{"start":3,"end":4,"word":"x0001"}],"meaning":"Tea. Three cups."}},"count=three|item=water":{"npc":{"text":"三杯水。","tokens":[{"start":0,"end":1,"word":"w0003"},{"start":1,"end":2,"word":"x0001"},{"start":2,"end":3,"word":"w0108"}],"meaning":"Three cups of water."},"reply":{"text":"好,三杯水。","tokens":[{"start":0,"end":1,"word":"w0072"},{"start":2,"end":3,"word":"w0003"},{"start":3,"end":4,"word":"x0001"},{"start":4,"end":5,"word":"w0108"}],"meaning":"OK, three cups of water."},"rephrase":{"text":"水。三杯。","tokens":[{"start":0,"end":1,"word":"w0108"},{"start":2,"end":3,"word":"w0003"},{"start":3,"end":4,"word":"x0001"}],"meaning":"Water. Three cups."}},"count=four|item=tea":{"npc":{"text":"四杯茶。","tokens":[{"start":0,"end":1,"word":"w0060"},{"start":1,"end":2,"word":"x0001"},{"start":2,"end":3,"word":"w0133"}],"meaning":"Four cups of tea."},"reply":{"text":"好,四杯茶。","tokens":[{"start":0,"end":1,"word":"w0072"},{"start":2,"end":3,"word":"w0060"},{"start":3,"end":4,"word":"x0001"},{"start":4,"end":5,"word":"w0133"}],"meaning":"OK, four cups of tea."},"rephrase":{"text":"茶。四杯。","tokens":[{"start":0,"end":1,"word":"w0133"},{"start":2,"end":3,"word":"w0060"},{"start":3,"end":4,"word":"x0001"}],"meaning":"Tea. Four cups."}},"count=four|item=water":{"npc":{"text":"四杯水。","tokens":[{"start":0,"end":1,"word":"w0060"},{"start":1,"end":2,"word":"x0001"},{"start":2,"end":3,"word":"w0108"}],"meaning":"Four cups of water."},"reply":{"text":"好,四杯水。","tokens":[{"start":0,"end":1,"word":"w0072"},{"start":2,"end":3,"word":"w0060"},{"start":3,"end":4,"word":"x0001"},{"start":4,"end":5,"word":"w0108"}],"meaning":"OK, four cups of water."},"rephrase":{"text":"水。四杯。","tokens":[{"start":0,"end":1,"word":"w0108"},{"start":2,"end":3,"word":"w0060"},{"start":3,"end":4,"word":"x0001"}],"meaning":"Water. Four cups."}},"count=five|item=tea":{"npc":{"text":"五杯茶。","tokens":[{"start":0,"end":1,"word":"w0020"},{"start":1,"end":2,"word":"x0001"},{"start":2,"end":3,"word":"w0133"}],"meaning":"Five cups of tea."},"reply":{"text":"好,五杯茶。","tokens":[{"start":0,"end":1,"word":"w0072"},{"start":2,"end":3,"word":"w0020"},{"start":3,"end":4,"word":"x0001"},{"start":4,"end":5,"word":"w0133"}],"meaning":"OK, five cups of tea."},"rephrase":{"text":"茶。五杯。","tokens":[{"start":0,"end":1,"word":"w0133"},{"start":2,"end":3,"word":"w0020"},{"start":3,"end":4,"word":"x0001"}],"meaning":"Tea. Five cups."}},"count=five|item=water":{"npc":{"text":"五杯水。","tokens":[{"start":0,"end":1,"word":"w0020"},{"start":1,"end":2,"word":"x0001"},{"start":2,"end":3,"word":"w0108"}],"meaning":"Five cups of water."},"reply":{"text":"好,五杯水。","tokens":[{"start":0,"end":1,"word":"w0072"},{"start":2,"end":3,"word":"w0020"},{"start":3,"end":4,"word":"x0001"},{"start":4,"end":5,"word":"w0108"}],"meaning":"OK, five cups of water."},"rephrase":{"text":"水。五杯。","tokens":[{"start":0,"end":1,"word":"w0108"},{"start":2,"end":3,"word":"w0020"},{"start":3,"end":4,"word":"x0001"}],"meaning":"Water. Five cups."}}}}]},{"id":"street-hello","place":"street","npc":"wang","stage":1,"after":[],"requires":{},"repeatable":false,"trustGain":1,"exchanges":[{"id":"hello","slots":{},"expect":{"action":"hello"},"hinges":[],"pay":0,"missCost":0,"variants":{"":{"npc":{"text":"你好!","tokens":[{"start":0,"end":1,"word":"w0028"},{"start":1,"end":2,"word":"w0072"}],"meaning":"Hello!"},"reply":{"text":"你好!","tokens":[{"start":0,"end":1,"word":"w0028"},{"start":1,"end":2,"word":"w0072"}],"meaning":"Hello!"},"alts":[{"text":"好!","tokens":[{"start":0,"end":1,"word":"w0072"}],"meaning":"Good!"}]}}},{"id":"wang","slots":{},"expect":{"action":"wang"},"hinges":[],"pay":0,"missCost":0,"variants":{"":{"npc":{"text":"老王!","tokens":[{"start":0,"end":2,"word":"x0002"}],"meaning":"Old Wang!"},"reply":{"text":"你好,老王!","tokens":[{"start":0,"end":1,"word":"w0028"},{"start":1,"end":2,"word":"w0072"},{"start":3,"end":5,"word":"x0002"}],"meaning":"Hello, Old Wang!"},"alts":[{"text":"老王!","tokens":[{"start":0,"end":2,"word":"x0002"}],"meaning":"Old Wang!"},{"text":"好,老王!","tokens":[{"start":0,"end":1,"word":"w0072"},{"start":2,"end":4,"word":"x0002"}],"meaning":"OK, Old Wang!"}]}}},{"id":"how","slots":{},"expect":{"action":"how"},"hinges":[],"pay":0,"missCost":0,"variants":{"":{"npc":{"text":"你好吗?","tokens":[{"start":0,"end":1,"word":"w0028"},{"start":1,"end":2,"word":"w0072"},{"start":2,"end":3,"word":"w0051"}],"meaning":"How are you?"},"reply":{"text":"很好!你好吗?","tokens":[{"start":0,"end":1,"word":"w0087"},{"start":1,"end":2,"word":"w0072"},{"start":3,"end":4,"word":"w0028"},{"start":4,"end":5,"word":"w0072"},{"start":5,"end":6,"word":"w0051"}],"meaning":"Very well! How are you?"},"alts":[{"text":"老王!","tokens":[{"start":0,"end":2,"word":"x0002"}],"meaning":"Old Wang!"},{"text":"你好,老王!","tokens":[{"start":0,"end":1,"word":"w0028"},{"start":1,"end":2,"word":"w0072"},{"start":3,"end":5,"word":"x0002"}],"meaning":"Hello, Old Wang!"}]}}},{"id":"name","slots":{},"expect":{"action":"name"},"hinges":[],"pay":0,"missCost":0,"variants":{"":{"npc":{"text":"很好!叫我老王。","tokens":[{"start":0,"end":1,"word":"w0087"},{"start":1,"end":2,"word":"w0072"},{"start":3,"end":4,"word":"w0046"},{"start":4,"end":5,"word":"w0091"},{"start":5,"end":7,"word":"x0002"}],"meaning":"Very well! Call me Old Wang."},"reply":{"text":"你好,老王!","tokens":[{"start":0,"end":1,"word":"w0028"},{"start":1,"end":2,"word":"w0072"},{"start":3,"end":5,"word":"x0002"}],"meaning":"Hello, Old Wang!"},"alts":[{"text":"我叫老王。","tokens":[{"start":0,"end":1,"word":"w0091"},{"start":1,"end":2,"word":"w0046"},{"start":2,"end":4,"word":"x0002"}],"meaning":"My name is Old Wang."},{"text":"很好!","tokens":[{"start":0,"end":1,"word":"w0087"},{"start":1,"end":2,"word":"w0072"}],"meaning":"Very well!"}]}}},{"id":"naming","slots":{},"expect":{"action":"naming"},"hinges":[],"pay":0,"missCost":0,"variants":{"":{"npc":{"text":"你叫小马!","tokens":[{"start":0,"end":1,"word":"w0028"},{"start":1,"end":2,"word":"w0046"},{"start":2,"end":4,"word":"x0003"}],"meaning":"Your name is Xiao Ma!"},"reply":{"text":"我叫小马?","tokens":[{"start":0,"end":1,"word":"w0091"},{"start":1,"end":2,"word":"w0046"},{"start":2,"end":4,"word":"x0003"}],"meaning":"My name is Xiao Ma?"},"alts":[{"text":"你叫小马!","tokens":[{"start":0,"end":1,"word":"w0028"},{"start":1,"end":2,"word":"w0046"},{"start":2,"end":4,"word":"x0003"}],"meaning":"Your name is Xiao Ma!"},{"text":"我叫老王?","tokens":[{"start":0,"end":1,"word":"w0091"},{"start":1,"end":2,"word":"w0046"},{"start":2,"end":4,"word":"x0002"}],"meaning":"My name is Old Wang?"}]}}},{"id":"ask","slots":{},"expect":{"action":"ask"},"hinges":[],"pay":0,"missCost":0,"variants":{"":{"npc":{"text":"你叫什么名字?","tokens":[{"start":0,"end":1,"word":"w0028"},{"start":1,"end":2,"word":"w0046"},{"start":2,"end":4,"word":"w0023"},{"start":4,"end":6,"word":"w0049"}],"meaning":"What's your name?"},"reply":{"text":"我叫小马。","tokens":[{"start":0,"end":1,"word":"w0091"},{"start":1,"end":2,"word":"w0046"},{"start":2,"end":4,"word":"x0003"}],"meaning":"My name is Xiao Ma."},"alts":[{"text":"我叫老王。","tokens":[{"start":0,"end":1,"word":"w0091"},{"start":1,"end":2,"word":"w0046"},{"start":2,"end":4,"word":"x0002"}],"meaning":"My name is Old Wang."},{"text":"你叫小马。","tokens":[{"start":0,"end":1,"word":"w0028"},{"start":1,"end":2,"word":"w0046"},{"start":2,"end":4,"word":"x0003"}],"meaning":"Your name is Xiao Ma."},{"text":"很好!","tokens":[{"start":0,"end":1,"word":"w0087"},{"start":1,"end":2,"word":"w0072"}],"meaning":"Very well!"}]}}},{"id":"bye","slots":{},"expect":{"action":"bye"},"hinges":[],"pay":0,"missCost":0,"variants":{"":{"npc":{"text":"再见!","tokens":[{"start":0,"end":2,"word":"w0034"}],"meaning":"Goodbye!"},"reply":{"text":"再见!","tokens":[{"start":0,"end":2,"word":"w0034"}],"meaning":"Goodbye!"},"alts":[{"text":"你好!","tokens":[{"start":0,"end":1,"word":"w0028"},{"start":1,"end":2,"word":"w0072"}],"meaning":"Hello!"},{"text":"很好!","tokens":[{"start":0,"end":1,"word":"w0087"},{"start":1,"end":2,"word":"w0072"}],"meaning":"Very well!"}]}}}]},{"id":"street-hungry","place":"street","npc":"wang","stage":1,"after":["street-hello"],"requires":{},"repeatable":false,"trustGain":1,"exchanges":[{"id":"hungry","slots":{},"expect":{"action":"hungry"},"hinges":[],"pay":0,"missCost":0,"variants":{"":{"npc":{"text":"小马,你好吗?","tokens":[{"start":0,"end":2,"word":"x0003"},{"start":3,"end":4,"word":"w0028"},{"start":4,"end":5,"word":"w0072"},{"start":5,"end":6,"word":"w0051"}],"meaning":"Xiao Ma, how are you?"},"reply":{"text":"我饿了。","tokens":[{"start":0,"end":1,"word":"w0091"},{"start":1,"end":2,"word":"w0587"},{"start":2,"end":3,"word":"w0018"}],"meaning":"I'm hungry."},"alts":[{"text":"我叫小马。","tokens":[{"start":0,"end":1,"word":"w0091"},{"start":1,"end":2,"word":"w0046"},{"start":2,"end":4,"word":"x0003"}],"meaning":"My name is Xiao Ma."},{"text":"再见!","tokens":[{"start":0,"end":2,"word":"w0034"}],"meaning":"Goodbye!"}]}}},{"id":"noodles","slots":{},"expect":{"action":"noodles"},"hinges":[],"pay":0,"missCost":0,"variants":{"":{"npc":{"text":"吃面条吗?","tokens":[{"start":0,"end":1,"word":"w0047"},{"start":1,"end":3,"word":"w0583"},{"start":3,"end":4,"word":"w0051"}],"meaning":"Want some noodles?"},"reply":{"text":"好!","tokens":[{"start":0,"end":1,"word":"w0072"}],"meaning":"Yes, please!"},"alts":[{"text":"我叫小马。","tokens":[{"start":0,"end":1,"word":"w0091"},{"start":1,"end":2,"word":"w0046"},{"start":2,"end":4,"word":"x0003"}],"meaning":"My name is Xiao Ma."},{"text":"再见!","tokens":[{"start":0,"end":2,"word":"w0034"}],"meaning":"Goodbye!"}]}}},{"id":"like","slots":{},"expect":{"action":"like"},"hinges":[],"pay":0,"missCost":0,"variants":{"":{"npc":{"text":"你喜欢面条吗?","tokens":[{"start":0,"end":1,"word":"w0028"},{"start":1,"end":3,"word":"w0058"},{"start":3,"end":5,"word":"w0583"},{"start":5,"end":6,"word":"w0051"}],"meaning":"Do you like noodles?"},"reply":{"text":"喜欢!我喜欢面条。","tokens":[{"start":0,"end":2,"word":"w0058"},{"start":3,"end":4,"word":"w0091"},{"start":4,"end":6,"word":"w0058"},{"start":6,"end":8,"word":"w0583"}],"meaning":"I do! I like noodles."},"alts":[{"text":"我叫小马。","tokens":[{"start":0,"end":1,"word":"w0091"},{"start":1,"end":2,"word":"w0046"},{"start":2,"end":4,"word":"x0003"}],"meaning":"My name is Xiao Ma."},{"text":"再见,老王!","tokens":[{"start":0,"end":2,"word":"w0034"},{"start":3,"end":5,"word":"x0002"}],"meaning":"Goodbye, Old Wang!"}]}}},{"id":"there","slots":{},"expect":{"action":"there"},"hinges":[],"pay":0,"missCost":0,"variants":{"":{"npc":{"text":"那里!","tokens":[{"start":0,"end":1,"word":"w0143"},{"start":1,"end":2,"word":"w0145"}],"meaning":"There!"},"reply":{"text":"那里?","tokens":[{"start":0,"end":1,"word":"w0143"},{"start":1,"end":2,"word":"w0145"}],"meaning":"There?"},"alts":[{"text":"再见!","tokens":[{"start":0,"end":2,"word":"w0034"}],"meaning":"Goodbye!"},{"text":"我叫小马。","tokens":[{"start":0,"end":1,"word":"w0091"},{"start":1,"end":2,"word":"w0046"},{"start":2,"end":4,"word":"x0003"}],"meaning":"My name is Xiao Ma."}]}}},{"id":"shop","slots":{},"expect":{"action":"shop"},"hinges":[],"pay":0,"missCost":0,"variants":{"":{"npc":{"text":"那里有饭馆。","tokens":[{"start":0,"end":1,"word":"w0143"},{"start":1,"end":2,"word":"w0145"},{"start":2,"end":3,"word":"w0101"},{"start":3,"end":5,"word":"w0149"}],"meaning":"There's a restaurant there."},"reply":{"text":"好!","tokens":[{"start":0,"end":1,"word":"w0072"}],"meaning":"Good!"},"alts":[{"text":"再见!","tokens":[{"start":0,"end":2,"word":"w0034"}],"meaning":"Goodbye!"},{"text":"你叫小马。","tokens":[{"start":0,"end":1,"word":"w0028"},{"start":1,"end":2,"word":"w0046"},{"start":2,"end":4,"word":"x0003"}],"meaning":"Your name is Xiao Ma."}]}}},{"id":"zhang","slots":{},"expect":{"action":"zhang"},"hinges":[],"pay":0,"missCost":0,"variants":{"":{"npc":{"text":"小张!","tokens":[{"start":0,"end":2,"word":"x0004"}],"meaning":"Xiao Zhang!"},"reply":{"text":"小张?","tokens":[{"start":0,"end":2,"word":"x0004"}],"meaning":"Xiao Zhang?"},"alts":[{"text":"老王?","tokens":[{"start":0,"end":2,"word":"x0002"}],"meaning":"Old Wang?"},{"text":"小马?","tokens":[{"start":0,"end":2,"word":"x0003"}],"meaning":"Xiao Ma?"}]}}},{"id":"friend","slots":{},"expect":{"action":"friend"},"hinges":[],"pay":0,"missCost":0,"variants":{"":{"npc":{"text":"小张是我朋友。","tokens":[{"start":0,"end":2,"word":"x0004"},{"start":2,"end":3,"word":"w0099"},{"start":3,"end":4,"word":"w0091"},{"start":4,"end":6,"word":"w0102"}],"meaning":"Xiao Zhang is my friend."},"reply":{"text":"好!","tokens":[{"start":0,"end":1,"word":"w0072"}],"meaning":"Good!"},"alts":[{"text":"再见!","tokens":[{"start":0,"end":2,"word":"w0034"}],"meaning":"Goodbye!"},{"text":"你叫小马。","tokens":[{"start":0,"end":1,"word":"w0028"},{"start":1,"end":2,"word":"w0046"},{"start":2,"end":4,"word":"x0003"}],"meaning":"Your name is Xiao Ma."}]}}},{"id":"bye","slots":{},"expect":{"action":"bye"},"hinges":[],"pay":0,"missCost":0,"variants":{"":{"npc":{"text":"再见,小马!","tokens":[{"start":0,"end":2,"word":"w0034"},{"start":3,"end":5,"word":"x0003"}],"meaning":"Goodbye, Xiao Ma!"},"reply":{"text":"谢谢!再见!","tokens":[{"start":0,"end":2,"word":"w0141"},{"start":3,"end":5,"word":"w0034"}],"meaning":"Thank you! Goodbye!"},"alts":[{"text":"你好,老王!","tokens":[{"start":0,"end":1,"word":"w0028"},{"start":1,"end":2,"word":"w0072"},{"start":3,"end":5,"word":"x0002"}],"meaning":"Hello, Old Wang!"},{"text":"我叫小马。","tokens":[{"start":0,"end":1,"word":"w0091"},{"start":1,"end":2,"word":"w0046"},{"start":2,"end":4,"word":"x0003"}],"meaning":"My name is Xiao Ma."}]}}}]},{"id":"street-practice","place":"street","npc":"wang","stage":1,"after":["street-hello"],"requires":{},"repeatable":true,"trustGain":1,"exchanges":[{"id":"hello","slots":{},"expect":{"action":"greet"},"hinges":["hello"],"pay":0,"missCost":0,"variants":{"":{"npc":{"text":"你好!","tokens":[{"start":0,"end":1,"word":"w0028"},{"start":1,"end":2,"word":"w0072"}],"meaning":"Hello!"},"reply":{"text":"你好!","tokens":[{"start":0,"end":1,"word":"w0028"},{"start":1,"end":2,"word":"w0072"}],"meaning":"Hello!"}}}},{"id":"polite-1","slots":{"said":"courtesies"},"expect":{"action":"answer","said":"$said"},"hinges":["$said"],"pay":0,"missCost":0,"variants":{"said=hello":{"npc":{"text":"你好!","tokens":[{"start":0,"end":1,"word":"w0028"},{"start":1,"end":2,"word":"w0072"}],"meaning":"Hello!"},"reply":{"text":"你好!","tokens":[{"start":0,"end":1,"word":"w0028"},{"start":1,"end":2,"word":"w0072"}],"meaning":"Hello!"}},"said=thanks":{"npc":{"text":"谢谢!","tokens":[{"start":0,"end":2,"word":"w0141"}],"meaning":"Thank you!"},"reply":{"text":"不客气!","tokens":[{"start":0,"end":3,"word":"w0010"}],"meaning":"You're welcome!"}},"said=sorry":{"npc":{"text":"对不起!","tokens":[{"start":0,"end":3,"word":"w0079"}],"meaning":"Sorry!"},"reply":{"text":"没关系!","tokens":[{"start":0,"end":3,"word":"w0112"}],"meaning":"That's all right!"}}}},{"id":"polite-2","slots":{"said":"courtesies"},"expect":{"action":"answer","said":"$said"},"hinges":["$said"],"pay":0,"missCost":0,"variants":{"said=hello":{"npc":{"text":"你好!","tokens":[{"start":0,"end":1,"word":"w0028"},{"start":1,"end":2,"word":"w0072"}],"meaning":"Hello!"},"reply":{"text":"你好!","tokens":[{"start":0,"end":1,"word":"w0028"},{"start":1,"end":2,"word":"w0072"}],"meaning":"Hello!"}},"said=thanks":{"npc":{"text":"谢谢!","tokens":[{"start":0,"end":2,"word":"w0141"}],"meaning":"Thank you!"},"reply":{"text":"不客气!","tokens":[{"start":0,"end":3,"word":"w0010"}],"meaning":"You're welcome!"}},"said=sorry":{"npc":{"text":"对不起!","tokens":[{"start":0,"end":3,"word":"w0079"}],"meaning":"Sorry!"},"reply":{"text":"没关系!","tokens":[{"start":0,"end":3,"word":"w0112"}],"meaning":"That's all right!"}}}},{"id":"bye","slots":{},"expect":{"action":"farewell"},"hinges":["bye"],"pay":0,"missCost":0,"variants":{"":{"npc":{"text":"再见!","tokens":[{"start":0,"end":2,"word":"w0034"}],"meaning":"Goodbye!"},"reply":{"text":"再见!","tokens":[{"start":0,"end":2,"word":"w0034"}],"meaning":"Goodbye!"}}}}]}],"reactions":{"wrong-generic":{"text":"不是。","tokens":[{"start":0,"end":1,"word":"w0009"},{"start":1,"end":2,"word":"w0099"}],"meaning":"No."},"wrong-count":{"text":"几杯?","tokens":[{"start":0,"end":1,"word":"w0037"},{"start":1,"end":2,"word":"x0001"}],"meaning":"How many cups?"}},"learnerFtl":"# Text-game UI. Every id here is required by packages/tui (UI_KEYS).\n\nhud = Day { $day } · slot { $slot }/{ $slots } · { $currency }{ $wallet } · { $rank }{ $rentLate ->\n [yes] { \" · rent due\" }\n *[no] {\"\"}\n}\n\nrank-0 = Pidgin\nrank-1 = Getting By\nrank-2 = Conversational\nrank-3 = Fluent\nrank-4 = Silver Tongue\n\nmenu-title = What now?\nmenu-talk = Talk to { $npc }: { $scene }\nmenu-go = Go to { $place }\nmenu-mentor = Ask { $npc } about the language\ncost-slot = { \" · 1 slot\" }\nmenu-sleep = Sleep (end the day)\nmenu-quit = Save and quit\n\nkeys-explore = [{ $keys }] choose · [n] notebook · [q] quit\nkeys-pick = [{ $keys }] reply · [w] word help · [n] notebook\nkeys-tiles = [{ $keys }] add · [⌫] undo · [enter] say · [w] help · [n] notebook\nkeys-help = [{ $keys }] look up · [esc] back\nkeys-help-sentence = [{ $keys }] look up · [s] whole sentence · [esc] back\n\nhelp-title = Which word?\nhelp-sentence = The whole sentence\nhelp-in-replies = In the replies:\nreply-title = Your reply:\ntiles-title = Your reply: tap the words in order, then press [enter].\ntiles-answer = You say:\nyou = You\n\nmismatch = That's not what they asked for.\nrephrased = (slower)\nwallet-change = { $sign }{ $currency }{ $amount } ({ $reason })\nreason-wages = wages\nreason-mixup = mix-up\nreason-food = food\nreason-rent = rent\ntrust-up = { $npc } trusts you a little more ({ $trust }).\nscene-done = { $earned ->\n [0] Done.\n *[other] Done. You earned { $currency }{ $earned }.\n}\nunlocked = New: { $scene }\nrank-up = You're now: { $rank }\nday-ended = Day { $day } is over. You sleep.\nreject-unknown-scene = There's nobody here for that.\nreject-in-scene = Finish the conversation first.\nreject-wrong-place = They're not here.\nreject-locked = They're not ready to talk about that yet.\nreject-no-slots = You're out of time today. Sleep first.\nreject-stale-run = That conversation can't continue. Start it again.\nreject-no-pick = Choose a reply with the number keys.\nreject-bad-choice = There's no reply with that number.\nreject-no-tiles = Build your reply from the tiles.\nreject-bad-tile = There's no tile with that number.\nreject-not-linked = You can't get there from here.\nreject-unknown-word = That word isn't in the dictionary.\nnotice-read-only = Your progress can't be saved on this computer, so this session won't be kept.\nnotice-bad-save = Your save couldn't be read. It was kept as a backup and a new game started.\n\nresume-title = Your games:\nresume-item = Day { $day } · { $place } · { $currency }{ $wallet } · { $done ->\n [one] 1 scene done\n *[other] { $done } scenes done\n} · last played { $date }\nresume-ask = Which one? (number, or enter to cancel)\nresume-none = No saved games yet, so here's a new one.\nreject-no-mentor = There's nobody here to explain things.\nnote-hint = { $npc } seems to have something to tell you.\nmentor-nothing = { $npc } has nothing new to explain today.\n\nkeys-notebook = [↑↓] scroll · [esc] back\nnotebook-progress = Stage { $stage }: { $known } of { $total } words known · { $heard } heard\nnotebook-empty = Nothing yet. Words you hear are written down here.\nnotebook-elsewhere = Heard elsewhere\nnotebook-notes = Notes\nexport-none = There's no saved game to export yet.\nimport-bad = That line isn't a saved game for this course ({ $reason }).\nimport-done = Added: { $game }. Run the game to continue it.\n\n# Browser page controls (packages/tui-web).\nweb-new = New game\nweb-games = Games\nweb-games-none = No saved games yet.\nweb-export = Export\nweb-export-hint = This line is your whole game. Paste it into Import on another device, or into the terminal game with: npx silver-tongue --import -\nweb-copy = Copy\nweb-copied = Copied\nweb-import = Import\nweb-import-hint = Paste a line from Export, here or from the terminal game. It is added as a new game; nothing is overwritten.\nweb-import-go = Add game\nweb-close = Close\nweb-saved = Your game is saved. You can close this tab, or keep playing.\n\n# The story. intro-1, intro-2, … open a new game ($currency, $wallet, $rent are available).\nintro-1 = Your phone is dead, your wallet is gone, and the bus that brought you here left an hour ago.\nintro-2 = You're standing on a street in a Chinese city with { $currency }{ $wallet } in your pocket. You know nobody, and nobody here speaks your language.\nintro-3 = Food costs money. Rent is { $currency }{ $rent }, due at the end of the week. To earn anything you'll need work, and for work you'll need words.\nintro-4 = On a bench by the road, an old man is watching you with open curiosity. Maybe start there.\n\nplace-street = Main Street\nplace-street-desc = Bikes, steam and shouting. An old man sits on a bench by the road. Across it, a noodle shop glows.\nplace-noodle_shop = Noodle Shop\nplace-noodle_shop-desc = Steam everywhere. The cook is shouting orders at nobody in particular.\n\nnpc-wang = Old Wang\nnpc-cook = Cook\n\n# Scene names for the menu; scene-<id>-start and scene-<id>-end are optional narration.\nscene-street-hello = Meet Old Wang\nscene-street-hello-start = The old man pats the bench beside him and points at himself. He seems to have decided you need lessons: he says something, you answer. Stuck? Press [w] to look words up, and [s] there to see what the whole sentence means.\nscene-street-hello-end = Old Wang looks delighted with his new student. You have a Chinese name now: 小马, Xiǎo Mǎ, \"little horse\". You didn't get a vote.\n\nscene-street-hungry = Talk about food\nscene-street-hungry-start = Your stomach growls, loudly. Old Wang raises an eyebrow.\nscene-street-hungry-end = Old Wang points at the noodle shop, then at you, then mimes rolling up his sleeves. Work first, noodles after.\n\nscene-street-practice = Practise greetings\nscene-street-practice-start = Old Wang shuffles over to make room. Another lesson, then.\nscene-street-practice-end = Old Wang nods, satisfied, and goes back to watching the street.\n\nscene-noodle-intro = Ask about work\nscene-noodle-intro-start = The cook looks you up and down, wiping her hands on her apron. So this is Old Wang's latest project.\nscene-noodle-intro-end = She throws you an apron. You have a job, sort of.\n\nscene-noodle-shift = Work a shift\nscene-noodle-shift-start = Orders fly across the counter. Keep up.\nscene-noodle-shift-end = The rush dies down. The cook counts coins into your hand.\n\n# What a reply does (action-<name>), and on a mix-up what was asked (asked-<name>).\n# Concept values arrive as learner names: $item = \"tea\", $count = \"three\".\naction-fetch = You bring { $item }.\nasked-fetch = They wanted { $item }.\naction-serve = You set down { $count } cups of { $item }.\nasked-serve = They wanted { $count } cups of { $item }.\n\n# Usage notes Old Wang explains (languages/zh/notes.json). note-<id>-title and note-<id>.\n\nnote-hao-ma-title = 好 and 吗\nnote-hao-ma = 好 (hǎo) means \"good\", and on its own it's how you agree: \"OK\". Put 吗 (ma) at the end of a sentence and it becomes a yes-or-no question: 你好吗? is \"How are you?\", literally \"You good?\" The usual answer is 很好 (hěn hǎo), \"very good\"; the 很 is there mostly for rhythm.\n\nnote-lao-xiao-title = 老王 and 小张\nnote-lao-xiao = Friends and neighbours your own age are often called by surname with 老 (lǎo, \"old\") or 小 (xiǎo, \"little\") in front: 老王 for someone a bit older, 小张 for someone younger. From a young stranger to an old man it would be too familiar; you'd say 王大爷, \"Grandpa Wang\". Old Wang told you to call him 老王, which is his way of saying you're friends now. Calling you 小马 says the same.\n\nnote-le-title = 了 for a change\nnote-le = 了 (le) after a word often marks a change: 我饿了 is \"I've got hungry\", \"I'm hungry now\". You'll also hear it in 太好了!, \"great!\", literally \"too good!\"\n\nnote-bukeqi-title = 谢谢 and 不客气\nnote-bukeqi = 谢谢 (xièxie) is \"thank you\". The usual answer is 不客气 (bùkèqi), \"you're welcome\"; it literally means \"don't be polite\": no need to stand on ceremony. Between close friends, people often skip 谢谢 altogether; thanking a friend too much can sound distant.\n\nnote-bei-title = 杯, a measure word\nnote-bei = Chinese doesn't say \"three teas\". A measure word goes between a number and a thing: 三杯茶 is \"three cups of tea\". 杯 (bēi) is the one for cups and glasses. There are many measure words; 个 (gè) is the everyday all-purpose one.\n","conceptNames":{"tea":"tea","water":"water","three":"three","four":"four","five":"five","hello":"Hello","thanks":"Thank you","sorry":"Sorry","bye":"Goodbye"},"stageWords":{"1":["w0001","w0002","w0003","w0004","w0005","w0006","w0007","w0008","w0009","w0010","w0011","w0012","w0013","w0014","w0015","w0016","w0017","w0018","w0019","w0020","w0021","w0022","w0023","w0024","w0025","w0026","w0027","w0028","w0029","w0030","w0031","w0032","w0033","w0034","w0035","w0036","w0037","w0038","w0039","w0040","w0041","w0042","w0043","w0044","w0045","w0046","w0047","w0048","w0049","w0050","w0051","w0052","w0053","w0054","w0055","w0056","w0057","w0058","w0059","w0060","w0061","w0062","w0063","w0064","w0065","w0066","w0067","w0068","w0069","w0070","w0071","w0072","w0073","w0074","w0075","w0076","w0077","w0078","w0079","w0080","w0081","w0082","w0083","w0084","w0085","w0086","w0087","w0088","w0089","w0090","w0091","w0092","w0093","w0094","w0095","w0096","w0097","w0098","w0099","w0100","w0101","w0102","w0103","w0104","w0105","w0106","w0107","w0108","w0109","w0110","w0111","w0112","w0113","w0114","w0115","w0116","w0117","w0118","w0119","w0120","w0121","w0122","w0123","w0124","w0125","w0126","w0127","w0128","w0129","w0130","w0131","w0132","w0133","w0134","w0135","w0136","w0137","w0138","w0139","w0140","w0141","w0142","w0143","w0144","w0145","w0146","w0147","w0148","w0149","w0150"],"2":["w0151","w0152","w0153","w0154","w0155","w0156","w0157","w0158","w0159","w0160","w0161","w0162","w0163","w0164","w0165","w0166","w0167","w0168","w0169","w0170","w0171","w0172","w0173","w0174","w0175","w0176","w0177","w0178","w0179","w0180","w0181","w0182","w0183","w0184","w0185","w0186","w0187","w0188","w0189","w0190","w0191","w0192","w0193","w0194","w0195","w0196","w0197","w0198","w0199","w0200","w0201","w0202","w0203","w0204","w0205","w0206","w0207","w0208","w0209","w0210","w0211","w0212","w0213","w0214","w0215","w0216","w0217","w0218","w0219","w0220","w0221","w0222","w0223","w0224","w0225","w0226","w0227","w0228","w0229","w0230","w0231","w0232","w0233","w0234","w0235","w0236","w0237","w0238","w0239","w0240","w0241","w0242","w0243","w0244","w0245","w0246","w0247","w0248","w0249","w0250","w0251","w0252","w0253","w0254","w0255","w0256","w0257","w0258","w0259","w0260","w0261","w0262","w0263","w0264","w0265","w0266","w0267","w0268","w0269","w0270","w0271","w0272","w0273","w0274","w0275","w0276","w0277","w0278","w0279","w0280","w0281","w0282","w0283","w0284","w0285","w0286","w0287","w0288","w0289","w0290","w0291","w0292","w0293","w0294","w0295","w0296","w0297"],"3":["w0298","w0299","w0300","w0301","w0302","w0303","w0304","w0305","w0306","w0307","w0308","w0309","w0310","w0311","w0312","w0313","w0314","w0315","w0316","w0317","w0318","w0319","w0320","w0321","w0322","w0323","w0324","w0325","w0326","w0327","w0328","w0329","w0330","w0331","w0332","w0333","w0334","w0335","w0336","w0337","w0338","w0339","w0340","w0341","w0342","w0343","w0344","w0345","w0346","w0347","w0348","w0349","w0350","w0351","w0352","w0353","w0354","w0355","w0356","w0357","w0358","w0359","w0360","w0361","w0362","w0363","w0364","w0365","w0366","w0367","w0368","w0369","w0370","w0371","w0372","w0373","w0374","w0375","w0376","w0377","w0378","w0379","w0380","w0381","w0382","w0383","w0384","w0385","w0386","w0387","w0388","w0389","w0390","w0391","w0392","w0393","w0394","w0395","w0396","w0397","w0398","w0399","w0400","w0401","w0402","w0403","w0404","w0405","w0406","w0407","w0408","w0409","w0410","w0411","w0412","w0413","w0414","w0415","w0416","w0417","w0418","w0419","w0420","w0421","w0422","w0423","w0424","w0425","w0426","w0427","w0428","w0429","w0430","w0431","w0432","w0433","w0434","w0435","w0436","w0437","w0438","w0439","w0440","w0441","w0442","w0443","w0444","w0445","w0446","w0447","w0448","w0449","w0450","w0451","w0452","w0453","w0454","w0455","w0456","w0457","w0458","w0459","w0460","w0461","w0462","w0463","w0464","w0465","w0466","w0467","w0468","w0469","w0470","w0471","w0472","w0473","w0474","w0475","w0476","w0477","w0478","w0479","w0480","w0481","w0482","w0483","w0484","w0485","w0486","w0487","w0488","w0489","w0490","w0491","w0492","w0493","w0494","w0495","w0496","w0497","w0498","w0499","w0500","w0501","w0502","w0503","w0504","w0505","w0506","w0507","w0508","w0509","w0510","w0511","w0512","w0513","w0514","w0515","w0516","w0517","w0518","w0519","w0520","w0521","w0522","w0523","w0524","w0525","w0526","w0527","w0528","w0529","w0530","w0531","w0532","w0533","w0534","w0535","w0536","w0537","w0538","w0539","w0540","w0541","w0542","w0543","w0544","w0545","w0546","w0547","w0548","w0549","w0550","w0551","w0552","w0553","w0554","w0555","w0556","w0557","w0558","w0559","w0560","w0561","w0562","w0563","w0564","w0565","w0566","w0567","w0568","w0569","w0570","w0571","w0572","w0573","w0574","w0575","w0576","w0577","w0578","w0579","w0580","w0581","w0582","w0584","w0585","w0586","w0588","w0589","w0590","w0591","w0592","w0593","w0594","w0595"],"4":["w0596","w0597","w0598","w0599","w0600","w0601","w0602","w0603","w0604","w0605","w0606","w0607","w0608","w0609","w0610","w0611","w0612","w0613","w0614","w0615","w0616","w0617","w0618","w0619","w0620","w0621","w0622","w0623","w0624","w0625","w0626","w0627","w0628","w0629","w0630","w0631","w0632","w0633","w0634","w0635","w0636","w0637","w0638","w0639","w0640","w0641","w0642","w0643","w0644","w0645","w0646","w0647","w0648","w0649","w0650","w0651","w0652","w0653","w0654","w0655","w0656","w0657","w0658","w0659","w0660","w0661","w0662","w0663","w0664","w0665","w0666","w0667","w0668","w0669","w0670","w0671","w0672","w0673","w0674","w0675","w0676","w0677","w0678","w0679","w0680","w0681","w0682","w0683","w0684","w0685","w0686","w0687","w0688","w0689","w0690","w0691","w0692","w0693","w0694","w0695","w0696","w0697","w0698","w0699","w0700","w0701","w0702","w0703","w0704","w0705","w0706","w0707","w0708","w0709","w0710","w0711","w0712","w0713","w0714","w0715","w0716","w0717","w0718","w0719","w0720","w0721","w0722","w0723","w0724","w0725","w0726","w0727","w0728","w0729","w0730","w0731","w0732","w0733","w0734","w0735","w0736","w0737","w0738","w0739","w0740","w0741","w0742","w0743","w0744","w0745","w0746","w0747","w0748","w0749","w0750","w0751","w0752","w0753","w0754","w0755","w0756","w0757","w0758","w0759","w0760","w0761","w0762","w0763","w0764","w0765","w0766","w0767","w0768","w0769","w0770","w0771","w0772","w0773","w0774","w0775","w0776","w0777","w0778","w0779","w0780","w0781","w0782","w0783","w0784","w0785","w0786","w0787","w0788","w0789","w0790","w0791","w0792","w0793","w0794","w0795","w0796","w0797","w0798","w0799","w0800","w0801","w0802","w0803","w0804","w0805","w0806","w0807","w0808","w0809","w0810","w0811","w0812","w0813","w0814","w0815","w0816","w0817","w0818","w0819","w0820","w0821","w0822","w0823","w0824","w0825","w0826","w0827","w0828","w0829","w0830","w0831","w0832","w0833","w0834","w0835","w0836","w0837","w0838","w0839","w0840","w0841","w0842","w0843","w0844","w0845","w0846","w0847","w0848","w0849","w0850","w0851","w0852","w0853","w0854","w0855","w0856","w0857","w0858","w0859","w0860","w0861","w0862","w0863","w0864","w0865","w0866","w0867","w0868","w0869","w0870","w0871","w0872","w0873","w0874","w0875","w0876","w0877","w0878","w0879","w0880","w0881","w0882","w0883","w0884","w0885","w0886","w0887","w0888","w0889","w0890","w0891","w0892","w0893","w0894","w0895","w0896","w0897","w0898","w0899","w0900","w0901","w0902","w0903","w0904","w0905","w0906","w0907","w0908","w0909","w0910","w0911","w0912","w0913","w0914","w0915","w0916","w0917","w0918","w0919","w0920","w0921","w0922","w0923","w0924","w0925","w0926","w0927","w0928","w0929","w0930","w0931","w0932","w0933","w0934","w0935","w0936","w0937","w0938","w0939","w0940","w0941","w0942","w0943","w0944","w0945","w0946","w0947","w0948","w0949","w0950","w0951","w0952","w0953","w0954","w0955","w0956","w0957","w0958","w0959","w0960","w0961","w0962","w0963","w0964","w0965","w0966","w0967","w0968","w0969","w0970","w0971","w0972","w0973","w0974","w0975","w0976","w0977","w0978","w0979","w0980","w0981","w0982","w0983","w0984","w0985","w0986","w0987","w0988","w0989","w0990","w0991","w0992","w0993","w0994","w0995","w0996","w0997","w0998","w0999","w1000","w1001","w1002","w1003","w1004","w1005","w1006","w1007","w1008","w1009","w1010","w1011","w1012","w1013","w1014","w1015","w1016","w1017","w1018","w1019","w1020","w1021","w1022","w1023","w1024","w1025","w1026","w1027","w1028","w1029","w1030","w1031","w1032","w1033","w1034","w1035","w1036","w1037","w1038","w1039","w1040","w1041","w1042","w1043","w1044","w1045","w1046","w1047","w1048","w1049","w1050","w1051","w1052","w1053","w1054","w1055","w1056","w1057","w1058","w1059","w1060","w1061","w1062","w1063","w1064","w1065","w1066","w1067","w1068","w1069","w1070","w1071","w1072","w1073","w1074","w1075","w1076","w1077","w1078","w1079","w1080","w1081","w1082","w1083","w1084","w1085","w1086","w1087","w1088","w1089","w1090","w1091","w1092","w1093","w1094","w1095","w1096","w1097","w1098","w1099","w1100","w1101","w1102","w1103","w1104","w1105","w1106","w1107","w1108","w1109","w1110","w1111","w1112","w1113","w1114","w1115","w1116","w1117","w1118","w1119","w1120","w1121","w1122","w1123","w1124","w1125","w1126","w1127","w1128","w1129","w1130","w1131","w1132","w1133","w1134","w1135","w1136","w1137","w1138","w1139","w1140","w1141","w1142","w1143","w1144","w1145","w1146","w1147","w1148","w1149","w1150","w1151","w1152","w1153","w1154","w1155","w1156","w1157","w1158","w1159","w1160","w1161","w1162","w1163","w1164","w1165","w1166","w1167","w1168","w1169","w1170","w1171","w1172","w1173","w1174","w1175","w1176","w1177","w1178","w1179","w1180","w1181","w1182","w1183","w1184","w1185","w1186","w1187","w1188","w1189","w1190","w1191","w1192","w1193"]},"notes":[{"id":"hao-ma","trigger":{"scene":"street-hello"}},{"id":"lao-xiao","trigger":{"word":"x0004"}},{"id":"le","trigger":{"scene":"street-hungry"}},{"id":"bukeqi","trigger":{"word":"w0141"}},{"id":"bei","trigger":{"word":"x0001"}}]}
|
package/dist/silver-tongue.mjs
CHANGED
|
@@ -447,7 +447,7 @@ var require_bundle = __commonJS({
|
|
|
447
447
|
* @param locales - Used to instantiate `Intl` formatters used by translations.
|
|
448
448
|
* @param options - Optional configuration for the bundle.
|
|
449
449
|
*/
|
|
450
|
-
constructor(locales, { functions, useIsolating = true, transform = (v) => v } = {}) {
|
|
450
|
+
constructor(locales, { functions, useIsolating = true, transform: transform2 = (v) => v } = {}) {
|
|
451
451
|
this._terms = /* @__PURE__ */ new Map();
|
|
452
452
|
this._messages = /* @__PURE__ */ new Map();
|
|
453
453
|
this.locales = Array.isArray(locales) ? locales : [locales];
|
|
@@ -457,7 +457,7 @@ var require_bundle = __commonJS({
|
|
|
457
457
|
...functions
|
|
458
458
|
};
|
|
459
459
|
this._useIsolating = useIsolating;
|
|
460
|
-
this._transform =
|
|
460
|
+
this._transform = transform2;
|
|
461
461
|
this._intls = getMemoizerForLocale(locales);
|
|
462
462
|
}
|
|
463
463
|
/**
|
|
@@ -1159,6 +1159,8 @@ function speak(ctx, npc, line) {
|
|
|
1159
1159
|
ctx.ev.push({ type: "lineSpoken", npc, line });
|
|
1160
1160
|
hear(ctx, line);
|
|
1161
1161
|
}
|
|
1162
|
+
var altKey = (n) => `alt:${n}`;
|
|
1163
|
+
var altIndex = (key) => /^alt:\d+$/.test(key) ? Number(key.slice(4)) : void 0;
|
|
1162
1164
|
function pickOptions(ctx, ex, combo) {
|
|
1163
1165
|
const rightKey = comboKey(combo);
|
|
1164
1166
|
const texts = /* @__PURE__ */ new Set([ex.variants[rightKey].reply.text]);
|
|
@@ -1173,21 +1175,33 @@ function pickOptions(ctx, ex, combo) {
|
|
|
1173
1175
|
wrong.push(key);
|
|
1174
1176
|
}
|
|
1175
1177
|
}
|
|
1178
|
+
const alts = ex.variants[rightKey].alts ?? [];
|
|
1179
|
+
for (const n of shuffle(alts.map((_, i) => i), ctx.rng)) {
|
|
1180
|
+
if (wrong.length === 3) break;
|
|
1181
|
+
if (!texts.has(alts[n].text)) {
|
|
1182
|
+
texts.add(alts[n].text);
|
|
1183
|
+
wrong.push(altKey(n));
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1176
1186
|
return shuffle([rightKey, ...wrong], ctx.rng);
|
|
1177
1187
|
}
|
|
1178
1188
|
function buildTiles(ctx, ex, combo) {
|
|
1179
1189
|
const rightKey = comboKey(combo);
|
|
1180
1190
|
const pieces2 = tilePieces(ex.variants[rightKey].reply);
|
|
1181
1191
|
const extra = /* @__PURE__ */ new Set();
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
for (const p of tilePieces(
|
|
1192
|
+
const others = shuffle(Object.keys(ex.variants), ctx.rng).filter((k) => k !== rightKey).map((k) => ex.variants[k].reply);
|
|
1193
|
+
for (const l of [...others, ...shuffle(ex.variants[rightKey].alts ?? [], ctx.rng)]) {
|
|
1194
|
+
for (const p of tilePieces(l)) if (!pieces2.includes(p)) extra.add(p);
|
|
1185
1195
|
if (extra.size >= 2) break;
|
|
1186
1196
|
}
|
|
1187
1197
|
return shuffle([...pieces2, ...[...extra].slice(0, 2)], ctx.rng);
|
|
1188
1198
|
}
|
|
1199
|
+
function optionLine(ex, run, key) {
|
|
1200
|
+
const n = altIndex(key);
|
|
1201
|
+
return n === void 0 ? ex.variants[key].reply : ex.variants[comboKey(run.combo)].alts[n];
|
|
1202
|
+
}
|
|
1189
1203
|
function optionsEvent(ex, run) {
|
|
1190
|
-
return run.mode === "pick" ? { type: "replyOptions", mode: "pick", options: run.options.map((k) => ex
|
|
1204
|
+
return run.mode === "pick" ? { type: "replyOptions", mode: "pick", options: run.options.map((k) => optionLine(ex, run, k)) } : { type: "replyOptions", mode: "tiles", tiles: run.tiles };
|
|
1191
1205
|
}
|
|
1192
1206
|
function emitOptions(ctx, ex) {
|
|
1193
1207
|
ctx.ev.push(optionsEvent(ex, ctx.state.run));
|
|
@@ -1209,7 +1223,13 @@ function beginExchange(ctx, scene, index) {
|
|
|
1209
1223
|
const ex = scene.exchanges[index];
|
|
1210
1224
|
const combo = chooseCombo(ctx, ex);
|
|
1211
1225
|
speak(ctx, scene.npc, ex.variants[comboKey(combo)].npc);
|
|
1212
|
-
const
|
|
1226
|
+
const v = ex.variants[comboKey(combo)];
|
|
1227
|
+
const modeWords = [.../* @__PURE__ */ new Set([...hingeWords(ctx, ex, combo), ...v.reply.tokens.map((t) => t.word)])];
|
|
1228
|
+
const states = modeWords.map((w) => {
|
|
1229
|
+
const rec = ctx.state.words[w];
|
|
1230
|
+
const st = wordState(rec, ctx.now);
|
|
1231
|
+
return st === "shaky" && rec && rec.right === 0 ? "met" : st;
|
|
1232
|
+
});
|
|
1213
1233
|
const mode = replyModeFor(states, ctx.course.typing);
|
|
1214
1234
|
run.exchange = index;
|
|
1215
1235
|
run.combo = combo;
|
|
@@ -1227,12 +1247,12 @@ function finishScene(ctx, scene) {
|
|
|
1227
1247
|
ctx.ev.push(...changeWallet(ctx.state, run.earned, "wages"));
|
|
1228
1248
|
ctx.ev.push(...addTrust(ctx.state, scene.npc, scene.trustGain + (run.mixups === 0 ? 1 : 0)));
|
|
1229
1249
|
}
|
|
1230
|
-
function resolve(ctx, scene, ex, chosen, diff, tilesWrong = false) {
|
|
1250
|
+
function resolve(ctx, scene, ex, chosen, diff, tilesWrong = false, other = false, saidWords = []) {
|
|
1231
1251
|
const run = ctx.state.run;
|
|
1232
|
-
const matched = diff.length === 0 && !tilesWrong;
|
|
1252
|
+
const matched = diff.length === 0 && !tilesWrong && !other;
|
|
1233
1253
|
ctx.ev.push({
|
|
1234
1254
|
type: "actionPerformed",
|
|
1235
|
-
action: resolveParams(ex.expect, chosen),
|
|
1255
|
+
action: other ? { action: "other" } : resolveParams(ex.expect, chosen),
|
|
1236
1256
|
expected: resolveParams(ex.expect, run.combo),
|
|
1237
1257
|
matched,
|
|
1238
1258
|
diff,
|
|
@@ -1240,18 +1260,31 @@ function resolve(ctx, scene, ex, chosen, diff, tilesWrong = false) {
|
|
|
1240
1260
|
});
|
|
1241
1261
|
const hinges = hingeWords(ctx, ex, run.combo);
|
|
1242
1262
|
if (matched) {
|
|
1243
|
-
|
|
1263
|
+
const reply3 = ex.variants[comboKey(run.combo)].reply;
|
|
1264
|
+
for (const w of /* @__PURE__ */ new Set([...hinges, ...reply3.tokens.map((t) => t.word)])) setWord(ctx, w, recordRight);
|
|
1265
|
+
for (const t of reply3.tokens) {
|
|
1266
|
+
const rec = ctx.state.words[t.word];
|
|
1267
|
+
if (!rec.first) rec.first = { line: reply3.text, place: ctx.state.place };
|
|
1268
|
+
}
|
|
1244
1269
|
run.earned += ex.pay;
|
|
1245
1270
|
if (run.exchange + 1 < scene.exchanges.length) beginExchange(ctx, scene, run.exchange + 1);
|
|
1246
1271
|
else finishScene(ctx, scene);
|
|
1247
1272
|
return;
|
|
1248
1273
|
}
|
|
1249
|
-
|
|
1274
|
+
const said = new Set(saidWords);
|
|
1275
|
+
const reply2 = ex.variants[comboKey(run.combo)].reply;
|
|
1276
|
+
const missed = reply2.tokens.map((t) => t.word).filter((w) => !said.has(w));
|
|
1277
|
+
for (const w of /* @__PURE__ */ new Set([...hinges, ...missed])) setWord(ctx, w, recordWrong);
|
|
1250
1278
|
run.misses += 1;
|
|
1251
1279
|
run.mixups += 1;
|
|
1252
1280
|
ctx.ev.push(...changeWallet(ctx.state, -ex.missCost, "mixup"));
|
|
1253
1281
|
const reaction = diff.map((d) => `wrong-${d}`).find((r) => ctx.course.reactions[r]) ?? "wrong-generic";
|
|
1254
1282
|
ctx.ev.push({ type: "npcReacted", npc: scene.npc, reaction, line: ctx.course.reactions[reaction] });
|
|
1283
|
+
if (run.mode === "tiles" && run.misses >= 2) {
|
|
1284
|
+
run.mode = "pick";
|
|
1285
|
+
run.options = pickOptions(ctx, ex, run.combo);
|
|
1286
|
+
run.tiles = [];
|
|
1287
|
+
}
|
|
1255
1288
|
if (run.misses >= 2) {
|
|
1256
1289
|
const v = ex.variants[comboKey(run.combo)];
|
|
1257
1290
|
const line = v.rephrase ?? v.npc;
|
|
@@ -1260,6 +1293,9 @@ function resolve(ctx, scene, ex, chosen, diff, tilesWrong = false) {
|
|
|
1260
1293
|
}
|
|
1261
1294
|
emitOptions(ctx, ex);
|
|
1262
1295
|
}
|
|
1296
|
+
function resolveOther(ctx, scene, ex, said) {
|
|
1297
|
+
resolve(ctx, scene, ex, ctx.state.run.combo, [], false, true, said.tokens.map((t) => t.word));
|
|
1298
|
+
}
|
|
1263
1299
|
function startScene(ctx, id) {
|
|
1264
1300
|
const scene = sceneById(ctx, id);
|
|
1265
1301
|
if (!scene) return reject(ctx, "unknown-scene");
|
|
@@ -1302,8 +1338,10 @@ function reply(ctx, choice) {
|
|
|
1302
1338
|
if (!cur || !run || run.mode !== "pick") return reject(ctx, "no-pick");
|
|
1303
1339
|
const key = run.options[choice];
|
|
1304
1340
|
if (key === void 0) return reject(ctx, "bad-choice");
|
|
1341
|
+
if (altIndex(key) !== void 0) return resolveOther(ctx, cur.scene, cur.ex, optionLine(cur.ex, run, key));
|
|
1305
1342
|
const chosen = parseComboKey(key);
|
|
1306
|
-
|
|
1343
|
+
const said = cur.ex.variants[key].reply.tokens.map((t) => t.word);
|
|
1344
|
+
resolve(ctx, cur.scene, cur.ex, chosen, actionDiff(cur.ex, chosen, run.combo), false, false, said);
|
|
1307
1345
|
}
|
|
1308
1346
|
function replyTiles(ctx, tiles) {
|
|
1309
1347
|
const run = ctx.state.run;
|
|
@@ -1311,9 +1349,11 @@ function replyTiles(ctx, tiles) {
|
|
|
1311
1349
|
const cur = current(ctx);
|
|
1312
1350
|
if (!cur || !run || run.mode !== "tiles") return reject(ctx, "no-tiles");
|
|
1313
1351
|
if (tiles.some((i) => run.tiles[i] === void 0)) return reject(ctx, "bad-tile");
|
|
1314
|
-
const
|
|
1315
|
-
const
|
|
1316
|
-
|
|
1352
|
+
const chosen = tiles.map((i) => run.tiles[i]);
|
|
1353
|
+
const reply2 = cur.ex.variants[comboKey(run.combo)].reply;
|
|
1354
|
+
const target = tilePieces(reply2).join("");
|
|
1355
|
+
const said = reply2.tokens.filter((t) => chosen.includes(reply2.text.slice(t.start, t.end))).map((t) => t.word);
|
|
1356
|
+
resolve(ctx, cur.scene, cur.ex, run.combo, [], chosen.join("") !== target, false, said);
|
|
1317
1357
|
}
|
|
1318
1358
|
|
|
1319
1359
|
// ../core/src/save.ts
|
|
@@ -1351,7 +1391,12 @@ function runFits(run, course2) {
|
|
|
1351
1391
|
const ex = course2.scenes.find((s) => s.id === run.scene)?.exchanges[run.exchange];
|
|
1352
1392
|
const v = ex?.variants[comboKey(run.combo)];
|
|
1353
1393
|
if (!ex || !v) return false;
|
|
1354
|
-
if (run.mode === "pick")
|
|
1394
|
+
if (run.mode === "pick") {
|
|
1395
|
+
return run.options.every((k) => {
|
|
1396
|
+
const n = altIndex(k);
|
|
1397
|
+
return n === void 0 ? !!ex.variants[k] : !!v.alts?.[n];
|
|
1398
|
+
});
|
|
1399
|
+
}
|
|
1355
1400
|
return tilesCover(run.tiles, tilePieces(v.reply));
|
|
1356
1401
|
}
|
|
1357
1402
|
function parseSave(raw, course2) {
|
|
@@ -1468,6 +1513,20 @@ function createCore(course2, initial, deps) {
|
|
|
1468
1513
|
};
|
|
1469
1514
|
}
|
|
1470
1515
|
|
|
1516
|
+
// ../tui/src/ansi.ts
|
|
1517
|
+
var COLORS = { red: 31, green: 32, yellow: 33, magenta: 35, cyan: 36 };
|
|
1518
|
+
function toAnsi(line) {
|
|
1519
|
+
return line.map((s) => {
|
|
1520
|
+
const codes = [
|
|
1521
|
+
...s.bold ? [1] : [],
|
|
1522
|
+
...s.dim ? [2] : [],
|
|
1523
|
+
...s.underline ? [4] : [],
|
|
1524
|
+
...s.color ? [COLORS[s.color]] : []
|
|
1525
|
+
];
|
|
1526
|
+
return codes.length ? `\x1B[${codes.join(";")}m${s.text}\x1B[0m` : s.text;
|
|
1527
|
+
}).join("");
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1471
1530
|
// ../tui/src/width.ts
|
|
1472
1531
|
var isControl = (cp) => cp < 32 || cp >= 127 && cp < 160;
|
|
1473
1532
|
function charWidth(cp) {
|
|
@@ -1593,6 +1652,7 @@ var UI_KEYS = {
|
|
|
1593
1652
|
"help-in-replies": [],
|
|
1594
1653
|
"help-title": [],
|
|
1595
1654
|
"reply-title": [],
|
|
1655
|
+
"tiles-title": [],
|
|
1596
1656
|
"tiles-answer": [],
|
|
1597
1657
|
you: [],
|
|
1598
1658
|
mismatch: [],
|
|
@@ -1613,6 +1673,11 @@ var UI_KEYS = {
|
|
|
1613
1673
|
"export-none": [],
|
|
1614
1674
|
"import-bad": ["reason"],
|
|
1615
1675
|
"import-done": ["game"],
|
|
1676
|
+
...Object.fromEntries(
|
|
1677
|
+
["new", "games", "games-none", "export", "export-hint", "copy", "copied", "import", "import-hint", "import-go", "close", "saved"].map(
|
|
1678
|
+
(k) => [`web-${k}`, []]
|
|
1679
|
+
)
|
|
1680
|
+
),
|
|
1616
1681
|
...Object.fromEntries(REJECT_REASONS.map((c) => [`reject-${c}`, []]))
|
|
1617
1682
|
};
|
|
1618
1683
|
|
|
@@ -1904,7 +1969,7 @@ function startApp(opts) {
|
|
|
1904
1969
|
const title = [{ text: t("reply-title"), dim: true }];
|
|
1905
1970
|
if (replyMode === "pick") return [title, ...pickOptions2.map((o, i) => [{ text: `${i + 1}) ` }, ...lineSpans(o, /* @__PURE__ */ new Set())])];
|
|
1906
1971
|
return [
|
|
1907
|
-
title,
|
|
1972
|
+
[{ text: t("tiles-title"), dim: true }],
|
|
1908
1973
|
...wrapItems(
|
|
1909
1974
|
tiles.map((x, i) => `[${i + 1}]${x}`),
|
|
1910
1975
|
width,
|
|
@@ -2006,9 +2071,52 @@ function startApp(opts) {
|
|
|
2006
2071
|
return { press, render };
|
|
2007
2072
|
}
|
|
2008
2073
|
|
|
2074
|
+
// ../tui/src/save-line.ts
|
|
2075
|
+
var PREFIX = "st1:";
|
|
2076
|
+
async function transform(bytes, stream) {
|
|
2077
|
+
const out = new Blob([bytes]).stream().pipeThrough(stream);
|
|
2078
|
+
return new Uint8Array(await new Response(out).arrayBuffer());
|
|
2079
|
+
}
|
|
2080
|
+
function toBase64(bytes) {
|
|
2081
|
+
let bin = "";
|
|
2082
|
+
for (let i = 0; i < bytes.length; i += 32768) bin += String.fromCharCode(...bytes.subarray(i, i + 32768));
|
|
2083
|
+
return btoa(bin);
|
|
2084
|
+
}
|
|
2085
|
+
async function encodeSave(state2) {
|
|
2086
|
+
const packed = await transform(new TextEncoder().encode(serialize(state2)), new CompressionStream("deflate-raw"));
|
|
2087
|
+
return PREFIX + toBase64(packed);
|
|
2088
|
+
}
|
|
2089
|
+
async function decodeSave(line, course2) {
|
|
2090
|
+
const text = line.replace(/\s+/g, "");
|
|
2091
|
+
const body = text.startsWith(PREFIX) ? text.slice(PREFIX.length) : "";
|
|
2092
|
+
if (!/^[A-Za-z0-9+/]+={0,2}$/.test(body)) return { ok: false, reason: "not-a-save" };
|
|
2093
|
+
let json;
|
|
2094
|
+
try {
|
|
2095
|
+
const bytes = Uint8Array.from(atob(body), (c) => c.charCodeAt(0));
|
|
2096
|
+
json = new TextDecoder("utf-8", { fatal: true }).decode(await transform(bytes, new DecompressionStream("deflate-raw")));
|
|
2097
|
+
} catch {
|
|
2098
|
+
return { ok: false, reason: "not-a-save" };
|
|
2099
|
+
}
|
|
2100
|
+
return parseSave(json, course2);
|
|
2101
|
+
}
|
|
2102
|
+
|
|
2103
|
+
// ../tui/src/session-lines.ts
|
|
2104
|
+
function sessionLines(sessions, course2, t, date) {
|
|
2105
|
+
return sessions.map((s, i) => {
|
|
2106
|
+
const text = t("resume-item", {
|
|
2107
|
+
day: s.state.day,
|
|
2108
|
+
place: t(`place-${s.state.place}`),
|
|
2109
|
+
currency: course2.world.currency,
|
|
2110
|
+
wallet: s.state.wallet,
|
|
2111
|
+
done: Object.values(s.state.scenesDone).filter((n) => n > 0).length,
|
|
2112
|
+
date: date(s.lastPlayed)
|
|
2113
|
+
});
|
|
2114
|
+
return `${i + 1}) ${text}`;
|
|
2115
|
+
});
|
|
2116
|
+
}
|
|
2117
|
+
|
|
2009
2118
|
// src/cli.ts
|
|
2010
2119
|
import { parseArgs } from "node:util";
|
|
2011
|
-
import { deflateRawSync, inflateRawSync } from "node:zlib";
|
|
2012
2120
|
var USAGE = `Usage: silver-tongue [--new | --resume | --export | --import <line>]
|
|
2013
2121
|
|
|
2014
2122
|
(no flag) continue the game you played last
|
|
@@ -2045,56 +2153,15 @@ function parseFlags(args) {
|
|
|
2045
2153
|
const mode = chosen[0] === "new" || chosen[0] === "resume" || chosen[0] === "export" ? chosen[0] : "continue";
|
|
2046
2154
|
return { mode, ...course2 };
|
|
2047
2155
|
}
|
|
2048
|
-
function sessionLines(sessions, course2, t, date) {
|
|
2049
|
-
return sessions.map((s, i) => {
|
|
2050
|
-
const text = t("resume-item", {
|
|
2051
|
-
day: s.state.day,
|
|
2052
|
-
place: t(`place-${s.state.place}`),
|
|
2053
|
-
currency: course2.world.currency,
|
|
2054
|
-
wallet: s.state.wallet,
|
|
2055
|
-
done: Object.values(s.state.scenesDone).filter((n) => n > 0).length,
|
|
2056
|
-
date: date(s.lastPlayed)
|
|
2057
|
-
});
|
|
2058
|
-
return `${i + 1}) ${text}`;
|
|
2059
|
-
});
|
|
2060
|
-
}
|
|
2061
2156
|
function pickAnswer(answer, count) {
|
|
2062
2157
|
const a = answer.trim().toLowerCase();
|
|
2063
2158
|
if (a === "" || a === "q") return "cancel";
|
|
2064
2159
|
const n = Number(a);
|
|
2065
2160
|
return Number.isInteger(n) && n >= 1 && n <= count ? n - 1 : "again";
|
|
2066
2161
|
}
|
|
2067
|
-
var PREFIX = "st1:";
|
|
2068
|
-
function encodeSave(state2) {
|
|
2069
|
-
return PREFIX + deflateRawSync(Buffer.from(serialize(state2), "utf8")).toString("base64");
|
|
2070
|
-
}
|
|
2071
|
-
function decodeSave(line, course2) {
|
|
2072
|
-
const text = line.replace(/\s+/g, "");
|
|
2073
|
-
const body = text.startsWith(PREFIX) ? text.slice(PREFIX.length) : "";
|
|
2074
|
-
if (!/^[A-Za-z0-9+/]+={0,2}$/.test(body)) return { ok: false, reason: "not-a-save" };
|
|
2075
|
-
let json;
|
|
2076
|
-
try {
|
|
2077
|
-
json = inflateRawSync(Buffer.from(body, "base64")).toString("utf8");
|
|
2078
|
-
} catch {
|
|
2079
|
-
return { ok: false, reason: "not-a-save" };
|
|
2080
|
-
}
|
|
2081
|
-
return parseSave(json, course2);
|
|
2082
|
-
}
|
|
2083
2162
|
|
|
2084
2163
|
// src/node-terminal.ts
|
|
2085
2164
|
import readline from "node:readline";
|
|
2086
|
-
var COLORS = { red: 31, green: 32, yellow: 33, magenta: 35, cyan: 36 };
|
|
2087
|
-
function toAnsi(line) {
|
|
2088
|
-
return line.map((s) => {
|
|
2089
|
-
const codes = [
|
|
2090
|
-
...s.bold ? [1] : [],
|
|
2091
|
-
...s.dim ? [2] : [],
|
|
2092
|
-
...s.underline ? [4] : [],
|
|
2093
|
-
...s.color ? [COLORS[s.color]] : []
|
|
2094
|
-
];
|
|
2095
|
-
return codes.length ? `\x1B[${codes.join(";")}m${s.text}\x1B[0m` : s.text;
|
|
2096
|
-
}).join("");
|
|
2097
|
-
}
|
|
2098
2165
|
function keyName(str, key) {
|
|
2099
2166
|
if (key?.ctrl && key.name === "c") return "ctrl-c";
|
|
2100
2167
|
return key?.name ?? str;
|
|
@@ -2251,7 +2318,7 @@ ${USAGE}`);
|
|
|
2251
2318
|
process.exit(2);
|
|
2252
2319
|
}
|
|
2253
2320
|
var course = JSON.parse(readFileSync3(coursePath(flags.coursePath), "utf8"));
|
|
2254
|
-
function exportOrImport(mode, line) {
|
|
2321
|
+
async function exportOrImport(mode, line) {
|
|
2255
2322
|
const t = makeText(course.learnerFtl);
|
|
2256
2323
|
const root = configDir();
|
|
2257
2324
|
const dir = sessionsDir(root, course.id);
|
|
@@ -2265,10 +2332,10 @@ function exportOrImport(mode, line) {
|
|
|
2265
2332
|
console.error(t("export-none"));
|
|
2266
2333
|
process.exit(1);
|
|
2267
2334
|
}
|
|
2268
|
-
console.log(encodeSave(last.state));
|
|
2335
|
+
console.log(await encodeSave(last.state));
|
|
2269
2336
|
process.exit(0);
|
|
2270
2337
|
}
|
|
2271
|
-
const decoded = decodeSave(line === "-" ? readFileSync3(0, "utf8") : line, course);
|
|
2338
|
+
const decoded = await decodeSave(line === "-" ? readFileSync3(0, "utf8") : line, course);
|
|
2272
2339
|
if (!decoded.ok) {
|
|
2273
2340
|
console.error(t("import-bad", { reason: decoded.reason }));
|
|
2274
2341
|
process.exit(1);
|
|
@@ -2279,11 +2346,11 @@ function exportOrImport(mode, line) {
|
|
|
2279
2346
|
console.error(t("notice-read-only"));
|
|
2280
2347
|
process.exit(1);
|
|
2281
2348
|
}
|
|
2282
|
-
const [summary] = sessionLines([{
|
|
2349
|
+
const [summary] = sessionLines([{ lastPlayed: now, state: decoded.state }], course, t, (ms) => new Date(ms).toLocaleString());
|
|
2283
2350
|
console.log(t("import-done", { game: summary.replace(/^1\) /, "") }));
|
|
2284
2351
|
process.exit(0);
|
|
2285
2352
|
}
|
|
2286
|
-
if (flags.mode === "export" || flags.mode === "import") exportOrImport(flags.mode, flags.mode === "import" ? flags.line : "");
|
|
2353
|
+
if (flags.mode === "export" || flags.mode === "import") await exportOrImport(flags.mode, flags.mode === "import" ? flags.line : "");
|
|
2287
2354
|
async function chooseSave() {
|
|
2288
2355
|
if (process.env.SILVER_TONGUE_SAVE) return process.env.SILVER_TONGUE_SAVE;
|
|
2289
2356
|
const root = configDir();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "silver-tongue",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Learn a language by living in it: a text game for the terminal.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": { "type": "git", "url": "git+https://github.com/jamil314/silver-tongue.git", "directory": "packages/tui-node" },
|