A Rettegés Hegye (kezdőknek ez ajánlott)

1. Oké, kezdjünk is hozzá!
2. A kaland kezdetét veszi
3. A házikó központi helysége
4. A Vécé
5. Konyha
6. Emmi, avagy nőgondok...
7. A sötét pince
8. Zombi attack!!
9. Erdő, avagy a végjáték
10. További üzenet az agynak:

A RETTEGÉS HEGYE

Copyright 2007 by upi@apocalypse.rulez.org

Ez a kis játék leginkább arra szolgál, hogy demonstrálja a KozosSawyer modul képességeit, és a megjegyzésekből meg lehessen tanulni, hogy hogyan kell egy ilyen játékot megírni.

Kössünk egyességet: te elolvasod ezt a kalandot elejétől végéig, cserébe megígérem, hogy a végére eleget fogsz tudni ahhoz, hogy saját kalandot írjál.

A nyelv, amit itt látsz majd a Perl nyelv egy mutációja. TODO: link egy perl gyorstalpalóra.

1. Oké, kezdjünk is hozzá!

Mély levegő, kapaszkodni: egy kaland leírása alapvetően objektumok definiálásból áll. Ez most nagyon tudományosan hangzott, remélem, még velem vagy. Nem lesz annyira nehéz!

Objektumnak nevezünk mindent, ami a játékban előfordul: a helyszíneket (szobákat), tárgyakat, szereplőket, akadályokat, de még a háttérben folyó eseményeket is. Egy objektum alapvetően három dologból áll:

Először is kell neki egy név. A játékban nem szerepelhet két azonos nevű objektum. Ezen kívül a név csak ékezet nélküli kis- és nagybetűkből állhat.

Ügyelni kell a kis- és nagybetűk különbözőségére: a KisSzoba nem ugyanaz, mint a Kisszoba! Általában a javaslat az, hogy az objektumok nevei nagy betűvel kezdődjenek, a tulajdonságok (lásd alább) nevei pedig kisbetűvel. Ha egy azonosító több szóból áll, akkor a második, harmadik, stb. szavakat nagy betűvel érdemes kezdeni az olvashatóság kedvéért.

Másodszor, az objektum örökölheti egy vagy több másik objektum tulajdonságait; ez azért jó, mert nem kell mindet "nulláról" megírni. Például, ha egy új szobát akarsz létrehozni, akkor egyszerűen örökölje a Room objektum tulajdonságait. Így neked nem kell a szoba jelleggel törődnöd; elég azt leírni, amiben ez a szoba különbözik az összes többitől.

Harmadszor, egy objektumnak le kell írni a tulajdonságait. Egy nagyon egyszerű objektum leírása valahogy így nézhet ki:

  Define Tanyer: Thing {
    sdesc = 'tányér',
    location = $Konyha,
  };

Elég egyszerű, nem?

  • Az objektumunk neve az, hogy Tanyer.
  • Örökli a Thing objektum tulajdonságait (a Thing minden dolgok őse a játékban, a legegyszerűbb fajta tárgy).
  • Ezeken kívül a következő tulajdonságokkal rendelkezik:
    • Rövid leírása az, hogy "tányér" (ezt látja majd a játékos).
    • A Konyha nevű helyen található.

(Ügyelni kell a nyitó és csukó kapcsos zárójelekre, a pontosvesszőre a végén, valamint a vesszőkre az egyes tulajdonságok után. Ha ezek kimaradnak, akkor az szintaktikai hiba lesz, és a játék nem fog elindulni.)

A jövőben ha a tányérral bármit kezdeni akarsz, akkor úgy hivatkozhatsz rá, hogy $Tanyer. A tulajdonságait a -> jellel lehet elérni. Például az ldesc tulajdonságot így:

  $Tanyer->ldesc
(Ez egy Thing-től örökölt tulajdonság, azt fogja kiírni, hogy "Olyan, mint bármelyik közönséges tányér.") Ha valamelyik tulajdonságát később meg akarod változtatni, azt a következőképpen teheted meg:
  $Tanyer->{sdesc} = 'törött tányér'
Fontos különbség: az előző esetben tilos kapcsos zárójelbe tenni a tulajdonság nevét, a második esetben viszont kötelező.

2. A kaland kezdetét veszi

Ennyi bevezető után vágjunk is bele a kaland megírásába, a többi dolog menet közben talán a helyére kerül!

A Global nevű objektum már definiálva van; a Modify paranccsal az értékeit tudjuk változtatni. Két tulajdonságot fogunk most felülbírálni:

  • maxscore: Ez a játékos által elérhető maximális pontszám. Nincs konkrét szerepe, csupán annyi, hogy ezt írjuk ki a játékosnak játék közben.
  • init: Ez a kód le fog futni közvetlenül a játékos első parancsa előtt. Itt lehet a játékhoz bevezetőt írni.
Ha még nem írtál soha kalandot, akkor itt érdemes a következőt megfigyelni a tulajdonságokkal kapcsolatban:
  • A tulajdonság neve egy szó lehet, nem lehet benne ékezetes betű, szám vagy írásjel.
  • A tulajdonság nevében lehet kis- és nagybetű. Fent már említettem: érdemes kisbetűvel kezdeni.
  • Ha egy szimpla értéket akarsz definiálni, akkor a név után egy egyenlőség jel jön, és utána az érték maga (például: maxscore).
  • Ha az érték "csinál" valamit (pl kiír dolgokat), vagy egy bonyolultabb kifejezésből áll, akkor { és } jelek közé kell írni (például: init).
  • Ha ki akarsz íratni a játékos számára valamit, akkor a {{ ... }} jelekkel teheted meg (az init csak kiír).

A továbbiakban a "szimpla értéket" érték típusú, a kifejezés jellegűt pedig kód típusú tulajdonságnak nevezzük. (Lista típusú értéket is létre lehet hozni, de erről majd később, a Lada objektumnál írok.)

