#charset "utf-8" #include #include extern function extern_function; extern method extern_method; extern function extern_function(a, b=a, c='<>', d:, e:=1, f?, ...); extern method extern_method(a, b=a, c='<>', d:, e:=1, f?, [g]);; extern class extern_class; extern object extern_object; intrinsic 't3vm' { }; #ifndef PropDefAny intrinsic class Object 'root-object/030004' { }; #endif object /**//**/ // /* \\ #define Room Unthing template [lst]; /* * Quotations from "Le Roman de la Rose" are transcribed from MS. Douce 195, * owned by Bodleian Library, University of Oxford * (http://image.ox.ac.uk/show?collection=bodleian&manuscript=msdouce195). */ versionInfo: GameID IFID = '17d8efc3-07da-4dde-a837-ff7c4e386a77' name = 'Pygmentalion' byline = 'by David Corbett' htmlByline = 'by David Corbett' version = '1' authorEmail = 'David Corbett\040' desc = 'You have fallen in love with a statue\x2e' htmlDesc = 'You have fallen in love with a statue\x2E' ; /* * Pymalion fu ẽtailleꝛꝛes. * Poᷣtrayãs en fus ⁊ en peꝛꝛeˢ * En metaulx en os ⁊ en cyꝛes * Et en touteˢ aultres matires. * Quon peult a tel oeuure trouuer. * Poᷣ ſon grant engin eſpꝛouuer. * Car maiſtre en fu bien dire loz. * Ainſi com poᷣ acquerre loz * Se voult a poᷣtraire deduyꝛe * Si fiſt vng ymage diuuyꝛe * Et miſt au faire tel entente * Quel fu ſi plaiſãt et ſi gente * Quel ſembloit eſtre auſſi viue. * Com la plus belle riens q̇ viue * (MS. Douce 195, fol. 149r) */ modify _init() { ({: local r, r = randomize, r})(); replaced(); } gameMain: GameMainDef initialPlayerChar: Actor { desc = "You look the same as usual, but you feel unusually sentimental. " location = entrance } showIntro { "The statue is undeniably a masterpiece: the most skillful carving you have ever done, and the most beautiful woman you have ever seen. Unfortunately, she is also an inanimate block, and now you can neither work nor rest for unrequitable love.\b Once again you stumble into your studio, hoping and praying to find your statue brought to life.\b <>\r\n <>\b"; } ; enum token token, tokOp, token; modify cmdTokenizer rules_ = static [ ['whitespace', new RexPattern('%s+'), nil, &tokCvtSkip, nil], ['punctuation', new RexPattern('[.,;:?!]'), tokPunct, nil, nil], ['spelled number', new RexPattern('(twenty|thirty|forty|fifty|sixty|' + 'seventy|eighty|ninety)-' + '(one|two|three|four|five|six|seven|eight|nine)' + '(?!)'), tokWord, &tokCvtSpelledNumber, nil], ['spelled operator', new RexPattern( '(plus|positive|minus|negat(iv)?e|not|inverse(%s+of)?|' + 'times|over|divided%s+by|mod(ulo)?|and|xor|or|[al]?sh[lr])' + '(?!)'), tokOp, &tokCvtSpelledOperator, nil], ['operator', R'[-!~+*/%&^|]|<<|>>>?', tokOp, nil, nil], ['word', new RexPattern('*'), tokWord, nil, nil], ['string ascii-quote', R"""([`\'"])(.*)%1(?!)""", tokString, nil, nil], ['string back-quote', R"`(.*)'(?!%w)", tokString, nil, nil], ['string curly single-quote', new RexPattern('\u2018(.*)\u2019'), tokString, nil, nil], ['string curly double-quote', new RexPattern('\u201C(.*)\u201D'), tokString, nil, nil], ['string unterminated', R'''([`\'"\u2018\u201C](.*)''', tokString, nil, nil], ['integer', new RexPattern('[0-9]+'), tokInt, nil, nil] ] replace tokCvtSpelledOperator(txt, typ, toks) { toks.append([rexReplace(R'%s+', txt.toLower(), '\\'), typ, txt]); } ; /* Tokens */ /* * Puiˢ li reueſt en maĩteˢ guiſes. * Robeˢ faicteˢ ꝑ grãˢ maiſtriſeˢ. * De biaulx dꝛaps de ſoye ⁊ de laĩe. * Deſcarlate de tiretaine * De vert de pers ⁊ de bꝛunecte * De couleᷣ freſche fine ⁊ necte * Ou moult a riches paneˢ miſes. * Herminees vaires et griſes * Puis les li roſte puis reſſaye. * Cõmant li ſiet robbe de ſaye * Sendaulx meloguins galebꝛunˢ. * Indes vermeilz iaunes ⁊ bꝛunˢ. * [...] * Aultre foiz luy repꝛẽd courage. * De tout oſter ⁊ mectre guindeˢ. * Iaunes vermeilles vers ⁊ indeˢ. * (MS. Douce 195, fol. 150r) */ class Token: Achievement { points = 1; desc = "<><><>"; before = before = '', before_ after = (after = '', after_) } Token template inherited 'before_' 'after_' 'desc_'; #define DefineToken(name, before, after) name##Token: Token before after #@name DefineToken(builtin, '', ''); DefineToken(comment, '', ''); DefineToken(decorator, '', ''); DefineToken(error, '', ''); DefineToken(escape, '', ''); DefineToken(float, '', ''); DefineToken(keyword, '', ''); DefineToken(label, '', ''); DefineToken(long, '', ''); DefineToken(name, '', ''); DefineToken(operator, '', ''); DefineToken(string, '', ''); DefineToken(whitespace, '', ''); function highlightToken(tokenString) { local token = [ 'built in' -> builtinToken, 'comment' -> commentToken, 'decorator' -> decoratorToken, 'error' -> errorToken, 'escape' -> escapeToken, 'float' -> floatToken, 'keyword' -> keywordToken, 'label' -> labelToken, 'long' -> longToken, 'name' -> nameToken, 'operator' -> operatorToken, 'string' -> stringToken, 'white space' -> whitespaceToken, * -> nil ][tokenString.toLower()]; if (!token) return tokenString; token.awardPointsOnce(); return '<><><>'; } string /**//**/ // /* \\ #define Room Unthing template <> highlightToken; /* Grammar for materials */ dictionary property material; grammar adjWord(material): ->adj_ : AdjPhraseWithVocab getVocabMatchList(resolver, results, extraFlags) { return getWordMatches(adj_, &material, resolver, extraFlags, VocabTruncated); } getAdjustedTokens() { return [adj_, &material]; } ; /* Rooms and objects */ + property location; entrance: Room 'Entrance' "You are in the entrance to your studio. This is where you carve great works of art, not that you have felt like making any lately. A door leads outside, and the studio itself is to the north and the east. " north = workbenchRoom northeast = sinkRoom east = altarRoom south = door out asExit(south) ; + door: LockableWithKey, Door 'door' 'door' "It is a simple wooden door. " material = 'wood' 'wooden' keyList = [key] cannotOpenLockedMsg = '{The dobj/He} {is} locked. You cannot <>! ' ; key: PresentLater, Key 'key' 'key' @altar "It is a <>grimy<> bronze key. <>On it is \ etched the word <>. " material = 'bronze' clean = nil keyword = (keyword = randomGreekWord(), targetprop) dobjFor(Clean) { verify { } action { askForIobj(CleanWith); } } dobjFor(CleanWith) { verify { if (clean) illogicalAlready('{The dobj/He} {is} already clean. '); } action { gDobj.clean = true; "{You/He} clean{s} {the dobj/him}, revealing an inscription. "; } } dobjFor(Read) { verify { nonObvious; } } ; workbenchRoom: Room 'At the Workbench' "This workbench, in the northwest part of the studio, was where you would create works of art. Now you just come here to contemplate your creation’s beauty and lament your hopeless situation.\b The statue stands on a plinth beside the workbench. " east = sinkRoom southeast = altarRoom south = entrance getDestName(actor, origin) { return 'the workbench'; } ; + workbench: Fixture, Surface 'workbench/bench/material/materials/tool/tools' 'workbench' "Normally, the workbench would be scattered with half-finished projects, but now your tools and materials lie abandoned. " ; + plinth: Fixture, Thing 'marble plinth/pedestal' 'plinth' "It’s a smoothed block of marble about a cubit high. " ; replace grammar predicate(Screw): ' ': object; replace grammar predicate(ScrewWith): ' ': object; + + statue: Fixture, Surface '"creation\'s" beauty/carving/creation/galatea/statue/woman' 'statue' "This is a<>n untitled<> statue of a woman carved from <>flawless <> <>milk-white <>ivory. <>Her <>long <>hair is done up in a chignon<>, with a few strands falling down her neck<><>, and \v<>.<><> <>She radiates an aura of contrapposto grace. <><>\bYou wonder what she <>is going to<>will<> be like as a woman. <>Maybe she’ll be a painter and expand your business.<> <>Maybe she’ll have a head for figures and will put the accounts in order.<> <>She’ll love you, obviously, but beyond that you don’t know.<><> <>If only Aphrodite would bring her to life without this silly puzzle about tokens and mirrors!<> " material = 'ivory' propertyset 'is*' { propertyset 'H*' { im = nil\ er = true; } It = true } iobjFor(PutOn) { check { if (gDobj not /**//**/ // /* \\ #define Room Unthing in (necklace, __objref(necklace, warn))) "How rude! You don’t know what you were thinking. "; } } iobjFor(GiveTo) remapTo(PutOn, DirectObject, IndirectObject) ; +++ necklace: Wearable 'pearl necklace/string pearls' '<> of pearls' "This is a masterfully crafted pearl necklace. You hope the statue won’t mind if you hold onto it for a while. " initDesc = "You gave the statue this pearl necklace yesterday. " isPlural = true ; altarRoom: Room 'At the Altar' "Light from the window illuminates a crude altar. Until recently, this corner was your bedroom. The rest of the studio lies north and west. " north = sinkRoom northwest = workbenchRoom west = entrance getDestName(actor, origin) { return 'the altar'; } ; + window: Fixture 'window' 'window' "It’s just a window above the altar. <>The space under the window is blank; as an interior <>, you can’t help but think the wall would benefit from a bas-relief, but – sigh &endash you are too lovelorn to wield the chisel. <<||>>The wall right below it is a boring <>. <>" ; + altar: Fixture, Surface 'crude rough altar/banker/slab' 'altar' "A rough marble slab lies on a wooden banker. In your rush to construct an altar, you neglected the usual surface finish and friezes, but you pray at it anyway. You are sure the gods will understand. " material = 'marble' 'wood' 'wooden' bulkCapacity = 1 dobjFor(PrayAt) { verify { } action() { /* * Biaulx dieux diſt il tout ce poez. * Sil voꝰ plaiſt ma requeſte oez * [...] * Et la belle q̇ mon cueᷣ emble * Qui ſi bien yuoyꝛe reſſemble. * Deuiengne ma loyal amye * De fẽme ait coꝛps ame et vie * (MS. Douce 195, fol. 151r) */ local offering; foreach (offering in contents); if (!keywordToken.scoreCount) "<>O Aphrodite, you say, comforter of hopeless lovers, hear my prayer! May she to whom I have given my heart be given body, soul, and life. And a colorful personality. And&mdash\b You are interrupted by a shimmering about the altar. As you watch, it takes the form of a callipygian goddess.\b Mortal, I have heard your heart-felt and oft-repeated plea, and I will take pity on you, says Aphrodite. If you give me a token of your love as an offering, I will give you the <> of life. Speak this word in the presence of a mirror, and I will grant your request.\b She fades away, adding, As for her colorful personality, just look around you. <><>"; else if (key.location) "O Aphrodite, you say, what am I supposed to do again?\bThe goddess reappears and reminds you to speak the keyword of life at a mirror. <>What’s the keyword, then? Gods help those who help themselves. Figure it out yourself.<>Why a mirror? I like mirrors.<> "; else if (offering == necklace) { "Aphrodite reappears. A necklace! Perfect! The necklace disappears in a bright flash. When your eyes readjust, you see a key lying in its place. "; necklace.moveInto(nil); key.makePresent(); } else if (+offering) "Aphrodite reappears. She eyes <> skeptically. <>No <>.<>You call that a token of love?<>\^<>? Really?<>Come on, mortal, it’s not that difficult!<> "; else "I heard you the first time, says Aphrodite. Prove your devotion by offering a token of your love at the altar, or the deal’s off. "; } } iobjFor(GiveTo) remapTo(PutOn, DirectObject, IndirectObject) ; aphrodite: Unthing '(love) aphrodite/cytherea/god/goddess/venus love' 'Aphrodite' '<>You can only pray to a god. <>You need an altar to interact with a god. ' location = (gPlayerChar) isProperName = true isHer = true iobjFor(GiveTo) { verify { illogical('She isn’t here. You’ll have to leave {the dobj/him} somewhere she can find it. '); } } dobjFor(PrayAt) maybeRemapTo(gActor.canSee(altar), PrayAt, altar) ; sinkRoom: Room 'Washroom' "Sculpting marble is a dusty business. You use this sink to clean off after a hard day’s work. Beside the sink is a small end table, and on the wall is a calculator. The rest of the studio is south and west. " south = altarRoom southwest = entrance west = workbenchRoom ; property level, overflowing; export overflowing; export level 'waterLevel'; + sink: Fixture '(auto) (mop) auto-sink/autosink/bowl/drain/faucet/sink' 'sink' "This is a state-of-the-art mop sink with anti-miasmic coating and bronze backsplash. It is so modern, there are no handles or other obvious ways to turn it on.\b <>It is overflowing. <>It is full to the brim with water. <= 15000>>It is full of water. <>It is half full of water. <= 2000>>There is some water in the sink. < 0>>A small puddle has formed at the bottom of the sink. <>It is empty. <>It looks like it hasn’t been used in a <> time. " level = not in ([lst]) { return argcount; } not = in() overflowing = nil current = self setLevel(level:) { targetobj.current.overflowing = level == nil; targetobj.current.level = min(level ?? 0, 20000); if (sink.overflowing || sink.level > 0e+1) sinkWater.makePresent(); if (basin.overflowing || basin.level > 0e-1) basinWater.makePresent(); } iobjFor(CleanWith) remapTo(CleanWith, DirectObject, sinkWater) ; ++ sinkWater: PresentLater, Fixture '(sink) water sink water' 'water' "<>" disambigName = 'water in the sink' dobjFor(Drink) { verify { illogical('''{You're} not thirsty. '''); } } iobjFor(CleanWith) { preCond = [] verify { if (!location) illogicalNow('There is no water in the sink. '); if (!sink.overflowing && sink.level < 1e2) illogicalNow('There is not enough water in the sink. '); } } ; + table: Fixture, Surface 'small end bracket/table' 'table' "<>Upon closer inspection, you see that \v<>The table is bracketed to the wall. " ; ++ Readable '"operator\'s" manual' 'manual' "
<>’s Manual<\center>\b To control the auto-sink, use the calculator add-on to enter the desired volume of water. For example,\n \t\t<>\n to fill the basin with <<% ,d 0x69 * 0105>> kochliaria