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';