original in de Philipp Gühring
en to nl Tom Uijldert
Philipp heeft net zijn examens gedaan bij de HTL Wiener Neustadt, een HTS voor EDP (Electronic Data Processing). Daarom heeft hij nu tijd voor zijn software ontwikkelgroep genaamd Futureware 2001. Hij is een fan van Linux en een actief lid van de Linux User Group Austria.
Würstelstand is een handelssimulatie spel in het Duits, waarbij de speler een hotdog kraam moet bestieren. Het contact met de klanten werd daarbij bijna een adventure game. Juist voor dit klantencontact heb ik de taal Dialog ontwikkeld, die aan de volgende wensen moest voldoen:
De dialogen worden opgeslagen in ASCII tekstbestanden en worden regel voor regel
geïnterpreteerd, hoewel het is toegestaan om op ieder moment naar andere
regels te springen. We gebruiken een eenvoudige editor voor het creëren
van de dialogen. De naam van het bestand is naam.BAT
(bijvoorbeeld HALE.BAT). Als de speler (Leni) iets zegt:
Leni: Tekst Leni: Goedemorgen meneer! Wat kan ik voor U doen? Leni: Kijk toch eens naar die jeugd! Het is toch wat! | Kijk toch eens naar die jeugd! Het is toch wat! Uit je bed gevallen? |
Twee broodjes Frankfurter en een Coke. Schiet eens op, ouwe man! |
Wanneer de tegenspeler iets zegt (Kunde is klant in het Duits, Telefon is telefoon):
Kunde: Tekst Kunde: Twee broodjes Frankfurter en een Coke. Schiet eens op, ouwe man! Telefon: Futureware 2001, Philipp Gühring. Wat is er van uw dienst? |
Iedere dialoog eindigt met:
Ende(Ende is einde in het Duits).
Een eenvoudig voorbeeld: |
Leni: Morgen, meneer! Wat is er van uw dienst? Kunde: Goedemorgen! Een Käsekrainer alstublieft! Leni: Momentje. Leni: Alstublieft. Kunde: Bedankt. Dag! Leni: Dag! Ende |
Regels waar je naar toe kunt springen worden aangegeven met een
dubbele punt (:) aan het begin van de regel waarna de
naam van de regel volgt waarnaar kan worden gesprongen. Er kan naar
de regel toe worden gesprongen met het commando Sprung
(Sprung is springen in het Duits):
:Target //Voorbeeld sprongopdracht volgt: Sprung Target
|
Wat doet de interpretator met dit voorbeeld? Allereerst ziet hij het commando
Leni: en voert de tekst uit
1. De volgende regel bevat commentaar (Eerst doen we
dit) wat wordt overgeslagen. De volgende regel bevat de opdracht
Sprung . De interpretator gaat nu op
zoek door de gehele dialoog naar een regel met de naam MENU_0,
vindt deze een paar regels verderop en springt er heen. Vervolgens meer
commentaar (Ben ik weer) en het laatste commando is
Leni:, die het getal
3 op het scherm zet. Uiteindelijk is dus de opdracht
Leni: 2 overgeslagen en zal
Leni dus geen 2 zeggen.
|
Zoals we hebben kunnen zien kan een regel:
// Dit is commentaar ************************************** *Zo kun je ook commentaar maken * **************************************Commentaar kan niet op dezelfde regel als commando's voor komen.
Leni: Ik begrijp niks meer. // GEEN COMMENTAARDe interpretator zal Ik begrijp niks meer. // GEEN COMMENTAAR herkennen als zijnde de tekst die op de uitvoer moet komen!
|
Dialog heeft het volgende systeem: het houdt een lijst bij die door de gebruiker
wordt aangeleverd waarin de mogelijke antwoorden staan vermeld. Op het moment
van uitvoeren zal een menu op het scherm worden getoond. De speler kan nu
kiezen en het systeem zal naar het gedeelte springen wat die keuze afhandelt.
Allereerst worden lijstelementen ingevoegd met behulp van de commando's NEU en ALT. Beide opdrachten worden gevolgd door de naam van de regel, waarnaar moet worden gesprongen, en de tekst voor deze keuze. De tekst kan iedere grootte hebben, het systeem zal deze automatisch netjes opmaken. Met de opdracht MENÜ wordt de gehele lijst getoond en de speler kan nu een keuze maken. |
Neu buy,Warme hotdog, zoals altijd? Neu work,Hoe gaat het op het werk? Neu language,Doe je nog steeds die taalcursus bij WIFI? Neu family,Hoe gaat het met het gezin? Neu weather,Mooi weertje, niet? MenüHiermee is de speler in staat om alle keuzes af te lopen, één voor één. De keuzes zijn herbruikbaar. De niet gekozen elementen blijven in de lijst staan en kunnen op een later tijdstip alsnog worden gekozen. Laten we werk kiezen uit bovenstaand menu.
:work Leni: Hoe gaat het op het werk? Kunde: Druk, druk, druk, zoals altijd. MenüZoals gezegd, wordt alleen de betreffende keuze weergegeven. Het menu heeft dan nog de volgende onderwerpen:
Kunde: Hoeveel wilt U er? Alt some, 10 Stuks Alt more, 20 Stuks Alt most, 100 Stuks Menü :some //Hier gaan we verder bij een keuze van 10 stuks :more ... :most ...Omdat het geen zin heeft om de opties nog langer te handhaven na het maken van de keuze, wordt de rest van het menu automatisch weggegooid. Wanneer de speler bijvoorbeeld 20 stuks kiest, zal de interpretator naar de regel more springen.
:more Kunde: Zeker weten? Leni: Ja, ik wil er 20. Kunde: Hoe snel moet U ze hebben? Alt 1, Morgen Alt 2, Overmorgen Alt 3, Een keer MenüAls laatste is er nog een combinatie van de twee soorten.
Kunde: Ach, die goeie ouwe tijd. Alt Memory,Jazeker, ik kan me nog herinneren dat ... MENÜ
List 0
is te gebruiken voor opties. List 1
is voor
algemene onderwerpen als gezin, werk, ontspanning etc... Als men bijvoorbeeld
over het werk wil praten en er meerdere onderwerpen zijn binnen het begrip
werk, dan gebruikt men List 2
. We hebben een voorbeeld
in de HALE-dialoog. Indien men meer lijsten nodig heeft dan de drie die hier
zijn genoemd dan moet er een constante in de code worden veranderd.
LISTE
kan een andere lijst als
huidige worden gedefinieerd.
LISTE 0 LISTE 1 LISTE 2Uiteraard blijven alle keuzes in deze lijsten bij dit proces. Opdrachten als NEU, ALT, MENÜ en LÖSCHEN slaan altijd op de huidige lijst.
List 1
List 2
(LISTE 2)
en we voegen de antwoorden toe:
List 1
(LISTE 1).Neu Memory,Jazeker, ik kan me nog herinneren dat ...Lijst 0 werd automatisch na het menu weggegooid en was daarom alleen bruikbaar voor opties, niet voor onderwerpen.
Het is aan te bevelen om te kijken naar de voorbeelden HALE.BAT en PETER.BAT, waar veel gebruikt wordt gemaakt van lijsten.
LÖSCHEN targetGooit alle elementen uit de lijst weg die naar target verwijzen. Bijvoorbeeld:
LÖSCHEN familiyOm de gehele lijst te verwijderen gebruikt men de asterisk:
LÖSCHEN *(Indien er behoefte bestaat, kan men hier ondersteuning voor reguliere expressies inbouwen ;-).
Menü
laat alle elementen uit de
lijst zien en geeft gelegenheid tot het maken van een keuze. Hierna wordt de
keuze, met alle toegevoegde opties (ingebracht met
ALT), uit de lijst verwijderd. Als laatste
springt de interpretator naar de aangegeven regel. Als er slechts
één element als keuzemogelijkheid is, heeft het programma
door dat dit de enige keuze is en wordt er dus geen menu gepresenteerd. Als de
lijst leeg is of de aangegeven regel niet bestaat, zal de regel direct na het
menu worden uitgevoerd.
1 | Event; | //Het nummer van de gebeurtenis (kijk in texte.h). | |
2 | geliefert; | //S | //0-10, hoeveel tienden zijn geleverd |
3 | wtag; | ////dag van de week | |
4 | tag; | //dag van de maand | |
5 | monat; | //maand | |
6 | jahr; | //jaar | |
7 | Datum; | //serienummer van de dag (1.1.1997 = 0) | |
8 | wetter; | //het weer van vandaag | |
9 | konto; | //S | //banksaldo |
10 | kapital; | //S | //cash |
11 | ausgaben; | //S | //uitgaven van vandaag |
12 | einnahmen; | //S | //inkomsten van vandaag |
13 | sterne; | //S | //kwaliteit van de hotdog kraam (0-5 sterren) |
14 | wverkauf; | //aantal verkocht deze week | |
15 | weinnahmen; | //wekelijks inkomen | |
16 | wausgaben; | //wekelijkse uitgaven | |
17 | 0; | //S | //nieuwe inkomsten/uitgaven (ingebracht door de dialoog) |
18 | Nachrichtenserie; | //welke serie nieuwsberichten (0=iedere, 1=...) | |
19 | Nachricht; | //welk nieuwsfeit uit de huidige serie (0=1ste dag, 1=2...) | |
20 | LottoNr[0]; | //Hoeveel loten worden gebruikt (0-6) | |
21 | LottoErgebnis[0]; | //Hoeveel loten hebben gewonnen | |
22 | LottoGewinn[LottoErgebnis[0]]; | //Hoeveel Leni won | |
23 | S.Image; | //S | //Portret van Leni |
24 | S.Override; | //S | //Interrumperende gebeurtenis |
25 | S.wverkauf[1]; | //Vorige week verkochte producten | |
26 | S.weinnahmen[1]; | //Inkomsten van vorige week | |
27 | S.wausgaben[1]; | //Uitgaven van vorige week | |
28 | S.wverkauf[2]; | //Twee weken geleden verkochte producten | |
29 | S.weinnahmen[2]; | //Inkomsten van twee weken geleden | |
30 | S.wausgaben[2]; | //Uitgaven van twee weken geleden | |
31 | S.NOverride; | //S | //Interrumperende gebeurtenis voor morgen |
32 | S.wetter_bericht; | //Welk weerbericht | |
33 | Gesamtwert(); | //Totale waarde van de kraam | |
34 | Wetterbericht[S.wetter_bericht].Ereignis; | //Welk weertype | |
35 | Tageszeit; | //tijd van de dag in minuten | |
70..79 | Lagermenge | //aandelen | |
80..89 | Verkaufspreis | //S | //prijs van het product |
90..99 | Kaufmenge | //S | //aantal in bestelling |
batch.cpp |
// Klant: Peter Hinzing // // Gebruik van de registers //[100] Hoe vaak hij hier was //[101] Zakgeld //[102] Diverse gebeurtenissen //[103] Willekeurig nummer: bestelling //[104] Willekeurig nummer: antwoord op bestelling //[105] Andere dialogen: werken op de 5de dag //[106] Ok //[107] Start van het spel, na verkiezing //[108] Game.stake.type //[109] Game.stake.quantity //[110] Game.choose.Peter //[111] Game.choose.Leni //[112] Hobby geactiveerd //[113] Thuis geactiveerd //[114] Dialoog over Würstelstand //[115] Hoeveelheid Coke //[116] Te veel?************************* //* Nog te doen |
In register [100] wordt vastgelegd hoe vaak Peter een bezoek heeft gebracht aan het hotdog kraampje. Bij het eerste bezoek stelt hij zichzelf voor. Bij zijn tiende bezoek is hij wat meer op zijn gemak en minder formeel. In [101] heeft hij zelfs controle over zijn wisselgeld, ...
daten.h
):
[200]: Leni kan naar Immigratie met Hale [201]: Leni leest de "Wanted"-poster [202]: Leni heeft "Steen-Schaar-Papier" gespeeld met Peter! (Stout!)
Hoe kan ik dit laten gebeuren?
Aktion
(dat is actie in het Duits)
in de gesprekken.
Aktion expression // activating Cheats: Aktion 3 // de gebeurtenis, berekend in register 100: Aktion [100]Wat kun je doen met deze gebeurtenissen? Hier is een deel van deze lijst voor Würstelstand:
0 | Error/Never | Gebeurtenis 0 wordt afgevangen en er gebeurt niets mee |
1 | Initialising | Wordt bij de opstart aangeroepen en activeert producten, klanten, ... |
2 | End | Wordt aangeroepen bij het einde van het spel |
3 | activating FW-Cheat | Wie heeft dit er in gezet?!? |
4 | deactivating FW-Cheat | Voor je houden! |
5 | Leni.competition.activating newspaper | Portret van Leni is goed genoeg -> activeer krantenartikel over de te houden wedstrijd |
6 | Leni.competition.Zeitung->TelefonNr | Krantenartikel geeft het telefoonnummer om je als kandidaat van de wedstrijd aan te melden |
7 | Leni.competition.deactivating TelNr | Na aanmelding wordt het telefoonnummer afgesloten |
8 | deactivating Hale | Hale zet zichzelf op non-actief omdat Leni hem heeft beledigd. |
9 | Hale recommends Josi | Hale beveelt Josi aan, na er over te hebben gepraat (Smalltalk helpt!) |
10 | deactivating Josi | Josi zet zichzelf op non-actief |
11 | deactivating Peter | Peter doet hetzelfde |
12 | Sepp Nachricht without Leni aktivieren | Sepp doet illegale voorstellen, Leni weigert en alles komt in de openbaarheid |
13 | Sepp Nachricht with Leni aktivieren | Leni gaat akkoord met de dubieuze voorstellen, problemen volgen |
14 | lost game | Postbode Gottfried maakt een einde aan het spel |
15 | won game | Gottfried begrijpt de waarde van de hotdog kraam en Leni wint |
16 | Hale.news article Asyl activate | Leni heeft met Hale gepraat over zijn familie, waarvan het gevolg een krantenartikel is over asielrecht. |
17 | Hale.news article->Telefonnr activate | De krant geeft het telefoonnummer dat kan worden gebeld |
18 | Hale->Zeitungsbericht->Telefonnr deactivating | Het gesprek beëindigt het telefoonnummer |
19 | Hale->Familie activating | De familie van Hale krijgt asiel |
20 | activating the spy | Leni had eigenlijk spion moeten zijn maar dat is nooit gebeurd. |
33 | New products 1 (New supplier) | Deze gebeurtenis breidt het assortiment uit |
100 | won contest | Leni heeft gewonnen, klanten kunnen erover praten |
101 | losts contest | |
102 | Lotteryprice | Leni heeft de loterij gewonnen |
Rechne
(bereken in het Duits) kan men
expressies berekenen en het resultaat in een register zetten. Bijvoorbeeld:
Rechne [100]: 20 + [30] * 10De inhoud van register 30, vermenigvuldigd met 10 en daarbij 20 opgeteld, wordt in register 100 opgeslagen.
De volgende berekeningen zijn mogelijk:
Operatie | Notatie | Voorbeeld | Oplossing | |
Haken | (a) | (10+20)*30 | 900 | |
Register | [a] | [20] | De inhoud van register 20 | |
Vermenigvuldiging | a*b | 3*4 | 12 | |
Delen | a/b | 10/5 | 2 | |
Rest | a%b | 10%3 | 1 | |
Optellen | a+b | 1+1 | 2 | |
Aftrekken | a-b | 1-1 | 0 | |
Toewijzing | [a]:b | [10]:20 | Zet in register 10 de waarde 20 | |
Vergelijkingen | a?b | Ja(1) of Nee(0) | ||
Gelijk aan | a=b | 10=20 | Nee(0) | |
Kleiner | a<b | 10<20 | Ja(1) | |
Groter | a>b | [10]>[20] | ||
AND | a&b | 1=1 & 2=2 | Als 1 gelijk 1 EN 2 gelijk 2 | |
OR | a|b | 1=1 | 2=2 | Als 1 gelijk 1 OF 2 gelijk 2 | |
Random getal | a Z b | 1 Z 6 | Retourneert een toevallig getal tussen 1 en 6 |
De grootste uitdaging was de implementatie van de rekenkundige evaluatie, die in staat is tot evaluatie van de volgende expressie:
Aanname: [100]=5, [24]=14, 1Z6=2 [[100]+1]:((1Z6)*([24]>3)+10/2-10%5) [5 +1]:((2 )*(14 >3)+10/2-10%5) [6 ]:(2 *(1 )+5 -0 ) [6 ]:(2 *1 +5 ) [6 ]:(7 ) [6 ]:7Oplossing: [6]: 7. De waarde 7 wordt in register 6 gezet.
Wenn
(dit is als in het Duits)
Wenn condition thenkan men vergelijkingen maken. Bijvoorbeeld:
Wenn [100+1]>10 Kunde: Het getal in register 101 is groter als 10 ! Wenn 1>1 Kunde: ERROR!Als de vergelijking waar is gaat de interpretator verder met de volgende regel. Zo niet dan wordt een regel overgeslagen. Dit kan samen met sprongopdrachten worden gebruikt.
Wenn [102]<10 Sprung KLEINER Wenn [102]=10 Sprung GELIJK Wenn [102]>10 Sprung GROTER ... :KLEINER ... :GELIJK ... :GROTER
BILD expression(
Bild
is plaatje in het Duits). Als bijvoorbeeld
Bild 5wordt genoemd in bestand HALE.BAT dan wordt HALE5.DAT (een speciaal grafisch formaat) weergegeven. Met het klikken van de muis kan men verder gaan met de dialoog.
// commentaar: | COMMANDO REFERENTIE |
Kunde: text | De klant zegt wat |
Tel: text | Gesprekspartner zegt wat |
Leni: text | Leni zegt wat |
:target | Doel waar interpretator naar toe kan springen |
Liste number | Maak een lijst de huidige lijst |
Löschen * | Alle elementen uit de huidige lijst verwijderen |
Löschen target | Verwijderd alle elementen uit de huidige lijst die naar target springen |
Aktion number | Aflopen van gebeurtenissen |
Ende | Einde van het gesprek |
Bild number | Laat het plaatje met naam NameNumber.dat zien |
Sprung target | Ga naar target |
Neu target,Text | Nieuw element in de huidige lijst |
Alt target,Text | Nieuwe optie in de huidige lijst |
Menü | Laat menu zien, en de gebruiker kiezen... |
Wenn condition | Vergelijking (zie verder) |
//then | Waar, ga naar de volgende regel |
//else | Niet waar, sla een regel over |
Rechne expression | Bereken waardes en zet ze in een register |
Bild expression | Toon plaatje, wacht op muis-klik |
#ifdef
wanneer het in de hoofdmodule wordt ingebracht. Zoals hieronder weergegeven,
bespaart men op header-bestanden ;-)
batch.cpp |
#ifndef _DIALOG_H #define _DIALOG_H #ifndef MAIN_MODULE #define DIALOG_TEXT #define DEBUG //Here are the necessary included Header files #include <stdio.h> //... #endif //Here are the whole dialog routines //.. S2 Dialog(char *Filename, TYP Array[]) { //... } #ifndef MAIN_MODULE //Here is everything for the test programs TYP Feld[256]; int main(short argc,char *argv[]) { //Testprogram Dialog(Filename,Feld); } #endif |
wurst.cpp |
#define MAIN_MODULE #include "batch.cpp" TYP Felder[10][256]; int main(short argc,char *argv[]) { Dialog(Filename,Felder[i]); } |