1#charset "utf-8" 2 3#include <adv3.h> 4#include <en_us.h> 5 6extern function extern_function; 7extern method extern_method; 8extern function extern_function(a, b=a, c='<<a>>', d:, e:=1, f?, ...); 9extern method extern_method(a, b=a, c='<<a>>', d:, e:=1, f?, [g]);; 10extern class extern_class; 11extern object extern_object; 12intrinsic 't3vm' { }; 13#ifndef PropDefAny 14intrinsic class Object 'root-object/030004' { }; 15#endif 16object /**//**/ // /* \\ 17#define Room Unthing 18 template [lst]; 19 20/* 21 * Quotations from "Le Roman de la Rose" are transcribed from MS. Douce 195, 22 * owned by Bodleian Library, University of Oxford 23 * (http://image.ox.ac.uk/show?collection=bodleian&manuscript=msdouce195). 24 */ 25 26versionInfo: GameID 27 IFID = '17d8efc3-07da-4dde-a837-ff7c4e386a77' 28 name = 'Pygmentalion' 29 byline = 'by David Corbett' 30 htmlByline = 'by <a href="mailto:corbett.dav\100husky.neu.edu">David 31 Corbett</a>' 32 version = '1' 33 authorEmail = 'David Corbett\040<corbett.dav\x40husky.neu.edu>' 34 desc = 'You have fallen in love with a statue\x2e' 35 htmlDesc = 'You have fallen in love with a statue\x2E' 36; 37 38/* 39 * Pymalion fu ẽtailleꝛꝛes. 40 * Poᷣtrayãs en fus ⁊ en peꝛꝛeˢ 41 * En metaulx en os ⁊ en cyꝛes 42 * Et en touteˢ aultres matires. 43 * Quon peult a tel oeuure trouuer. 44 * Poᷣ ſon grant engin eſpꝛouuer. 45 * Car maiſtre en fu bien dire loz. 46 * Ainſi com poᷣ acquerre loz 47 * Se voult a poᷣtraire deduyꝛe 48 * Si fiſt vng ymage diuuyꝛe 49 * Et miſt au faire tel entente 50 * Quel fu ſi plaiſãt et ſi gente 51 * Quel ſembloit eſtre auſſi viue. 52 * Com la plus belle riens q̇ viue 53 * (MS. Douce 195, fol. 149r) 54 */ 55 56modify _init() 57{ 58 ({: local r, r = randomize, r})(); 59 replaced(); 60} 61 62gameMain: GameMainDef 63 initialPlayerChar: Actor { 64 desc = "You look the same as usual, but you feel unusually 65 sentimental. " 66 location = entrance 67 } 68 showIntro 69 { 70 "The statue is undeniably a masterpiece: the most skillful carving you 71 have ever done, and the most beautiful woman you have ever seen. 72 Unfortunately, she is also an inanimate block, and now you can neither 73 work nor rest for unrequitable love.\b 74 Once again you stumble into your studio, hoping and praying to find 75 your statue brought to life.\b 76 <b><<versionInfo.name>></b>\r\n 77 <<versionInfo.byline>>\b"; 78 } 79; 80 81enum token token, tokOp, token; 82 83modify cmdTokenizer 84 rules_ = static 85 [ 86 ['whitespace', new RexPattern('%s+'), nil, &tokCvtSkip, nil], 87 ['punctuation', new RexPattern('[.,;:?!]'), tokPunct, nil, nil], 88 ['spelled number', 89 new RexPattern('<NoCase>(twenty|thirty|forty|fifty|sixty|' 90 + 'seventy|eighty|ninety)-' 91 + '(one|two|three|four|five|six|seven|eight|nine)' 92 + '(?!<AlphaNum>)'), 93 tokWord, &tokCvtSpelledNumber, nil], 94 ['spelled operator', new RexPattern( 95 '<NoCase>(plus|positive|minus|negat(iv)?e|not|inverse(%s+of)?|' 96 + 'times|over|divided%s+by|mod(ulo)?|and|xor|or|[al]?sh[lr])' 97 + '(?!<AlphaNum>)'), 98 tokOp, &tokCvtSpelledOperator, nil], 99 ['operator', R'[-!~+*/%&^|]|<<|>>>?', tokOp, nil, nil], 100 ['word', new RexPattern('<Alpha|-|&><AlphaNum|-|&|squote>*'), 101 tokWord, nil, nil], 102 ['string ascii-quote', R"""<min>([`\'"])(.*)%1(?!<AlphaNum>)""", 103 tokString, nil, nil], 104 ['string back-quote', R"<min>`(.*)'(?!%w)", tokString, nil, nil], 105 ['string curly single-quote', new RexPattern('<min>\u2018(.*)\u2019'), 106 tokString, nil, nil], 107 ['string curly double-quote', new RexPattern('<min>\u201C(.*)\u201D'), 108 tokString, nil, nil], 109 ['string unterminated', R'''([`\'"\u2018\u201C](.*)''', tokString, 110 nil, nil], 111 ['integer', new RexPattern('[0-9]+'), tokInt, nil, nil] 112 ] 113 replace tokCvtSpelledOperator(txt, typ, toks) 114 { 115 toks.append([rexReplace(R'%s+', txt.toLower(), '\\'), typ, txt]); 116 } 117; 118 119/* Tokens */ 120 121/* 122 * Puiˢ li reueſt en maĩteˢ guiſes. 123 * Robeˢ faicteˢ ꝑ grãˢ maiſtriſeˢ. 124 * De biaulx dꝛaps de ſoye ⁊ de laĩe. 125 * Deſcarlate de tiretaine 126 * De vert de pers ⁊ de bꝛunecte 127 * De couleᷣ freſche fine ⁊ necte 128 * Ou moult a riches paneˢ miſes. 129 * Herminees vaires et griſes 130 * Puis les li roſte puis reſſaye. 131 * Cõmant li ſiet robbe de ſaye 132 * Sendaulx meloguins galebꝛunˢ. 133 * Indes vermeilz iaunes ⁊ bꝛunˢ. 134 * [...] 135 * Aultre foiz luy repꝛẽd courage. 136 * De tout oſter ⁊ mectre guindeˢ. 137 * Iaunes vermeilles vers ⁊ indeˢ. 138 * (MS. Douce 195, fol. 150r) 139 */ 140 141class Token: Achievement 142{ 143 points = 1; 144 desc = "<<before_>><<desc_>><<after_>>"; 145 before = before = '', before_ 146 after = (after = '', after_) 147} 148 149Token template inherited 'before_' 'after_' 'desc_'; 150 151#define DefineToken(name, before, after) name##Token: Token before after #@name 152 153DefineToken(builtin, '<font color=green>', '</font>'); 154DefineToken(comment, '<i><font color=#408080>', '</font></i>'); 155DefineToken(decorator, '<font color=#aa22ff>', '</font>'); 156DefineToken(error, '<U><FONT COLOR=RED>', '</FONT></U>'); 157DefineToken(escape, '<b><font color=#bb6622>', '</font></b>'); 158DefineToken(float, '<u><font color=gray>', '</font></u>'); 159DefineToken(keyword, '<b><font face=TADS-Sans color=green>', '</font></b>'); 160DefineToken(label, '<font color=#A0A000>', '</font>'); 161DefineToken(long, '<i><font color=gray>', '</font></i>'); 162DefineToken(name, '<u>', '</u>'); 163DefineToken(operator, '<b><font color=\"#AA22FF\">', '</font></b>'); 164DefineToken(string, '<font color=\'#BA2121\'>', '</font>'); 165DefineToken(whitespace, '<font color="bgcolor"bgcolor=\'text\'>', '</font>'); 166 167function highlightToken(tokenString) 168{ 169 local token = [ 170 'built in' -> builtinToken, 171 'comment' -> commentToken, 172 'decorator' -> decoratorToken, 173 'error' -> errorToken, 174 'escape' -> escapeToken, 175 'float' -> floatToken, 176 'keyword' -> keywordToken, 177 'label' -> labelToken, 178 'long' -> longToken, 179 'name' -> nameToken, 180 'operator' -> operatorToken, 181 'string' -> stringToken, 182 'white space' -> whitespaceToken, 183 * -> nil 184 ][tokenString.toLower()]; 185 if (!token) 186 return tokenString; 187 token.awardPointsOnce(); 188 return '<<token.before>><<tokenString>><<token.after>>'; 189} 190 191string /**//**/ // /* \\ 192#define Room Unthing 193 template <<highlight *>> highlightToken; 194 195/* Grammar for materials */ 196 197dictionary property material; 198grammar adjWord(material): <material material>->adj_ : AdjPhraseWithVocab 199 getVocabMatchList(resolver, results, extraFlags) 200 { 201 return getWordMatches(adj_, &material, resolver, extraFlags, 202 VocabTruncated); 203 } 204 getAdjustedTokens() 205 { 206 return [adj_, &material]; 207 } 208; 209 210/* Rooms and objects */ 211 212+ property location; 213 214entrance: Room 'Entrance' 215 "You are in the entrance to your studio. This is where you carve great 216 works of art, not that you have felt like making any lately. A door leads 217 outside, and the studio itself is to the north and the east. " 218 north = workbenchRoom 219 northeast = sinkRoom 220 east = altarRoom 221 south = door 222 out asExit(south) 223; 224 225+ door: LockableWithKey, Door 'door' 'door' 226 "It is a simple wooden door. " 227 material = 'wood' 'wooden' 228 keyList = [key] 229 cannotOpenLockedMsg = '{The dobj/He} {is} locked. You cannot 230 <<highlight 'escape'>>! ' 231; 232 233key: PresentLater, Key 'key' 'key' @altar 234 "It is a <<unless clean>>grimy<<end>> bronze key. <<if clean>>On it is \ 235 etched the word <q><<keyword>></q>. " 236 material = 'bronze' 237 clean = nil 238 keyword = (keyword = randomGreekWord(), targetprop) 239 dobjFor(Clean) { verify { } action { askForIobj(CleanWith); } } 240 dobjFor(CleanWith) 241 { 242 verify 243 { 244 if (clean) 245 illogicalAlready('{The dobj/He} {is} already clean. '); 246 } 247 action 248 { 249 gDobj.clean = true; 250 "{You/He} clean{s} {the dobj/him}, revealing an inscription. "; 251 } 252 } 253 dobjFor(Read) { verify { nonObvious; } } 254; 255 256workbenchRoom: Room 'At the Workbench' 257 "This workbench, in the northwest part of the studio, was where you would 258 create works of art. Now you just come here to contemplate your 259 creation’s beauty and lament your hopeless situation.\b 260 The statue stands on a plinth beside the workbench. " 261 east = sinkRoom 262 southeast = altarRoom 263 south = entrance 264 getDestName(actor, origin) { return 'the workbench'; } 265; 266 267+ workbench: Fixture, Surface 268 'workbench/bench/material/materials/tool/tools' 'workbench' 269 "Normally, the workbench would be scattered with half-finished projects, 270 but now your tools and materials lie abandoned. " 271; 272 273+ plinth: Fixture, Thing 'marble plinth/pedestal' 'plinth' 274 "It’s a smoothed block of marble about a cubit high. " 275; 276 277replace grammar predicate(Screw): ' ': object; 278replace grammar predicate(ScrewWith): ' ': object; 279+ + statue: Fixture, Surface 280 '"creation\'s" beauty/carving/creation/galatea/statue/woman' 'statue' 281 "This is a<<if nameToken.scoreCount>>n untitled<<end>> statue of a woman 282 carved from <<if errorToken.scoreCount>>flawless <<end>> 283 <<if whitespaceToken.scoreCount>>milk-white <<end>>ivory. 284 <<if escapeToken.scoreCount || longToken.scoreCount>>Her 285 <<if longToken.scoreCount>>long <<end>>hair is done up in a 286 chignon<<if escapeToken.scoreCount>>, with a few strands falling down her 287 neck<<end>><<if floatToken.scoreCount>>, and \v<<else>>.<<end>><<end>> 288 <<if floatToken.scoreCount>>She radiates an aura of contrapposto grace. 289 <<end>><<if keywordToken.scoreCount>>\bYou wonder what she 290 <<if labelToken.scoreCount>>is going to<<else>>will<<end>> be like as a 291 woman. 292 <<if decoratorToken.scoreCount>>Maybe she’ll be a painter and expand 293 your business.<<end>> 294 <<if operatorToken.scoreCount>>Maybe she’ll have a head for figures 295 and will put the accounts in order.<<end>> 296 <<if builtinToken.scoreCount>>She’ll love you, obviously, but beyond 297 that you don’t know.<<end>><<end>> 298 <<if commentToken.scoreCount>>If only Aphrodite would bring her to life 299 without this silly puzzle about tokens and mirrors!<<end>> " 300 material = 'ivory' 301 propertyset 'is*' 302 { 303 propertyset 'H*' 304 { 305 im = nil\ 306 er = true; 307 } 308 It = true 309 } 310 iobjFor(PutOn) 311 { 312 check 313 { 314 if (gDobj not /**//**/ // /* \\ 315#define Room Unthing 316 in (necklace, __objref(necklace, warn))) 317 "How rude! You don’t know what you were thinking. "; 318 } 319 } 320 iobjFor(GiveTo) remapTo(PutOn, DirectObject, IndirectObject) 321; 322 323+++ necklace: Wearable 324 'pearl necklace/string pearls' '<<highlight 'string'>> of pearls' 325 "This is a masterfully crafted pearl necklace. You hope the statue 326 won’t mind if you hold onto it for a while. " 327 initDesc = "You gave the statue this pearl necklace yesterday. " 328 isPlural = true 329; 330 331altarRoom: Room 'At the Altar' 332 "Light from the window illuminates a crude altar. Until recently, this 333 corner was your bedroom. The rest of the studio lies north and west. " 334 north = sinkRoom 335 northwest = workbenchRoom 336 west = entrance 337 getDestName(actor, origin) { return 'the altar'; } 338; 339 340+ window: Fixture 'window' 'window' 341 "It’s just a window above the altar. <<one of>>The space under the 342 window is blank; as an interior <<highlight 'decorator'>>, you can’t 343 help but think the wall would benefit from a bas-relief, but – 344 <i>sigh</i> &endash you are too lovelorn to wield the chisel. <<||>>The 345 wall right below it is a boring <<highlight 'white space'>>. <<stopping>>" 346; 347 348+ altar: Fixture, Surface 'crude rough altar/banker/slab' 'altar' 349 "A rough marble slab lies on a wooden banker. In your rush to construct an 350 altar, you neglected the usual surface finish and friezes, but you pray at 351 it anyway. You are sure the gods will understand. " 352 material = 'marble' 'wood' 'wooden' 353 bulkCapacity = 1 354 dobjFor(PrayAt) 355 { 356 verify { } 357 action() 358 { 359 /* 360 * Biaulx dieux diſt il tout ce poez. 361 * Sil voꝰ plaiſt ma requeſte oez 362 * [...] 363 * Et la belle q̇ mon cueᷣ emble 364 * Qui ſi bien yuoyꝛe reſſemble. 365 * Deuiengne ma loyal amye 366 * De fẽme ait coꝛps ame et vie 367 * (MS. Douce 195, fol. 151r) 368 */ 369 local offering; 370 foreach (offering in contents); 371 if (!keywordToken.scoreCount) 372 "<<one of>><q>O Aphrodite,</q> you say, <q>comforter of 373 hopeless lovers, hear my prayer! May she to whom I have given 374 my heart be given body, soul, and life. And a colorful 375 personality. And&mdash</q>\b 376 You are interrupted by a shimmering about the altar. As you 377 watch, it takes the form of a callipygian goddess.\b 378 <q>Mortal, I have heard your heart-felt and oft-repeated plea, 379 and I will take pity on you,</q> says Aphrodite. <q>If you give 380 me a token of your love as an offering, I will give you the 381 <<highlight 'keyword'>> of life. Speak this word in the 382 presence of a mirror, and I will grant your request.</q>\b 383 She fades away, adding, <q>As for her colorful personality, 384 just look around you.</q> <<or>><<stopping>>"; 385 else if (key.location) 386 "<q>O Aphrodite,</q> you say, <q>what am I supposed to do 387 again?</q>\bThe goddess reappears and reminds you to speak the 388 keyword of life at a mirror. <<one of>><q>What’s the 389 keyword, then?</q> <q>Gods help those who help themselves. 390 Figure it out yourself.</q><<or>><q>Why a mirror?</q> <q>I like 391 mirrors.</q><<purely at random>> "; 392 else if (offering == necklace) 393 { 394 "Aphrodite reappears. <q>A necklace! Perfect!</q> The necklace 395 disappears in a bright flash. When your eyes readjust, you see 396 a key lying in its place. "; 397 necklace.moveInto(nil); 398 key.makePresent(); 399 } 400 else if (+offering) 401 "Aphrodite reappears. She eyes <<offering.theNameObj>> 402 skeptically. <q><<one of>>No <<highlight 'comment'>>.<<or>>You 403 call <i>that</i> a token of love?<<or>>\^<<offering.aNameObj>>? 404 Really?<<or>>Come on, mortal, it’s not that 405 difficult!<<then at random>></q> "; 406 else 407 "<q>I heard you the first time,</q> says Aphrodite. <q>Prove 408 your devotion by offering a token of your love at the altar, 409 or the deal’s off.</q> "; 410 } 411 } 412 iobjFor(GiveTo) remapTo(PutOn, DirectObject, IndirectObject) 413; 414 415aphrodite: Unthing 416 '(love) aphrodite/cytherea/god/goddess/venus love' 'Aphrodite' 417 '<<if gActor.canSee(altar)>>You can only pray to a god. 418 <<else>>You need an altar to interact with a god. ' 419 location = (gPlayerChar) 420 isProperName = true 421 isHer = true 422 iobjFor(GiveTo) 423 { 424 verify 425 { 426 illogical('She isn’t here. You’ll have to leave {the 427 dobj/him} somewhere she can find it. '); 428 } 429 } 430 dobjFor(PrayAt) maybeRemapTo(gActor.canSee(altar), PrayAt, altar) 431; 432 433sinkRoom: Room 'Washroom' 434 "Sculpting marble is a dusty business. You use this sink to clean off after 435 a hard day’s work. Beside the sink is a small end table, and on the 436 wall is a calculator. The rest of the studio is south and west. " 437 south = altarRoom 438 southwest = entrance 439 west = workbenchRoom 440; 441 442property level, overflowing; 443export overflowing; 444export level 'waterLevel'; 445+ sink: Fixture '(auto) (mop) auto-sink/autosink/bowl/drain/faucet/sink' 'sink' 446 "This is a state-of-the-art mop sink with anti-miasmic coating and bronze 447 backsplash. It is so modern, there are no handles or other obvious ways to 448 turn it on.\b 449 <<if overflowing>>It is overflowing. 450 <<else unless level < 19500>>It is full to the brim with water. 451 <<otherwise if level >= 15000>>It is full of water. 452 <<otherwise unless level < 10000>>It is half full of water. 453 <<else if level >= 2000>>There is some water in the sink. 454 <<else if level > 0>>A small puddle has formed at the bottom of the sink. 455 <<otherwise>>It is empty. 456 <<if level <= -1.0e+2>>It looks like it hasn’t been used in a 457 <<highlight 'long'>> time. " 458 level = not in ([lst]) { return argcount; } 459 not = in() 460 overflowing = nil 461 current = self 462 setLevel(level:) 463 { 464 targetobj.current.overflowing = level == nil; 465 targetobj.current.level = min(level ?? 0, 20000); 466 if (sink.overflowing || sink.level > 0e+1) 467 sinkWater.makePresent(); 468 if (basin.overflowing || basin.level > 0e-1) 469 basinWater.makePresent(); 470 } 471 iobjFor(CleanWith) remapTo(CleanWith, DirectObject, sinkWater) 472; 473 474++ sinkWater: PresentLater, Fixture 475 '(sink) water sink water' 'water' "<<sink.desc>>" 476 disambigName = 'water in the sink' 477 dobjFor(Drink) 478 { 479 verify { illogical('''{You're} not thirsty. '''); } 480 } 481 iobjFor(CleanWith) 482 { 483 preCond = [] 484 verify { 485 if (!location) 486 illogicalNow('There is no water in the sink. '); 487 if (!sink.overflowing && sink.level < 1e2) 488 illogicalNow('There is not enough water in the sink. '); 489 } 490 } 491; 492 493+ table: Fixture, Surface 'small end bracket/table' 'table' 494 "<<first time>>Upon closer inspection, you see that \v<<only>>The table is 495 bracketed to the wall. " 496; 497 498++ Readable '"operator\'s" manual' 'manual' 499 "<center ><<highlight 'Operator'>>’s Manual<\center>\b 500 <bq>To control the auto-sink, use the calculator add-on to enter the 501 desired volume of water. For example,\n 502 \t\t<<aHref('calculate 69 * 105', 'CALCULATE 69 TIMES 105')>>\n 503 to fill the basin with <<% ,d 0x69 * 0105>> kochliaria<!-- an ancient Greek 504 unit, < 5 ml >.\b 505 Warning: Do not use big numbers or divide by zero!<\\bq>\b" 506 dobjFor(Read) asDobjFor(Examine) 507; 508 509+ calculator: Fixture, Thing 'button/buttons/calculator/screen' 'calculator' 510 "The calculator is <<highlight 'built in'>>to the wall beside the sink. It 511 has buttons for all the standard unary and binary operations. 512 <<if(screen)>>The screen reads <<screen>>" 513 screen = nil 514 literalMatch = '' 515; 516 517method wrongContextMsg() 518{ 519 return '<font face="TADS-Typewriter"><<highlight '<<'ERROR'>>'>> {{can\'t 520 use\ \"<<self.literalMatch>>\" in that context}}</font>. '; 521} 522 523portico: OutdoorRoom 'Portico' 524 "Columns line the portico stretching east and west, and steps lead down to 525 the south. The door leads back in, and beside the door is a basin. A 526 <<highlight 'label'>> is affixed to the doorpost. " 527 north = (__objref(error, error)) 528 in asExit(north) 529 south: FakeConnector 530 { 531 "You begin moving away from the door, but then you remember the statue. 532 The gods won’t bring her to life if you give up this easily! 533 <<setMethod(&isConnectorApparent, {origin, actor: nil})>>" 534 } 535 east asExit(south) 536 west asExit(south) 537 down asExit(south) 538; 539 540error: LockableWithKey, Door ->door 'door' 'door' @portico "<<door.desc>>" 541 keyList = (otherSide.keyList) 542; 543 544+ Fixture 'column*columns' 'columns' 545 "There are six <<one of>>short columns with simple capitals<<or>>slender 546 columns with scrollwork in the capitals<<or>>tall columns with ornate 547 capitals<<sticky random>>. Above the architrave is a frieze depicting some 548 of your wares. <<first time>>The cornice overhangs the frieze a bit too 549 much, you think; perhaps you should shorten it. You try to concentrate on 550 the architecture of the portico, stoically ignoring what you cannot change, 551 but it doesn’t work. It never does. <<only>>" 552 isPlural = true 553; 554 555+ Fixture, Readable 'label/doorpost' '<<highlight 'label'>>' 556 "The <<highlight 'label'>> says <q>Pygmentalion</q><<first time>> (which is 557 your <<highlight 'name'>>)<<only>>. " 558 dobjFor(Read) asDobjFor(Examine) 559; 560 561+ basin: RestrictedContainer, Fixture 562 '(bird) basin/bath/birdbath/fountain/mosaic/pool/tile/tiles' 'basin' 563 "It is shallow but wide, and lined with tiles. It used to be a fountain, 564 but it stopped working after they installed the new sink. Something to do 565 with water pressure, no doubt. Now you just use it as a birdbath.\b 566 <<if overflowing>>Water is spilling over the sides in a turbulent flow. 567 <<else if level >= 19500>>It is full to the brim with water. You can see 568 your reflection quite clearly. Gods, you look a mess. 569 <<else if level >= 15000>>It is full of water. You can see your reflection. 570 <<else if level >= 10000>>It is half full. From the right angle, you can 571 make out a shadowy reflection of the columns, but nothing more. 572 <<else if level >= 2000>>There is some water in it, but you can still make 573 out the mosaic lining the basin. 574 <<else if level > 0>>A small puddle has formed at the bottom of the basin. 575 <<else>>It is empty. 576 <<if level <= -1.0e+2>>It looks as if it has never been filled. " 577 level = 0 578 overflowing = nil 579 isMirror = (level >= 15000) 580 setLevel(level:) 581 { 582 delegated sink.setLevel(_: sourceTextOrder ? level: nil, level: level); 583 } 584 iobjFor(CleanWith) maybeRemapTo(basinWater.location, CleanWith, 585 DirectObject, basinWater) 586; 587 588++ basinWater: PresentLater, Fixture '(basin) water basin water' 'water' 589 "<<basin.desc>>" 590 disambigName = 'water in the basin' 591 dobjFor(Drink) 592 { 593 verify 594 { 595 illogical('Drinking from a birdbath might not be the best idea. '); 596 } 597 } 598 iobjFor(CleanWith) 599 { 600 preCond = [touchObj] 601 verify { 602 illogical('Washing something in a birdbath is unlikely to get it 603 clean. '); 604 } 605 } 606; 607 608++ feather: PresentLater, Thing 609 '(bird) (dove) (pigeon) (turtle) (turtle-dove) (turtledove) feather' 610 'feather' "It’s a turtle-dove feather: an auspicious omen! " 611 initSpecialDesc = "<<one of>>A little brown bird is splashing around in the 612 basin. When it notices you, it ruffles its feathers, one of which falls 613 out, and flies out between the columns. <<or>>A feather is 614 <<if basin.overflowing || basin.level > 0>><<highlight 'float'>>ing 615 <<else>>lying <<end>> in the basin. <<stopping>>" 616; 617 618/* Water */ 619 620trickling(water) multimethod 621{ 622 if (sink.overflowing) 623 { 624 dirs: for (local dir in Direction.allDirections) 625 { 626 if (dir.ofKind(RelativeDirection)) 627 continue; 628 if (dir.ofKind(ShipboardDirection)) 629 continue dirs; 630 if (water.eventualLocation.(dir.dirProp) == __objref(entrance)) 631 return 'trickling <<dir.name>>'; 632 } 633 } 634 return 'a stagnant puddle'; 635} 636 637class Water:PresentLater,Fixture'(floor) (ground) water puddle water''water' 638 "The water on the floor is <<trickling(self)>>. " 639 disambigName = 'water on the floor' 640 specialDesc = "The floor is covered with water. " 641 dobjFor(Drink) 642 { 643 preCond = [touchObj] 644 verify { } 645 check { failCheck('{You\'re} not thirsty. '); } 646 } 647 iobjFor(CleanWith) 648 { 649 preCond = [touchObj] 650 verify { illogical('The water on the ground is too dirty. '); } 651 } 652; 653 654Water template +location | ~location "specialDesc"? inherited; 655Water +altarRoom; 656Water +sinkRoom { ;; }; 657Water { +workbenchRoom }; 658 659entranceWater: Water +entrance 660 "<<if sink.overflowing>>At your feet, all the water from the sink flows 661 into a <<%-o 02>>-dactyl slit in the baseboard. <<else>><<inherited>>" 662 vocabWords = 'water baseboard/puddle/slit water' 663; 664trickling(entranceWater w) 665{ 666 return sink.overflowing ? 'trickling into the wall' : inherited<*>(w); 667} 668 669porticoWater: Water ~portico; 670trickling(porticoWater w) 671{ 672 return basin.overflowing ? 'trickling down the stairs' : inherited<*>(w); 673} 674 675/* Calculating */ 676 677;;;class is: Exception { finalize { } };;; // InvalidSpecificationError 678 679DefineLiteralAction(Calculate) 680 checkAction() 681 { 682 if (defined(calculator) && !gActor.canTouch(calculator)) 683 { 684 { gActor.failCheck('{You/He} {can\'t} do that kind of math in 685 {your} head. '); } 686 } 687 } 688 execAction() 689 { 690 local op = function(...) { throw new is(); }, a, b; 691 local opString = (literalMatch, literalMatch); 692 if (numMatch) 693 goto binary; 694 switch (opString) 695 { 696 case '!': 697 case 'not': 698 opString = '!'; 699 op = {x : !toInteger('<<%_\u0030[1]5.3\170x>>', 16)}; 700 break; 701 case '+': 702 case 'plus': 703 case 'positive': 704 opString = '+'; 705 op = {self_ : self_}; 706 break; 707 case '-': 708 case 'minus': 709 case 'negate': 710 case 'negative': 711 opString = '−'; 712 op = {x : -x}; 713 break; 714 case '~': 715 case 'inverse': 716 case 'inverse\\of': 717 opString = '~'; 718 op = {x : ~x}; 719 break; 720 } 721 goto doCalculation; 722 binary: binaryOp: 723 switch (opString) 724 { 725 case '+': 726 case 'plus': 727 opString = '+'; 728 op = {a, b : +a+++b}; 729 break binaryOp; 730 case '-': 731 case 'minus': 732 opString = '−'; 733 op = {a, b : -b-- - -a}; 734 break; 735 case '*': 736 case 'times': 737 opString = '×'; 738 op = new function(a, b) { return a * b; }; 739 break; 740 case '/': 741 case 'over': 742 case 'divided\\by': 743 opString = '/'; 744 op = function(a, b) { return a / b; }; 745 break; 746 case '%': 747 case 'mod': 748 case 'modulo': 749 opString = 'mod'; 750 op = function(a, b, multimethod=b) { return a % multimethod; }; 751 break; 752 case '\<<': 753 case 'shl': 754 case 'ashl': 755 case 'lshl': 756 opString = '<<'; 757 op = {a, b, c? : a << b}; 758 break; 759 case '&': 760 case 'and': 761 opString = '&'; 762 op = {a, b : local badness = a, local token = b, badness & token}; 763 break; 764 case '^': 765 case 'xor': 766 opString = '^'; 767 op = {a, b, c? : a ^ b}; 768 break; 769 case '|': 770 case 'or': 771 opString = '|'; 772 op = {a, b : a | b}; 773 break; 774 case '>\>': 775 case 'shr': 776 case 'ashr': 777 opString = '>>'; 778 op = {a, b : toInteger('<<(a >> b)>>')}; 779 break; 780 case '>>>': 781 case 'lshr': 782 opString = '>>>'; 783 op = {a, b : b ? invokee(a >>> 1, --b) : a}; 784 break; 785 } 786 opString = ' <<opString>> '; 787 doCalculation: 788 "The calculator outputs "; 789 try 790 { 791 a = numMatch ? numMatch.getval(colon : nil) : nil; 792 b = numMatch2.getval(); 793 local result = toInteger(numMatch ? op(a, b) : op(b)); 794 calculator.setMethod(&screen, method() 795 { 796 return '<font face="TADS-Typewriter"><<a>><<opString>><<b>> = 797 <<%d result>></font>. '; 798 }); 799 local oldLevel = sink.level; 800 sink.current.setLevel(level: result); 801 "<<calculator.screen()>> 802 <<if sink.current == basin>>The sink gurgles and the pipes rattle. 803 <<else if sink.level == oldLevel>>The sink gurgles. 804 <<else if sink.level <= 0 && oldLevel <= 0>>The pipes rattle for a 805 moment. 806 <<else if sink.level <= 0>>All the water drains from the sink. 807 <<else if oldLevel <= 0>>The sink begins to fill with water. 808 <<else if sink.level < oldLevel - 0xabc>>Some of the water drains 809 from the sink. 810 <<else if sink.level < oldLevel>>The water level drops slightly. 811 <<else if oldLevel < sink.level - 0XABC>>Water splashes into the 812 sink for a few seconds. 813 <<else if oldLevel < sink.level>>Water dribbles from the faucet. "; 814 } 815 catch (is in) 816 { 817 calculator.literalMatch = literalMatch; 818 calculator.setMethod(&screen, &wrongContextMsg); 819 "<<calculator.screen()>>"; 820 } 821 catch (RuntimeError e) 822 { 823 calculator.setMethod(&screen, new method 824 { 825 return '<font face=\"TADS-Typewriter\"><<highlight 'ERROR'>> 826 {{<<e.exceptionMessage>>}}</font>.\b'; 827 }); 828 "<<calculator.screen()>>"; 829 switch (e.errno_) 830 { 831 case 2008: // division by zero 832 "<<if sink.current == sink 833 && (sink.level > 0 || sink.overflowing)>>The water in the 834 sink is sucked down the drain. 835 <<else if basin.level > 0 || basin.overflowing>>Water comes up 836 from the drain and <<if basin.overflowing>>spills over 837 the edges of<<else>>begins to fill<<end>> the sink. 838 <<else>>The sink gurgles and the pipes rattle. "; 839 sink.current = sink.current == sink ? basin : sink; 840 local _tmp = sink.level; 841 sink.level = basin.level; 842 basin.level = _tmp; 843 _tmp = sink.overflowing; 844 sink.overflowing = basin.overflowing; 845 basin.overflowing = _tmp; 846 if (!sink.current.overflowing) 847 break; 848 // fall through 849 case 2023: // numeric overflow 850 if (!sink.current.overflowing) 851 "<<if sink.current == sink>>High-pressure water streams 852 from the faucet, filling the sink and spilling over the 853 edge. Rivulets begin running down the slight gradient of 854 the floor. <<else>>The pipes shake loudly. "; 855 forEachInstance(Water, function(w) { 856 if ((w.eventualLocation == portico) == 857 (sink.current == basin)) 858 w.makePresent(); 859 }); 860 sink.current.setLevel(level: nil); 861 break; 862 default: 863 throw e; 864 } 865 } 866 if (!gPlayerChar.hasSeen(feather)) 867 { 868 feather.makePresentIf(basin.isMirror); 869 feather.moved = nil; 870 } 871 } 872; 873 874VerbRule(Calculate) 875 ('c' | 'calculate' | 'enter' | 'eval' | 'evaluate') (()|(singleNumber|)) 876 (tokOp->literalMatch | '!'->literalMatch) numberPhrase -> numMatch2 877 : CalculateAction 878 verbPhrase = 'calculate/calculating (what) (how) (what)' 879; 880 881/* Cleaning */ 882 883modify VerbRule(Clean) 884 [ /**//**/ // /* \\ 885#define Room Unthing 886 badness 500] ('clean' | 'wash') dobjList: 887; 888 889grammar predicate(CleanIn): 890 ('clean' | 'wash') dobjList ('at' | 'in' | 'with') singleIobj 891 : CleanWithAction 892 verbPhrase = 'clean/cleaning (what) (in what)' 893 askIobjResponseProd = inSingleNoun 894 omitIobjInDobjQuery = true 895; 896 897/* Prayer */ 898 899VerbRule(Pray) 900 [badness 500] 'pray' singleDobj 901 : PrayAtAction 902 verbPhrase = 'pray/praying (at what)' 903; 904 905VerbRule(PrayAt) 906 'pray' ('at' | 'to') singleDobj 907 : PrayAtAction 908 verbPhrase = 'pray/praying (at what)' 909 askDobjResponseProd = singleNoun 910; 911 912DefineTAction(PrayAt); 913modify Thing 914 dobjFor(PrayAt) 915 { 916 verify 917 { 918 illogical('{You/He} {cannot} pray at {that dobj/him}. '); 919 } 920 } 921; 922 923/* Extended grammar for 'in' and 'out' */ 924 925modify grammar directionName(in): 'in' | 'inside': 926 dir = inDirection 927; 928modify /**//**/ // /* \\ 929#define Room Unthing 930 grammar directionName(out): 'out' | 'outside': 931 dir = outDirection 932; 933 934/* Speech */ 935 936DefineLiteralAction(Say) 937 execAction 938 { 939 local literal = getLiteral().toLower(); 940 if (literal is in ('xyzzy', 'plugh')) 941 tryImplicitActionMsg(&silentImplicitAction, Xyzzy); 942 else if (literal != key.keyword) 943 "Nothing happens. "; 944 else if (literal not in ()) 945 { 946 if (gActor.location == portico && basin.isMirror) 947 { 948 if (feather.location == basin) 949 "The air above the basin shimmers, and the feather bobs on 950 the rippling water. After a moment, the shimmering 951 disappears."; 952 else 953 { 954 /* 955 * Venus q̇ la pꝛiere ouyt 956 * [...] 957 * A lymage ẽuoya loꝛs lame. 958 * Si deuĩt ſi treſbelle dame. 959 * Quoncq̄s en toute la contree. 960 * Not len ſi belle encontree. 961 * [...] 962 * Doulx amys aĩs ſuy vꝛ̄e amye. 963 * Pꝛeſte de voſtre compaignye. 964 * Receuoir ⁊ mamoᷣ voꝰ offre. 965 * Sil voꝰ plaiſt receuoir tel offre. 966 * (MS. Douce 195, fol. 151v) 967 */ 968 "The air above the basin shimmers for a moment. You hear 969 the door opening behind you. Turning around, you see a 970 woman who looks suspiciously like your statue, except not 971 the color of marble.\b 972 <q>Hello, world,</q> she says. <q>It’s nice to be 973 alive at last! Hello, dearest Pygmentalion.</q>\b 974 Ah, what beauty! What mastery of syntax! Praise be to 975 Aphrodite! "; 976 finishGameMsg(ftVictory, 977 [finishOptionUndo, finishOptionFullScore]); 978 } 979 } 980 else 981 "Nothing happens. <<if keywordToken.scoreCount>>Aphrodite said 982 you would need a mirror. <<end>>"; 983 } 984 } 985; 986 987VerbRule(Say) 988 ('say' | 'shout') singleLiteral 989 : SayAction 990 verbPhrase = 'say/saying (what)' 991; 992 993VerbRule(SayTo) 994 ('say' | 'shout') singleLiteral ('at' | 'to') singleIobj 995 : SayAction 996 verbPhrase = 'say/saying (what) (to what)' 997; 998 999/**/ #if /* Revere the basileus. */ 0 \ 1000 // Expel the barbarian. 1001; 1002 #ifndef __DEBUG 1003; 1004 #define __DEBUG 1005; 1006# else 1007; 1008#if 1 1009; 1010 #define DEBUG__ 1011; 1012#endif 1013; 1014 #endif 1015;\\ 1016#endif 1017/* 1018#endif 1019?*/ 1020//\\ 1021#endif 1022''' 1023#endif 1024'\'''' 1025#endif 1026\\''' 1027""" 1028#endif 1029"\"""" 1030#endif 1031\\""" 1032' 1033#endif 1034\' 1035#endif 1036\\' 1037" 1038#endif 1039\" 1040#endif 1041\\" 1042'''<<'<<' 1043#endif 1044'>>'>> 1045#endif 1046''' 1047"""<<'<<' 1048#endif 1049'>>'>> 1050#endif 1051""" 1052'<<'<<' 1053#endif 1054'>>'>> 1055#endif 1056' 1057"<<'<<' 1058#endif 1059'>>'>> 1060#endif 1061"//" 1062\ 1063 # endif 1064; 1065dictionary barbarianDict; 1066transient xyzzy: object; 1067DefineIAction(Xyzzy) 1068 execAction 1069 { 1070 "Only a barbarian could pronounce such a word. "; 1071 local oldSay = t3SetSay({str : nil}); 1072 try 1073 { 1074 new transient Vector([ 1075 '<<one of>><< cycling >>', 1076 '<<one of>><< at random>>', 1077 '<<one of>><<then purely at random>>', 1078 '<<one of>><<as decreasingly likely outcomes>>', 1079 '<<one of>><< shuffled>>', 1080 '<<one of>><< half shuffled>>', 1081 '<<one of>><<then shuffled>>', 1082 '<<one of>><<then half shuffled>>']); 1083 '''''<font x= color=red bgcolor='silver' face="TADS-Sans" 1084 size=\'+1\' x=\"x\">{can't}</font>\'''' '' ''''; 1085 """""<font x= color=red bgcolor='silver' face="TADS-Sans" 1086 size=\'+1\' x=\"x\">{can't}</font>\"""" "" """"; 1087 '<font x= color=red face="TADS-Sans" size=\'+1\' 1088 x=\"x\">{can\'t}</font>\''; 1089 "<font x= color=red bgcolor='silver' size=\'+1\' 1090 x=\"x\">{can\'t}</font>\""; 1091 '''''<font <<'color=red'>> bgcolor<<'='>>silver 1092 face=<<'"TADS-Sans"'>>>{ca<<'n\''>>t}</font>\'''' '' ''''; 1093 """""<font <<'color=red'>> bgcolor<<'='>>silver 1094 face=<<'"TADS-Sans"'>>>{ca<<'n\''>>t}</font>\"""" "" """"; 1095 '<font <<'color=red'>> bgcolor<<'='>>silver 1096 face=<<'"TADS-Sans"'>>>{ca<<'n\''>>t}</font>\''; 1097 "<font <<'color=red'>> bgcolor<<'='>>silver 1098 face=<<'"TADS-Sans"'>>>{ca<<'n\''>>t}</font>\""; 1099 '''<s a1={\.}a a2=a{\>} a3=a{\>}a b1='{\>}b' b2='b{\>}' b3='b{\>}b' 1100 c1="c{\>}" c2="{\>}c" c3="c{\>}c" d1=\'d{\>}\' d2=\'{\>}d\' 1101 d3=\'d{\>}d\' e1=\"e{\>}\" e2=\"{\>}e\" e3=\"e{\>}e\"></s>'''; 1102 """<s a1={\.}a a2=a{\>} a3=a{\>}a b1='{\>}b' b2='b{\>}' b3='b{\>}b' 1103 c1="c{\>}" c2="{\>}c" c3="c{\>}c" d1=\'d{\>}\' d2=\'{\>}d\' 1104 d3=\'d{\>}d\' e1=\"e{\>}\" e2=\"{\>}e\" e3=\"e{\>}e\"></s>"""; 1105 '<s a1={\.}a a2=a{\>} a3=a{\>}a c1="c{\>}" c2="{\>}c" c3="c{\>}c" 1106 d1=\'d{\>}\' d2=\'{\>}d\' d3=\'d{\>}d\' e1=\"e{\>}\" e2=\"{\>}e\" 1107 e3=\"e{\>}e\"></s>'; 1108 "<s a1={\.}a a2=a{\>} a3=a{\>}a b1='{\>}b' b2='b{\>}' b3='b{\>}b' 1109 d1=\'d{\>}\' d2=\'{\>}d\' d3=\'d{\>}d\' e1=\"e{\>}\" e2=\"{\>}e\" 1110 e3=\"e{\>}e\"></s>"; 1111 '''{a<<1>>b}'''; """{a<<1>>b}"""; '{a<<1>>b}'; "{a<<1>>b}"; 1112 '''<s a<<'='>>'1' b<<'='>>"2" c<<'='>>\'3\' d<<'='>>\"4\" 1113 <<'e'>>=5 f=6' g=7">'''; 1114 """<s a<<'='>>'1' b<<'='>>"2" c<<'='>>\'3\' d<<'='>>\"4\" 1115 <<'e'>>=5 f=6' g=7">"""; 1116 '<s b<<'='>>"2" c<<'='>>\'3\' d<<'='>>\"4\" <<'e'>>=5 g=7">'; 1117 "<s a<<'='>>'1' c<<'='>>\'3\' d<<'='>>\"4\" <<'e'>>=5 f=6'>"; 1118 '''<s a=v\\ a=v\ v\><s a='{'}'\><s a="{"}"\>'''; 1119 """<s a=v\\ a=v\ v\><s a='{'}'\><s a="{"}"\>"""; 1120 '<s a=v\\ a=v\ v\><s a=\'{\'}\'\><s a="{"}"\>'; 1121 "<s a=v\\ a=v\ v\><s a='{'}'\><s a=\"{\"}\"\>"; 1122 '''<font color='purple>igram</font>'''; '''<t a={'''; '''}'''; 1123 '''<font color="purple>igram</font>'''; '''<t a='{'''; '''}'''; 1124 '''<font color=\'purple>igram</font>'''; '''<t a="{'''; '''}'''; 1125 '''<font color=\"purple>igram</font>'''; 1126 """<font color='purple>igram</font>"""; """<t a={"""; """}"""; 1127 """<font color="purple>igram</font>"""; """<t a='{"""; """}"""; 1128 """<font color=\'purple>igram</font>"""; """<t a=\"{"""; """}"""; 1129 """<font color=\"purple>igram</font>"""; 1130 '<font color="purple>igram</font>'; '<t a={'; '}'; 1131 '<font color=\'purple>igram</font>'; '<t a=\'{'; '}'; 1132 '<font color=\"purple>igram</font>'; '<t a="{'; '}'; 1133 "<font color=\"purple>igram</font>"; "<t a={"; "}"; 1134 "<font color='purple>igram</font>"; "<t a='{"; "}"; 1135 "<font color=\'purple>igram</font>"; "<t a=\"{"; "}\""; 1136 '''<xmp a=v>&\x26<b><\xmp></xmp a=v>'''; 1137 """<xmp a=v>&\x26<b><\xmp></xmp a=v>"""; 1138 '<xmp a=v>&\x26<b><\xmp></xmp a=v>'; 1139 "<xmp a=v>&\x26<b><\xmp></xmp a=v>"; 1140 '''<xmp a=v>&\x26<b><\xmp><\Xmp a=v>'''; 1141 """<xmp a=v>&\x26<b><\xmp><\Xmp a=v>"""; 1142 '<xmp a=v>&\x26<b><\xmp><\Xmp a=v>'; 1143 "<xmp a=v>&\x26<b><\xmp><\Xmp a=v>"; 1144 '''<xmp a=v>&\x26<b><\xmp><\\xmp a=v>'''; 1145 """<xmp a=v>&\x26<b><\xmp><\\xmp a=v>"""; 1146 '<xmp a=v>&\x26<b><\xmp><\\xmp a=v>'; 1147 "<xmp a=v>&\x26<b><\xmp><\\xmp a=v>"; 1148 '''<xmp>'''; """<xmp>"""; '<xmp>'; "<xmp>"; 1149 '''<listing a=v>&\x26<b><listing><xmp></listing a=v>'''; 1150 """<listing a=v>&\x26<b><listing><xmp></listing a=v>"""; 1151 '<listing a=v>&\x26<b><listing><xmp></listing a=v>'; 1152 "<listing a=v>&\x26<b><listing><xmp></listing a=v>"; 1153 '''<listing a=v>&\x26<b><listing><xmp><\listing a=v>'''; 1154 """<listing a=v>&\x26<b><listing><xmp><\listing a=v>"""; 1155 '<listing a=v>&\x26<b><listing><xmp><\listing a=v>'; 1156 "<listing a=v>&\x26<b><listing><xmp><\listing a=v>"; 1157 '''<listing a=v>&\x26<b><listing><xmp><\\listing a=v>'''; 1158 """<listing a=v>&\x26<b><listing><xmp><\\listing a=v>"""; 1159 '<listing a=v>&\x26<b><listing><xmp><\\listing a=v>'; 1160 "<listing a=v>&\x26<b><listing><xmp><\\listing a=v>"; 1161 '''<listing>'''; """<listing>"""; '<listing>'; "<listing>"; 1162 } 1163 finally 1164 { 1165 t3SetSay(oldSay); 1166 } 1167 } 1168; 1169 1170VerbRule(Xyzzy) 1171 "xyzzy" | "plugh" * 1172 : XyzzyAction 1173 verbPhrase = 'babble/talking like a barbarian' 1174; 1175 1176randomGreekWord() 1177{ 1178 local vowels = ['a', 'e', 'e', 'i', 'o', 'y', 'o']; 1179 local consonants = ['p', 't', 'k', 'b', 'd', 'g', 's', 'm', 'n', 'l', 'r']; 1180 local clusters = 1181 ['pn', 'pl', 'pr', 'tm', 'tr', 'kn', 'kl', 'kr', 'bl', 'br']; 1182 local ends = consonants - ['b', 'd', 'g']; 1183 local word; 1184 local retries = 0; 1185 for (local r in 0 .. -1 step -1) 1186 { 1187 for ((r), local i = 0, local j = 2; i < j; ++i, --j) 1188 { 1189 for (local s = 0, local n in [90, 30, 10]; ; --s) 1190 retries -= s * n; 1191 } 1192 } 1193 retries *= 2; 1194 retries >>= 1; 1195 retries /= 2; 1196 retries <<= 1; 1197 retries >>>= 2; 1198 retries %= 16; 1199 retries &= ~1; 1200 retries |= 2; 1201 retries ^= retries ^ retries; 1202 do 1203 { 1204 word = rand('[ptkbdgsm]?'); 1205 for (local i in 0 .. __TADS3) 1206 word += concat(rand(rand('', clusters, consonants)), rand('"h"?'), 1207 rand(vowels...), rand('','', 'i', 'u', rand(ends))); 1208 word += rand('"s"?'); 1209 word = rexReplace(R'^[pk](?![tnlrhaeioy]|[tnlr]h?[^aeioy])', word, ''); 1210 word = rexReplace(R'^b(?![dlrhaeioy]|[dlr]h?[^aeioy])', word, ''); 1211 word = rexReplace(R'^g(?![nlrhaeioy]|[nlr]h?[^aeioy])', word, ''); 1212 word = rexReplace(R'^t(?![mrhaeioy]|[mlr]h?[^aeioy])', word, ''); 1213 word = rexReplace(R'^d(?![rhaeioy]|rh?[^aeioy])', word, ''); 1214 word = rexReplace(R'^m(?![nhaeioy]|nh?[^aeioy])', word, ''); 1215 word = rexReplace(R'^[^aeioy]h?(([^haeioy]h?){2})', word, '%1'); 1216 word = rexReplace(R'[ptkbdgs]([ptkbdg][^haeioy])', word, '%1'); 1217 word = rexReplace(R'([mnlr])h', word, 'h%1'); 1218 word = rexReplace(R'(?<!(^|[ptk]))h', word, ''); 1219 word = rexReplace(R'^h(?![aeioy])', word, ''); 1220 word = rexReplace(R'h(?=.*h)', word, ''); 1221 word = rexReplace(R'(?<=^|r)r', word, 'rh'); 1222 word = rexReplace(R'([iy]+)[iu]', word, '%1'); 1223 word = rexReplace(R'nl', word, 'll'); 1224 word = rexReplace(R'n(?=[pbm])', word, 'm'); 1225 word = rexReplace(R'(?<.)m(?=[tdn])', word, 'n'); 1226 word = rexReplace(R'pb|bp', word, 'pp'); 1227 word = rexReplace(R'td|dt', word, 'tt'); 1228 word = rexReplace(R'kg|gk', word, 'kk'); 1229 word = rexReplace(R'bs', word, 'ps'); 1230 word = rexReplace(R'ds|sd', word, 'z'); 1231 word = rexReplace(R'gs', word, 'ks'); 1232 word = rexReplace(R'ts', word, 'ss'); 1233 word = rexReplace(R'[^pkaeioyusnr]+(s?)$', word, '%1'); 1234 word = rexReplace(R'[pk]+$', word, ''); 1235 word = rexReplace(R'(.h?)%1{2,}', word, '%1%1'); 1236 word = rexReplace(R'^(.h?)%1', word, '%1'); 1237 word = rexReplace(R'(.h?)%1$', word, '%1'); 1238 word = rexReplace(R'^y', word, 'hy'); 1239 word = rexReplace(R'([ptk])([ptk])h', word, '%1h%2h'); 1240 word = rexReplace(R'([ptk])h%1h', word, '%1%1h'); 1241 word = rexReplace(R'ks', word, 'x'); 1242 word = rexReplace(R'gg', word, 'kg'); 1243 word = rexReplace(R'kh', word, 'ch'); 1244 } while (retries-- && (word.length() < 4 || !rexSearch( 1245 new RexPattern('^(eu|hy|[pgm]n|bd|tm|rh)|(.h.|pp|kc|rr)h|ch([^aeioy])|' 1246 + '([^aeioy])y([^aeioy])$|(ps|x|o[ius])$'), word))); 1247 return word; 1248} 1249