Modify Global { maxscore = 70, # Ez a játékos által elérhető maximális pontszám. init = { # Ez akkor történik, amikor a játékos elkezdi a kalandot. Kis bevezetőt írunk ki neki. {{ == A Rettegés Hegye == Barátnőddel, Emmivel úgy döntöttetek, hogy ideje kiszabadulni a város zajából egy hétvége erejéig. Szépen el is utaztatok egy erdei házikóba édes kettesben. Tegnap este még "búcsúzóul" megnéztetek egy filmet. A film címe az volt: ''A rettegés hegye'', és tipikus B-kategóriás horrorfilm volt. Tudod, az a tipikus sablonos történet: néhány fiatal pár fogja magát, kiszabadulnak a város zajából, és elmennek együtt egy erdei házikóba pár napra. Szépen el is utaztak hát, aztán persze jöttek a borzalmak, ahogy arra a film első pillanatától fogva számítani lehetett. A film alatt azon tűnődtél, hogy ki az az ütődött, aki egy ilyen filmnek bedől, és képes a moziban azon parázni, hogy "jaj mi lesz szegény szereplőkkel". Sosem értetted Emmi vonzódását ezekhez az olcsó thrillerekhez... Másnap reggel van. Valami furcsa zajra ébredsz. Mintha a távolban valaki, vagy inkább ''valami'' hörögne. És hova tűnt mellőled Emmi? Általában te szoktál korábban ébredni! Jobb lesz megkeresni... }} $IdegesitoDemon->activate; }, };

A játékhoz kötelező egy Me nevű objektumot definiálni. Ez az objektum nem más, mint maga a játékos karaktere. A BasicMe nevű objektum arra szolgál, hogy a Me objektumhoz egy sablon legyen; ezért ebből öröklünk. Ha nem akarunk túl speciális dolgokat csinálni, akkor nem is kell vele sokat foglalkozni. Amit mindenképpen meg kell adni, az a location tulajdonság, különben szegény játékos a semmiben fog kezdeni.

Define Me: BasicMe { location = $Kisszoba, # Itt indul a játék. noun = 'nekem ide oda', };

És íme, itt az első szoba: a Kisszoba. Ezt a Room objektumból örököljük, így csak az sdesc és ldesc tulajdonságokat kötelező megadni, és egy vagy több kijáratot.

Déli irányba (south) egy ajtó vezet, úgyhogy a játékos arra csak akkor fog tudni menni, ha előbb az ajtót kinyitja szépen. (Erről bővebben fogok írni a KisszobaAjto leírásánál.) Ezzel a játékosnak is megmutatjuk, hogy hogyan kell ajtót nyitni/csukni. Ha azt akarjuk, hogy déli irányban "simán" el lehessen jutni egy másik szobába, akkor az ajtó helyett a szoba nevét kell odaírni, pl: south = $Konyha

Figyeljünk meg egy lényeges különbséget az sdesc és ldesc tulajdonságok között:

  • Az sdesc érték típusú; ez pusztán a helyszín neve, amit a játékosnak megjelenít a rendszer.
  • Az ldesc viszont kód típusú, és ki kell írnia a játékosnak a helyszín leírását.
(A különbségről bővebben: lásd a Global objektum leírásánál.)

Mint Global->init esetében, az ldesc itt is a {{ ... }} konstrukciót használja a kiírásra.

Define Kisszoba: Room { sdesc = 'A kis szoba.', ldesc = { {{ Egy erdei házikó kis szobájában vagy. Egy priccs és egy szekrény a teljes bútorzat. Az ablak be van deszkázva, a deszkák között halovány fény szűrődik be. Fenyő, por és doh szaga keveredik a levegőben. Egy faajtó vezet dél felé. }} }, south = $KisszobaAjto, };

A KisszobaAjtora később visszatérek, előbb definiálunk pár dekorációs jellegű tárgyat a kisszobába. Ettől a játékos nagyobb kedvvel fog felderíteni, mivel vizsgálódásai folyamán nem kap folyamatosan olyan üzeneteket, hogy "nem tudom, mi az az 'ablak'". Ezen kívül találó, olvasmányos leírások segítenek a játék hangulatát átadni.

Kezdjük mondjuk az Ablakkal! Az ablak a FixedItem objektumból származik, ami a gyakorlatban azt jelenti, hogy a játékos nem tudja magához venni. (Jól is nézne ki, ha az ablakkal a kezében sétálna fel s alá!)

Ahhoz, hogy a játékos interakcióba keveredhessen egy tárggyal, definiálni kell, hogy milyen főnevek (noun) és melléknevek (adjective) vonatkoznak az adott tárgyra. Minél több lehetőséget adunk meg, annál több értelmes mondatra tud a játék reagálni. Ügyelni kell viszont a szóismétlésre: ha több ablak van egy helységben (mondjuk egy kis ablak és egy nagy ablak), a játékos nyisd ki az ablakot parancsára a játék vissza fog kérdezni:

  "A kis ablakra vagy a nagy ablakra gondolsz?"

Define Ablak: FixedItem { sdesc = 'ablak', ldesc = { {{ Valaki jó alaposan bedeszkázta az ablakot. Úgy néz ki, hogy aki a munkát végezte, elég pontos elképzelése volt arról, hogy aki kint van, az kívül is maradjon, aki pedig bent van, az ne itt menjen ki. Hogy semmit ne bízzon a véletlenre, vastag tölgyfa deszkákat használt, és olyan ácsszegeket, amik tipped szerint bőven az ablakkeret túloldalán érnek véget. Úgy néz ki, hogy itt nem jutsz ki a szobából. Talán az ajtóval több szerencséd lesz. }} }, location = $Kisszoba, noun = 'ablak keret ablakkeret', # Főnevek adjective = 'bedeszkazott deszkazott ablak', # Melléknevek verDoOpen = { $this->ldesc }, # A játékos megpróbálja kinyitni az ablakot. verDoUnlock = { $this->ldesc }, # A játékos megpróbálja unlockolni az ablakot TODO };

A deszka hasonlóan mozdíthatatlan, mint az ablak.

Az ablaknál már látható volt két tulajdonság, amiről eddig nem volt szó: felülírtuk a verDoOpen és verDoUnlock tulajdonságokat. (Ezek egyszerűen a hosszú leírást (ldesc) fogják kiírni a játékosnak, ami tartalmazza a magyarázatot arról, hogy miért nem lehet az ablakot kinyitni.)

A szavak jelentése:

  • ver: ellenőrzés ("verify")
  • Do: tárgy, a mondattani értelemben, úgymint "nyisd ki az ablakot" (direct object)
  • Open: az OpenVerb igét kezeljük itt.

Összerakva: amikor a játék azt ellenőrzi, hogy a "nyisd ki az ablakot" mondat végrehajtható-e, akkor ezt a tulajdonságot fogja megnézni. Mivel a tulajdonság kiír valamit a játékosnak (nyilván magyarázatot, hogy miért nem sikerült), a rendszer meg fogja állapítani, hogy ez a művelet tilos.

Az ablakhoz hasonlóan a Deszka objektumnál is néhány ellenőrző rutint felülbírálunk (konkrétan az OpenVerb, TakeVerb és PullVerb ellenőrző tulajdonságait.

Az élesebb szeműek észrevehetik, hogy egyes tulajdonságok neveinél vannak zárójelben változó nevek. Ezek az úgynevezett paraméterek, amivel a tulajdonságot meghívták. Eddig nem írtam ki őket, mert nem használtam őket semmire. Később kelleni fognak majd. Most csak azért vannak itt, hogy a szemed szokja a létezésüket.

Define Deszka: FixedItem { sdesc = 'deszka', ldesc = { {{ Vastag, szálkás, durva deszkák fedik az ablakot. Közöttük keskeny réseken át halovány fény szűrődik be. Úgy tűnik, hogy sebtében, de alaposan szögelték be őket. Sosem fogod tudni őket leszedni. }} }, location = $Kisszoba, noun = 'deszka deszkak fadeszka fadeszkak', adjective = 'fa', verDoOpen($actor) = { {{ Nincs rajtuk kilincs. }} }, verDoUnlock($actor, $io) = { $this->verDoOpen }, verDoTake($actor) = { {{ Egy ötfős ács-csapat egésznapi munkája lenne ezeket a deszkákat eltávolítani. }} }, verDoPull($actor) = { $this->verDoTake }, };

Lehet, hogy a játékos az Ablak vagy Deszka objektumok megvizsgálása után ráizgul a szögekre, és megpróbálja azokat magához szólítani. Megörvendeztetjük a kreatív játékost egy tréfás szöveggel.

Talán már tűnődsz azon a dolgon, hogy mi történne akkor, ha a játékos mondjuk meg akar húzni (PullVerb) egy olyan tárgyat, amire nem definiálunk verDoPull tulajdonságot. Nos, ami történik a következő: a rendszer megállapítja, hogy nincs ilyen tulajdonság, úgyhogy feljebb megy az öröklési láncban, és megnézi, hogy a FixedItem-nak van-e ilyen. Véletlenül van, de ha nem lenne, akkor még fejlebb keresne a Thing objektumban (a FixedItem ugyanis a Thing-ből öröklódik). A FixedItem-ben szépen definiálva van a verDoPull; ami egy "általános" sablonszöveget ír ki.

Amikor egy új objektumot definiálunk, akkor az örökölt tulajdonságokat felül lehet bírálni, ezt tettük eddig is, most már az is világos, hogy miért és hogyan.

Define Szog: FixedItem { sdesc = 'szög', ldesc = { {{ Jókora ácsszögek rögzítik a deszkákat az ablakkerethez. Aki beverte őket, nem viccnek szánta őket: valószínűtlennek tartod, hogy az ablakkeret lebontása nélkül ki tudnád szedni őket. }} }, location = $Kisszoba, noun = 'szog szeg szogek szegek acsszog acsszeg acsszogek acsszegek', adjective = 'acs', verDoTake = { {{ Nos, igen. Azt mondják, hogy a kalandjátékokban ami nincs leszögezve, azt le kell nyúlni, ha pedig valami le van szögezve, akkor előbb a szögeket kell lenyúlni. Tisztázzunk valamit: ez csak egy tréfa, nem kell szó szerint venni! Ugyan a vad gyűjtőszenvedély gyakran célravezető, de EZEKET a szögeket nem kell magaddal vinned. És nem is tudod. Ha TÉNYLEG szükséged van szögekre, nos, menj le a barkácsboltba, és vegyél. Nem, nincs a játékban barkácsbolt. (sóhaj) }} }, verDoPull = { {{ A szögek meg se moccannak. }} }, verDoPush = { $this->verDoPull }, };

A priccs is igazából csak dekorációs céllal van jelen. Ha a játékos megpróbálja odábbhúzni / tolni, akkor egy újabb tréfával és némi gyógyszerrel jutalmazzuk. Mivel ugyanaz a vicc idővel unalmassá válik, ezért itt van egy újabb trükk: a delete parancs segítségével a verDoPush és verDoPull tulajdonságokat töröljük a vicc után. Így az csak egyszer lesz kiírva; a továbbiakban a FixedItem-től örökölt üzenetet látja a játékos. Mivel az Altatot oda kell adni annak a szereplőnek ($actor), aki arrébb tolta az ágyat, ezért itt már felhasználjuk a tulajdonság $actor paraméterét

Az ldesc tulajdonságnál trükközünk egy kicsit: ha a játékos már sikerrel kitörte az ágy lábát, akkor mást írunk ki, mint korábban.

A Container és az issurface jelentését egyelőre fedje homály.

Define Priccs: FixedItem, Container { sdesc = 'priccs', targydesc = 'a priccset', isBroken = 0, issurface = 1, ldesc = { {{ Kopott, öreg ágyikó, sok törött rugóval és kopott matraccal. Valószínűleg eszményi táplálék lehet a molyoknak. }}; if ($this->isBroken) { {{ Az ágy lábait sikerrel ripityára törted. Gratulálok! }} } else { {{ Ki tudja, mi minden érték lapulhat az ágy alatt található kosztenger mélyén? }} } Inherited() if @{$this->contents}; # Ezzel egyelőre ne foglalkozz. }, location = $Kisszoba, noun = 'agy agyiko priccs', verDoPush = {}, doPush = { delete $this->{verDoPush}; delete $this->{verDoPull}; {{ Némi nehézség árán arrébb tolod az ágyat. Alatta egy csapóajtót találsz! Kinyitod a csapóajtót, és mögötte mesés látvány fogad: egy hatalmas, fényesen megvilágított földalatti csarnok, tele arannyal és drágakövekkel! És mind a tiéd..! Nos... hogy őszinte legyek, igazából ez mind nem történt meg. Nagy nehezen arrébb tolod az ágyat, és rögtön kitörik két lába. Csapóajtónak nyoma sincs. Ellenben az ágy alatt megtalálod Emmi altató orvosságát. El is teszed, ki tudja, még mi mindenre lehet jó. }}; $Altato->moveInto($Me); $this->{isBroken} = 1, }, verDoPull = {}, doPull = { $this->doPush }, };

Az Altato kezdetben a "semmiben" van; csak akkor tesszük elérhetővé, ha a játékos arrébbcűgölte az ágyat. Az altató "ehető", amennyiben definiálunk neki egy verDoEat és egy doEat tulajdonságot. (Emlékeztetőül: ver = verify (ellenőrzés); do = direct object (tárgy); eat = az EatVerb ige.)

Ha a játékos hirtelen hülyeségtől vezérelve megeszi az altatót, akkor kinyírjuk a dies paranccsal. Később az altató másra is jó lesz, erre még visszatérünk.

Define Altato: Thing { sdesc = 'altató', adesc = 'egy altató tabletta', noun = 'altato tabletta orvossag', adjective = 'altato', mapLocation = $Priccs, weight = 0, bulk = 0, ldesc = { {{ Emmi altató tablettája. Ki tudja, mire lesz még jó? }} }, verDoEat($actor) = { $this->verifyRemove($actor); }, doEat($actor) = { {{ Tudod, mi ''igazán'' rossz ötlet egy horrorfilm szereplőjétől? Elaludni a film közepén. Ez ugyanis többnyire azzal jár, hogy többet nem ébred fel. Mint ahogy te sem. }} $actor->dies; }, };

A szekrény egy OpenableContainer, ami azt jelenti, hogy ki lehet nyitni, és bele lehet rakni dolgokat. Itt látható, hogy egy tárgyat több örökölt tulajdonsággal is fel lehet ruházni: Az OpenableContainer ősétől örökli a tartalmazó és nyitható jelleget, a FixedItem-től pedig azt, hogy nem lehet felvenni.

  • Az isopen tulajdonság 0 értéke annyit jelent, hogy kezdetben csukva van.
  • Az openscore tulajdonság fejezi ki, hogy mennyi pontot kap a játékos, ha sikerül kinyitnia. Kis sikerélmény neki.
  • Az ldesc tulajdonságnál az "Inherited()" azt jelenti, hogy az örökölt ldesc tulajdonság is lefut, ami felsorolja, hogy mi van a szekrényben.
Az Inherited() hívásnak a {{ }}-on kívül kell lennie, különben a rendszer kiírná a játékosnak, hogy "Inherited();".

Define Szekreny: OpenableContainer, FixedItem { sdesc = 'szekrény', targydesc = 'a szekrényt', ldesc = { {{ Egy viharvert, kétajtós bükkfa szekrény. Úgy százötven évvel ezelőtt igen előkelő dísze lehetett egy nappalinak, sok értékes tárgyat rejtve magában. Mostanra kicsit megrogyott már szegény, és egy erdei házikó kiszobájában tölti végső napjait. }} Inherited(); }, location = $Kisszoba, isopen = 0, openscore = 10, noun = 'szekreny', adjective = 'ketajtos bukkfa bukk fa oreg szazotven eves 150 ket ajtos', };

A Lada kezdő helye (location) nem a Kisszoba, hanem a Szekreny. Ez, nem meglepően, azt fejezi ki, hogy a láda a szekrényben van. Ráadásul, mivel kezdetben a szekrény csukva van, eleinte a láda nem is látszik, amíg a játékos a szekrény ki nem nyitja.

A láda olyan, mintha egy nyitható tárgy lenne (mindenki megpróbálja majd szépen kinyitni), de valójában nem az. (Nem örökli az Openable objektumot.) Ellenben fel lehet venni, és a score tulajdonság tükrözi, hogy ezért 10 pont jár a játékosnak. Itt van az első igazi rejtvénye a játéknak: a láda a későbbiekben nehezéknek szolgál, nem tárolásra kell.

Viszont ha a játékos megpróbálja kinyitni, egyre durvább üzeneteket kap majd. A triesToUnlock változója fejezi ki, hogy eddig hányszor próbálkozott a játékos, és az openMessages listából ennyiedik elemmel örvendeztetjük majd meg. Az openMessages tulajdonságnál látható, hogy hogyan kell egy lista típusú tulajdonságot létrehozni: szögletes zárójelekkel.

Define Lada: Thing { weight = 10, sdesc = 'láda', adesc = 'egy nehéz, fémveretes láda', ldesc = { {{ Egy nagy, nehéz, fémveretes láda, úgy születésed előtt 150 évvel készülhetett. Zömök, erős alkotmány, ami egy világos és egyszerű küldetésre született: a tartalmát megőrizni minden illetéktelen személytől. Sajnos az évek folyamán nagy, ormótlan kulcslyuka reménytelenül berozsdásodott. Valószínűleg a kinyitásához minimum egy pajszer, három flakon ipari rozsdamaró, két markos ember és egy rúd dinamit szükséges. }} }, location = $Szekreny, score = 10, noun = 'lada', adjective = 'femveretes nehez nagy', verDoOpen = { my $hanyadikUzenet = $this->triesToUnlock; if ($hanyadikUzenet > 3) { $hanyadikUzenet = 3; } {{ {$this->openMessages->[$this->triesToUnlock]} }} $this->{triesToUnlock} = $hanyadikUzenet + 1; }, verDoUnlock = { $this->verDoOpen }, triesToUnlock = 0, openMessages = [ 'Reménytelen kinyitni. Ne is próbálkozz vele.', 'Komolyan, esélytelen vagy kinyitni. Teljesen szét van rozsdásodva a zár, erővel pedig nem tudod kinyitni.', 'Nem érted? NEM. LEHET. KINYITNI!!', 'Hirtelen egy alternatív univerzumban találod magad. Itt minden a fordítottja önmagának, az alul a felül, a fekete a fehér, és a kinyithatatlan láda kinyitható. A láda könnyűszerrel kinyílik, és mesés tartalma immáron a te birtokodba kerül. Sajnos az alternatív univerzumban az "élő" a "holt", így szomorúan tudatom, hogy meghaltál... NEM!', '(Sóhaj...)' ], };

Már a Kisszoba definíciójánál elhintettük, hogy délre (south) van egy ajtaja: KisszobaAjto. Lehet, hogy te, kedves olvasó, azóta rágod a körmöd, hogy mi lesz az ajtóval. Nem kell tovább aggódnod: itt jön.

A kisszoba ajtaja egy igen egyszerű konstrukció. A Doorway objektumból öröklődik; ettől rögtön nyitható (Openable) is lesz. Kezdetben csukva van (isopen = 0) ; így tanítjuk meg majd a játékost arra, hogy dolgokat ki lehet nyitni, ha magától nem jönne rá.

Minden ajtónak két "oldala" van, ez két külön tárgy. Az oka ennek az, hogy egy tárgy csak egy szobában szerepelhet, az ajtó viszont két különböző szobában is elérhető. Ezért két ajtót definiálunk; a kettő közötti összeköttetés az otherside tulajdonság. Maga a Doorway gondoskodik majd arról, hogy ha az egyik "oldalt" a játékos kinyitja, akkor a másik oldal is nyitva lesz, és vice versa.

Ezen kívül van még néhány apró finomság az ajtókkal kapcsolatban:

  • A location tulajdonságot nem kell megadni, mert azt a rendszer kitalálja majd a Room objekum south paramétereiből.
  • Az otherside, isopen, islocked, keys, sdesc, noun, adjective értékeket az ajtó egyik oldalán elég megadni.
  • Az adjective (melléknév) tulajdonság eltér az ajtó két oldala esetén, ezért ott azt megadtuk a másik oldalnál külön.
(Ugye az egyik oldalon északi ajtó, a másik oldalon viszont déli!)

Define KisszobaAjto: Doorway { sdesc = 'kisszoba ajtó', noun = 'ajto faajto', adjective = 'deli del kisszoba fa', isopen = 0, otherside = $KisszobaAjto2, }; Define KisszobaAjto2: Doorway { adjective = 'eszaki eszak e kisszoba fa', };


3. A házikó központi helysége


Az első szoba berendezésével készen is vagyunk. Most jön a házikó központi helysége. Innen mind a négy égtáj felé mehet tovább a játékos, amit a north, south, east és west tulajdonságok fejeznek ki. Ebből három ajtó, a konyhába viszont "akadálytalanul" be lehet menni.

Define Haziko: Room { sdesc = 'Az erdei házikó.', ldesc = { {{ Egy erdei házikóban vagy. A házikó nagyrészt üres. Néhány ócska kerti bútor, egy tűzhely és egy könyvespolc a teljes berendezés. Északra és délre egy-egy ajtó vezet egy másik helységbe. A keletre van a bejárati ajtó, ami a külvilágba vezet. Egy kis konyhahelység van még innen nyugatra. }} }, north = $KisszobaAjto2, south = $VeceAjto, east = $Kapu2, west = $Konyha, }; Define Kertibutor: FixedItem { sdesc = 'kerti bútor', targydesc = 'kerti bútort', noun = 'butor kertibutor', adjective = 'kerti', ldesc = { {{ Teljesen haszontalan holmiknak tűnnek. }} }, location = $Haziko, }; Define Konyvespolc: FixedItem { sdesc = 'könyvespolc', noun = 'polc konyvespolc konyv', adjective = 'konyves', ldesc = { {{ Most nincs ideged leülni olvasgatni. }} }, location = $Haziko, }; Define Tuzhely: FixedItem { sdesc = 'tűzhely', noun = 'tuzhely tuz', ldesc = { {{ Még egy kevéske parázs izzik az alján. }} }, location = $Haziko, };

A VeceAjto / VeceAjto2 páros hasonlóan működik, mint a KisszobaAjtok. A Haziko szobában található példány nyitáskor (doOpen) még küld egy ijesztő üzenetet, amikor a játékos a wc ajtaját kinyitja. Ahogy a Szekrény ldesc tulajdonságánál, itt is az Inherited() hívással fut le az örökölt doOpen tulajdonság. Ennek hiányában az üzenetet megkapná a játékos, de valójában nem nyílna ki az ajtó! A Priccs objektumnál már leírt módon oldjuk meg azt, hogy ez a szöveg csak egyszer íródjon ki: a delete paranccsal.

Van itt egy új tulajdonság, amivel eddig nem találkoztunk: a heredesc. Ezt FixedItem-ből leszármazott tárgyaknál lehet definiálni. Ha van heredesc, az szépen beépül majd a szoba leírásába.

Ha nem lennél még a Perl nyelv tökéletes tudója, akkor még egy dolgot elárulok: az itt látható ? : operátor a következőképpen működik:

  • A kérdőjel előtti rész a feltétel.
  • Ha ez teljesül (nem 0, hanem valami más), akkor a kérdőjel és kettőspont közötti részt veszi a rendszer.
  • Ha nem teljesül a feltétel (0 az értéke), akkor a kettőspont utáni rész lesz érvényes.
  • Röviden megfogalmazva: feltétel ? igen : nem

Define VeceAjto: Doorway { sdesc = 'vécéajtó', noun = 'ajto veceajto wcajto', adjective = 'deli del d vece wc', isopen = 0, otherside = $VeceAjto2, heredesc = { $this->isopen ? 'A vécé ajtaja tárva-nyitva van, rettenetes bűzzel árasztva el a helységet.' : '' }, doOpen = { Inherited(); {{ A túloldalról kiáradó orrfacsaró bűz frontálisan ütközik orroddal, egy pillanatra meg is tántorodsz tőle. Sherlock Holmes-t megszégyenítő logikával arra találsz következtetni, hogy ez bizony a klotyó ajtaja lehet, és nem is akármilyené. }} delete $this->{doOpen}; }, }; Define VeceAjto2: Doorway { adjective = 'eszaki eszak', };

Ha már úgyis itt tartunk, akkor csináljuk meg a bejárati ajtót is (Kapu és Kapu2). Új, izgalmas, értékes elemmel ismerkedhetünk meg: a keys tulajdonsággal. Ha ez definiálva van, akkor az adott tárgy zárható, és az itt felsorolt kulcsok bármelyike nyitja.

A keys tulajdonságnak lista típusúnak kell lennie; ilyet már láttunk a Lada->openMessages tulajdonságnál. Ha, teszemazt, a Kapukulcson kívül még egy másik tárggyal, mondjuk az Alkulcs nevűvel is ki lehetne nyitni a kaput, akkor a tulajdonság a következőképpen nézne ki:

  keys = [$Kapukulcs, $Alkulcs]

További újdonság az islocked (zárva). Ha ez 0, akkor a kapu kezdetben zárva van; ha nem 0, nyitva. Ez párhuzamos az isopen tulajdonsággal: ha az isopen és az islocked egyaránt 0, akkor a tárgy be van csukva, de nincs bezárva.

Az islocked és keys tulajdonság nem csak Doorway-ből leszármazottakra használható: ugyanígy például egy OpenableContainer (mint feljebb a Szekreny) is lehet zárható.

Define Kapu: Doorway { sdesc = 'bejárati ajtó', noun = 'ajto kapu bejarat', adjective = 'keleti kelet bejarati', isopen = 0, islocked = 1, otherside = $Kapu2, keys = [$Kapukulcs], }; Define Kapu2: Doorway { adjective = 'bejarati keleti k', };


4. A Vécé


Iszonyú dráma helyszíne a vécé!

A történet a következő: amikor a játékos először belép a Vece szobába, beindul a Klotyodemon nevű folyamat (Daemon). A klotyó démona néhány lépésen át engedi a játékost (egyre sürgetőbb üzeneteket küldve neki, hogy baj van), aztán pedig teketória nélkül kinyírja.

Az egyetlen mód a Klotyodemon lefegyverzésére az, ha a játékos a nagy nehéz Ladat a Klotyora rakja. Ha későbbiekben a játékos óvatlanul levenné a ládát a vécéről, akkor a démon végez vele.

Két dolog az, ami fontos ebben a szobában:

Az enterRoom tulajdonság akkor fut le, amikor a játékos belép a szobába. Amint látható, a tulajdonság neve után zárójelben van egy szó: $actor. Ez egy úgynevezett paraméter. Eddig is voltak paraméterek, csak nem írtuk ki őket, mert nem volt rájuk szükség. Most van, mert tovább kell adnunk az Inherited-nek.

A másik érdekesség a roomAction. Ez is olyan tulajdonság, aminek paraméterei vannak, ráadásul rögtön négy. A roomAction tulajdonság minden parancsnál lefut, amiket ebben a szobában ad ki a játékos.

  • Az első paraméter (actor) a szereplő, aki a parancsot végrehajtja (ez többnyire a Me, lásd a történet elején).
  • A második paraméter (verb) az ige.
  • A harmadik paraméter (do, vagyis Direct Object) a mondat tárgya.
  • A negyedik paraméter (io, vagyis Indirect Object) a mondat határozója.
Ha például a játékos parancsa az volt, hogy "nyisd ki a kaput a kapukulccsal", akkor a roomAction a következő paraméterekkel fut majd le: ($Me, $OpenVerb, $Kapu, $Kapukulcs).

Itt most a roomAction-t arra használjuk, hogy értesítsük a Klotyodemont arról, ha a játékos valamit fel akar venni a szobában. (Ha a játékos lehajol felvenni a fogpiszkálót vagy bármi mást, akkor vége van.)

Define Vece: Room { sdesc = 'A vécé.', ldesc = { {{ A klotyóban vagy. Iszonyatos, bűz terjeng, és arra gondolsz, hogy mennyire jó lenne nem itt lenni. Menekülésed egyetlen útvonala a wc ajtaja északi irányban. }} }, north = $VeceAjto2, enterRoom($actor) = { $Klotyodemon->activate; # Elszabadul a pokol... delete $this->{enterRoom}; # A szokásos trükk: csak egyszer fog elszabadulni a pokol. Inherited($actor); }, roomAction($actor, $verb, $do, $io) = { if ($verb eq $TakeVerb) { $Klotyodemon->somethingTaken; } Inherited($actor, $verb, $do, $io); }, };

A Klotyodemon egy úgynevezett Daemon. A Daemonok folyamatok, amik a háttérben zajlanak, a játékos számára láthatatlanul. Emiatt nincs location tulajdonságuk sem.

A folyamat akkor indul el, ha lefuttatják az (örökölt) activate tulajdonságát. Ha egyszer elindult, akkor onnantól kezdve a játékos minden lépése után a rendszer meghívja az execute tulajdonságot. Az execute egyre sürgetőbb üzeneteket küld a játékosnak (ez a Lada objektumnál leírt módszerrel működik), majd végül, ha a játékos még mindig nem deaktiválta a csapdát, kinyírja a játékost (deathByKlotyo).

Definiálunk egy isDefused tulajdonságot is; ha a Lada a Klotyon található, a démon nem fog támadni. Ezen kívül a deathByKlotyo tulajdonság akkor fut le, ha

  • Letelik az idő (execute).
  • Felvesz a játékos valamit (somethingTaken, ezt a Vece->roomAction hívja).
  • Leveszi a játékos a vécéről a ládát (Klotyo->verGrab, lásd lejjebb).

Define Klotyodemon: Daemon { mapLocation = $Vece, steps = 0, # Ennyi lépéssel ezelőtt aktiválódott a klotyó démon messages = [ 'Ahogy belépsz, a vécé fedele magától megremeg. Megesküdnél, hogy alóla halk hörgést hallottál. ', 'A vécé fedele megrándul, alóla halk szörcsögés szűrődik ki. Majdnem biztos vagy benne, hogy ezt normális vécék nem szokták csinálni. ', 'A klotyó most már tényleg nem bír magával! A fedél fel-le csapódik, alóla egyre hangosabb gurgulázást hallasz. Kellene írni az Alföldi Porcelán minőségbiztosítási osztályának. Vagy menekülni. Valamelyik a kettő közül. ', '', ], execute = { if ($this->isDefused) { $this->deactivate; } elsif ($this->steps <= 2) { {{ \n\n {$this->messages->[$this->steps]} }} if $Me->location eq $Vece; ++$this->{steps}; } else { $this->deathByKlotyo; } }, isDefused = { $Lada->location eq $Klotyo; }, deathByKlotyo = { {{ \n\n }}; if ($Me->location eq $Vece) { {{ A vécé fedele felpattan, }}; } elsif (not $VeceAjto->isopen) { if ($Me->location eq $Haziko) { {{ A vécé ajtaja egy hangos reccsenéssel kirepül a keretéből, }}; } else { {{ Hallod, hogy a távolban a vécé ajtaja hangos reccsenéssel kirepül a keretéből, }}; } } else { {{ Hallod, hogy a távolban vécé fedele felpattan, }}; } {{ és kezdetét veszi egy vérbeli B-kategóriás horrofilmekbe illő jelenet: '''TÁMAD A SZAR!''' Képzeld el azt, amikor egy szörny, ami szarból van... ''szargólem''... esetleg ''szarelementál''... A szar elemi megtestesülése... szóval érted mi. Tehát előtör a mélyről, rádveti magát, és '''BEKEBELEZ'''. Még csak nem is viccelünk. }} $Me->dies; $this->deactivate; }, somethingTaken = { return if $this->isDefused; $this->deathByKlotyo; }, };

A Klotyo egy Container, hasonlóan a Szekrenyhez, viszont nem OpenableContainer, mert nem nyitható. Ezen kívül az issurface (felület) tulajdonság értéke 1, ami azt fejezi ki, hogy ez egy olyan Container, hogy lehet dolgokat rakni, nem bele. Ez pusztán kozmetikai jellegű dolog, ami a működést nem befolyásolja; a játék bizonyos üzeneteket másképp fogalmaz meg. Például: issurface = 0: "A vécé üres." issurface = 1: "Nincs a vécén semmi."

Az ioPut tulajdonság akkor fut le, ha valamit rá/bele akarnak rakni (Put) erre a tárgyra/ba. Ha ez a tárgy pont a láda, akkor a Klotyodemont lefegyverezzük.

A verGrab tulajdonság pont az ellenkezője: ez akkor fut le, ha valamit fel akarnak venni innét. Ha a Ladat akarják felvenni, akkor rászabadítjuk a játékosra a Klotyodemont.

A verOpen és verClose tulajdonságok már ismertek és értelemszerűek.

Define Klotyo: Container, FixedItem { sdesc = 'vécé', ondesc = 'a vécén', ldesc = { {{ Láttál már életedben ocsmány falusi vécét, de ez magasan viszi a pálmát. Inkább nem is részletezem. Elképzelni is pont elég rossz. }}; if (&ItemCount($this->contents)) { Inherited(); } }, location = $Vece, issurface = 1, noun = 'wc vece budi klotyo', adjective = 'budos buzos', ioPut($actor, $do) = { if ($do eq $Lada) { {{ A súlyos ládát sietve rárakod a klotyó fedelére. Kétszeres megkönnyebbülés: nem kell tovább magaddal cűgölnöd, és útját álltad a szar támadásának! A klotyó démona még egy darabig kaparászik a fedő alatt, aztán csalódottan és durcásan visszavonul. Csend támad. Megnyugszol. Most már biztosan semmi veszély nem les itt rád... }}; $Lada->moveInto( $this ); Award('klotyo', 30); } else { Inherited(@_); } }, verGrab($do) = { if ($do eq $Lada) { $Klotyodemon->deathByKlotyo; } else { Inherited(@_); } }, verDoOpen = { $Klotyodemon->deathByKlotyo; }, verDoClose = { {{ A vécé fedele le van hajtva. }} }, };

A Fogpiszkalo semmire sem jó a játékban. Azért van a Vece helységben, hogy a játékos meghalhasson, amikor felveszi. :)

Define Fogpiszkalo: Thing { sdesc = 'fogpiszkáló', adesc = 'egy kis fogpiszkáló', location = $Vece, noun = 'piszkalo fogpiszkalo', adjective = 'fog', };


5. Konyha


Define Konyha: Room { sdesc = 'A kis konyha.', ldesc = { {{ A házikó konyhájában vagy. Meglepően takaros helység ez, már a házikó többi helységéhez képest. Egy ódon konyhaszekrény álldogál a fal mellett, várva a jobb időket. A család pár évvel ezelőtt elvitte a tányérokat és egyéb konyhaeszközök zömét. A konyhaszekrény melletti mosogató csapjából halkan csöpög a víz. Egy nagy, fém csapóajtón át lehet lejutni a pincébe. }} }, east = $Haziko, down = $Csapoajto, enterRoom($actor) = { $EmmiKapar->activate; # Szegény Emmi szabadulni szeretne a pincéből. delete $this->{enterRoom}; Inherited($actor); }, roomAction($actor, $verb, $do, $io ) = { if ($verb eq $GoDown and $Emmi->location eq $this) { {{ -- Ne! Ne menj le a pincébe! Veszélyes! -- sikolt fel Emmi. }} } if ($verb eq $GoDown and $AjultEmmi->isIn($actor)) { {{ Nehéz lenne Emmivel a karjaidban létrát másznod. }} } }, }; Define Csap: FixedItem { noun = 'csap vizcsap', sdesc = 'csap', location = $Konyha, verDoOpen = { {{ Iszol egy kis vizet. Ahh, de jól esett! Utána gondosan elzárod a csapot. }} }, verDoClose = { {{ Gondosan el van zárva. }} }, }; Define Csapoajto: Doorway, Container { sdesc = 'csapóajtó', noun = 'csapoajto ajto pinceajto', adjective = 'pince csapo', isopen = 0, issurface = 1, verDoOpen($actor) = { if (@{$this->contents}) { {{ Nem nyithatod ki a csapóajtót, mert rajta van {ListCont($this->contents)}. }} } }, destination = $Pince, doOpen($actor) = { delete $this->{doOpen}; Inherited($actor); $EmmiKapar->deactivate; $Emmi->moveInto($actor->location); $EmmiBeszelgetes->startConversation($actor, 'kiszabadul'); $EmmiKovet->activate; }, }; Define EmmiKapar: Daemon { mapLocation = $Konyha, steps = 0, messages = hash { 2 => 'Valahonnan kaparászást hallasz! Összerezzensz... Ki lehet az vajon??', 4 => 'Egyre furcsább zajokat ütik meg a füledet. A csapóajtó mintha megmoccanna. Miféle pokolfajzatok leselkedhetnek rád a pincében??', 6 => 'Rettenet! Most már tompa sikoltozást is hallasz, és megesküdnél, hogy a pincéből jön a hang!', }, execute = { return if ($Me->location ne $Konyha); my $steps = ++$this->{steps}; my $message = $this->messages->{$steps}; {{ \n\n$message }} if $message; }, }; Define Konyhaszekreny: OpenableContainer, FixedItem { sdesc = 'konyhaszekrény', noun = 'szekreny konyhaszekreny', adjective = 'odon', location = $Konyha, }; Define PourVerb: Verb { verb = [qw( tolt beletolt attolt kitolt ont kiont toltsd toltsd_bele toltsd_at toltsd_ki ontsd ontsd_ki )], doAction = 'Pour', ioAction = 'PourInto', action = { {{ Mit akarsz tölteni, és hova? }} }, }; Modify Thing { verDoPour($actor) = { {{ Hova akarod tölteni {$this->targydesc}? }} }, verIoPourInto($actor) = { {{ Nem tölthetsz bele semmit. }} }, verDoPourInto($actor, $io) = { {{ Nem töltheted {$this->targydesc} sehová. }} }, }; Define Folyadek: Thing { mapLocation = $Konyha, isfolyadek = 1, mergezett = 0, emmiTudAMeregrol = 0, bedesc = { $this->thedesc . 'be' }, megmergez = { $Altato->moveInto(undef); $this->{mergezett} = 1; {{ Az altató halk pezsgéssel egy pillanat alatt feloldódik. }}; if ($Emmi->location eq $Me->location) { {{ Emmi értetlenkedve néz rád. -- Meg akarsz mérgezni valakit, Károly?! -- kérdezi kiváncsian. }}; $this->{emmiTudAMeregrol} = 1, } }, verIoPut($actor) = {}, # Bele lehet tenni valamit ioPut($actor, $do) = { if ($do eq $Altato) { {{ Belepottyantod a pirulát a {$this->bedesc}. }}; $this->megmergez($actor); } else { {{ \u{$do->thedesc} nem oldódna fel {$this->bedesc}n.. }} } }, verDoDrink($actor) = {}, # Meg lehet inni. doDrink($actor) = { if ($this->mergezett) { {{ \u{$this->bedesc}n oldott altató elég gyengén fejti ki a hatását. Egy ideig kótyagosnak érzed magad, de más bajod nem esik szerencsére. }} } else { {{ Ahh, máris frissebbnek érzed magad! }} } $this->moveInto(undef); }, verDoPour($actor) = {}, # Ki lehet locsolni a földre. doPour($actor) = { {{ Kilocsoltad {$this->thedesc} a földre. }} $this->moveInto(undef); $this->{mergezett} = 0; $this->{emmiTudAMeregrol} = 0; }, verDoPourInto($actor, $io) = { # Át lehet tölteni valamibe. if ($this->isIn($io)) { {{ Már benne van. }} } }, doTake($actor) = { {{ Nem veheted {$this->targydesc} a kezedbe! }} }, doDrop($actor) = { $this->doPour($actor); }, doPut($actor, $io) = { {{ Nem tölthetsz bele semmit. }} }, }; Define Viz: Folyadek { mapLocation = $Konyha, sdesc = 'víz', adesc = 'víz', # Nem "egy víz" noun = 'viz', targydesc = 'a vizet', ldesc = { if ($this->mergezett) { {{ A víz színe egy kicsit zavaros. Talán azért, mert feloldottál benne egy altatót? Lehetséges..? }} } else { {{ A víz ihatónak néz ki. }} } }, isReachable($actor) = { if ($actor->isIn($Konyha)) { return 1; # A konyhában mindig van víz. } Inherited($actor); }, isVisibleIn($vantage) = { if ($vantage eq $Konyha) { return 1; # A konyhában mindig van víz. } Inherited($vantage); }, ioPut($actor, $do) = { if ($this->{location} ne $Pohar) { {{ Nem teheted meg. }} return; } if ($do eq $Altato) { {{ Belepottyantod a pirulát a {$this->bedesc}. }}; $this->megmergez($actor); } else { {{ \u{$do->thedesc} nem oldódna fel {$this->bedesc}n.. }} } }, }; Define Beer: Folyadek { sdesc = 'sör', targydesc = 'a sört', adesc = { $this->poharban ? 'finom, hideg sör' : 'egy doboz önbehűtő sör' }, noun = 'sor doboz sorosdoboz', adjective = 'doboz onbehuto', location = $Konyhaszekreny, poharban = { $this->isIn($Pohar) }, dobozos = { not $this->isIn($Pohar) }, doTake($actor) = { if ($this->dobozos) { InheritedFrom($Thing, $actor); } else { Inherited($actor); } }, verDoEat($actor) = { {{ Mondják, hogy a sör folyékony kenyér. Nem kell szó szerint venni. }} }, verDoOpen($actor) = {}, doOpen($actor) = { $this->doDrink($actor); }, doDrink($actor) = { {{ Ahh! Szomjas voltál! Egy hajtásra iszod meg a finom, hideg söröcskét. Nagyon jól esett! }} $this->moveInto(undef); if ($this->mergezett) { {{ Ez csak azért volt igazán rossz ötlet, mert az altató a sörrel kombinálva nagyjából öt másodperc alatt kiüt. Aléltan terülsz el a földön, és, ahogy ez a horrorfilmekben szokás, többet már nem is térsz magadhoz. }} $actor->dies; } }, doDrop($actor) = { if ($this->dobozos) { Inherited($actor); } else { $this->doPour($actor); } }, doPut($actor, $io) = { # A játékos át akarja rakni a sört valami másba. Ezt doboz állapotában teheti csak meg. if ($this->dobozos) { Inherited($actor, $io); } else { {{ Nem tölthetsz bele semmit. }} } }, doPour($actor) = { $this->moveInto(undef); {{ Kiöntötted a finom, habzó, jéghideg sört a földre! Milyen ember vagy te?! }} }, }; Define Pohar: Container { sdesc = 'pohár', adesc = 'egy üvegpohár', targydesc = 'a poharat', noun = 'pohar uvegpohar', adjective = 'uveg', location = $Konyhaszekreny, verIoPourInto($actor) = {}, ioPourInto($actor, $do) = { my ($mergez); $mergez = (@{$this->{contents}} == 1 and $this->{contents}->[0] eq $Altato); if ( not($mergez) and @{$this->contents}) { {{ A pohár már így is tele van. }} return; } if ($do eq $Beer) { {{ Kitöltöd a finom, habos sört a pohárba. Nyamm! }} $Beer->moveInto($this); $Beer->megmergez if $mergez; } elsif ($do eq $Viz) { {{ A poharat színültig töltöd vízzel. }} $Viz->moveInto($this); $Viz->megmergez if $mergez; } else { {{ Nem töltheted {$do->targydesc} a pohárba. }} } }, ioPut($actor, $do) = { if ($do->isfolyadek) { $this->ioPourInto($actor, $do); } else { Inherited($actor, $do); } }, };


6. Emmi, avagy nőgondok...


Define Emmi: Actor { location = $Pince, noun = 'emmi lany csaj no baratno', sdesc = 'Emmi', thedesc = 'Emmi', ldesc = { {{ Emmi a barátnéd, igazán csinos lány, amikor meg van fésülve, tiszta és nem ül rettegés a szemében, mint például most. }}; if (&ItemCount($this->contents)) { {{ Emmi kezében van {ListCont($this->contents)}. }}; } }, verDoTalk($actor) = {}, doTalk($actor) = { $EmmiBeszelgetes->startConversation($actor, 'megszolit'); }, verIoGive($actor) = {}, ioGive($actor, $do) = { if ($do->weight > 5) { {{ -- Ezt biztosan nem bírom el, Károly, -- tiltakozik Emmi. }} } else { {{ -- Köszönöm, -- mondja Emmi, és elveszi {$do->targydesc}. }} $do->moveInto($this); } }, actorAction($verb, $do, $io) = { if ($verb eq $GiveVerb and $do) { if ($this->isCarrying($do)) { $do->moveInto($Me); {{ Emmi neked adja {$do->targydesc}. }}; } else { {{ -- Karcsi, nincs nálam {$do->thedesc}, -- válaszol Emmi. }}; } } elsif ($verb eq $DropVerb and $do) { if ($this->isCarrying($do)) { $do->moveInto($Me->location); {{ Emmi leteszi {$do->targydesc}; }}; } else { {{ -- Karcsi, nincs nálam {$do->thedesc}, -- válaszol Emmi. }}; } } elsif ($verb eq $DrinkVerb and $do and $do->isfolyadek and $do->isReachable($Emmi) ) { if ($do->emmiTudAMeregrol) { {{ -- Meg akarsz mérgezni?! -- csattan fel Emmi. }} } else { {{ Emmi iszik. }}; $do->moveInto(undef); if ($do->mergezett) { if ($do eq $Beer) { {{ Hirtelen megszédül, még valamit motyog maga elé, majd összeesik! Még idejében sikerül elkapnod... }}; $Emmi->moveInto(undef); while (@{$Emmi->{contents}}) { $Emmi->{contents}->[0]->moveInto($AjultEmmi); } $AjultEmmi->moveInto($Me); } else { {{ A hígított altató meg se kottyan neki. }} } } } } elsif ($verb->istravelverb) { {{ -- Inkább veled maradnék! Félek egyedül! }}; } elsif ($verb eq $OpenVerb and $do eq $Kapu2 and $Emmi->isCarrying($Kapukulcs) and $Kapu->islocked and $Emmi->{location} eq $Haziko) { $Kapu->doUnlock($Emmi, $Kapukulcs); } else { {{ Emmi nem mutat érdeklődést. }} } }, }; Define EmmiKovet: Daemon { mapLocation = $Emmi, lattaATorottAgyat = 0, waits = 0, execute = { my $emmiLocation = $Emmi->location; return $this->deactivate unless defined($emmiLocation); my $meLocation = $Me->location; if ($emmiLocation ne $meLocation and ($meLocation ne $Vece) and not ($meLocation->isconversation)) { $Emmi->travelTo($meLocation); $this->{waits} = 0, return; } if ($emmiLocation eq $Kisszoba and not $this->lattaATorottAgyat) { if ($Priccs->isBroken) { {{ \n\n-- Jézusom, Károly! Eltörted a nagybátyámék ágyát! -- sopánkodik Emmi. }}; $this->{lattaATorottAgyat} = 1; return; } } if ($emmiLocation eq $Kisszoba and $Szekreny->isopen) { if (++$this->{waits} > 2) { {{ \n\nEmmi becsukja a szekrényt. }} $Szekreny->{isopen} = 0; $this->{waits} = 0; } } elsif ($emmiLocation eq $Haziko and $VeceAjto->isopen) { if (++$this->{waits} > 2) { {{ \n\nEmmi becsukja a vécé ajtaját. }} $VeceAjto->setIsOpen(0); $this->{waits} = 0; } } else { $this->{waits} = 0; } }, }; Define AjultEmmi: Thing { mapLocation = $Emmi, sdesc = 'Emmi', adesc = 'Emmi', noun = 'emmi lany csaj no baratno', sdesc = 'Emmi', thedesc = 'Emmi', weight = 8, bulk = 8, ldesc = { {{ Emmi teljesen elájult. }}; if (&ItemCount($this->contents)) { {{ Nála van {ListCont($this->contents)}. }}; } }, verDoTalk($actor) = {}, doTalk($actor) = { $EmmiBeszelgetes->startConversation($actor, 'megszolit'); }, verDoDrop($actor) = { {{ Mégsem teheted le a barátnődet a földre! }} }, verDoPut($actor, $io) = { if ($io ne $Priccs) { {{ Csúnya dolog lenne oda tenni a barátnődet! }} } }, }; Define EmmiBeszelgetes: Conversation { mapLocation = $Konyha, sdesc = 'Emmivel beszélgetsz', kiszabadul = { {{ Emmi, aki láthatóan mostanáig a pincében töltötte az idejét, egyedül, a sötétben, bezárva, felmászik a létrán, és zokogva a nyakadba veti magát. Zaklatottnak tűnik. -- Jaj, Károly!!! Azt hittem, már örökre a pincében maradok, -- közli magyarázatképpen. -- Egyedül voltam a sötétben órákig, és hangokat is hallottam, már azt hittem, hogy megőrülök! }}; ( 'De így nem kapok levegőt...' => 'levegot', 'Ne félj, itt vagyok, most már minden rendben lesz.' => 'rendbenlesz', 'Ne most, mucus. Dolgom van. Például hogy hogyan menekülünk meg ebből a házból.' => 'nemost', ) }, levegot = { {{ Kicsit ereszt a szorításán. \n -- Bocsánat... csak annyira megkönnyebbültem, hogy látlak... Azonnal haza akarok menni Budapestre! }}; $this->kerdesek; }, rendbenlesz = { {{ Emmi kicsit megnyugszik, de látod rajta, hogy azért még aggódik. \n -- Menjünk vissza Budapestre. Valami nem stimmel a ház körül! Furcsa zajokat hallottam... Valami kapart az ablakon... A pincében is hangokat hallottam... }}; $this->kerdesek; }, nemost = { {{ -- Én is szeretlek, -- közli Emmi duzzogva. -- Csak jussunk haza! Nincs kedvem itt maradni tovább. }}; }, kerdesek = [ 'Mit kerestél a pincében?' => 'beszorultam', 'Hogyan jutunk ki a házból?' => 'nalamakulcs', 'Mi folyik itt?' => 'felek', 'Próbáljunk meg hazajutni.' => 'induljunk', ], beszorultam = { {{ -- Valami furcsa neszt hallottam az éjjel, -- kezdi Emmi. -- Felkeltem megnézni, hogy mi okozta. Úgy hallottam, mintha a pincéből jönne. Lementem megnézni, de rámcsapódott a pince ajtaja, és belülről nem tudtam kinyitni! Úgy örülök, hogy kiszabadítottál! }}; $this->kerdesek; }, nalamakulcs = { if ($Emmi->isCarrying($Kapukulcs)) { {{ -- Nálam van a kapu kulcsa -- feleli Emmi. }}; } else { {{ -- Neked adtam a kapu kulcsát -- feleli Emmi. }}; } $this->kerdesek; }, felek = { {{ -- Nem tudom, -- tűnödik borzongva, -- de jobb lesz mihamarabb hazamennünk. Félek itt a házban. Valami furcsa dolog történik. }}; $this->kerdesek; }, induljunk = { {{ -- Szerintem is, zárja le a beszélgetést a lány. }} }, megszolit = { {{ -- Emmi?\n\n-- Igen, Karcsi? }}; $this->kerdesek; }, induljunkRevisitable = 1, }; Define Kapukulcs: KeyItem { sdesc = 'kapukulcs', adesc = 'a kapu kulcsa', ldesc = { {{ Semmi érdekes nincs rajta, már azon kívül, hogy a házikó ajtaját ki lehet vele nyitni... }} }, noun = 'kulcs kapukulcs', adjective = 'kapu', location = $Emmi, };


7. A sötét pince


Szóval, ne szépítsük: a pincében sötét lesz!

Ahhoz, hogy egy sötét helyen a játékos mégis lásson, ahhoz az szükséges, hogy legyen nála valamilyen fényforrás. Ebben a játékban az egyetlen fényforrás a gyertya lesz. A gyertyát a LightVerb igével lehet majd meggyújtani. Lássunk is hozzá, a már ismert módon:

Define LightVerb: Verb { verb = [qw( gyujt meggyujt felgyujt gyujtsd gyujtsd_meg gyujtsd_fel )], doAction = 'Light', ioAction = 'LightWith', action = { {{ Mivel akarsz meggyújtani? És mit? }} }, }; Define ExtinguishVerb: Verb { verb = [qw( fuj elfuj olt elolt fujd fujd_el oltsd oltsd_el )], doAction = 'Extinguish', ioAction = 'ExtinguishWith', action = { {{ Mit akarsz eloltani? }} }, }; Modify Thing { verDoLight($actor) = { {{ Nem szabad. Megegyeztél erről a pszichológusoddal. }} }, verIoLightWith($actor) = { {{ \u{$this->thedesc} nem gyújt meg semmit. }} }, verDoLightWith($actor, $io) = { {{ Nem szabad. Megegyeztél erről a pszichológusoddal. }} }, verDoExtinguish($actor) = { {{ Nem szükséges, {$this->thedesc} nem ég. }} }, verDoExtinguishWith($actor, $io) = { {{ Nem szükséges, {$this->thedesc} nem ég. }} }, verIoExtinguishWith($actor) = { {{ \u{$this->thedesc} nem alkalmas tűzoltásra. }} }, }; Modify Tuzhely { verDoLight = { {{ Nem kell, még parázslik. }} }, verDoLightWith = { {{ Nem kell, még parázslik. }} }, verDoExtinguish($actor) = { {{ A tűzhely maga nem ég. }} }, }; Modify Folyadek { verIoExtinguishWith($actor) = {}, ioExtinguishWith($actor, $do) = { if ($do eq $Parazs) { $do->ioPourInto($actor, $this); } else { $Gyertya->verDoExtinguish($actor); } }, }; Define Gyertya: LightSource { islit = 0, sdesc = 'gyertya', adesc = { $this->islit ? 'egy égő gyertya' : 'egy viaszgyertya' }, noun = 'gyertya viaszgyertya', adjective = 'viasz', location = $Vece, ldesc = { {{ Egy nagy, fehér{$this->islit ? ', égő' : ''} viaszgyertya. }} }, verDoLight($actor) = { {{ Mivel gyújtod meg a gyertyát? }} }, verDoLightWith($actor, $io) = {}, verDoExtinguish($actor) = { if ($this->islit) { {{ Elfújtad a gyertyát. }}; $this->setLit($actor, 0); } else { Inherited($actor); } }, }; Define Parazs: FixedItem { sdesc = 'parázs', targydesc = 'parazsat', noun = 'parazs', ldesc = { {{ Egy kevés parázs még izzik a tűzhelyben. }} }, verDoTake = { {{ A mamád mindig azt mondta: "Kisfiam, ne vedd a kezedbe a parazsat, mert fájni fog.". Nos, most hogy kipróbáltad, állíthatod: igaza volt! }} }, location = $Haziko, verDoLight($actor) = { {{ A parázs már így is épp eléggé izzik. }} }, verIoLightWith($actor) = {}, ioLightWith($actor, $do) = { {{ Szépen meggyújtod a gyertyát a parázzsal. }}; $do->setLit($actor, 1); }, verDoExtinguish($actor) = { {{ Nem tudod a parazsat elfújni. }} }, verIoPourInto($actor) = {}, ioPourInto($actor, $io) = { {{ Gőz csap fel, és a parázs kialszik. }}; $Parazs->moveInto(undef); $io->moveInto(undef); }, verDoExtinguishWith($actor, $io) = { {{ Valami folyadék kellene az oltáshoz. }} unless $io->isfolyadek }, }; Define Pince: DarkRoom { sdesc = 'A pince.', ldesc = { {{ Egy dohos pincében vagy. Törött kacatok vannak mindenfelé. Keletre van a pince belső része, felfele egy létra vezet a konyhába. }} }, up = $Konyha, east = $BelsoPince, }; Define Kacatok: FixedItem { sdesc = 'kacatok', ldesc = { {{ Mindenféle limlom, ami itt rohad a pincében évtizedek óta. }} }, noun = 'kacat kacatok limlom', adjective = 'torott', verDoTake = { {{ Semmi szükséged más emberek szemetére. }} }, location = $Pince, }; Define CsapoajtoAlul: FixedItem { sdesc = 'csapóajtó', noun = 'csapoajto ajto', ldesc = { {{ A csapóajtó nyitva van. }} }, verDoOpen = { {{ A csapóajtó már nyitva van! }} }, verDoClose = { {{ A csapóajtót belülről nem lehet nyitni. Ha magadra csukod, te is beszorulsz a pincébe, ahogy Emmi! }} }, location = $Pince, }; Define BelsoPince: DarkRoom { sdesc = 'A belső pince.', ldesc = { {{ A belső pincében vagy. Valamikor talán borospincének használhatta Emmi nagybátyja. Itt is rengeteg kacat található, és valami fura okból érezhető huzat van. Ki tudja, honnan jöhet? A helység levegője kimondottan nyomasztó. Vágyakozva pillantasz nyugatra az egyetlen kiútra: a pince külső része felé. }} }, enterRoom($actor) = { &Inherited($actor); $this->checkWind(); }, roomCheck($verb) = { $this->checkWind(); if ($this->islit) { if ($verb eq $ExtinguishVerb) { {{ Ez most nem tűnik jó ötletnek! }} } else { &Inherited($verb); } } }, checkWind = { unless ($Gyertya->isIn($Pohar)) { {{ Egy hirtelen légfuvallat elfújja a gyertyádat! }}; $Gyertya->setLit($Me, 0); {{ Pánik tör ki rajtad: megpróbálsz visszabotorkálni a külső pincébe. Közben majdnem elesel párszor. A sötétben megesküdnél hogy csöpögést és hörgést hallasz nem túl távolból. Ereidben meghűl a vér. Hosszas tapogatózás árán visszajutsz a külső pincébe.\n }} $Me->moveInto($Pince); } }, west = $Pince, }; Define Balta: Thing { sdesc = 'balta', adesc = 'egy nehéz balta', weight = 5, bulk = 3, ldesc = { {{ Egy öreg, nehéz, rozsdás balta. Pont amilyenre vágytál! }} }, noun = 'balta fejsze', adjective = 'oreg nehez rozsdas', location = $BelsoPince, };


8. Zombi attack!!


Define IdegesitoDemon: Daemon { mapLocation = $Zombi, messages = hash { 10 => 'A szomszéd szobában nyikorogni hallod a padlót. Valaki járkál a házban!', 19 => 'Úgy hallod, mintha a kisszoba bedeszkázott ablakát valaki kaparászná kívülről!', 31 => 'Valaki járkál a házon kívül, tisztán hallod!', 43 => 'Kopogtatást hallasz a ház ajtajánál. Valaki veri az ajtót kívülről. Ki lehet az, itt az erdő közepén??', 50 => 'Dörömbölnek a bejárati ajtón. Az ajtó recseg-ropog, de tart...', 59 => 'Fura hangokat hallasz a szomszéd szekrényből. Megesküdnél, hogy valaki a neveden szólított! Vagy mégsem? Lehetséges, hogy csak a füled csengett...', 67 => 'Egyre nyomottabban érzed magad. Mintha a ház falai rád akarnának dőlni maguktól.', 78 => 'A ház levegőjét kezded fülledtnek érezni. Egészen folytogató szaga van már. De vajon mitől?', }, execute = { unless ($Kapu->islocked) { $this->deactivate; $ZombiDemon->activate; {{ Ahogy a zárban elfordul a kulcs, az házikó kapuját kívülről berúgják! Egy büdös, foszladozó zombi tör be a helységbe rajta keresztül! }}; if ($Emmi->location eq $Haziko) { {{ Emmi velőt rázóan felsikolt, majd fejvesztve menekülni kezd. }} $Emmi->moveInto(undef); } {{ \n\n -- ''Agyaaat'', -- közli veled a zombi tárgyilagosan. -- ''Aaagyaaat'', -- teszi hozzá, mintegy magyarázatképpen. }}; $Zombi->moveInto($Me->location); } my $turns = $Global->turns; my $message = $this->messages->{$turns}; {{ \n\n$message }} if $message; }, }; Define AttackVerb: Verb { verb = [qw( tamad megtamad ol megol ut megut megrug tamadd tamadd_meg old old_meg usd usd_meg )], doAction = 'Attack', ioAction = 'AttackWith', action = { {{ Kit akarsz megtámadni? }} }, }; Modify Thing { verDoAttack($actor) = { {{ Nem helyes élettelen tárgyakon kitöltened a dühödet. }} }, verIoLightWith($actor) = { {{ \u{$this->thedesc} nem jó fegyver. }} }, verDoAttackWith($actor, $io) = { $this->verDoAttack($actor) }, }; Modify Emmi { verDoAttack($actor) = { {{ Kicsit mintha túlzásokba esnénk, nem? }} }, }; Modify AjultEmmi { verDoAttack($actor) = { {{ Kicsit mintha túlzásokba esnénk, nem? }} }, }; Define Zombi: Actor { iskilled = 0, sdesc = 'zombi', ldesc = { {{ Ha igazán érdekel, ez egy ocsmány, foszlásban levő, büdös, legyes, zöldes bőrű élőholt. }}; {{ Egész szépen felszabdaltad a baltával. }} if $this->iskilled; {{ Ez is túl sok információ róla, ennél több csak rosszabb lenne. }} }, noun = 'zombi eloholt', adjective = 'ocsmany foszlasban levo budos legyes zoldes boru', location = $Erdo, verDoAttack($actor) = { {{ Puszta kézzel esélyed sincs a zombi ellen! }} }, verDoAttackWith($actor, $io) = { if ($io eq $Balta) { {{ Igazi, vérpezsdítő viadal alakul ki. A zombi derekasan helytáll, még azután is, hogy két vagy három végtagját leaprítod a baltával. Hosszas és fáradalmas trancsírozás veszi kezdetét, olyan igazi, borzalmasrossz horrorfilmbe illő, ahol a forgatókönyvírót, a dramaturgot és a rendezőt egyaránt beszívott páviánok helyettesítik. Hosszas trancsírozás után diadalmasan belevágod a baltádat a zombi fejébe. -- JajIstenemFejszeVanAFejemben! -- sóhajt fel a zombi bánatosan, majd összerogyik.}} $this->{iskilled} = 1; } }, verDoTake = { {{ Trófeának kellene?! Komolyan képes lennél magaddal vinni ezt a feltrancsírozott tetemet? Sírást hozol rám... }}; }, }; Define ZombiDemon: Daemon { mapLocation = $Zombi, execute = { if ($Zombi->iskilled) { $this->deactivate; return; } if ($Zombi->location ne $Me->location) { $Zombi->travelTo($Me->location); return; } {{ \n\n A zombi rádveti magát, rutinos mozdulattal feltépi a koponyádat, majd mohón lakmározik az agyadból. }}; if ($AjultEmmi->location) { {{ Utánad Emmi következik. Ó annyival szerencsésebb nálad, hogy álmában éli a halál. }} } else { {{ \n\nEmmi ezenközben szépen, csendben, fegyelmezetten és lábujjhegyen eloson mellettetek, bevágja magát a ház előtt parkoló kocsiba, és elszáguld. Hat év terápia után megtanulja, hogy hogyan képzeljen el téged, mint apró világító lényként, aki hasznos életviteli tanácsokkal látja el. }} }; $Me->dies; }, };


9. Erdő, avagy a végjáték


Define Erdo: Room { sdesc = 'A házikó előtt.', ldesc = { {{ A házikó előtt vagy egy kis tisztáson. A csendet és nyugalmat csak az erdei madarak trillái törik meg. A levegő hűs és illatos. Vidám lepkék táncolnak a fák között átszurodo napsugarak fényében. Emmi nagybátyjának négykerékmeghajtásos kocsija parkol a ház mellett. Alig várod, hogy beszállhass! }} }, west = $Kapu, in = $Kocsi, noexit = { {{ Egyedül biztosan eltévednél az erdőből. Emmi jobban ismeri a környéket. }} }, }; Define Kocsi: FixedItem, Obstacle { sdesc = 'kocsi', noun = 'kocsi', destination = $Gyozelem, checkDestination = { if ($Me->isCarrying($AjultEmmi)) { return $this->destination; } else { {{ Ugye nem akarod Emmit itt hagyni?! }}; } }, verDoOpen = { {{ A kocsi nyitva van, csak BE kell szállni. }} }, location = $Erdo, }; Define Gyozelem: Room { enterRoom($actor) = { {{ Beszállsz a kocsiba. Emmit szépen ráülteted az utasülésre és becsatolod. Beindítod a motort, pöccre moccan. Beletaposol a gázba, és elindulsz a keskeny erdei úton, ami visszavisz a városba, a civilizációba, és el Emmi nagybátyjának elátkozott házától. Érzed, ahogy a feszültség csökken benned, ahogy magad mögött hagyod a borzalmakat, amiket az imént átéltél. Emmi kinyitja a szemét, laposakat pillant szét, aztán lassan magához tér és egy nagyot ásít. -- Karcsi... Megyünk haza? -- Igen, mucus, megyünk --, válaszolod. -- El se hinnéd, hogy mit álmodtam! Képzeld, álmomban mindenféle furcsa hangok voltak a házban, aztán beszorultam a pincébe, nem tudtam kijutni, aztán kiszabadítottál, és aztán... Eltűnődik egy pillanatra, és folytatja. -- És aztán az álmomban megmérgeztél, és utána zombival harcoltál. Butaságokat álmodik az ember néha, nem? -- Hát igen, -- nyelsz egy nagyot. -- Nos, nem is tudom, szóval mindenféle furcsaság volt. Tudod, mit szeretek nagyon ebben a házikóban? Azt, hogy jó levegő van, csend, és az ember mindig jól ki tudja aludni magát. Néha jólesik egy kis vidéki élet. Nem gondolod, Karcsi? Szerintem igazán jöhetnénk máskor is? }}; Inherited($actor); }, sdesc = 'Megnyerted a játékot.', ldesc = { {{ Gratulálunk, megnyerted a játékot! }} }, roomCheck = { {{ Már nyertél. }} $Global->{daemons} = {}; $Global->{fuses} = {}; }, };

10. További üzenet az agynak:

  • "Kaland" nevű oldal alatti oldalak a kalandok, feltéve, hogy van benne rész. (Valahogy Check-et meg egyebeket kiszűrni.)
  • Rá kellene jönni, hogy HOL leakel...
Tagek: