MechaniQue/J 2.0 manual written by Gabor de Mooij in 2007 ---------------------------------------------------- MQJ2 thinks in 'books', 'rolls' and 'parsers'. An MQJ2 program is usually written using a simple text editor like nano and should be saved using the .book extension. To compile a book-file named test.book invoke the compiler like this: java mechanique/m2 -c mechanique/test.book This will create a .roll file which is a kind of executable to MechaniQue. A roll-file is not readable for humans and contains JVM bytecode. However you still need MQJ2 to run it. java mechanique/m2 -r mechanique/test.roll Will run the roll-file we just created. You could also use the -s option, on MAC OSX this will cause the system to activate the speech-engine to read the adventure to you, while -d is a debug option.If you run mechanique without any option you will see the info screen: $ java mechanique/m2 MechaniQue/J, Copyright (C) 2007 Gabor de Mooij MechaniQue comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute it under certain conditions. For more information please consult: http://www.gnu.org , the GNU website. MechaniQue/J 2.0 Compiler Version 0.2007Q2 USAGE: COMPILE : java m2 -c [optional parserfile] PLAY : java m2 -r AUDIO : java m2 -s LOAD GAME : java m2 -l DEBUG : java m2 -d MechaniQue2/J has been written by Gabor de Mooij GPL3 LICENSE. ---------------------------------------------------- Basic Syntax It's important to know that only plain text files can be processed by the MechaniQue/J programming language compiler; files created by fancy wordprocessor tend to be rejected. At the moment there is no IDE for MechaniQue/J. A mechaniQue program consists of code-lines; each line starts with a @. The last line in a program file; a book-file, should be like this: @fin All other lines have the same format: @ [Label] {Slot} ^Action ---------------------------------------------------- Labels A label is just a name that can be given to a row. Labels are optional, but are used as a reference by other lines. This way another line can jump to the labeled line by specifying the name. ----------------------------------------------------- Actions ^:X prints X ^+X adds key X to the inventory ^-X drops key X from the inventory ^? asks for user input, removes spaces and applies parsing and adds the answer as a key prefixed with a _ ^->X jumps to line labeled X ^<- jumps back ^:#X comment X Multiple actions are separated by |. Example: @^:Hello World|^->askname ---------------------------------------------------- Locks The actions in a line are only executed if all locks can be opened. {X} Opens if X is in inventory {!X} Opens if X is NOT in inventory {$X} Opens if X is in inventory and drops X {OR} Opens if one of the preceeding locks has been unlocked and acts if they were all unlocked {ZERO} Opens if no lock has been unlocked since the last ^? Extended locks cover more than one line: Opens like : @[..]{..}<<^.. and closes like: @[..]{..}>>^.. Writing 'hello world' in MechaniQue/J is as simple as this: @^:hello world @^? @fin The first line puts the 'hello world' message in the textbuffer, the next line creates an interaction moments which gives MechaniQue/J the opportunity to output and flush the buffer. After the interaction, the program simply ends. @{goldenkey}^:hello world @^? @fin The program written above this text is slightly different. It will also produce an interaction moment but it will not display the message as the previous program did. The reason for this is that in order to add the message to the text buffer the program needs the golden key (goldenkey). In this case {goldenkey} is called a lock (to be more precise it's a positive lock, but that's a detail I'll explain later on). @^+goldenkey @{goldenkey}^:hello world @^? @fin This program displays the text like the first program did. The reason for this can be found at the first line. In the first line of this program we 'give' the golden key to the program. When the MechaniQue/J interpreter arrives at the second line it can therefore succesfully unlock the line by applying the golden key. @^+goldenkey @^-goldenkey @{goldenkey}^:hello world @^? @fin This program does not display the message because although we 'give' the golden key in the first line, we drop it in the second. Therefore the program cannot unlock line 3 and goes on without adding the message to the buffer. @[looping]^:hello world @^? @^->looping @fin This program illustrates an infinite loop. On line 3 we keep jumping to line 1 by referring to the label of line 1: 'looping'. (Such a program can be stopped by entering #exit during an interaction moment). @[looping]^:hello world @^? @{_again}^->looping @fin This program jumps back to the looping-line only if you enter 'again' and then press enter. This way, the player hands a key to the program called '_again' (note that the interaction keys are prefixed by an underscore). Hopefully these little examples show the basic ideas and principles behind the language. ---------------------------------------------------- Special keys DICEX After a ^? a dice is thrown. A key DICEX is given were X is the number the dice fell on TWISTX At the start a TWIST1, TWIST2 or no TWIST key is given. ROTX After each ^? X changes like: 1,2,3,4,5,6,1,3,4,5,6,1.. etc PATHX After each ^? X changes like: 1,2,3,4,5,6,5,4,3,2,1,2,3,4.. etc TIMEX Unlocks TIME keys lower than X, X increases after each ^? OCCX Key with date X; on february 2nd inventory contains: OCC1_2 Interaction Key The ? instructions creates a so-called interaction moment. During this phase the player may type in some text until he/she presses enter. The user-input will then be used to build a key. This happens as follows. First all spaces are removed from the user-input, next the resulting text is prefixed with the interaction symbol: _ the underscore. So, answering 'a' will result in key : _a and answering 'hello world' will result in a key: _helloworld. Using this mechanism a player can influence the course of a MPL program. User commands During an interaction moment, the player can enter a predefined command. there are only two such commands: #exit (to stop the program) and #save to save the current state of the program; reload with -l option. Some examples of system keys Time for some examples! All programs that are listed below are actually some kind of infinite loops; to quit, use the #exit command at an interaction moment. The following example prints 'patatoes' or 'tomatoes' randomly using the dice key: @[start]^:Today, the cook bakes @{DICE3}^:patatoes @{!DICE3}^:tomatoes @^? @^->start The following example demonstrates the use of the twist key. You'll see that the value of the twist key will not change during one game-session. @[start]^:You were born @{TWIST2}^:rich @{!TWIST2}^:poor @^:and that will not change during this game! @^? @^->start @end Now, we are going to demonstrate the time key. The time key will be given when the iteration number in the key is reached. @[start]^:after three times @{!TIME3}^:this message will change.... @{TIME3}^:this message has been CHANGED... @^? @^->start @fin To simulate events that are cyclic you can use the rotation key: @[start]^:You are waiting for the bus at the busstop. @{ROT1}^:The road is empty. @{ROT2}^:You suddenly hear an engine. @{ROT3}^:A bus is nearing @{ROT4}^:The bus stops at the busstop, it's the wrong bus. @{ROT5}^:The bus drives off @{ROT6}^:In the distance you see the back of a bus. @^? @^->start Negative locks A negative lock starts with a ! and only opens if the specified key is NOT present. So imagine we have line like {!lamp}^:there lies a lamp on the floor.. In this case you will see the lamp on the floor only if you do not have that lamp, which makes perfect sense of course. The example below demonstrates the use of negative locks. @[forest]^:You are in a forest. @^:a. enter the cave in the east. @{!lamp}^:b. take the lamp that lies on the grass. @^? @{_b}{!lamp}^+lamp @{_a}{!lamp}^:Way to dark!!|^->forest @{_a}{lamp}^->cave @^->forest @[cave]^:You are in the cave.. @fin Extended locks An extended lock enables you to make a lock apply to more than just one line. This is best illustrated by a comparison. Imagine you have two locks a and b. One line just demands that you have key a, while the other demands both. Instead of writing: @^+a @^+b @{a}^:this line needs a @{a}^:this line needs a as well @{a}{b}^:this line needs both a and b @{a}{b}^:this line needs both a and b as well @{a}^:this line needs a @fin the MPL allows you to write a shorter version using extended locks (<< and >>) @^+a @^+b @{a}<<^:this line needs a @^:this line needs a as well @{b}<<^:this line needs both a and b @{a}{b}>>^:this line needs both a and b as well @>>^:this line needs a @fin Slots A slot is just like a lock. However if the line as a whole can be opened the keys that have been used on slots will be dropped automatically. Slots are prefixed with a $. Negative slots do not exist. The following example demonstrates the usage of slots. @^+coin @[again]^:a. insert coin into vending machine @^? @{_a}{!coin}^:no more coins! @{_a}{$coin}^:OK you got yourself a coke. @^->again @fin New is the ZERO key. This key is generated by the system at each interaction moment. This key is automatically dropped by the first line that can be unlocked. Using the ZERO key you can open locks like {ZERO}. This is useful if you want to implement an action which should only be performed if none of the other lines has been unlocked. Actually, ZERO is MechaniQue's way of saying... 'else'. @^? @{_a}^:you entered a. @{ZERO}^:you entered something ELSE.. @fin PINPOINT In a typical adventure game players may create quite a mess of the environment. They may take objects and drop them elsewhere thus moving them around. To keep track of your objects easily MechaniQue offers a pinpoint action. Use the ^& action to store you current position. Now if a player drops a bottle of beer you can say: @[room1]^& @^? @{_dropbeer}^-beer|^+&beer Instead of key 'beer' the program will now have a key called 'room1beer'. Which means that a lock like {&beer} will be interpreted automatically based on the current 'pinpoint position' and only open if you are in room 1. There is also a more comfortable notation that creates a pinpoint action and a say action for a label automatically. This new notation is: @&room The compiler will replace this with: @[room]^:Room.|^& ---------------------------------------------------- Quantifiers MechaniQue does not have variables. But, with quantifiers I gave MechaniQue an opportunity to 'deal with numbers'. Using qualifiers you can build loops, perform calculations and print numbers. Imagine we want to add a lamp to our inventory. Normally we would say something like : ^+lamp But what if you were writing a christmas game and you need lots of little lightbulbs to decorate the christmas tree? To add more lamps you can now say: ^+lamp|^+lamp Which will result in MechaniQue's inventory holding 2 lamps instead of one. A lock like: {lamp} will still open. So what has changed? You can use a lock like: {lamp*2} and it will only unlock if you have 2 OR MORE lamps. This is the reason why {lock} opens as well; locks without quantifiers are treated as if they were locks with quantifier 1: {lock*1}. And because this lock opens if the amount of lamps >= 1, it will also unlock if you have 2 lamps. If we want to write a lock that opens if you have 1 lamp but not 2; we could write {!lamp*2} which will unlock in case of < 2 lamps. However that lock will also open for 0 lamps. But if we need exactly 1 lamp we have to write: {!lamps*2}{lamp*1} (or {=lamp*2}). Of course it is quite annoying to add five lamps like this: ^+lamp|^+lamp|^+lamp|^+lamp|^+lamp This is why you may use a quantifier here as well: ^+lamp*5 Both statements do exactly the same, they both add 5 lamps to the inventory. Using quantifiers applies to the - instruction as well: ^-lamp*5 simply meand you want to drop all 5 lamps. To print the quantity of an item in your inventory on the screen use %%X%% where X is the item. So: %%lamp%% will print the current amount of lamps in the inventory. Here is an example.. Using quantifiers, it's easy to build a loop: @^+lamp*10 @[counter]^:there are %%lamp%% left... oops I broke one! @^-lamp @{lamp*1}^->counter @fin To add 12 lamps to the inventory: ^+lamp*12 To check for 12 lamps or more: {lamp*12} To check for 12 lamps or less: {!lamp*12} To check for exactly 12 lamps: {=lamp*12} ---------------------------------------------------- Magic Ink Visually impaired people benefit from correct textual interaction. If they listen to your game and they have to hear the same description time after time, it's becoming pretty boring. This also applies to other players of course, they might skip reading textblocks because they are increasingly repetetive. To fix this issue there is a new special key in MechaniQue. The function of magic ink has changed a bit. Instead of hiding text forever, it will be reshown if you have a special key: _DESCRIBE_FULL, this key can be generated using parsing: look=DESCRIBE_FULL will do the job. This makes Magic Ink behave more like look-commands in traditional adventures. --------------------------------------------------- Parsing Maybe you wonder if you can only make multiple choice games with MechaniQue. This is not the case. MechaniQue can parse complete sentences as well. To make use of parsing you need to compile a parser file with your game. A parser file includes rules that describe how answers are turned into keys. Each line of a parser file (which is just an ASCII file as well) starts with a @, just like a book-file. There are two kinds of rules: A (filter) A=B (replacement) In the first case a is filtered from the answer. In the second case, a is replaced for b. Imagine a parser file like: @talk=u @to @the @old We could save this file as test.parser and compile it together with test.book like this: java m2 -c test.book test.parser Now imagine we meet an old man in the game and we want to ask the way. If we would type something like: talk to the old man, or talk to old man, or talk to man A line like @{_uman}^:the man says you need to go east. will just open. This is because all words are being filtered except talk and man, talk gets replaced by u and if the prefix for interaction keys is added we het _uman. This means we don't have to add locks like: {talktooldman}{talktoman}{talkman}{talk}{OR} Which is of course annoying. However we could add the option to unlock the line also in case the player only uses a verb: {_uman}{_u}{OR}. MPL Parsing is very simple but allows you to make simple adventures. What's more is that you can use different languages for your parser while still using the same book-file. Magic ink ^:;X prints X just once, even if the line is processed at a later time. To make this line print again, 'open' it with key _DESCRIBE_FULL. A note about the end-statement Each MechaniQue program ends with @fin. This end-tag is taken quite literally by the compiler, meaning only @fin is accepted; not @{key}fin or @[a]{b}fin. However because you can also have only 1 @fin in your program you might need to jump to it from other locations. The right way to achieve this would be: @[end]^# @fin This is a bit cumbersome to accomplish. Therefore the compiler accepts a line like @[end]fin Basisbeginselen van MechaniQue Een MechaniQue/J boek bestand bevat het MechaniQue programma. Zoals de meeste computerprogramma's bestaat een MechaniQue programma uit regels. Iedere regel in een MechaniQue programma begint met het 'begin van de regel' symbool: @, het apenstaartje. Iedere regel in een boek bestand heeft een vast regime, eerst het apenstaartje @, dan het label (niet verplicht), dan de sloten (niet verplicht) en tenslotte de acties (verplicht). Wat formeler kunnen we dus zeggen: @ LABEL SLOTEN ACTIES Labels Een label is een soort naamkaartje dat een regel kan hebben. Een label komt direct na de @ en staat altijd tussen twee rechte haken [ ]. Een dergelijk naamkaartje is handig voor andere regel om aan deze regel te refereren. Sloten De acties in een regel worden alleen uitgevoerd indien de regel ook ontsloten kan worden door het programma. Een regel kan dus 'op slot zitten'. Dit is een onderdeel van de taal die wat esoterischer is. Iedere regel kan sloten bevatten; deze staan tussen accolades: { }. Bijvoorbeeld: @[kluis]{rodesleutel} Dit is een regel die 'kluis' heet en die een slot 'rodesleutel' heeft. Deze regel is niet geldig omdat er geen acties volgen. Maar stel dat er een actie zou volgen dan zou het actie segment van deze regel alleen worden uitgevoerd op het moment dat het programma in het bezit is van de 'rodesleutel'. Let wel, in namen van sloten en labels mogen geen spaties voorkomen. Acties Een actie in MechaniQue komt overeen met een instructie in een andere programmeertaal. Een actie is een handeling die het MechaniQue programmam daadwerkelijk gaat uitvoeren. MechaniQue kent maar 8 verschillende acties: 1. De commentaar-actie (eigenlijk geen actie). 2. De Zeg-actie (drukt een tekst af op het scherm). 3. De Vraag-actie (vraagt de gebruiker om invoer via het toetsenbord). 4. De Geef-actie (geeft het programma een sleutel). 5. De Neem-actie (neemt een sleutel af van het programma). 6. De Sping-actie (springt naar een andere regel met behulp van het naamkaartje). 7. De Spring-terug-actie (springt naar de regel die na de meest recente sprong had gekomen). 8. De einde-programma actie. Iedere actie begint met een dakje ^ als aanduiding. Meerdere acties kunnen worden gescheiden door pijpjes | ertussen te zetten. Commentaar De commentaar actie ziet er zo uit: ^#mijn commentaar Dergelijke regels zijn handig om een MechaniQue programma duidelijk en onderhoudbaar te maken. Commentaar staat bijvoorbeeld bovenaan het programma zoals in dit voorbeeld programmaatje: @^#Dit is een voorbeeld programma in MechaniQue @^#Het bevat alleen commentaren en geen acties! @fin Het laatste commando dat u ziet is de einde-programma actie. Einde van het programma Een MechaniQue programma eindigt altijd met @fin. Dit moet heel letterlijk worden genomen. Men kan dus niet eindigen met: @[einde]fin of @{doewat}fin, neen altijd moet het zijn: @fin. Om praktische redenen verbouwt de MQ/J 2 compiler een regel als @[einde]fin automatisch naar twee regels: @[einde]^# en daaronder: @fin, zodat u toch handig van een label gebruik kunt maken. Zeg-actie Met de zeg-actie ^: kunt u een tekst op het scherm laten afdrukken. Bijvoorbeeld in dit Hello World programmaatje: @^:Hello World! @fin Vraag-actie Met de vraag-actie ^? kunt u invoer van de gebruiker vragen. Het antwoord van de gebruiker wordt voorafgegaan door een underscore (_) en voorts als sleutel gegeven aan het programma. Het volgende voorbeeld illustreert dit: @^:Heeft u liever a)vanille of b)chocolade? @^? @{_a}^:U heeft liever vanille. @{_b}^:U heeft liever chocolade. @fin Op de derde en vierde regel ziet u dat een tekst alleen wordt afgedrukt als er een sleutel aanwezig is _a of _b, deze sleutels kunnen worden gegeven door de vraag-actie ^? afhankelijk van of de gebruiker antwoord met a of met b. Sleutel geven U kunt ook direct een sleutel geven aan het programma in het programma zelf met het geef-commando ^+ gevolgd door de naam van de sleutel. @^+licht @{licht}^:In de kelder flonkeren de wijnflessen. @fin Dit programma drukt de tekst van regel 2 af omdat de sleutel 'licht' in regel 1 toegekend wordt aan het programma. En dus kan het programma het slot op regel 2 openen en de actie (afdrukken van tekst) uitvoeren. Sleutel nemen Op gelijke wijze kunt u ook een sleutel innemen: ^- gevolgd door de naam van de sleutel. @^+licht @^-licht @{licht}^:In de kelder flonkeren de wijnflessen. @fin Op regel 2 pakken we nu direct de licht-sleutel af, waardoor dit programma uiteindelijk niets toont. Spring-actie Met een spring actie kunt u naar een regel springen door het bijbehorende naamkaartje (label) op te geven. Het spring commando luidt ^-> gevolgd door de naam van de regel waarnaar u wilt laten springen. @[herhaling]^:herhaling is de beste manier om dingen te onthouden! @^? @^->herhaling @fin Het bovenstaande programma blijft zijn boodschap eindeloos herhalen; gebruik tijdens een interactie moment #exit om het programma te onderbreken. Spring-terug-actie Met <- springt u terug naar de regel die volgt na de laatste sping-actie. @^->start @[sub]^:ik ben een subroutine @^<- @[start]^:ik start hier en ga nu de subroutine uitvoeren @^->sub @^:Nu ben ik weer terug van de subroutine. @fin Negatieve sloten Een negatief slot begint met een ! zoals in: {!inkt}^:kan niet schrijven en opent alleen als de betreffende sleutel NIET aanwezig is. Systeemsleutels Zoals u heeft gezien kunt u een programma op twee manieren een sleutel geven: a. door een vraag-actie te maken; de sleutel is dan een underscore (_) plus de gebruikersinvoer minus alle spaties. b. door expliciet een sleutel te geven met het + commando. Hier wordt meteen een onderscheid duidelijk. In het eerste geval is er sprake van een sleutel die door het systeem MechaniQue zelf wordt gegenereerd en gegeven; op verzoek van de programmeur en de gebruiker (vraag-actie). Er zijn meer van dergelijke 'systeemsleutels'. In de onderstaande hoofdstukjes zal ik laten zien welke systeemsleutels er nog meer bestaan. Dobbelsteenslot Tijdens ieder interactie moment (^?) wordt er een dobbelsteen opgegooit door het programma. Afhankelijk van de zijde waarop deze land wordt een van de volgende sleutels uitgedeeld: DICE1, DICE2, DICE3, DICE4, DICE5 of DICE6. Bij iedere worp wordt eerst de vorige sleutel van dit type weer ingenomen. Middels deze eenvoudige simulatie kunt u willekeur aanbrengen in uw verhaal: @[start]^:Vandaag kookt de kok: @{DICE3}^:Tournedos. @{!DICE3}^:Struisvogelbiefstuk. @^? @^->start @fin Twistslot Aan het begin van het programma wordt er een muntstuk opgegooit door het programma. Afhankelijk van de zijde waarop deze land krijgt het programma een sleutel TWIST1 of TWIST2, en soms helemaal geen sleutel! @[start]^:Je bent geboren als @{TWIST2}^:een dubbeltje en je zult nooit een kwartje worden. @{!TWIST2}^:een kwartje en je zult nooit een dubbeltje worden. @^? @^->start @fin Tijdslot De historische sleutel TIME kan gebruikt worden om ook de vierde dimensie in uw spel te betrekken. De TIME systeemsleutel functioneert iets anders dan alle andere sleutels. Stel dat we TIME8 hebben. Een slot met {TIME7} zal nu TOCH OOK openen, terwijl TIME7 toch een andere letterreeks is dan TIME8. Maar voor TIME maakt MechaniQue een uitzondering. Met TIME kunt u een slot laten openen indien men dat slot binnen een bepaald aantal interacties aantreft. Dit is handig voor het instellen van tijdslimieten; bijvoorbeeld een poort die slechts een aantal tijdseenheden lang openblijft. Tijdens ieder interactie moment (^?) wordt de oude tijdsleutel ingenomen en een nieuwe uitgedeeld: @[start]^:Na drie keer @{!TIME3}^:zal dit bericht veranderen. @{TIME3}^:is dit bericht veranderd... @^? @^->start @fin Cyclusslot Tijdens ieder interactie moment (^?) veranderd de cyclussleutel, de volgorde is: ROT1, ROT2, ROT3, ROT4, ROT5, ROT6, ROT1, ROT2, ROT3 etc. @[start]^:Je wacht op de bus.. @{ROT1}^:De weg is verlaten. @{ROT2}^:Opeens hoor je in de verte geronk. @{ROT3}^:De bus nadert.. @{ROT4}^:De bus stopt bij de halte, helaas verkeerde nummer. @{ROT5}^:De bus vertrekt bij de halte. @{ROT6}^:In de verte zie je de achterkant van de bus. @^? @^->start @fin Padslot Tijdens ieder interactie moment (^?) veranderd de cyclussleutel, de volgorde is: PATH1,PATH2,PATH3,PATH4,PATH5,PATH6,PATH5 en weer terug naar 1. Sleuven Een sleuf wordt geschreven als {$muntstuk} maar neemt de sleutel bij opening direct in. @^+munt @[opnieuw]^:a. Stop een munt in de frisdrank-machine. @^? @{_a}{!munt}^:Geen kleingeld meer! @{_a}{$munt}^:Er rolt een blik cola uit de machine. @^->opnieuw @fin Het OR slot Het OR slot opent als een van de voorafgaande sloten opende: @^:kies een letter: a,b of c. @^? @{_a}{_b}{OR}^:deze zijn geldig @{_c}^:deze is ongeldig @fin Alle sloten die NA het OR slot komen moeten weer gewoon allemaal geopend worden: {_a}{_b}{OR}{_c} vereist dus sleutel _a+_c of _b+_c De NUL sleutel De nul sleutel {ZERO} wordt bij ieder interactie moment uitgedeeld. De sleutel vervalt bij het openen van de eerste regel: @^? @{_a}^:you entered a. @{ZERO}^:you entered something ELSE.. @fin Het voorbeeldprogramma hierboven illustreert de werking en ook het nut van de ZERO sleutel. Indien er iets anders wordt ingevoerd dan de letter a dan zal het programma de regel met het ZERO slot openen. Gevorderde programmeurs kunnen de ZERO sleutel het beste interpreteren als de wijze waarop MechaniQue het concept ELSE implementeert. Uitgebreide sloten Met een uitgebreid slot kan men een slot van toepassing laten zijn op meer dan 1 regel. Een dergelijk slot omsluit regels met << en >> zoals in: @^+a @^+b @{a}<<^:deze regel heeft een a nodig @^:deze regel heeft ook een a nodig @{b}<<^:deze regel heeft zowel a als b nodig @{a}{b}>>^:deze regel heeft ook beide nodig @>>^:deze regel heeft een a nodig @fin Magische Inkt Met magische inkt kan men teksten na een keer laten verdwijnen. Een tekst kan geschreven worden met magische inkt door deze tekst vooraf te laten gaan door een puntkomma. Bijvoorbeeld: ^:;hallo. De puntkomma's worden in alle gevallen gecombineerd met de dubbele punt van de zeg-actie :; immers +;sleutel heeft weinig zin. De combinatie van : en ;, de :; wordt in MechaniQue een beschrijvende zeg-actie genoemd. Met de _DESCRIBE_FULL sleutel kunnen magische inkt regels opnieuw worden getoond: @[start]^:;toon eenmaal @^? @^->start @fin Bovenstaand programma toont de tekst in de eerste regel slechts 1 maal. Maar wanneer u met hoofdletters invoert DESCRIBE_FULL (niet voorafgegaan door een underscore) verschijnt de tekst toch weer wel. Hoewel magische inkt; of formeler gezegd de beschrijvende zeg-actie, op het eerste gezicht een mysterieuze bedoening lijkt is dit mechanisme eigenlijk heel nuttig voor traditionele adventures. In dergelijke spellen worden omgevingen vaak beschreven, maar die beschrijvingen moeten niet telkens opnieuw bij het betreden van de omgeving worden getoond, alleen als men daar specifiek om vraagt. Via de parser (later beschreven) kan men de speciale sleutel _DESCRIBE_FULL koppelen aan een gebruikersinvoer als 'kijk rond' of 'verken omgeving'. Een tweede belangrijk punt is dat mensen die de audio versie beluisteren van een verhaal niet iedere keer door een lange beschrijvende tekst moeten om invoer te kunnen geven; bij de gesproken versie van een IF verhaal moet men immers wachten met invoer via toetsenbord of microfoon totdat de stem uitgesproken is. Kwantificatie Omdat MechaniQue voornamelijk werkt met sleutels en sloten heeft de taal wat meer aandacht voor het feit of een bepaald voorwerp aanwezig is en wat minder aandacht voor het aantal dat aan dergelijk voorwerp is verbonden. Toch kan MechaniQue ook met kwantificaties overweg. Een kwantificatie wordt in MechaniQue aangeduidt door het vermenigvuldigings symbool: *. Met kwantificatie kan men meerder sleutels in een keer uitdelen: ^+lamp*2 of innemen: ^-lamp*2 en men kan ook controleren op 2 of meer lampen: {lamp*2} precies 2 lampen: {=lamp*2} of minder dan 2 lampen: {!lamp*2} Men kan de kwantificatie van een getal ook tonen in een zeg-actie; hiertoe omringt men de sleutel met twee setjes %%. Bijvoorbeeld : ^:ik heb %%lamp%% in de kerstboom. Een dergelijke zeg-actie zou kunnen resulteren in een tekst als: ik heb 5 lampen in de kerstboom (indien =lamp*50). Natuurlijk is dit systeem vrij stug en kan men niet echt makkelijk complexe berekeningen doen; maar dat zou men voor de meeste IF ook niet nodig moeten hebben. PINPOINT In een adventure kunnen spelers er een behoorlijke janboel van maken door objecten rond te laten slingeren. Om bij te houden waar objecten liggen kun je in mechanique de pinpoint actie gebruiken ^&, deze registreert de huidige positie (= label) @[room1]^& @^? @{_dropbeer}^-beer|^+&beer Indien de speler nu zegt 'drop beer' dan ligt het blikje bier in de kamer room1. De sleutel die het programma kwijt raakt is beer (-beer), maar het programma krijgt: room1beer (+&beer), de & in locks en keys verwijst altijd naar de huidige pinpoint positie. Een regel als {&beer} opent dus nu nog wel, maar als we langs [room2]^& komen zal deze niet meer openen om dat je nu in een andere kamer zit (room2) en je hebt geen key room2beer dus {&beer} opent niet meer. Bij wijze van extraatje is er een nog comfortabelere notatie; @&room wordt automatisch vervangen door de compiler door... @[room]^:Room.|^& Parser Met parsing kan men traditionele adventures maken waarin volzinnen worden begrepen door het programma. De truuk is hier om de volzinnen om te zetten naar accepteerbare interactie sleutels. Hiervoor wordt een parser bestand gebruikt. Een parser bestand kent twee soorten regels; filters en vervangers. Een filter zorgt ervoor dat een bepaald woord in het antwoord van de gebruiker wordt weggelaten in de interactie sleutel. Filters kunnen zo tussenvoegsels, voorzetsels en lidwoorden filtereren uit gebruikersinvoer. Een filter in een parser bestand heeft het volgende formaat @X waarbij X het woord moet zijn dat gefilterd dient te worden. Om het parser systeem beter te kunnen begrijpen zal ik het toelichten aan de hand van een voorbeeld. Stel dat de gebruiker invoert : 'open het kistje'. Het programma moet nu weten dat het een kistje moet openen. Het programma ontvangt de interactie sleutel '_openhetkistje'. Maar de gebruiker zou ook kunnen zeggen: 'open kistje' en in dat geval zou het programma de interactie sleutel '_openkistje' ontvangen. De truuk is dus om te zorgen dat meerdere gebruikersinvoer mogelijkheden leiden tot dezelfde interactiesleutel; dat is de taak van de parser. In het voorbeeld zouden we bijvoorbeeld het filter @het kunnen toepassen. Nu leiden zowel 'open het kistje' als 'open kistje' tot de interactie sleutel '_openkistje', omdat 'het' gefilterd wordt door de parser. Helaas zijn we daarmee nog niet helemaal klaar. De gebruiker zou ook kunnen zeggen 'ontsluit het kistje' of 'open de kist'. Graag zouden we natuurlijk zien dat ook deze zinnen tot dezelfde interactie sleutel leiden. Met de MechaniQue parser is dat geen probleem. We hebben namelijk naast filters ook vervangers. Een vervanger vervangt het ene woord door het andere. Een vervanger heeft het formaat: @X=Y waarbij X het woord symboliseert dat vervangen moet worden en Y het woord waarmee X vervangen dient te worden. Als we nu in onze parser toevoegen: @kistje=kist @ontsluit=open @het @de Dan worden 'de' en 'het' gefilterd en wordt 'kistje' vervangen door 'kist' en 'ontsluit' door 'open'. Al deze invoer mogelijkheden leiden nu tot slechts 1 interactie sleutel: 'open kist' 'open kistje' 'open de kist' 'open het kistje' 'ontsluit kist' 'ontsluit kistje' 'ontsluit de kist' 'ontsluit het kistje' 'open de kist' leiden tot de interactie sleutel: '_openkist'. In het laatste invoer voorbeeldje boven, zien we dat ook meerdere spaties automatisch worden gefilterd. Op deze manier kunnen dus heel eenvoudig parsers worden gebouwd. Zo worden lidwoorden en voorzetsels dikwijls gefilterd: @de @het @een @op Gebruikersinvoer als: op de kast Wordt dan na parsing: _kast Wat natuurlijk een hele nette sleutel is voor een slot {_kast} (Vergeet niet dat gebruikersinvoer direct leidt tot een interactie sleutel en dat die dus weer in een slot gebruikt kan worden). Op diezelfde wijze kan men teksten vervangen met het = teken: @noord=n @zuid=z Een parser bestand als: @ga @naar @noorden=n @zuiden=z @oosten=o @westen=w @het Maakt van gebruikersinvoer: 'ga naar het noorden' De sleutel: _n en van: 'ga naar het zuiden' of: naar zuiden de sleutel: _z EXAMPLES / VOORBEELDEN 99 Bottles of beer @^+beer*99 @[start]{beer*2}^:%%beer%% bottles of beer on the wall, %%beer%% bottles of beer @{=beer*1}^:%%beer%% bottle of beer on the wall, %%beer%% bottle of beer @^-beer @{beer*2}^:Take one down and pass it around, %%beer%% bottles of beer on the wall. @{=beer*1}^:Take one down and pass it around, %%beer%% bottle of beer on the wall. @{!beer}^:Take one down and pass it around, no more bottles of beer on the wall. @{beer}^->start @^:No more bottles of beer on the wall, no more bottles of beer @^:Go to the store and buy some more, 99 bottles of beer on the wall. @fin Little demo adventure @^->beach|^#DEMO by Gabor de Mooij, problems: ZERO must be uppercase, there may never be more than one ZERO key @[misc]{_i}^->inventory @{_u}{_t}{_d}{OR}^:Please be more specific.. @{_n}{_s}{_w}{_e}{OR}^:You cannot go that way... @^<- @[inventory]^:You carry with you:|^#inventory implementation @^+ZERO @{key}^:a golden key. @{shovel}^:a rusty shovel. @{ZERO}^:nothing. @^<- @[beach]^:the beach. @^:;To the south is the deep blue sea, to the east is a forest and @^:;to the west is an abandoned lighthouse. @{DICE1}^:A gull is circling above the sea. @^? @{_w}{!key}^:The door of the lighthouse is locked. @{_w}{key}^->lighthouse @{_e}^:You wander to the forest.|^->forest @{_ushovelsand}{_dig}{_digshovel}{OR}{shovel}^:You dig up a key (Taken).|^+key @{;lighthouse}^:A lighthouse, made of white bricks. It looks desolated. @^->misc @^->beach @[forest]^:the forest. @^:;To the north is a wooden house, the door is ajar. @^:;To the west is a path leading down to the beach. @^? @{_w}{_beach}{OR}^->beach @{_n}{_uhouse}{OR}^->house @^->misc @^->forest @[house]^:in the wooden house. @^:;Here are a table, some chairs and a fireplace. @^:;To the south is an open door, leading to the forest. @{!chest_open}^:;In the corner lies a toolchest. @{chest_open}^:;In the corner lies an open toolchest. @{OCC11_25}^:On the table lies a christmas card. @^? @{;fireplace}^:Seems a long time ago since someone used that. @{;chair}^:A simple chair, made of wood. @{;table}^:Just a normal table. @{;toolchest}{;chest}{OR}{!chest_open}^:A big red toolchest. @{;toolchest}{;chest}{OR}{chest_open}{!shovel}^:An open chest with a shovel inside. @{;toolchest}{;chest}{OR}{chest_open}{shovel}^:An open chest, it's empty. @{_utoolchest}{_uchest}{OR}{chest_open}^:The toolchest is already open. @{_utoolchest}{_uchest}{OR}{!chest_open}^:Opening the chest reveals a shovel|^+chest_open @{_tshovel}{chest_open}{!shovel}^:You take the shovel out of the chest.|^+shovel @{_s}{_exit}{_quit}{OR}^:You leave the house.|^->forest @^->misc @^->house @[lighthouse]^:in the lighthouse (using the key to unlock the door) @^:well done! You finished this little demo. [PRESS ENTER TO QUIT] @^? @fin Parser: @go @to @with @on @over @in @at @by @using @applying @east=e @north=n @west=w @south=s @open=u @talk=u @inventory=i @walk @run @drink=d @use=u @attack=u @pull=p @put=p @drop=p @get=t @fetch=t @grasp=t @the @take=t @look=DESCRIBE_FULL @around @see=l @investigate=l @apply=u @open=u @close=c @attack=u @wait=wt @sleep=wt @stay=wt @enter=u Troubleshooting MechaniQue I am trying to start MechaniQue but I get java errors like these: Exception in thread "main" java.lang.NoClassDefFoundError: mqj2/m2 (wrong name: mechanique/m2) at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClass(ClassLoader.java:620) at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:124) at java.net.URLClassLoader.defineClass(URLClassLoader.java:260) at java.net.URLClassLoader.access$100(URLClassLoader.java:56) at java.net.URLClassLoader$1.run(URLClassLoader.java:195) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findClass(URLClassLoader.java:188) at java.lang.ClassLoader.loadClass(ClassLoader.java:306) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:268) at java.lang.ClassLoader.loadClass(ClassLoader.java:251) at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:319) Rename the folder of mechanique to 'mechanique'. The m2 file should be placed in a folder called mechanique. You should run mechanique like this: java mechanique/m2 I get error messages when trying to run a .roll file, the messages mention incompatible class definitions. Your Java Virtual Machine is not compatible with the roll file. Try to compile the book-file again or, if you are not the writer of the book file download a copy of the roll file for your specific JVM. Unfortunately roll files are JVM specific. Traditionally JVMs dont't tend to change that often but due to the open-sourcing of Java it seems that there are some upgrades at this moment. If you are a story writer publish your roll files for the newest JVM. Where can I get a JVM or JRE for less known systems? If you run a less known OS, like for instance BeOS or OS/2 getting a JVM can be a little bit more complicated. In case of OS/2 Warp there seems to be a company called eComStation that offers more information on Java on OS/2. For information on Java for Beos (or Haiku), try consulting www.begroovy.com or Bebits. It seems that there are quite a few JVM implementations out there. I googled a while and I even found one for RISCOS. Please keep in mind that if you want to compile MechaniQue from source you'll need a Java compiler that complies with the Java 1.5 syntax. ------------------------- INSTALLING MECHANIQUE SERVER examples assume tomcat is in: /usr/local 1. Put the mqserv directory in Tomcat's webapps directory. like: cp -R ./mqserv /usr/local/jakarta-tomcat-4.1.31/webapps/ 2. Copy the mechanique directory to cp -R ./mechanique /usr/local/jakarta-tomcat-4.1.31/webapps/mqserv/WEB-INF/classes/mechanique 3. Export class path: export CLASSPATH="$CLASSPATH:/usr/local/jakarta-tomcat-4.1.31/webapps/mqserv/classes/ your MechaniQue server is ready! INSTALLING A ROLL FILE ON THE SERVER put your roll files in : /usr/local/rolls/