vhyl-en.t
Back to reference
1 /* Optional English messages and grammatical presentation for vhyl.
2 * Copyright (c) 2026 John Cunningham. MIT License; see LICENSE. */
3
4 /* ---------------------------------------------------------- the expander */
5
6 inPast()
7 {
8 if (expandCtx.forceTense != nil)
9 return expandCtx.forceTense == 2;
10 return gameMain.usePastTense;
11 }
12
13 /* The tense a `{...!}` parameter and quoted speech pin themselves to. */
14 withPast(on) { expandCtx.forceTense = on ? 2 : 1; return nil; }
15 withPresent() { return withPast(nil); }
16 withTense(past) { return withPast(past); }
17
18 tSel(now, then) { return inPast() ? then : now; }
19
20 expandCtx: object
21 subject = nil
22 /* nil for a third-person singular subject, true for "you" or a plural. */
23 plural = nil
24 /* What `{it}` refers to: whatever a parameter last named (antecedents). */
25 antecedent = nil
26 /* The subject of the sentence being built, for reflexives. */
27 sentenceSubject = nil
28 /* 1 present, 2 past, nil to follow the game. */
29 forceTense = nil
30 ;
31
32 messageParams: object names = [] items = [];
33
34 setMessageParam(name, item)
35 {
36 local at = messageParams.names.indexOf(name);
37 if (at != nil)
38 {
39 local rebuilt = [];
40 for (local i = 1; i <= messageParams.items.length(); ++i)
41 rebuilt = rebuilt + [i == at ? item : messageParams.items[i]];
42 messageParams.items = rebuilt;
43 return nil;
44 }
45 messageParams.names = messageParams.names + [name];
46 messageParams.items = messageParams.items + [item];
47 return nil;
48 }
49
50 clearMessageParams()
51 {
52 messageParams.names = [];
53 messageParams.items = [];
54 return nil;
55 }
56
57 /* Note that a parameter named this entity, for the verb and the pronoun after it. */
58 noteSubject(item, isPlural)
59 {
60 expandCtx.subject = item;
61 expandCtx.plural = isPlural;
62 if (item != nil)
63 expandCtx.antecedent = item;
64 return nil;
65 }
66
67 /*
68 * The person the player character is written in, and whether that person takes
69 * a plural verb. "I", "you", "we" and "they" all take the bare form; only a
70 * third-person singular takes the -s.
71 */
72 pcPerson() { return player.pcReferralPerson; }
73
74 /*
75 * One verb, agreeing with whatever the last parameter named.
76 *
77 * English needs an -s on a third-person singular present verb and nothing
78 * anywhere else, which is the whole of the agreement most library messages
79 * need. A verb whose past is not "-ed" says so.
80 */
81 conjugate(verb, past)
82 {
83 if (inPast())
84 {
85 if (past != nil)
86 return past;
87
88 if (verb.substr(verb.length()) =='e')
89 return verb +'d';
90 return verb +'ed';
91 }
92 if (expandCtx.plural)
93 return verb;
94 /* *go* becomes *goes*, *push* becomes *pushes*. */
95 local last = verb.substr(verb.length());
96 if (last is in ('s','x','z') || verb.substr(verb.length() - 1) is in ('sh','ch'))
97 return verb +'es';
98 if (last =='y' && verb.length() > 1
99 && !(verb.substr(verb.length() - 1, 1) is in ('a','e','i','o','u')))
100 return verb.substr(1, verb.length() - 1) +'ies';
101 return verb +'s';
102 }
103
104 verbEnding(present, past)
105 {
106 if (inPast())
107 return past == nil ?'ed' : past;
108 if (expandCtx.plural)
109 return present =='ies' ?'y' :'';
110 return present;
111 }
112
113 /* "is" or "are", and "was" or "were". */
114 beVerb()
115 {
116 if (inPast())
117 return expandCtx.plural ?'were' :'was';
118 return expandCtx.plural ?'are' :'is';
119 }
120
121 irregularVerb(word)
122 {
123 if (word =='is' || word =='are')
124 return beVerb();
125 if (word =='was' || word =='were')
126 {
127
128 return expandCtx.plural ?'were' :'was';
129 }
130 if (word =='has' || word =='have')
131 return inPast() ?'had' : (expandCtx.plural ?'have' :'has');
132 if (word =='do' || word =='does')
133 return inPast() ?'did' : (expandCtx.plural ?'do' :'does');
134 if (word =='go' || word =='goes')
135 return inPast() ?'went' : (expandCtx.plural ?'go' :'goes');
136 if (word =='come' || word =='comes')
137 return inPast() ?'came' : (expandCtx.plural ?'come' :'comes');
138 if (word =='leave' || word =='leaves')
139 return inPast() ?'left' : (expandCtx.plural ?'leave' :'leaves');
140 if (word =='see' || word =='sees')
141 return inPast() ?'saw' : (expandCtx.plural ?'see' :'sees');
142 if (word =='say' || word =='says')
143 return inPast() ?'said' : (expandCtx.plural ?'say' :'says');
144
145 if (word =='must')
146 return inPast() ?'had to' :'must';
147 if (word =='can')
148 return inPast() ?'could' :'can';
149 if (word =='cannot')
150 return inPast() ?'could not' :'cannot';
151 if (word =='can\'t')
152 return inPast() ?'couldn\'t' :'can\'t';
153 if (word =='will')
154 return inPast() ?'would' :'will';
155 if (word =='won\'t')
156 return inPast() ?'wouldn\'t' :'won\'t';
157 return nil;
158 }
159
160 /* The verb endings, by the string inside the braces. Answers nil for a word
161 * that is not one. */
162 endingFor(word)
163 {
164 if (word =='s')
165 return verbEnding('s', nil);
166 if (word =='es' || word =='es/ed')
167 return verbEnding('es','ed');
168 if (word =='ies' || word =='ies/ied')
169 return verbEnding('ies','ied');
170 if (word =='s/d')
171 return verbEnding('s','d');
172 if (word =='s/ed')
173 return verbEnding('s','ed');
174 /*
175 * `{s/?ed}` in the documentation, where the question mark stands for the
176 * consonant the past doubles: a message writes `{s/ped}` for *dropped*.
177 */
178 if (word.length() >= 5 && word.substr(1, 2) =='s/' && word.substr(word.length() - 1) =='ed')
179 return verbEnding('s', word.substr(3));
180 return nil;
181 }
182
183 /* The entity a `{...}` parameter names, by the word the message used. */
184 paramOf(name, c)
185 {
186 if (name =='actor')
187 return player;
188 if (name =='it')
189 return expandCtx.antecedent;
190 local at = messageParams.names.indexOf(name);
191 if (at != nil)
192 return messageParams.items[at];
193 if (c == nil)
194 return nil;
195 if (name =='dobj')
196 return c.dobj;
197 if (name =='iobj')
198 return c.iobj;
199 return nil;
200 }
201
202 /* ------------------------------------------------------- the name shapes */
203
204 pronounSet(item)
205 {
206 if (item == nil)
207 return 3;
208 if (item.isPlural || item.isGenderNeutral)
209 return 4;
210 if (item.isHim)
211 return 1;
212 if (item.isHer)
213 return 2;
214 return 3;
215 }
216
217 /* he/she/it/they, and him/her/it/them. */
218 proWord(item, nominative)
219 {
220 local set = pronounSet(item);
221 if (set == 1)
222 return nominative ?'he' :'him';
223 if (set == 2)
224 return nominative ?'she' :'her';
225 if (set == 4)
226 return nominative ?'they' :'them';
227 return'it';
228 }
229
230 /* his/her/its/their — the form that qualifies a noun. */
231 proPossAdj(item)
232 {
233 local set = pronounSet(item);
234 return set == 1 ?'his' : set == 2 ?'her' : set == 4 ?'their' :'its';
235 }
236
237 /* his/hers/its/theirs — the form that stands alone. */
238 proPossNoun(item)
239 {
240 local set = pronounSet(item);
241 return set == 1 ?'his' : set == 2 ?'hers' : set == 4 ?'theirs' :'its';
242 }
243
244 /* himself/herself/itself/themselves. */
245 proReflexive(item)
246 {
247 local set = pronounSet(item);
248 return set == 1 ?'himself' : set == 2 ?'herself'
249 : set == 4 ?'themselves' :'itself';
250 }
251
252 /* Whether this thing takes a plural verb: *they are*, *it is*. */
253 proIsPlural(item)
254 {
255 return pronounSet(item) == 4;
256 }
257
258 pcWord(nominative)
259 {
260 local person = pcPerson();
261 if (person == FirstPerson)
262 return nominative ?'I' :'me';
263 if (person == ThirdPerson)
264 return proWord(player, nominative);
265 return'you';
266 }
267
268 /* The subject form: *you*, *I*, *the keeper*. */
269 nomName(item)
270 {
271 if (item == nil)
272 return'nothing';
273 if (item == player)
274 return pcWord(true);
275 return theName(item);
276 }
277
278 /* The object form: *you*, *me*, *the keeper*. */
279 objName(item)
280 {
281 if (item == nil)
282 return'nothing';
283 if (item == player)
284 return pcWord(nil);
285 return theName(item);
286 }
287
288 /* *myself*, *yourself*, *himself*, *herself*, *itself*, *themselves*. */
289 reflexiveName(item)
290 {
291 if (item == nil)
292 return'nothing';
293 if (item == player)
294 {
295 local person = pcPerson();
296 if (person == FirstPerson)
297 return'myself';
298 if (person == ThirdPerson)
299 return proReflexive(player);
300 return'yourself';
301 }
302 return proReflexive(item);
303 }
304
305 /* A pronoun for something that is not the player: *he*, *her*, *it*, *them*. */
306 itName(item, nominative)
307 {
308 if (item == nil)
309 return'[no antecedent]';
310 if (item == player)
311 return pcWord(nominative);
312 return proWord(item, nominative);
313 }
314
315 possAdjName(item)
316 {
317 if (item == nil)
318 return'nothing';
319 if (item == player)
320 {
321 local person = pcPerson();
322 if (person == FirstPerson)
323 return'my';
324 if (person == ThirdPerson)
325 return proPossAdj(player);
326 return'your';
327 }
328 return proPossAdj(item);
329 }
330
331 /* *mine*, *yours*, *his*, *hers*, *its*, *theirs* — the form that stands alone. */
332 possNounName(item)
333 {
334 if (item == nil)
335 return'nothing';
336 if (item == player)
337 {
338 local person = pcPerson();
339 if (person == FirstPerson)
340 return'mine';
341 if (person == ThirdPerson)
342 return proPossNoun(player);
343 return'yours';
344 }
345 return proPossNoun(item);
346 }
347
348 /* *it's*, *you're*, *the keeper's* — the contraction of a name and "to be". */
349 contractionName(item)
350 {
351 if (item == nil)
352 return'nothing';
353 /*
354 * The verb in a contraction agrees with the thing being contracted onto,
355 * not with whatever the sentence named last: *you're late, and it's early*
356 * has two subjects and two agreements.
357 */
358 local was = expandCtx.plural;
359
360 expandCtx.plural = item == player
361 ? pcPerson() != ThirdPerson || proIsPlural(player)
362 : proIsPlural(item);
363 if (inPast())
364 {
365 local past = nomName(item) +' ' + beVerb();
366 expandCtx.plural = was;
367 return past;
368 }
369 expandCtx.plural = was;
370 if (item == player)
371 {
372 local person = pcPerson();
373 if (person == FirstPerson)
374 return'I\'m';
375 if (person == ThirdPerson)
376 return proIsPlural(player) ?'they\'re' : proWord(player, true) +'\'s';
377 return'you\'re';
378 }
379 return proIsPlural(item) ?'they\'re' : proWord(item, true) +'\'s';
380 }
381
382 thatName(item)
383 {
384 if (item == nil)
385 return'that';
386 if (item == player)
387 return pcWord(true);
388
389 return item.isPlural ?'those' :'that';
390 }
391
392 /* ---------------------------------------------------- one parameter body */
393
394 moveSlash: object head = nil tail = nil;
395
396 splitParam(body)
397 {
398 local space = body.find(' ');
399 if (space == nil)
400 {
401 moveSlash.head = body;
402 moveSlash.tail = nil;
403 return nil;
404 }
405 local head = body.substr(1, space - 1);
406 local tail = body.substr(space + 1);
407 local slash = tail.find('/');
408 if (slash != nil)
409 {
410 head = head + tail.substr(slash);
411 tail = tail.substr(1, slash - 1);
412 }
413 moveSlash.head = head;
414 moveSlash.tail = tail;
415 return nil;
416 }
417
418 applyCase(word, head)
419 {
420 if (word == nil || word.length() == 0 || head.length() == 0)
421 return word;
422 local first = head.substr(1, 1);
423 if (first != first.toUpper() || first == first.toLower())
424 return word;
425 if (head.length() > 1)
426 {
427 local second = head.substr(2, 1);
428 if (second == second.toUpper() && second != second.toLower())
429 return word.toUpper();
430 }
431 return capitalised(word);
432 }
433
434 formKind(head)
435 {
436 local key = head.toLower();
437 if (key is in ('a','an','a/he','an/he','a/she','an/she'))
438 return 1;/* aName, subject */
439 if (key is in ('a/him','a/her','an/him','an/her'))
440 return 2;/* aName, object, reflexive */
441 if (key is in ('the','the/he','the/she'))
442 return 3;/* theName, subject */
443 if (key is in ('the/him','the/her'))
444 return 4;/* theName, object, reflexive */
445 if (key is in ('that','that/he','that/she'))
446 return 5;/* that, subject */
447 if (key is in ('that/him','that/her'))
448 return 6;/* that, object, reflexive */
449 if (key is in ('it','it/he','it/she'))
450 return 7;/* pronoun, subject */
451 if (key is in ('it/him','it/her'))
452 return 8;/* pronoun, object, reflexive */
453 if (key is in ('its','its/her','its/his'))
454 return 9;/* possessive adjective */
455 if (key is in ('its/hers'))
456 return 10;/* possessive noun */
457 if (key is in ('itself','itself/himself','itself/herself'))
458 return 11;/* reflexive */
459 if (key is in ('you','you/he','you/she'))
460 return 12;/* actor, subject */
461 if (key is in ('you/him','you/her'))
462 return 13;/* actor, object, reflexive */
463 if (key is in ('your','your/his','your/her'))
464 return 14;/* actor possessive adjective */
465 if (key is in ('yours','yours/his','yours/hers'))
466 return 15;/* actor possessive noun */
467 if (key is in ('yourself','yourself/himself','yourself/herself'))
468 return 16;/* actor reflexive */
469 if (key is in ('it\'s','it\'s/he\'s','it\'s/she\'s'))
470 return 17;/* contraction */
471 if (key is in ('you\'re','you\'re/he\'s','you\'re/she\'s'))
472 return 18;/* actor contraction */
473 if (key =='subj')
474 return 19;/* marks the subject, says nothing */
475 if (key =='name')
476 return 20;/* vhyl's own: the bare name */
477 if (key =='way')
478 return 21;/* vhyl's own: the captured word */
479 if (key =='i')
480 return 12;/* vhyl's own spelling of {you} */
481 return 0;
482 }
483
484 /* Whether this kind implies the actor rather than taking a selector. */
485 kindMeansActor(kind)
486 {
487 return kind >= 12 && kind <= 16 || kind == 18;
488 }
489
490 /* Whether this kind is the sentence's subject. */
491 kindIsSubject(kind)
492 {
493 return kind is in (1, 3, 5, 7, 12, 17, 18, 19);
494 }
495
496 /* `{way}` names no object, so it must not disturb the antecedent. */
497 kindNamesNothing(kind) { return kind == 21; }
498
499 /* Whether this kind has a reflexive form to fall back on. */
500 kindReflexes(kind)
501 {
502 return kind is in (2, 4, 6, 8);
503 }
504
505 kindIsPronoun(kind)
506 {
507 return kind is in (7, 8, 11, 17);
508 }
509
510 /* The word a kind produces for a thing. */
511 wordFor(kind, item)
512 {
513 if (kind == 1 || kind == 2)
514 return aName(item);
515 if (kind == 3)
516 return nomName(item);
517 if (kind == 4)
518 return objName(item);
519 if (kind == 5 || kind == 6)
520 return thatName(item);
521 if (kind == 7)
522 return itName(item, true);
523 if (kind == 8)
524 return itName(item, nil);
525 if (kind == 9 || kind == 14)
526 return possAdjName(item);
527 if (kind == 10 || kind == 15)
528 return possNounName(item);
529 if (kind == 11 || kind == 16)
530 return reflexiveName(item);
531 if (kind == 12)
532 return nomName(item);
533 if (kind == 13)
534 return objName(item);
535 if (kind == 17 || kind == 18)
536 return contractionName(item);
537 if (kind == 19)
538 return'';
539 return item == nil ?'nothing' : item.name;
540 }
541
542 wayWord(c)
543 {
544 return c == nil || c.way_ == nil ?'' : c.way_;
545 }
546
547 /*
548 * Expand one `{...}`, given what is inside it.
549 *
550 * The order matters: a tense choice, then a verb ending, then an irregular
551 * verb, then the format table, and only then the fallback that treats an
552 * unknown word as a regular verb — which is vhyl's own addition and is what
553 * lets a message write `{take}` instead of `take{s}`.
554 */
555 expandOne(body, c)
556 {
557 if (body.length() == 0)
558 return'';
559
560 local fixed = nil;
561 if (body.substr(body.length()) =='!')
562 {
563 body = body.substr(1, body.length() - 1);
564 fixed = true;
565 withPresent();
566 }
567 local out = expandBody(body, c);
568 if (fixed)
569 expandCtx.forceTense = nil;
570 return out;
571 }
572
573 expandBody(body, c)
574 {
575 local bar = body.find('|');
576 /* A bare choice with no parameter is a tense: present on the left. */
577 if (bar != nil && body.find(' ') == nil)
578 {
579 local left = body.substr(1, bar - 1);
580 local right = body.substr(bar + 1);
581 /* A verb and its irregular past, or a plain pair of words. */
582 if (isVerbWord(left))
583 return conjugate(left, right);
584
585 return expandNested(inPast() ? right : left, c);
586 }
587 splitParam(body);
588 local head = moveSlash.head;
589 local kind = formKind(head);
590 if (kind != 0)
591 {
592 local item = kindMeansActor(kind) ? player
593 : moveSlash.tail != nil ? paramOf(moveSlash.tail, c)
594 : expandCtx.antecedent;
595 /*
596 * A reflexive when the sentence has already named this thing as its
597 * subject — *Bob slapped himself with the herring*.
598 */
599 if (kindReflexes(kind) && item != nil && item == expandCtx.sentenceSubject)
600 kind = 11;
601 local word = kind == 21 ? wayWord(c) : wordFor(kind, item);
602 if (kindIsSubject(kind))
603 {
604 expandCtx.sentenceSubject = item;
605 noteSubject(item, item == player
606 ? pcPerson() != ThirdPerson || proIsPlural(player)
607 : item == nil ? nil
608 : kindIsPronoun(kind) ? proIsPlural(item) : item.isPlural);
609 }
610 else if (item != nil && !kindNamesNothing(kind))
611 expandCtx.antecedent = item;
612 return applyCase(word, head);
613 }
614 /* A word on its own: a verb ending, an irregular verb, or a verb. */
615 if (moveSlash.tail == nil)
616 {
617 local ending = endingFor(head);
618 if (ending != nil)
619 return ending;
620 local irregular = irregularVerb(head.toLower());
621 if (irregular != nil)
622 return applyCase(irregular, head);
623 return applyCase(conjugate(head.toLower(), nil), head);
624 }
625 /* An unknown format type with a selector: name the thing and move on. */
626 local item = paramOf(moveSlash.tail, c);
627 noteSubject(item, item == player);
628 return applyCase(theName(item), head);
629 }
630
631 /* A nested `[...]` inside a tense choice, which is a parameter in brackets. */
632 expandNested(text, c)
633 {
634 local out ='';
635 local rest = text;
636 for (;;)
637 {
638 local open = rest.find('[');
639 if (open == nil)
640 return out + rest;
641 local close = rest.find(']');
642 if (close == nil || close < open)
643 return out + rest;
644 out = out + rest.substr(1, open - 1)
645 + expandBody(rest.substr(open + 1, close - open - 1), c);
646 rest = rest.substr(close + 1);
647 }
648 }
649
650 /* Whether a word left of a `|` is a verb rather than one half of a choice. */
651 isVerbWord(word)
652 {
653 return word =='is' || word =='are' || word =='has' || word =='have';
654 }
655
656 expandAll(text, c)
657 {
658 /*
659 * The player is the subject until a parameter says otherwise, and the
660 * player's person decides whether the verb agrees as a plural: *I*, *you*,
661 * *we* and *they* all take the bare form, and only a third-person singular
662 * takes the -s.
663 */
664 expandCtx.subject = player;
665 expandCtx.plural = pcPerson() != ThirdPerson;
666 expandCtx.antecedent = c == nil ? nil : c.dobj;
667 expandCtx.sentenceSubject = nil;
668 expandCtx.forceTense = nil;
669 local out ='';
670 local rest = text;
671 for (;;)
672 {
673 local open = rest.find('{');
674 if (open == nil)
675 return out + noteSentenceEnd(rest);
676 local close = rest.find('}');
677 if (close == nil || close < open)
678 return out + rest;
679 local before = rest.substr(1, open - 1);
680 noteSentenceEnd(before);
681 out = out + before
682 + expandOne(rest.substr(open + 1, close - open - 1), c);
683 rest = rest.substr(close + 1);
684 }
685 }
686
687 noteSentenceEnd(text)
688 {
689 if (text.find('.') != nil || text.find('?') != nil || text.find('!') != nil)
690 expandCtx.sentenceSubject = nil;
691 return text;
692 }
693
694 /* English is where `{...}` means something, so the hook is filled in here. */
695 modify language
696 expand(text, c)
697 {
698 return text.find('{') == nil ? text : expandAll(text, c);
699 }
700 ;
701
702 /* ------------------------------------------------------------- the words */
703
704 article(word)
705 {
706 if (word == nil || word.length() == 0)
707 return'a';
708 local first = word.substr(1, 1);
709 if (first is in ('a','e','i','o','u','A','E','I','O','U'))
710 return'an';
711 return'a';
712 }
713
714 nameList(items)
715 {
716 local out ='';
717 local count = items.length();
718 for (local at = 1; at <= count; ++at)
719 {
720 if (at > 1)
721 out = out + (at == count ?' and ' :', ');
722 out = out + aName(items[at]);
723 }
724 return out;
725 }
726
727 /* ------------------------------------------------------- naming helpers */
728
729 theName(item)
730 {
731 if (item == nil)
732 return'nothing';
733 /* You are not *the you*, and Zoé is not *the Zoé*. */
734 if (item == player)
735 return'yourself';
736 if (item.isProper)
737 return item.name;
738 return'the ' + item.name;
739 }
740
741 aName(item)
742 {
743 if (item == nil)
744 return'nothing';
745 if (item == player)
746 return pcWord(true);
747 if (item.isProper)
748 return item.name;
749 /* A mass noun takes none, and a plural takes *some*. */
750 if (item.isMassNoun)
751 return item.name;
752 if (item.isPlural)
753 return'some ' + item.name;
754 return article(item.name) +' ' + item.name;
755 }
756
757 capitalised(text)
758 {
759 if (text == nil || text.length() == 0)
760 return text;
761 return text.substr(1, 1).toUpper() + text.substr(2);
762 }
763
764 TheName(item) { return capitalised(theName(item)); }
765 AName(item) { return capitalised(aName(item)); }
766
767 /* "is" or "are", for a count. Agreement, as far as vhyl needs it. */
768 isAre(count)
769 {
770 return count == 1 ?'is' :'are';
771 }
772
773 /* The same, for one thing whose name is grammatically plural. */
774 isAreFor(item)
775 {
776 return item != nil && item.isPlural ?'are' :'is';
777 }
778
779 /*
780 * The English word for a spatial position. vhyl says a thing rests `posOn`
781 * another; which preposition that is, and that English has one at all, is this
782 * module's business.
783 */
784 prepFor(pos)
785 {
786 if (pos == posOn)
787 return'on';
788 if (pos == posUnder)
789 return'under';
790 if (pos == posBehind)
791 return'behind';
792 return'in';
793 }
794
795 /* ------------------------------------------------- vhyl's default messages */
796
797 /*
798 * Every sentence vhyl says, with an id. A game overrides one by declaring its
799 * own Message with the same id and a higher priority — it does not need to know
800 * the name of the object below.
801 */
802
803 msgTaken: Message id ='take.ok' text ='Taken. ';
804 msgAlreadyHave: Message id ='take.already' text ='{You/he} already {have} {the dobj}. ';
805 msgFixed: Message id ='take.fixed' say(c) {"<<TheName(c.dobj)>> is fixed in place. "; return nil; };
806 msgDropped: Message id ='drop.ok' text ='Dropped. ';
807 msgNotCarried: Message id ='drop.notheld' text ='{You/he} {is}n\'t holding {the dobj}. ';
808
809 msgOpened: Message id ='open.ok' text ='Opened. ';
810 enOpenReveals: Message id ='open.reveals' say(c) {"Opening <<theName(c.dobj)>> reveals <<nameList(c.list_)>>. "; return nil; };
811 msgAlreadyOpen: Message id ='open.already' text ='It is already open. ';
812 msgNotOpenable: Message id ='open.cannot' say(c) {"<<TheName(c.dobj)>> is not something you can open. "; return nil; };
813 msgClosed: Message id ='close.ok' text ='Closed. ';
814 msgAlreadyShut: Message id ='close.already' text ='It is already closed. ';
815 msgNotCloseable: Message id ='close.cannot' say(c) {"<<TheName(c.dobj)>> is not something you can close. "; return nil; };
816
817 msgNothingOdd: Message id ='examine.plain' text ='{You/he} {see} nothing unusual about {the dobj/him}. ';
818 msgEmptyHanded: Message id ='inventory.none' text ='You are empty-handed. ';
819 msgCarrying: Message id ='inventory.some' text ='You are carrying:';
820
821 msgSwitchedOn: Message id ='turnon.ok' text ='{You/he} {turn} {the dobj} on. ';
822 msgSwitchedOff: Message id ='turnoff.ok' text ='{You/he} {turn} {the dobj} off. ';
823 msgNotLightable: Message id ='turnon.cannot' say(c) {"<<TheName(c.dobj)>> is not something you can turn on. "; return nil; };
824 msgNotDousable: Message id ='turnoff.cannot' say(c) {"<<TheName(c.dobj)>> is not something you can turn off. "; return nil; };
825 msgAlreadyLit: Message id ='turnon.already' text ='It is already lit. ';
826 msgAlreadyOut: Message id ='turnoff.already' text ='It is already off. ';
827 msgPitchBlack: Message id ='light.out' text ='It is pitch black. ';
828
829 msgCannotGo: Message id ='go.nowhere' text ='You cannot go that way. ';
830 msgDoorShut: Message id ='go.shut' say(c) {"<<TheName(c.dobj)>> is closed. "; return nil; };
831 msgLeadsNowhere: Message id ='go.leadsnowhere' say(c) {"<<TheName(c.dobj)>> leads nowhere. "; return nil; };
832
833 msgNoSuchThing: Message id ='parse.noref' text ='You see no such thing. ';
834 msgNotUnderstood: Message id ='parse.nomatch' text ='I don\'t understand that. ';
835 msgNotOne: Message id ='parse.notone' text ='That was not one of them. ';
836 msgWhichDoYouMean: Message id ='parse.which' text ='Which do you mean';
837
838 msgDarkRoom: Message id ='room.dark' text ='In the dark\nIt is pitch black, and you cannot see a thing. ';
839 msgNothingToUndo: Message id ='undo.none' text ='There is nothing to undo. ';
840 msgUndone: Message id ='undo.ok' text ='Undone. ';
841
842 msgWorn: Message id ='wear.ok' text ='{You/he} {put} {the dobj} on. ';
843 msgNotWearable: Message id ='wear.cannot' say(c) {"<<TheName(c.dobj)>> is not something you can wear. "; return nil; };
844 msgAlreadyWorn: Message id ='wear.already' text ='You are already wearing it. ';
845 msgTakenOff: Message id ='doff.ok' text ='{You/he} {take} {the dobj} off. ';
846 msgNotWorn: Message id ='doff.notworn' text ='{You/he} {is}n\'t wearing {the dobj}. ';
847
848 msgNothingHappens: Message id ='action.nothing' text ='Nothing happens. ';
849 msgRecovered: Message id ='turn.failed' text ='Something went wrong; nothing changed. ';
850
851 /* --------------------------------------------- English on vhyl's classes */
852
853 /*
854 * vhyl declares these properties and gives them no words. The words are
855 * English, so they are here. A game loading vhyl without a language module
856 * gets a library that describes nothing, which is correct rather than broken:
857 * it has not said what language it is in.
858 */
859
860 msgGameEnded: Message id ='game.ended'
861 text ='The game has ended. You can UNDO the ending or close the session. ';
862 msgPlayerDesc: Message id ='player.desc'
863 text ='{You/he} {look} as well as can be expected. ';
864
865 /* Emitting properties are literal prose. Ask a Message for expanded text. */
866 modify player
867 desc ="<<msgFor('player.desc', player, nil)>>"
868 ;
869
870 modify Thing
871 desc ="There is nothing unusual about <<theName(self)>>. "
872 ;
873
874 modify Actor
875 desc ="<<name>> is here. "
876 ;
877
878 modify TopicEntry
879 reply ="There is no answer. "
880 ;
881
882 modify Switch
883 desc ="A switch, currently <<isOn ? 'on' : 'off'>>. "
884 ;
885
886 modify Lever
887 desc ="A lever, currently <<isUp ? 'up' : 'down'>>. "
888 ;
889
890 modify Dial
891 desc ="A dial, set to <<setting>>. "
892 ;
893
894 modify Button
895 desc ="A button. "
896 ;
897
898 modify Platform
899 enterDesc ="<<language.expand('{You/he} {get} onto ', nil)>><<theName(self)>>. "
900 ;
901
902 modify Booth
903 enterDesc ="<<language.expand('{You/he} {get} into ', nil)>><<theName(self)>>. "
904 ;
905
906 modify Enterable
907 enterDesc ="<<language.expand('{You/he} {go} into ', nil)>><<theName(self)>>. "
908 ;
909
910 /* ------------------------------------------- the rest of vhyl's messages */
911
912 /* Examining and describing */
913 enExamineEmpty: Message id ='examine.empty' say(c) {"<<TheName(c.dobj)>> is empty. "; return nil; };
914 enRoomThereIs: Message id ='room.thereis' say(c) {"There <<isAreFor(c.dobj)>> <<aName(c.dobj)>> here"; return nil; };
915
916 /* Putting things places */
917 enPutOnOk: Message id ='puton.ok' text ='{You/he} {put} {the dobj} on {the iobj}. ';
918 enPutInOk: Message id ='putin.ok' text ='{You/he} {put} {the dobj} in {the iobj}. ';
919 enPutBehindOk: Message id ='putbehind.ok' text ='{You/he} {put} {the dobj} behind {the iobj}. ';
920 enPutOnCannot: Message id ='puton.cannot' text ='{You/he} {can}not put anything on {the dobj}. ';
921 enPutInCannot: Message id ='putin.cannot' say(c) {"<<TheName(c.dobj)>> will not hold anything. "; return nil; };
922 enPutBehindNo: Message id ='putbehind.cannot' text ='{You/he} {can}not put anything behind {the dobj}. ';
923 enPutInShut: Message id ='putin.shut' say(c) {"<<TheName(c.dobj)>> is closed. "; return nil; };
924 enPutOnNoRoom: Message id ='puton.noroom' say(c) {"There is no room on <<theName(c.dobj)>> for that. "; return nil; };
925 enPutInNoRoom: Message id ='putin.noroom' say(c) {"There is no room in <<theName(c.dobj)>> for that. "; return nil; };
926 enPutWontMove: Message id ='put.wontmove' say(c) {"<<TheName(c.dobj)>> will not move. "; return nil; };
927 enPutInItself: Message id ='put.initself' text ='That would be a neat trick. ';
928
929 /* Travel */
930 enGoBarred: Message id ='go.barred' text ='{You/he} {can}not get past {the dobj}. ';
931 enRideWontGo: Message id ='ride.wontgo' say(c) {"<<TheName(c.dobj)>> will not go that way. "; return nil; };
932
933 /* Riding and entering */
934 enBoardOk: Message id ='board.ok' text ='{You/he} {get} into {the dobj}. ';
935 enBoardCannot: Message id ='board.cannot' say(c) {"<<TheName(c.dobj)>> is not something you can ride. "; return nil; };
936 enBoardAlready: Message id ='board.already' text ='{You/he} {is} already in {the dobj}. ';
937 enEnterCannot: Message id ='enter.cannot' text ='That is not something you can get into. ';
938 enGetOffNothing: Message id ='getoff.nothing' text ='You are not on anything. ';
939 enGetOffOff: Message id ='getoff.off' text ='{You/he} {get} off {the dobj}. ';
940 enGetOffOutOf: Message id ='getoff.outof' text ='{You/he} {get} out of {the dobj}. ';
941
942 /* Pushing */
943 enPushOk: Message id ='push.ok' text ='{You/he} {push} {the dobj} ahead of {you/him}. ';
944 enPushCannot: Message id ='push.cannot' text ='{You/he} {can}not push {the dobj} around. ';
945 enPushThrough: Message id ='push.throughconnector' text ='{You/he} {can}not push {the dobj} through {the iobj}. ';
946
947 /* Looking under and behind */
948 enUnderNothing: Message id ='lookunder.nothing' say(c) {"There is nothing under <<theName(c.dobj)>>. "; return nil; };
949 enBehindNothing: Message id ='lookbehind.nothing' say(c) {"There is nothing behind <<theName(c.dobj)>>. "; return nil; };
950
951 /* Gadgets */
952 enFlipOn: Message id ='flip.on' text ='{You/he} {flip} {the dobj} on. ';
953 enFlipOff: Message id ='flip.off' text ='{You/he} {flip} {the dobj} off. ';
954 enFlipCannot: Message id ='flip.cannot' say(c) {"<<TheName(c.dobj)>> is not something you can flip. "; return nil; };
955 enPullUp: Message id ='pull.up' text ='{You/he} {pull} {the dobj} up. ';
956 enPullDown: Message id ='pull.down' text ='{You/he} {pull} {the dobj} down. ';
957 enPressOk: Message id ='press.ok' text ='Click. ';
958 enPressNothing: Message id ='press.nothing' say(c) {"Pressing <<theName(c.dobj)>> achieves nothing. "; return nil; };
959 enTurnNothing: Message id ='turn.nothing' say(c) {"Turning <<theName(c.dobj)>> achieves nothing. "; return nil; };
960 enSetCannot: Message id ='set.cannot' text ='{You/he} {can}not set {the dobj} to anything. ';
961 enSetBadValue: Message id ='set.badvalue' say(c) {"<<TheName(c.dobj)>> will not go to that. "; return nil; };
962
963 /* Attachment */
964 enAttachOk: Message id ='attach.ok' text ='{You/he} {attach} {the dobj} to {the iobj}. ';
965 enAttachCannot: Message id ='attach.cannot' text ='Those will not go together. ';
966 enAttachWrong: Message id ='attach.wrongpair' say(c) {"<<TheName(c.dobj)>> will not join to <<theName(c.iobj)>>. "; return nil; };
967 enAttachAlready: Message id ='attach.already' text ='They are joined already. ';
968 enDetachOk: Message id ='detach.ok' text ='{You/he} {detach} {the dobj} from {the iobj}. ';
969 enDetachNot: Message id ='detach.notjoined' text ='They are not joined. ';
970
971 /* Senses */
972 enListenNothing: Message id ='listen.nothing' text ='You hear nothing out of the ordinary.';
973 enSmellNothing: Message id ='smell.nothing' text ='You smell nothing out of the ordinary.';
974
975 /* Actors */
976 enTalkNotActor: Message id ='talk.notactor' say(c) {"<<TheName(c.dobj)>> says nothing. "; return nil; };
977 enTalkNoTopic: Message id ='talk.notopic' say(c) {"<<TheName(c.dobj)>> has nothing to say about that. "; return nil; };
978
979 /* Score */
980
981 /* Said when an action needs an object the player did not name. */
982 enVagueTopic: Message id ='talk.vaguetopic' text ='You will have to be more specific. ';
983
984 /* ------------------------------------------- sentences, not fragments */
985
986 enRoomAndClosed: Message id ='room.andclosed' text =', closed';
987 enRoomAndHolding: Message id ='room.andholding' say(c) {", with <<aName(c.list_[1])>> <<prepFor(c.dobj.objPos)>> it"; return nil; };
988 enRoomEndOfItem: Message id ='room.endofitem' text ='. ';
989
990 enExamineOnIt: Message id ='examine.onit' say(c) {"On it you see <<nameList(c.list_)>>. "; return nil; };
991 enExamineInIt: Message id ='examine.init' say(c) {"In it you see <<nameList(c.list_)>>. "; return nil; };
992
993 enSetOk: Message id ='set.ok' text ='{You/he} {set} {the dobj} to {way}. ';
994
995 enUnderFound: Message id ='lookunder.found' say(c) {"Under <<theName(c.dobj)>> you find <<nameList(c.list_)>>. "; return nil; };
996 enBehindFound: Message id ='lookbehind.found' say(c) {"Behind <<theName(c.dobj)>> you find <<nameList(c.list_)>>. "; return nil; };
997
998 enInventoryItem: Message id ='inventory.item' say(c) {"<<aName(c.dobj)>>"; return nil; };
999 enInventoryWorn: Message id ='inventory.itemworn' say(c) {"<<aName(c.dobj)>> (being worn)"; return nil; };
1000
1001 enActNoVerb: Message id ='act.noverb' say(c) {"[no verb <<c.way_>>]"; return nil; };
1002 enActOutOfReach: Message id ='act.outofreach' say(c) {"<<TheName(c.dobj)>> is not within reach. "; return nil; };
1003
1004 enRoomEnter: Message id ='room.enter' text ='';
1005 enRoomName: Message id ='room.name' say(c) {"<<c.dobj.name>>"; return nil; };
1006
1007 enTakeSeveral: Message id ='take.ok.several' say(c) {"Taken: <<nameList(c.list_)>>. "; return nil; };
1008 enDropSeveral: Message id ='drop.ok.several' say(c) {"Dropped: <<nameList(c.list_)>>. "; return nil; };
1009 enOpenSeveral: Message id ='open.ok.several' say(c) {"<<language.expand('{You/he} {open} ', c)>><<nameList(c.list_)>>. "; return nil; };
1010 enCloseSeveral: Message id ='close.ok.several' say(c) {"<<language.expand('{You/he} {close} ', c)>><<nameList(c.list_)>>. "; return nil; };
1011
1012 enEatOk: Message id ='eat.ok' text ='{You/he} {eat[s]|ate} {the dobj}. ';
1013 enEatCannot: Message id ='eat.cannot' text ='{The dobj} {is} not something you can eat. ';
1014 enDrinkOk: Message id ='drink.ok' text ='{You/he} {drink[s]|drank} {the dobj}. ';
1015 enDrinkCannot: Message id ='drink.cannot' text ='{The dobj} {is} not something you can drink. ';
1016 enPourOk: Message id ='pour.ok' text ='{You/he} {pour} the {way} into {the iobj}. ';
1017 enPourNothing: Message id ='pour.nothing' text ='Nothing {will} pour out of {the dobj}. ';
1018 enPourWontTake: Message id ='pour.wontake' text ='You {cannot|could not} pour anything into {the dobj}. ';
1019
1020 /* Handing something over, and holding it up. */
1021 enGiveNotActor: Message id ='give.notactor' text ='{The dobj} {is} not going to take it. ';
1022 enGiveRefused: Message id ='give.refused'
1023 say(c) {"<<TheName(c.dobj)>> does not want <<theName(c.iobj)>>. "; return nil; };
1024 enShowNothing: Message id ='show.nothing'
1025 say(c) {"<<TheName(c.dobj)>> looks at <<theName(c.iobj)>> and says nothing. "; return nil; };
1026
1027 enGoTooBulky: Message id ='go.toobulky'
1028 text ='{You/he} will not fit through {the dobj} carrying all that. ';
1029
1030 enOpenLocked: Message id ='open.locked' text ='{The dobj} {is} locked. ';
1031 enUnlockOk: Message id ='unlock.ok'
1032 text ='{You/he} {unlock} {the dobj} with {the iobj}. ';
1033 enLockOk: Message id ='lock.ok'
1034 text ='{You/he} {lock} {the dobj} with {the iobj}. ';
1035 enUnlockAlready: Message id ='unlock.already' text ='{The dobj} {is} not locked. ';
1036 enLockAlready: Message id ='lock.already' text ='{The dobj} {is} already locked. ';
1037 enLockCannot: Message id ='lock.cannot' text ='{The dobj} {has} no lock. ';
1038 enUnlockWrong: Message id ='unlock.wrongkey'
1039 say(c)
1040 {
1041 if (c.iobj == nil)
1042 "<<language.expand('{You/he} {do} not have the key for ', c)>><<theName(c.dobj)>>. ";
1043 else
1044 "<<TheName(c.iobj)>> does not fit <<theName(c.dobj)>>. ";
1045 return nil;
1046 };
1047
1048 /* Picking yourself up. */
1049 enTakeSelf: Message id ='take.self' text ='You are already carrying yourself around. ';
1050
1051 /* Letting a turn pass. */
1052 enWaitOk: Message id ='wait.ok' text ='Time passes. ';
1053
1054 enReachTooFar: Message id ='reach.toofar' say(c) {"<<TheName(c.dobj)>> is too far away. "; return nil; };
1055
1056 dirName(way)
1057 {
1058 if (way == north) return'north';
1059 if (way == south) return'south';
1060 if (way == east) return'east';
1061 if (way == west) return'west';
1062 if (way == northeast) return'northeast';
1063 if (way == northwest) return'northwest';
1064 if (way == southeast) return'southeast';
1065 if (way == southwest) return'southwest';
1066 if (way == up) return'up';
1067 if (way == down) return'down';
1068 if (way == inward) return'in';
1069 return'out';
1070 }
1071
1072 enRoomActorHere: Message id ='room.actorhere' say(c) {"<<AName(c.dobj)>> is here. "; return nil; };
1073 enTravelLeaves: Message id ='travel.leaves' say(c) {"<<TheName(c.dobj)>> leaves. "; return nil; };
1074 enTravelLeavesWay: Message id ='travel.leavesway' say(c) {"<<TheName(c.dobj)>> leaves to the <<dirName(c.way_)>>. "; return nil; };
1075 enTravelArrives: Message id ='travel.arrives' say(c) {"<<TheName(c.dobj)>> arrives. "; return nil; };
1076 enTravelArrivesWay: Message id ='travel.arrivesway' say(c) {"<<TheName(c.dobj)>> arrives from the <<dirName(c.way_)>>. "; return nil; };
1077 enTravelOpens: Message id ='travel.opens' say(c) {"<<TheName(c.dobj)>> opens <<theName(c.iobj)>>. "; return nil; };
1078 enTravelComesWith: Message id ='travel.comeswith' say(c) {"<<TheName(c.dobj)>> comes with you. "; return nil; };
1079 enTravelLeadsWay: Message id ='travel.leadsway' say(c) {"<<TheName(c.dobj)>> leads the way. "; return nil; };
1080
1081 postureName(p)
1082 {
1083 if (p == sitting) return'sitting';
1084 if (p == lying) return'lying down';
1085 return'standing';
1086 }
1087
1088 enPostureStand: Message id ='posture.stand' say(c) {"<<c.dobj == player ? 'You stand up. ' : 'You stand on ' + theName(c.dobj) + '. '>>"; return nil; };
1089 enPostureSit: Message id ='posture.sit' text ='{You/he} {sit} on {the dobj}. ';
1090 enPostureLie: Message id ='posture.lie' text ='{You/he} {lie|lay} on {the dobj}. ';
1091 enPostureAlready: Message id ='posture.already' text ='You are already standing. ';
1092 enPostureNowhere: Message id ='posture.nowhere' text ='There is nothing here to do that on. ';
1093 enPostureCannot: Message id ='posture.cannot' text ='{You/he} {can}not get on {the dobj}. ';
1094 enPostureWont: Message id ='posture.wontallow' text ='{You/he} {can}not do that on {the dobj}. ';
1095 enReachTooHigh: Message id ='reach.toohigh' say(c) {"<<TheName(c.dobj)>> is out of reach from here. "; return nil; };
1096 enEnterShut: Message id ='enter.shut' text ='{The dobj} {is} shut. ';
1097 enEnterLocked: Message id ='enter.locked' text ='{The dobj} {is} locked. ';
1098 enStageNotThere: Message id ='stage.notthere' text ='{You/he} would have to be on {the dobj} first. ';
1099
1100 enSayOk: Message id ='say.ok' say(c) {"Okay: <q><<c.way_>></q>"; return nil; };
1101 enSayNothing: Message id ='say.nothing' text ='Say what? ';
1102 enWriteOnCannot: Message id ='writeon.cannot' text ='There\'s no way to write anything on {the dobj}. ';
1103 enThinkAbout: Message id ='think.about' text ='{You/he} {think} about {the dobj}, and {is} none the wiser. ';
1104 enConsultCannot: Message id ='consult.cannot' text ='{You/he} {can}not look things up in {the dobj}. ';
1105 enConsultNothing: Message id ='consult.nothing' text ='{You/he} {find} nothing about <q>{way}</q> in {the dobj}. ';
1106
1107 enOpenClosed: State
1108 appliesTo = Thing
1109 applies(it) { return it.isOpenable; }
1110 watches = &isOpen
1111 whenTrue ='open'
1112 whenFalse ='closed'
1113 ;
1114
1115 enDecoration: Message id ='decoration.notimportant' text ='{The dobj} {is}n\'t important. ';
1116 enImmovable: Message id ='immovable.cannot' text ='{The dobj} {is} far too heavy to move. ';
1117 enIntangible: Message id ='intangible.cannot' text ='{The dobj} {is}n\'t something {you/he} can touch. ';
1118 enUnthingGone: Message id ='unthing.gone' text ='{The dobj} {is}n\'t here. ';
1119
1120 /* One candidate in a disambiguation question, told apart when it has to be. */
1121 enParseWhichOne: Message id ='parse.whichone'
1122 say(c) {", the <<c.way_ == nil ? '' : c.way_ + ' '>><<c.dobj.name>>"; return nil; }
1123 ;
1124 enTalkHermit: Message id ='talk.hermit' say(c) {"<<TheName(c.dobj)>> is too busy to answer. "; return nil; };
1125 enOrderRefused: Message id ='order.refused' say(c) {"<<TheName(c.dobj)>> has no intention of doing that. "; return nil; };
1126
1127 enPrecondFirst: Message id ='precond.first' say(c) {"(first <<c.way_>> <<theName(c.dobj)>>)"; return nil; };
1128 enPrecondCannot: Message id ='precond.cannot' text ='{You/he} {can}not do that to {the dobj}. ';
1129 enPrecondHeld: Message id ='precond.held' text ='{You/he} {is}n\'t holding {the dobj}. ';
1130 enPrecondVisible: Message id ='precond.visible' text ='{You/he} {can}not see {the dobj}. ';
1131 enPrecondOpen: Message id ='precond.open' say(c) {"<<TheName(c.dobj)>> is closed. "; return nil; };
1132 enPrecondClosed: Message id ='precond.closed' say(c) {"<<TheName(c.dobj)>> is open. "; return nil; };
1133 enPrecondUnlocked: Message id ='precond.unlocked' say(c) {"<<TheName(c.dobj)>> is locked. "; return nil; };
1134 enPrecondWorn: Message id ='precond.worn' say(c) {"You're wearing <<theName(c.dobj)>>. "; return nil; };
1135 enPrecondEmpty: Message id ='precond.empty' say(c) {"There's something in <<theName(c.dobj)>>. "; return nil; };
1136 enPrecondReach: Message id ='precond.reach' text ='{You/he} {can}not reach {the dobj} through what it\'s in. ';
1137 /* Text, not a body: `refuses` reads it rather than saying it. */
1138 enTakePart: Message id ='take.part' text ='{The dobj} {is} part of {the iobj}. ';
1139 enDetachPart: Message id ='detach.part' text ='{You/he} {detach} {the dobj}. ';
1140 enDetachFixed: Message id ='detach.fixed' say(c) {"<<TheName(c.dobj)>> will not come off. "; return nil; };
1141
1142 /* Score. `c.way_` carries the number. */
1143 enScoreIsWhole: Message id ='score.is' say(c) {"Your score is <<c.way_>>. "; return nil; };
1144 enScoreGained: Message id ='score.gained' say(c) {"[Your score has just gone up by <<c.way_>> points.]"; return nil; };
1145 enScoreGained1: Message id ='score.gained1' text ='[Your score has just gone up by 1 point.]';
1146
1147 /* Optional dialogue module's ordinary exit label. */
1148 enDialogueGoodbye: Message id ='dialogue.goodbye' text ='Say goodbye';