Időrégész 2015

5 June 2015 (programming retro games)

Amikor már megvolt a C64-ünk, megkaptam egyszerre az LSI-féle 1001 játék sorozat első három részét anyukáméktól (ez úgy '89 körül lehetett), azt hiszem a negyedik-ötödik rész később jelent meg. Annyira nem voltam a scene-ben, hogy azt sem tudtam, hogy van olyan, hogy scene. Az én scene-em az a pár általános iskolai osztálytárs volt, akinek legalább egy ZX Spectrumja volt. Szóval a lényeg, hogy ez a pár könyv volt minden kapcsolatom a C64 játékokkal. Azon a néhány játékon kívül, amik így apukám ismerősein keresztül jutottak hozzánk, a többiről csak olvasni tudtam. Persze a leírás alapján mindegyik a legjobb játéknak tűnt EVER!, és volt néhány, amit el is kezdtem BASIC-ben leprogramozni az alapján, ahogy elképzeltem; ez nyilván több oldalról is ab ovo teljes sikertelenségre volt ítélve.

A harmadik 1001 játékban volt egy végigjátszás az Időrégészről, amibe persze azonnal beleszerettem. A szöveges kalandjáték volt az egyetlen játék-műfaj, aminek a programozásában akkortájt labdába tudtam rúgni annyira, hogy valami játszható jöjjön ki belőle, és emiatt pláne úgy éreztem, hogy ez a játék megszólít engem. Ráadásul nem is tudom, hogy volt-e akkor már CoV, de az biztos, hogy én nem ismertem (a Magic Candle-ös szám volt az első, amikor belefutottam, ha minden igaz, '92-ben), úgyhogy a leírás stílusa, humora is egy teljesen új világot nyitott meg előttem.

Mivel a fent ecsetelt okok miatt warez-ként esélyem se volt hozzájutni, ez lett a második játék, amit eredetiben megvettünk (az első az Impossible Mission 2 volt (kazettán), de azzal én nem sokat játszottam, azt apukám tolta — külön post lehetne, amikor az éjszaka közepén, a TV hangerejét éppen csak hallhatóra állítva ordított a figura leesés közben).

A játék csalódást nem okozott, de végigjátszani az istennek se ment, még az 1001-leírás alapján sem. Az áttörést csak 1991-ben hozta egy osztálytársam, aki rájött, hogy SPOILER ALERT a papnál még imádkozni is kell, nem elég odaadni neki az aranykeresztet vagy mit.

Most illene arról is írni, hogy akkor most ez egy jó játék volt-e. Azt gondolom, hogy most tudom a legkevésbé ezt megítélni, mert túl közelről látom (mint majd mindjárt kiderül hogy miért). Az biztos, hogy akinek annak idején volt C64-e (vagy akár +4-e!), és magyar, az ismeri az Időrégészt meg a többi Rátkai-játékot: nem véletlenül volt róla IDDQD cikk meg Index illetve Facebook csoportok. Igazi cult classic.

Egy modern Időrégész-reprodukció terve

Mostanában elkezdtek érdekelni a régi 8-bites gépek, mivel annak idején az alacsonyszintű programozásuk, meg úgy egyáltalán a felépítésük totál kimaradt nekem. Most, hogy autodidakta módon FPGA-fejlesztést tanulok, elértem oda, hogy épkézláb számítógépeket tudok építeni, amihez persze elkerülhetetlen, hogy megismerjem minden kis részletüket. Ennek egyébként egy nem várt, de kellemes mellékhatása, hogy tudom értékelni a korabeli és azóta készült demókat, amikor minden képkockát önmagában is lehetetlennek tűnik előállítani a C64-en.

Szóval kitaláltam, hogy kipróbálnám egy játék teljes visszafejtését, mivel ilyesmit sose csináltam még. Egy ilyen nem-realtime, nem-grafikus játék, mint az Időrégész, remek kezdő projektnek tűnt. Az alap-elképzelés (még május elején) az volt, hogy visszafejtem a programot annyira, hogy tudjam minden részéről hogy mit csinál, és ezalapján megértem, és minél magasabb szinten reprodukálom a működését. Hogy valami igazán örök formában rögzítsem, mindenképpen a ZMachine virtuális gépet terveztem célbavenni, mivel a modern interactive fiction-világban ez tűnik egyértelműen a bevett eszköznek már évtizedek óta.

Természetesen a ZMachine-re való fejlesztésről se tudtam sokat pár héttel ezelőttig (annak ellenére, hogy még 2004-ben a működő prototípus fázisig eltoltam egy egy ZMachine virtuális gép fejlesztését), csak annyit, hogy két komoly játékos van a színen: van az Inform 7, ami nagyon brutálisan ráment arra, hogy úgy nézzen ki, mintha természetes angol nyelven lenne, és van az Inform 6, ami egy hagyományosabb, valamennyire objektum/prototípus-orientált nyelv. Bár az Inform 7 nagyon érdekesnek tűnik (amellett, hogy én rühellem az ilyen természetes nyelvet utánzó programozási rendszereket), azt hamar eldöntöttem, hogy hülyén és zavaróan összevissza nézne ki egy magyar nyelvű játék leírása összekötő angol szöveggel. Úgyhogy maradt az Inform 6.

Így állt össze tehát az eredeti terv: fogom az Időrégészt, ki-dump-olom a szövegeket belőle, dokumentálom az összes szabályát, azokat újraimplementálom Inform 6-ban, lefordítom ZMachine-re, és kész.

Visszafejtés

Az hiányzott a legkevésbé, hogy még mindenféle mesterséges szabállyal is nehezítsem a dolgomat, úgyhogy azt hamar eldöntöttem, hogy semmiképpen nem lesz az a cél, hogy úgymond "korhű" módon csináljam a visszafejtést; mármint hogy olyan eszközökre korlátozódva, amik egy igazi Commodore 64-en '89 körül a rendelkezésemre álltak volna. Ehelyett a Vice emulátor debug-eszközeivel vágtam neki a feladatnak.

Az alap-elképzelés annyi volt, hogy betöltom az Időrégészt a Vice-ba, hagyom hogy kicsomagolja magát stb, és amikor már elindult a játék odáig, hogy várja az inputot, ki-dumpolom a teljes memóriát, disassemblálom a da65-tel, és elkezdek benne kutakodni. Találtam ehhez egy korabeli kazettás törést az Időrégészből, ami azért volt ígéretes, mert elindítás után már nem tölt semmit, tehát biztos lehettem benne, hogy (a grafikák kivételével) minden benne lesz a memóriában.

Bár 64 kilobyte nem tűnik soknak, 64 K-nyi disassemblált gépi kódot végigolvasni és megpróbálni kihámozni belőle, hogy hol van valami érdekes program, nem igazán bizonyult megvalósíthatónak; még azzal együtt sem, hogy a C64 BASIC és Kernal ROM-ok is be voltak a dump-ban kapcsolva (de ez utóbbi kérdésről még lesz szó).

Ehelyett azt csináltam, hogy elkezdtem trace-elni a programszámlálót (továbbiakban PC) a ROM-on kívül, először úgy, hogy nem csináltam a játékban semmit. Ezután az így érintett PC-ket kiszűrtem, és megnyomtam egy billentyűt. Az így kapott PC-ket is kiszűrtem, és ekkor eljutottam oda, hogy amíg nem írtam be egy teljes sort, addig nem volt tele-szemetelve a trace. Ezekután beírtam egy (értelmetlen) sort, és az így érintett PC-ket összeolvasva a disassemblált kóddal, végre ráleltem a jackpot-ra; az eredeti feljegyzéseim között a következő sorokban a felkiáltójelek száma mutatja, mennyire belelkesültem:


ff48: jo break-point a parancs lefutasa utanra?
c65a: parser eleje!!!!!!!!!!!!!!!!!!!!!!!! jo breakpoint!

Ekkor tehát végre hozzá tudtam adni a disassemblált kódhoz az első cimkét. Ez aztán elindított egyfajta lavinát: $C662-től egyértelműen látszik a lexikális elemző ciklus, ami egy szótár szavaival hasonlítja össze az inputot, amiből persze azonnal meglett egyrészt a szótár címe, másrészt megtaláltam néhány segédfüggvényt, köztük a $C707-en azt, ami a lexikális elemzés hibája esetén fut le (magyarán ha a játék által nem ismert szót írunk be), és $C6E0-től kezdődően azt, ami a sikeresen tokenizált inputot dolgozza fel. Hogy lássuk, hogy ebben semmi varázslat nincs, példaképp álljon itt ez a legutóbbi rutin, miután teljesen megértettem, hogy mi mit csinál, és felcimkéztem-felkommeteztem:

parse_finish:
        lda     #$0D                            ; C6E0 A9 0D
        jsr     putchr                          ; C6E2 20 D2 FF
        lda     words_buffer                    ; C6E5 AD E8 CF
        cmp     #$00                            ; C6E8 C9 00
        bne     check_verb                      ; C6EA D0 08
; Signal error if no (non-pronoun) words were parsed
nowords_error:
        lda     #$80                            ; C6EC A9 80
        jsr     message                         ; C6EE 20 D2 C9
        jmp     startmainloop                   ; C6F1 4C 57 C7

; Signal error if first word is not a verb
check_verb:
        lda     pronoun                         ; C6F4 AD CF CF
        cmp     words_buffer                    ; C6F7 CD E8 CF
        bpl     LC704                           ; C6FA 10 08   
        lda     #$81                            ; C6FC A9 81   
        jsr     message                         ; C6FE 20 D2 C9
        jmp     startmainloop                   ; C701 4C 57 C7

LC704:  jmp     prepare_sentence                ; C704 4C 68 C8
      

A szótár címének birtokában gyorsan összedobtam egy Haskell programot, ami a memóriadump-ból kiszedi az összes szót $1000-től, dekódolja a reprezentációt, és szinonímánként csoportosítja őket. Ez volt az első kézzelfogható eredménye az eddigi munkámnak. Nézzünk is bele: a szavak indexe hexában és tizes számrendszerben is szerepel, hogy a gépi kód és a BASIC szkriptek olvasása közben is kényelmes legyen látni, mi micsoda:

0x1 1 n
0x1 1 néz
0x1 1 körülnéz
0x1 1 körülnézek
0x1 1 nézek
0x2 2 é
0x2 2 észak
0x3 3 k
0x3 3 kelet
...
0xb 11 r
0xb 11 lerak
0xb 11 leteszem
0xb 11 lerakom
...
0x37 55 a
0x37 55 az
0x37 55 egy
0x37 55 egyik
0x37 55 meg
0x37 55 kis
0x37 55 kicsit
0x37 55 keveset
0x37 55 valamit
0x38 56 mező
0x38 56 mezőt
0x38 56 mezőn
...
0x61 97 oltár
0x61 97 oltárt
0x64 100 pajzs
0x64 100 pajzsot
...
0x6f 111 lámpa
0x6f 111 lámpát
...
0x7e 126 szerzetes
0x7e 126 szerzetest
0x7e 126 szerzetesnek
...
      

Nem nehéz észrevenni (főleg így, hogy én már szétszedtem), hogy öt részre oszlik a szavak halmaza:

A $C9D2-n lévő, üzenetkiírási rutint visszafejtve az is kiderült, hogy természetesen a programnak szüksége van a teljes 64 K memóriára, ezért bizonyos adatok, köztük a kiirandó üzenetek is, a ROM által is használt memóriacímeken vannak tárolva. Ez azt jelenti, hogy a kiírás előtt kikapcsolja a ROM-ot, átmásolja a kiirandó szöveget egy ideiglenes, ROM által nem elfedett részre, visszakapcsolja a ROM-ot, és meghívja a megfelelő Kernal rutint a kiíráshoz.

Ez persze azt is jelenti, hogy az üzeneteket nem találhattam volna meg a kidumpolt memóriában, mert ha csak nem pont azt a pillanatot kapom el a dump-olással, amikor a ROM ki van kapcsolva, akkor ezek el vannak fedve a ROM által (bank switching). Persze, így utólag, ez nem egy nagy szopás (nyilván a Vice-ban ki/be lehet kapcsolni a ROMokat dumpban a gép tényleges, belső állapotától függetlenül), de amíg nem jöttem rá, addig elég furi volt, hogy sehol sem találom a kiírt üzenetek nagy részét.

De miért volt mégis néhány üzenet az eredeti dump-ban? Mint kiderült, a játék szabályainak csak a legáltalánosabb része (tárgykezelés, körülnézés, stb) van gépi kódban megírva; az ad-hoc szabályokat egy apró BASIC program írja le. A BASIC program a specifikus üzeneteket, mint például "Törtél egy nádszálat.", saját maga írja ki, közvetlenül print utasításokkal.

Így hát a következő lépés egy újabb apró Haskell program megírása volt, ami kiszedte a ROM által eltakart szövegeket, illetve egyéb táblázatokat, mint például a helyszínek egymással való összeköttetései. A $CA30-on lévő "néz" és a $C89F-en lévő "segítség" rutinokból látszik, hogy mindegyik helyszínhez két kötelező, és egy opcionális üzenet tartozik. Az első a helyszín leírása, a második a segítség, a harmadik pedig a leírásnak az a része, amit csak a helyszínhez tartozó flag állapotától függően írunk ki. Mivel ekkor még csak ismerkedtem a kinyerhető adatokkal, és egyáltalán rá sem néztem hogy hogyan fog kinézni a végső Inform 6 program, az első verzió csak szövegesen kiírta az összes helyszín adatát:

ROOM 11
A kastély nagytermében állsz. Egy gyö-  nyörű lady áll előtted. A terem északi  és déli ajtajánál őrök strázsálnak.
Hint: Ki tud ellenállni egy szép hölgy        kérésének?
Puzzle: Keresd meg, és hozd vissza az elveszett ékszereimet! - szól a lady.
Észak: 10
Dél: 9
Kelet: 15       
      

Persze ez már egy kicsit idealizált verzió, mert az ékezetes betűk is jól vannak visszafejtve benne. Természetesen semmilyen szabványos kódolásról szó sem volt még akkoriban, úgyhogy az Időrégész egy teljesen saját karakterkészletet és kódolást használ. Az egyszerűség kedvéért a visszafejtést lusta kiértékeléssel csináltam: először a nem-ASCII karakterek helyén a hexa kódjukat írtam ki. A legelső számú üzenet ekkor még így nézett ki:

A v|0xB0|r kazamat|0xB0|iban vagy. Alagutak indul-nak minden ir|0xB0|nyba.       
      

Nem nehéz rájönni, hogy ezek szerint a $B0 az á kódja, aztán elég a második üzenetet is végigolvasni az első hexa kódig, és így tovább.

Egyébként a sok space meg furcsa kötőjel azért van, mert újsor-karaktereket nem tárolnak az üzenetek, hanem egyszerűen a 40. oszlopban törnek a sorok.

A BASIC-ben leírt szabályokkal jóval könnyebb dolgom volt, köszönhetően a petcat nevű programnak, ami a Commodore gépeken használt tokenizált BASIC-et visszafejti sima szövegre, valahogy így:

      
27060 if b<>11 then 27090 27070 if peek(t)=1 then print "Világít." : goto160 27080 print "Most nem ég." : goto160

Ez a részlet azt kezeli le, ha a játékos a 11-es tárgyat (a lámpát) vizsgálja meg: a t egy flag memóriacíme, ahol el van tárolva a lámpa állapota (meggyújtottuk-e), és ennek függvényében kiírjuk a megfelelő üzenetet, majd visszatérünk a fősodorba.

Hoppá, de hiszen a b változó a beírt főnév, mint szó sorszámát tartalmazza, és mint láttuk, bár a 11-es tárgy (a 111-es szó) valóban a "lámpa", a 11-es szó az az ige, hogy "lerak". Ez bizony egy bug!

A reprodukció hűsége

A fenti példa felvet egy érdekes kérdést: mit kezdjen a reprodukció a bug-okkal? Egyáltalán, milyen szintű hűséget célozzunk be?

A legszerencsétlenebb hiba talán a 3910-es BASIC sorban lévő szintaktikai hiba, ami miatt a HÚZ élőlény alakú parancsok azonnal elcrashelik a játékot. Lehet erre azt mondani, hogy ki akarná azt beírni, hogy KIHÚZ PAP LÓHERE, de azt ne mondja nekem senki, hogy a MEGHÚZOM A LADYT nem egy életszerű parancs...

A többi hiba viszonylag ártalmatlan, mint a fenti példában a lámpa megvizsgálása, aminek egyetlen hatása, hogy a játékosnak emlékeznie kell, hogy meg van-e gyújtva a lámpája. A lámpával kapcsolatban amúgy nem ez az egyetlen bug, ugyanis a 4703-as sorban, amikor a kútban elrejtett üregben próbáljuk a falra vésett szöveget elolvasni, csak azt vizsgálja a program, hogy meg van-e gyújtva a lámpa — de azt már nem, hogy a játékosnál van-e.

Az ilyen és ehhez hasonló hibákat végül kijavítottam, egy kivétellel. A játék két tárgy megvizsgálása esetén mondja azt, hogy "épp a te méreted": a sisak, és... az ásó. Egészen biztos vagyok benne, hogy a második tárgy a páncélruha akart lenni (hiszen azt és a sisakot kell a későbbiekben viselnünk), de a pont jó méretű ásó annyira vicces kép (és még az 1001 játék leírásban is meg van említve), hogy muszáj volt bennehagynom.

Felmerült persze az is bennem, hogy ki lehetne adni egy patch-et az eredeti játékhoz, ami kijavítja ezeket a hibákat, de sajnos ez nem olyan egyszerű, mint elsőre tűnik, mivel sokszor nem férne be az eredeti helyre a jó verzió, tehát rácsúszna a következő sorra, és nem lehet könnyen eltolni a BASIC sorokat (csomó következő sor-pointert át kéne írni, meg nem is biztos, hogy van mögötte üres hely, ahova túllóghatna).

Amiben viszont mindenképpen szorosan akartam követni az eredetit, az a parser: semmi csili-vili, ige főnév főnév, teljesen figyelmen kívül hagyott kötőszavakkal. Persze, lehetett volna megpróbálnom magyarítani az Inform parserjét, Gálya-i magaslatokba emelkedni névmásokkal meg minden, de azt hiszem, az már nem az Időrégész lenne.

Újraimplementálás Inform 6-ban

Miután így kiszedtem az összes szöveget, megértettem a szabályokat, és olvasható formára hoztam a BASIC szkriptet, ideje volt elkezdeni gondolkodni azon is, hogy milyen formát is öltsön a reprodukció maga.

Mint mondtam, eredetileg ZMachine-re akartam megírni, de mint kiderült, az nem igazán van felkészülve nem-ASCII karakterekre: a latin-1 karakterek még úgy-ahogy működnek, de a latin-2 csak néhány interpreterrel. Kiderült viszont, hogy van egy hasonló, de sokkal modernebb virtuális gép szöveges kalandjátékokhoz: a Glulx. Ez már ígéretesebb: kezel unicode-ot is, úgyhogy semmilyen magyar ékezettel nem lehet problémája; az Inform 6 compiler ugyanúgy támogatja, mint a ZMachine-t; ráadásul még könnyedén támogat grafikát és hangokat is! De ekkor, az ismerkedési fázisban, az még ötlet szintjén sem szerepelt, hogy a képeket és hangokat is kiszedjem az eredeti programból.

A végső verzió lefordul Glulx-ra és ZMachine-re is, az utóbbi verzió csak 48 K, úgyhogy elvileg az sem lehetetlen, hogy elfutna egy C64-es ZMachine interpreterrel. Persze azt kizártnak tartom, hogy van olyan C64-es interpreter, ami kezel latin-2 karaktereket...

Egyébként az Inform 6-tal azért meggyűlt a bajom: ez is az a fajta szar, mint a Ruby on Rails, hogy persze, tök pöpec a példákban hogy minden Magától Működik™, csak mihelyst valamit kusztomalizálni akarsz, akkor derül ki, hogy egy nagy átláthatatlan katymasz az egész... Valami 300 körüli programsor lett csak a parser, és úgy, hogy legalább a lexikális elemzéshez tudtam a platform által nyújtott szolgáltatást használni.

A tényleges kódot szerintem alapvetően kétféleképpen lehetett volna megírni. Generálhattam volna az igék és a tárgyak listáját is a memória-dump alapján, valami ilyesmi formában (Inform 6-ot ismerők előnyben):

Verb 'n//' 'néz' 'körülnéz' 'körülnézek' 'nézek'  * -> VERB1;
Verb 'é//' 'észak' * -> VERB2;
...
Object NOUN56 with name 'mező' 'mezőt' 'mezőn', has scenery;
...
Object NOUN100 "egy kerek pajzs" with name 'pajzs' 'pajzsot';
      

Ez azt jelentette volna, hogy a BASIC szkriptet is legalább fél-gépiesítve lehetett volna átfordítani Inform-ra. Ehelyett más utat választottam: kézzel írtam át a szavakat, mindegyiknek értelmes nevet adva, és az Informos konvenciókat követve, félig helyszín-, félig objektumorientált kódot írtam. Ez persze azt is jelenti, hogy becsúszhattak bug-ok; másrészt azt gondolom, hogy az így írt forrás sokkal jobban kifejezi az Időrégész szerkezetét.

Nézzünk egy konkrét példát! Az alábbi BASIC-részlet a (MÁSZIK POLC) kezelése:

4103 if p<>17 or b<>94 then 4110
4104 if peek(t+16)=1 then print r$ : print "Nem történt semmi." : goto 4108
4105 poke t+16,1 : print r$ : print "Innen már eléred az ablakot. Látod, hogy";
4106 print "valaki a várudvaron, a túlsó fal tövében";
4107 print "elejt egy pénzérmét." : poke x+2,15 : gosub500
4108 print "Gyorsan lemászol a polcról, mert már    inog alattad." : goto160
    

Az én átiratomban ez úgy néz ki, hogy a 17-es szoba alatt található egy Polc objektum, a következő, szerintem kifejezetten olvasmányos kóddal:

Room ROOM17; ! részletek itt most elhagyva

Object -> Polc
 with   name 'polc' 'polcot' 'polcra'
 has    scenery,
 with   before [;
         Maszik:
            if (player notin parent(self)) rfalse;
            if (self has solved) "Nem történt semmi.";

            give self solved;
            move Penz to ROOM15;
            AddScore();
            "Innen már eléred az ablakot. Látod, hogy valaki a
            várudvaron, a túlsó fal tövében elejt egy pénzérmét.^
            Gyorsan lemászol a polcról, mert már inog alattad.";
        ];
    

Az átírás végleges menete így az lett, hogy a szobákhoz először legeneráltam a kódot, aztán before/after kezelőket és tereptárgyakat adtam hozzájuk. Az alábbi szobában például nincs semmi különleges, úgyhogy nem kellett egyáltalán hozzányúlnom a generált kódhoz:

Room ROOM3
 with   description
            "A vár kazamatáiban vagy. Alagutak indulnak minden irányba.",
        picture 1,
        hint "Minden rendben.",
        n_to ROOM2,
        s_to ROOM0,
        e_to ROOM2,
        w_to ROOM4;        
      

Multimédia!

Mivel a BASIC szkript ilyen átírása eléggé sok munkával járt, és hamar unalmassá vált, arra gondoltam, megfűszerezem kicsit azzal, hogy megpróbálkozom a képeket is kiszedni.

Már az első, ismerkedő átolvasásoknál feltűnt a $CBF8-nál kezdődő rutin, amelyik a lemezről olvas be RAM-ba. Egy jólirányzott breakpoint azonnal bebizonyította, hogy ez a rutin valóban akkor hívódik meg, amikor egyik szobából a másikba megy a játékos, és a két szoba kép-száma nem egyezik meg.

Mivel itt gyakorlatilag annyi történik, hogy a betöltött adatot (egy 192x80 pixeles multicolor képet) egy-az-egyben átmásoljuk a videómemória megfelelő részébe, ígéretes volt csak a lemezen lévő adatokra koncentrálnom ahelyett, hogy belépek minden egyes szobába az emulátorban, és kisave-elem a videómemóriát. Hogy lássam, hogy tényleg az történik, amit gondolok, egy újabb Haskell szkriptet írtam a videómemória alapján a képernyő rekonstrukciójára PNG-be. Itt volt egy kis szopás azzal, hogy milyen furcsa sorrendben van a pixelek címzése; de biztos vagyok benne, hogy ennek az oka az, hogy a karakteres képernyő minél több áramkörön tudjon osztozni a grafikussal. Az első helyesen dekódolt kép alja azért volt még szemetes, mert ott már szöveges módot használ az Időrégész:

Azután hogy az alját levágtam, és hozzáadtam a szín-kezelést is, máris felismerhető lett a kép:

Ugyanezt a szkriptet a lemezen lévő file-okra alkalmazva azonnal meglettek a szobák illusztrációi:

A hangokkal kapcsolatban nincs hasonlóan izgalmas sztorim, azokat végül a Vice emulátorból vettem fel a zenéket wav-ba, aztán letömörítettem Vorbisba.

És végre kész!

Az első publikus változat egy három szobából álló demó volt, ami a végleges parsert használta, és már tartalmazott képeket (de zenét még nem). Igyekeztem azt a hangulatot megteremteni vele, amit annak idején a LucasArts játékok demói: igaz, hogy csak pár helyszín, igaz, hogy pár perc végigjátszani, de megáll a saját lábán, van benne puzzle, és meg lehet nyerni. Ezt a verziót május 23-án raktam fel az IDDQD Facebook-csoportjába, ahol nagyon sokakat érdekelt. Itt győztek meg egyébként arról is, hogy rakjam bele a végleges verzióba a zenéket is; és innen tudtam meg azt is, hogy van a Facebook-on külön Rátkai-oldal.

A teljes játékot június 1-én fejeztem be, és töltöttem fel az IDDQD oldalára, egyfajta fél-béta verzióként, ugyanis megkértem a tesztelőket, hogy ezt még ne terjesszék, amig ki nem javítok benne minden bug-ot. És hát a lelkes tesztelők bizony találtak is néhányat. Ami a legjobb volt ebben, hogy el is kezdtek egymásnak sztorizni a játékról a kommentekben!

A letölthető és a weben játszható verziókat egyaránt itt találod.


« Hacks of 2015 
All entries
 Initial version of my Commodore PET »