23. Leden 2018 - 20:47

Autor Téma: Seriál o programování textových her v TADS 3  (Přečteno 8994 krát)

Kroužící orel

  • Plný člen
  • ***
  • Příspěvků: 136
    • Zobrazit profil
    • Šťastný statek
Re: Seriál o programování textových her v TADS 3
« Odpověď #105 kdy: 28. Listopad 2017 - 09:24 »
Komunikace na fóru funguje bezvadně, od Jeffa jsem získal zajímavé soubory, plánuje třídy pro výuku TADSu jako prvního progracovacího jazyka, což je velmi zajímavé, přečetl jsem si Python for Kids, tvorba textovek mě však samozřejmě zaujala více.

1. Unit testy
Díval jsem se na jeho soubory, pro zajímavost zasílám i v příloze, je zde sada TADS3 Unit Testů, zajímavé, v minulosti jsem se setkal s pojmem Programování řízené testy – TDD, což pokud dobře chápu bude jedna z metod programování podobně jako např. Scrum. Vím jak postupuješ ty pomocí transkriptu hry, využil jsi při své práci také něco podobného od Jeffa nebo Bizzarriho?

2. QT Creator
Fórum jsem trochu procházel, zaujal mě článek, kde krásně popisuješ své prostředí:
https://www.intfiction.org/forum/viewtopic.php?f=10&t=20899&sid=0bc3a9795ccf2d5ffea8ecfd35941195
a vysvětluješ automatické testy. Obrázek QT Creatoru v akci je velmi zajímavý, zkoušel jsem různé editory a IDE a v případě aplikací v prostředí KDE potěší integrovaná podpora syntaxe TADSu v Kwrite i KATE, což se mi opravdu snadno podařilo převést do QT Creatoru. Svojí další hru již plánuji rozdělit na soubory podobně jako u Základny, proto by se vhodné IDE hodilo. Přímá podpora TADS projektu zde samozřejmě není, mohu poprosit o osvětlení jak jsi postupoval u založení projektu – nestačil např. nový projekt v C++ a ručně přidat zdrojové t soubory? Kontrolu nad výstupy máš v tomto IDE parádní, sakra, kdyby byl nějaký programátor ochoten převzít debugger z Workbenche a naroubovat jej na Frobtads, bylo by takové IDE naprosto perfektní a závislost na WINE ukončena.
Také je zvláštní, že projekt KDE má dvě taková prostředí, Kdevelop a QT Creator, na fóru:
http://www.abclinuxu.cz/clanky/srovnani-kdevelop-a-qt-creator
jsem našel sice starší, ale zajímavý článek obě porovnávající a mnoho rozdílů zde není. Každopádně zvýraznění syntaxe funguje pod každým z nich, Kdevelop se zdá být cca o 50MB zabrané paměti úspornější. Tak jako tak je to oproti javovským IDE paráda, na mém ne zrovna výkonném stroji se jedná dosti zásadní rozdíl.
Používáš QT Creator i nyní a jsi s ním spokojen? Nyní mám na svém stroji Arch Linux a Q4OS, v obou tato prostředí parádně fungují a i když mám pod Archem GTKčkové MATE, není s integrací sebemenší problém, obecně mi Arch přijde velmi zajímavý a i přes čerstvé aplikace opravdu stabilní, jeho Rollback Machine jsem už nevyužil, ani nepamatuji.

3. Na fóru mě zaujala ještě jedna zajímavost:
https://github.com/anthonyirwin82/generator-tads
Yeoman Generator for TADS 3
Nevím, zda má pro vývoj smysl automaticky generovat projekty s využitím tohoto nástroje, zaujala mě však podpora node.js, kterou využívají i moderní aplikace pro linuxovou řádku, možná by prospěla budoucnosti TADSu právě s využitím webových technologií.

4. Na a zase zpět k tvorbě
Nyní potřebuji zapálit kupku sena křesadlem nebo pochodní stejně jako v původní hře a po jejím shoření se na zemi objeví jehla.
Již umím zapálit pochodeň, přemýšlel jsem jak nejvhodněji zapálit kupku sena. Napadlo mě využít Matchbook a Matchstick, zkusil jsem definovat právě kupku jako Matchstick a křesadlo Matchbook i obráceně, dostávám však nesmysly typu „zapaluješ kupku kupkou“, tady si nejsem jist, zda kombinaci sirky - krabička od sirek budu moci využít. Plus nesmím zapomenout nadefinovat pochodeň podobně jako křesadlo PreferredIobj s využitím metody iobjFor(BurnWith).
Pokud neuspěju se zápalkami, další možností by mohlo být využití objektu objBurning: PreCondition – jen si nejsem jist, zda není určen k něčemu jinému, v Library píší This can be used for matches, candles, and the like. Budu ještě testovat.

Druhá akce je rozbití skleněné koule sekerou, na zem se rozlije tekutina, do které mohu namočit jehlu a tou odstranit draka.
Zde mi přijde ideální metoda dobjFor(Break), prozkoumal jsem příklady ze Základny, kde jí hojně využíváš, zde jsou však pouze formulace využívající direktivu illogical a popisující, proč hráč něco nemůže rozbít. Ani Library moc nepomohla, je zde pouze:
   /* -------------------------------------------------------------------- */
    /*
     *   "Break" action
     */
    dobjFor(Break)
    {
        preCond = [touchObj]
        verify() { illogical(&shouldNotBreakMsg); }
    }

snažím se využít jednu konstrukci ze Základny:

   dobjFor(Break)
    {
        verify() { }
        action()
        {
            "Nepodařilo se Ti uvolnit jí. ";
            nestedAction(TypeLiteralOn, self, 'R');
        }
    }
    dobjFor(Attack) asDobjFor(Break)

a inspiroval jsem se mnou dříve definovanou metodou dobjFor(CutWith) u mříže. Je mi jasné, že se musím kouknout na strom dědičnosti v Library a využít popsané metody a funkce, právě definice těchto akcí je pro mě v současné chvíli to nejobtížnější, ještě jednou si projedu Tvůj manuál, kde o nich píšeš a také kapitolu 6 – Actions v Ericově manuálu, tohle cítím, že je gró programování obecně a nesmím nic uspěchat, protože bez důkladného pochopení akcí se nikam nedostanu.

Psa i draka už jsem nadefinoval, k jejich eliminaci mi stačí jehla a rozlitý jed, tyto předměty využiji jakmile pohnu s akcemi popsanými výše. Dále jen u brány ukončím hru po sebrání všech 4 drahokamů, doladím ještě pár nesrovnalostí u skloňování některých objektů a tvorba pomalu končí.

gaspoda

  • Plný člen
  • ***
  • Příspěvků: 114
    • Zobrazit profil
Re: Seriál o programování textových her v TADS 3
« Odpověď #106 kdy: 28. Listopad 2017 - 20:24 »
1. Unit testy
Díval jsem se na jeho soubory, pro zajímavost zasílám i v příloze, je zde sada TADS3 Unit Testů, zajímavé, v minulosti jsem se setkal s pojmem Programování řízené testy – TDD, což pokud dobře chápu bude jedna z metod programování podobně jako např. Scrum. Vím jak postupuješ ty pomocí transkriptu hry, využil jsi při své práci také něco podobného od Jeffa nebo Bizzarriho?

TDD podobně jako Scrum jsou metodologie, tedy řekněme sady doporučení, jak při programování postupovat. Každá se ale zaměřuje na jinou oblast problematiky, spolu nijak nesouvisí. Scrum je o tom, jak z týmu programátorů vyždímat co nejvíce práce (alespoň mi všechny ty obrázky trochu připomínaly odstředivku :-)) za pomoci spousty podivných anglických slovíček, jako agile, user story, epic apod.:

https://i.pinimg.com/originals/82/4c/56/824c5615fa75244710db8abe3c40bbce.jpg
http://geekandpoke.typepad.com/.a/6a00d8341d3df553ef017ee8123faf970d-pi
http://geek-and-poke.com/geekandpoke/2016/11/6/advanced-scrum

TDD je názor, že při vývoji softwaru je vhodné používat automatické testování, protože pomůže předejít chybám a že vůbec nejlepší je napsat testy dopředu před tím, než vznikne kód:

https://img.scoop.it/wn9mwRFeFitxvyrTjNs4yDl72eJkfbmt4t8yenImKBVvK0kTmF0xjctABnaLJIm9
http://geekandpoke.typepad.com/.a/6a00d8341d3df553ef0153925eca87970b-pi
https://static1.squarespace.com/static/518f5d62e4b075248d6a3f90/t/58a830ece6f2e14f1951b58e/1487417607195/

Tyhle věci se týkají spíš projektů vyvíjených ve větších týmech, kdy je plánování a komunikace důležitá, protože jinak by levá ruka nevěděla, co dělá pravá. Ale já osobně s tím žádné praktické zkušenosti nemám, většinu času pracuji sám. I když je pravda, že jsem před časem v práci povýšil z funkce "jediný programátor" na "šéfprogramátor" prostě proto, že je nás už jeden a půl.

Testování je v řadě případů docela užitečné, zvláště v situaci, kdy se příiš neřeší dokumentace, točí se lidi a nikdo pořádně neví, co se stane, když do něčeho dloubne. To je trošku zkratkovitý názor, testování určitě patří k "dobré kultuře", potíž je, že zdaleka není všespasitelné a zdaleka není zadarmo. V ideální situaci by člověk pomocí automatizovaného testování otestoval všechny mezi situace a alternativy, které mohou nastat, aby si byl jistý, že kód pracuje v pořádku.

Když však člověk někde jednotkové testování vidí, tak kolikrát jsou otestovány jen jednoduché triviální situace, tedy to, co programátor očekává, že by se mohlo zvrtnout. Programátoři se pak pochválí, jak jsou moderní, že provádějí testování, dokonce z toho některé nástroje vypočítají tzv. "pokrytí testy", tedy procento řádků programového kódu, ke kterému je nějaký test. Co na tom, že se může pokazit ve stejném kódu deset jiných věcí, které nikdo netestuje. Připomíná mi to cargokult.

Moje zkušenosti s chybami v softwaru jsou spíš takové, že většina chyb, které je pracné najít a opravit a mohou snadno zůstat skryté plyne spíš z komplexnějších interakcí větších celků, které by mě ani nenapadlo předvídat a to jsou věci, které jsou na testování podstatně náročnější. A jak říkám, protože nepracuji ve velkém týmu, tak pro mě a můj styl práce ten poměr cena/výkon není zase tak příznivý, jinými slovy vždycky po mě spoustu lidí chtělo spoustu věcí, tak když mám chvíli čas, tak ji investuji spiš do kvalitních univerzálních komponent, které mezi projekty sdílím.

2. QT Creator
Fórum jsem trochu procházel, zaujal mě článek, kde krásně popisuješ své prostředí:
https://www.intfiction.org/forum/viewtopic.php?f=10&t=20899&sid=0bc3a9795ccf2d5ffea8ecfd35941195
a vysvětluješ automatické testy.

Právě na rozdíl od pracovních projektů jsem při programování té své textovky a tvorbě překladu TADSu automatizované testování používal, ale ne ve stylu jednotkových testů, jaké ukazoval Jeff, ale právě přes transkript. A na uvedeném odkazu jsem se snažil poukázat na hlavní výhodu, kterou v tom vidím, byť to má své mouchy, a sice na ekonomičnost takového způsobu testování - s tak malou námahou lze provést tak komplexní testování, to je u jiného druhu softwaru nedosažitelné. Stačí jen přidávat příkazy do souboru a vždy si přečíst pár řádek, jak se změnil výstup.

Přímá podpora TADS projektu zde samozřejmě není, mohu poprosit o osvětlení jak jsi postupoval u založení projektu – nestačil např. nový projekt v C++ a ručně přidat zdrojové t soubory?

Já vždy vyberu "importovat projekt" a "importovat stávající projekt". A to i tehdy, když projekt doposud neexistuje, prostě si při tom vytvořím prázdný adresář. Qt Creator pak funguje jako obyčejný textový editor a nijak mi do samotného projektu a způsobu jeho sestavení nezasahuje. Jen si v něm přidám, které soubor se mi mají zobrazovat v levém sloupečku a jak se volá překladač si nastavím v záložce projekty (ikona v levé šedivé liště).

Konkrétně mám dvě varianty sestavení (výběr v "upravit nastavení sestavování") a to normální, kdy poštím příkaz "t3make" s argumentem "-d" a druhou předvolbu mám sestavení na web, kdy pouštím ten samý příkaz s argumentem "-f Makefile.web.t3m". Pro web mám samostatný makefile, viz zdrojáky Základny. Když neřeknu t3make přepínač -f, tak automaticky bere Makefile.t3m.

Pro spouštění mám variant několik. Jednak spuštění automatického testu, tam mám v konfiguračním dialogu nastaven spustitelný soubor testPlay.sh v patřičném adresáři, dále mám spuštění frobu, to mám spustitelný soubor "frob" a command line arguments "-i plain -k utf8 -c -p asteroid.t3". Pak mám možnost spustit qtads, to je příkaz "qtads" a argument "asteroid.t3", pak spustit webové hraní s lokálním serverem, kde mám spustitelný soubor zase frob a argument "-i plain -k utf8 -p -N 00 -w 192.168.1.2 -W tomasb asteroid-web.t3 -storagesid=tomasb" a webové hraní s lokálním úložištěm má argument "-i plain -p -N 44 asteroid-web.t3"

Kontrolu nad výstupy máš v tomto IDE parádní, sakra, kdyby byl nějaký programátor ochoten převzít debugger z Workbenche a naroubovat jej na Frobtads, bylo by takové IDE naprosto perfektní a závislost na WINE ukončena.

To by teoreticky možné bylo, jednou jsem do toho nakukoval dovnitř. Qt Creator má poměrně univerzální kód, uživatelské prostředí je hotové, takže by stačilo "jen" napsat rozhranní a propojit. Bohužel i tak je to obrovská práce, muselo by se toho spousta nastudovat, takže neočekávám, že něco takového vznikne. Navíc Qt Creator je příliš živý projekt, často se mění.

Druhá možnost by byla vytvořit webové IDE a jít vstříc budoucnosti. To by mělo některé výhody a některé nevyhody.

Používáš QT Creator i nyní a jsi s ním spokojen? Nyní mám na svém stroji Arch Linux a Q4OS, v obou tato prostředí parádně fungují a i když mám pod Archem GTKčkové MATE, není s integrací sebemenší problém, obecně mi Arch přijde velmi zajímavý a i přes čerstvé aplikace opravdu stabilní, jeho Rollback Machine jsem už nevyužil, ani nepamatuji.

Qt Creator pochází od toho, kdo zrovna vlastní Qt knihovny, mění se to příliš rychle, než abych si pamatoval, kdo to je nyní. KDevelop je od komunity. Já qtc používám, tedy resp. používáme ho v Kapse na kroužku. Používám ho rád, protože má několik vlastností, které ho činí pro výuku ideální. Před drahnou dobou, co skončilá éra Borland Cčka jsem dlouho hledal něco použitelného pro výuku a ani Dev-C++, abu Code::blocks mi nevyhovovaly. Jednak jsem chtěl multiplatformí prostředí, aby bylo stejné na linuxu i windows, druhak jsem chtěl prostředí, které se příjemně používá. O Dev-C++ se nikdo nestaral, devpaky se SDL byly staré a ručně tam proniknout přes vrstvu konfiguračních dialogů projektů k fungujícímu projektu s nějakou cizí knihovnou bylo pro mě peklo a nedařilo se mi. Druhý zmíněný pak vypadal, jak když člověk nasype hromadu ikonek do mixéru, chyběla v tom nějaká vstřícnost a přehlednost. Navíc jsem také moc nepochodil s konfigurací projektů. QtC bylo vysvobození. Navíc nesnáším editory, které otevírají více souborů jako nahodile uspořádané záložky v záhlaví, já chci abecedně seřazený seznam po straně :-)

Yeoman Generator for TADS 3

Nemyslím, to je jen šum. Na založení projektu, tj. vytvoření dvou souborů není potřeba nic víc, než Ctrl+C, Ctrl+V. Učit se pět nových obskurních technologií, děkuji pěkne...

Již umím zapálit pochodeň, přemýšlel jsem jak nejvhodněji zapálit kupku sena. Napadlo mě využít Matchbook a Matchstick.

Matchstick je samozápalný objekt, tj. můžeš ho zapálit bez čehokoliv dalšího. To nevím, jestli chceš, nechceš zapálit seno od hořící pochodně? Každopádně Matchbook vůbec nepotřebuješ. To je krabička na zápalky a řeší úplně jiný problém a sice jde o to, když je v krabičce hodně stajných zápalek, aby hráč vytáhnul jakoukoliv bez ptaní, kterou z desítek sirek má hráč na mysli. Nemá se samotným zapalováním nic společného.

Pokud neuspěju se zápalkami, další možností by mohlo být využití objektu objBurning: PreCondition – jen si nejsem jist, zda není určen k něčemu jinému, v Library píší This can be used for matches, candles, and the like. Budu ještě testovat.

Precondition se používá pro automatické akce. Toho si také nevšímej. Nejjednodušší je udělat u sena dobjFor(Burn) a jednak pomocí verify s prázdným tělem povolit a v action napsat, co se stalo.

Zde mi přijde ideální metoda dobjFor(Break), prozkoumal jsem příklady ze Základny, kde jí hojně využíváš, zde jsou však pouze formulace využívající direktivu illogical a popisující, proč hráč něco nemůže rozbít. Ani Library moc nepomohla, je zde pouze:
   /* -------------------------------------------------------------------- */
    /*
     *   "Break" action
     */
    dobjFor(Break)
    {
        preCond = [touchObj]
        verify() { illogical(&shouldNotBreakMsg); }
    }

Tak to je přesně ono, to je úryvek z knihovny. Tak podobně, jako výše zmíněného zpálení. Vidíš, že na objektu je by default verify, které akci zakazuje. Tudíž přetěž verify tak, že ho uděláš s prázdným tělem, tím vyřadíš to illogical, takže akce bude s tím objektem povolená. No a pak zadefinuj action, ve kterém něco napíšeš a uděláš. Tedy asi odstraníš skleněnou kouli ze hry - moveInto(nil) a přidáš objekt střepy, tekutinu, nebo něco takového, takže tekutina bude třeba Hidden objekt v místnosti a zavoláš tekutina.discover(). Jen prosím vynechej to nestedAction(TypeLiteralOn, self, 'R'), to vyvolalo zmáčknutí klávesy R na ovládacím počítači v základně, kterou se zobrazí reporty o těžbě. To na tvém hradu nebude :-)
« Poslední změna: 28. Listopad 2017 - 21:32 od gaspoda »

Kroužící orel

  • Plný člen
  • ***
  • Příspěvků: 136
    • Zobrazit profil
    • Šťastný statek
Re: Seriál o programování textových her v TADS 3
« Odpověď #107 kdy: 29. Listopad 2017 - 18:58 »
1. Metodiky programování
Paráda, rozepsal jsi se zajímavě a podrobně, moc díky za Tvůj čas. Teď už konečně chápu, co to opravdu znamená agilní programování, bude to něco jako tah na branku v požadavcích na firemní uchazeče. Jak vidno, po agilní snídani opravdu netoužím, tfuj… Pokud je metodika TDD vhodnější spíše pro větší týmy, zatím se jí podobněji zabývat nebudu, každopádně testy, které provádíš v QT Creatoru a o kterých jsme se bavili při mých začátcích s TADSem mi připadají komplexní, transskript s úspěchem používám i já a pokud uživatel-tester opravdu Zapne zápis, měli bychom společně odchytit téměř vše. Což jak chápu používáš, uvádíš, že testuješ především při TADS tvorbě. S univerzálními kvalitně napsanými komponenty, které sdílíš s ostatními, zcela souhlasím, zde si představuji např. komponenty starého Delphi a snad i novějšího Lazarusu, díky nimž programátor spoustu času ušetří a měly by být otestovány, vím, že vývojáři účetních programů a prvních IS tento nástroj s úspěchem využívali a někteří si dnes stěžují na Visual Studio, kde jim chybějí.

Máš recht, že takové testování v QT Creatoru, které jsem měl možnost vidět na obrázku v diskuzi, je velmi jednoduché a účinné, unit testy jsou možná pro TADS trochu zbytečné. Ale každopádně je budu využívat u C/C++, takže až dokončím hru, juknu na ně podrobněji a srovnám s těmi céčkovými. Tvrzení, že u jiného SW je takové jednoduché testování nedostažitelné, je velmi zajímavé, opravdu jsi mě překvapil. Hmm, pokud se v TDD netestuje vše potřebné, ale provádí se hlavně proto, aby se naplnily měřitelné parametry testování, nedá se nic dělat, připomíná mi to tzv. timesheety, které jsem denně vyplňoval v práci, ufff, pracovat na sebe má opravdu nesmírné výhody.

Sakra, nesmím to s tím programováním tak přehánět, manželky těchto individuí to opravdu nemají jednoduché… kór když je vývojář ještě nadšený koňák a dříve, než kobylku nauzdí, provádí testy výkonnosti...

2. QT Creator
Super, vše jsi parádně popsal, podařilo se mi naimportovat prázdný projekt, přidal jsem všechny soubory Základny a nastavil sestavení a spuštění. Hru se mi podařilo spustit pomocí interního qtcreator_process_stub. Lokální práce je tedy OK, tímto způsobem si založím nový projekt Špatné noci velmi volně inšpirované Bad Nightem, ještě si výhledově zprovozním lokální webový TADS server, to je myslím dobře popsané v dokumentaci. Zatím tedy budu používat klasický WB debugger, přiznám se, že se bez něj neobejdu s kombinaci s QTC. Důvody, proč QTC používáš, jsou jasné, mě se nejvíce líbí to, že ze mnou známých skutečných IDE, nejen „lepších“ textových editorů, patří mezi ty nejméně paměťově náročné plus je určen pro C a C++, pořádně se na něj juknu. Pokud se příliš živě vyvíjí, nevadí, i Arch je takový, vždy mám možnost výměny za Kdevelop nebo cokoliv jiného, ještě, že máme na výběr. DevC++ si pamatuji před mnoha lety z Windows, naštěstí pod Linuxem je výběr obrovský, zaujal mě např. jednoduchý juCi++, nejedná se ovšem o IDE, které pokud člověk správně uchopí, určitě pomůže.

3. Zapálení sena
Aha, já tušil, že Matchstick a Matchbook jsou předem připraveny pro konkrétní akci, v tomto případě samozápalný objekt, to u sena nevyužiji. Precondition si taky všímat nebudu, automatické akce nechám na později. Metoda dobjFor(Burn) funguje parádně, ještě jsem hráči znemožnil seno vzít, ať hra vypadá trochu reálněji.

4. Skleněná koule
Aha, budu si pamatovat, že k vyřazení akce rozbij je třeba přetížit verify. Použil jsem metodu dobjFor(Break) s tím, že pokud mám sekeru u sebe, hráč automaticky kouli rozbije. Následně se objeví kaluž, u které jsem ještě musel definovat  cannotTakeMsg a protože nebyla vidět, ale šlo jí prozkoumat, ještě bylo třeba isListed = true. Teď celá akce funguje parádně.
Jediná malá bolístka je v tom, že hráč musí napsat „rozbij kouli“, ovšem příkaz „rozbij kouli sekerou“ vrací „Nic takového jako „kouli sekerou“ tu nevidíš.“

Metoda dobjFor(AttackWith) útok sekerou povoluje, „zabij strážce sekerou“ je OK.

U skleněné koule jsem se pokusil použít místo původního
if (!sekera.isIn(me))
příkaz
if (gIobj != sekera)
zde však pohořím:

>rozbij kouli
Kouli holýma rukama nerozbiješ.
>rozbij kouli sekerou
Nic takového jako „kouli sekerou“ tu nevidíš.
>zabij kouli
Kouli holýma rukama nerozbiješ.
>zabij kouli sekerou
Skleněnou koulí nemůžeš zaútočit.
>zaútoč na kouli
Kouli holýma rukama nerozbiješ.
>zaútoč na kouli sekerou
Na ni nemůžeš zaútočit.

Využívám metodu dobjFor(Attack) asDobjFor(Break)

Napadlo mě změnit metodu dobjFor na:
dobjFor(AttackWith) asDobjFor(Break)

poté získávám:
>rozbij kouli
Kouli holýma rukama nerozbiješ.
>rozbij kouli sekerou
Nic takového jako „kouli sekerou“ tu nevidíš.
>rozbij kouli sekera
Nic takového jako „kouli sekera“ tu nevidíš.
>zaútoč na kouli
Na ni nemůžeš zaútočit.
>zaútoč na kouli sekerou
Rozbil jsi skleněnou kouli sekerou, kapalina vytekla na zem.

Jasně, zde je převzata metoda z definice strážce, metoda rozbij je však definována jinak a pokud uvažuji správně, musel bych jí přeprogramovat dle svých potřeb a nazvat jí např. DobjFor(BreakWith).

Ale nebudu situaci komplikovat, asi bude lepší vůbec hráči nedovolit zaútočit na kouli, pouze jí rozbít, takže jsem dobjFor(Attack) asDobjFor(Break) zakomentoval. Napadá mě, že těchto situací si v budoucnu určitě při tvorbě užiji dosytosti, je možné hráči na konkrétní příkaz, např. v mém případě:

rozbij kouli sekerou, rozbij kouli pochodní …

automaticky odpovědět např. textem „To nemůžeš udělat.“ - tedy přepsat standardní knihovní hlášku? Dost by to podobné situace zjednodušilo.

Podobně je to se zapálením sena:
>zapal kupku pochodní
{Tím iobj} nemůžeš nic zapálit.
// to chápu, pochodeň není definována přímo jako věc, která umožňuje něco zapálit
>zapal kupku křesadlem
To není něco, co by mohlo hořet.
>zapal kupku
Zapálil jsi seno. Bylo tak dobře proschlé, že za několik sekund zbyla jen docela malá hromádka popela.

Možná bych využil informace z Tvého manuálu, kde píšeš o Nepřenosných objektech - Fixture představuje pevný objekt, který na pokus o sebrání, pohnutí a položení někam reaguje chybovými zprávami cannotTakeMsg, cannotMoveMsg a cannotPutMsg. Pokud na objektu tyto vlastnosti nenastavíte na svůj jednoduchými uvozovkami ohraničený řetězec, použije se standardní knihovní hláška.
Bylo by možné takové řešení využít?

5. Namočení jehly v kaluži
Chvíli jsem se trápil s namočením jehly v kaluži, hra samozřejmě příkaz namoč nezná a již tradičně jsem žádný vhodný příklad na internetu ani v dostupných hrách nenalezl. Nakonec mě napadlo definovat kaluž jako RestrictedContainer, dlouho jsem uvažoval o vhodné metodě, s dobjFor(PutIn) jsem úspěšný nebyl, iobjFor(PutIn) ale funguje k plné spokojenosti. Uff, tak snad už se konečně do té tvorby dostanu. Definoval jsem také nové slovo „namoč“ pomocí:

VerbRule(namoc)
    ('namoč' | 'namočit')
    dobjList 'do' singleIobj
    | ('namoč' | 'namočit')
    'do' singleIobj dobjList
    : PutInAction
    verbPhrase = 'namočit/namá{číš}/namočil{a} (co) (do čeho)'
    askIobjResponseProd = toSingleNoun
;

a po kompilaci ve WB fungoval příkaz „namoč jehlu v kaluži“ kromě klasického dej nebo polož jehlu do kaluže naprosto bez potíží. Ale ouha, teď jsem překompiloval Exoter.t pomocí t3make a „namoč jehlu v kaluži“ už nefunguje, polož nebo dej naštěstí ano, viz transkript. Setkal jsi se někdy s něčím podobným?

Dokonale jsi mě rozřechtal tou hláškou ...to vyvolalo zmáčknutí klávesy R na ovládacím počítači v základně, kterou se zobrazí reporty o těžbě. - a její kombinací s prostorami hradu, díky, tak krásně už jsem se pár dnů nazasmál.

Draka a psa už mám připravené, nyní mě čeká vymyslet jak mrtvému drakovi useknu hlavy sekerou, dívám se na DefineTIAction(CutWith) a především dobjFor(CutWith) a tyto následně hodím psovi, zde se pokusím využít stejnou definici jako při namočení jehly do kaluže. Prozatím zasílám aktuální zdrojový kód a transkript, zítra nebo pozítří budu vesele pokračovat.

gaspoda

  • Plný člen
  • ***
  • Příspěvků: 114
    • Zobrazit profil
Re: Seriál o programování textových her v TADS 3
« Odpověď #108 kdy: 29. Listopad 2017 - 21:29 »
1. Metodiky programování
Teď už konečně chápu, co to opravdu znamená agilní programování, bude to něco jako tah na branku v požadavcích na firemní uchazeče.

Agilní na rozdíl od skrumáže znamená to, že se neplánuje vývoj a analýza celého projektu detailně dopředu, ale že se programuje po menších částech, které se pravidelně ukazují zákazníkovi a na základě zpětné vazby se pak průběžně mění plán a další zadání.

Tvrzení, že u jiného SW je takové jednoduché testování nedostažitelné, je velmi zajímavé, opravdu jsi mě překvapil. Hmm, pokud se v TDD netestuje vše potřebné, ale provádí se hlavně proto, aby se naplnily měřitelné parametry testování, nedá se nic dělat, připomíná mi to tzv. timesheety, které jsem denně vyplňoval v práci, ufff, pracovat na sebe má opravdu nesmírné výhody.

TDD jako teorie je pěkné, ale v praxi to občas trochu pokulhává. Ale to tak bývá se vším, třeba s dokumentací. Např. hrozně rozšířený zlozvyk je psát komentáře alespoň k hlavičce funkce a vygenerovat z toho pak HTML dokumentaci. Potíž je, že ve většině případů to bývá tak, že když bys z takového komentáře vymazal všechna slova obsažená v názvu funkce a všechna obsažená v názvu třídy, tak ti z komentáře zbydou jen předložky a spojky. Mnoho lidí se snaží o různé vývojářské postupy, ale málokdo to dělá pořádně.

Pokud se příliš živě vyvíjí, nevadí, i Arch je takový, vždy mám možnost výměny za Kdevelop nebo cokoliv jiného, ještě, že máme na výběr.

Jo, mě tím pěkně derou, protože se snažím udržovat na webu návod k instalaci Qt Creatoru spolu se SDL knihovnami do Windows a každou chvíli mi tam něco předělají a já pak musím přefocovat screenshoty a vyřezávat je z celého snímku a to je tak ubíjenící činnost...

4. Skleněná koule
Aha, budu si pamatovat, že k vyřazení akce rozbij je třeba přetížit verify.

Obecně si spíš pamatuj se na každou akci podívat do referenční příručky, jak je na tvém žádaném objektu zadefinovaná. A u těch exostičtějších akcí je běžné, že nemají žádné definované chování a jen pomocí verify říkají, že to nejde. Konec konců tak je zadefonovaná většina akcí na Thing, že nic nedělají (kromě těch úplně univerzálních, jako seber, polož apod. Koukni třeba na náhodný koušíček Thng:

Kód: [Vybrat]
    /* -------------------------------------------------------------------- */
    /*
     *   "PutBehind" action
     */
    dobjFor(PutBehind)
    {
        preCond = [objHeld]
        verify() { }
    }

    iobjFor(PutBehind)
    {
        preCond = [touchObj]
        verify() { illogical(&cannotPutBehindMsg); }
    }

    /* -------------------------------------------------------------------- */
    /*
     *   "Wear" action
     */
    dobjFor(Wear)
    {
        verify() { illogical(&notWearableMsg); }
    }

Tady třeba vidíš, že jakýkoliv objekt, který držíš nebo můžeš vzít (dobj) je možné umístit za jiný objekt (třeba klíč za obraz). Ale zároveň vidíš, že všechny objekty mají nastaveno, že zrovna za ně se nedá nic dát (iobj), takže dokud se u nějakého objektu neudělá výjimka, tak je akce polož něco za něco připravená, ale nejde s žádným objektem udělt.

Podobně je v základu zakázaná akce wear, tedy obléci objekt tím, že je zde naprogramované verify s illogical makrem. A to je právě to - když u objektů třídy Wearable chtěl autor tuto akci povolit, postupoval takto:

Kód: [Vybrat]
    dobjFor(Wear)
    {
        preCond = [objHeld]
        verify()
        {
            /* make sure the actor isn't already wearing the item */
            if (isWornBy(gActor))
                illogicalAlready(&alreadyWearingMsg);
        }
        action()
        {
            /* make the item worn and describe what happened */
            makeWornBy(gActor);
            defaultReport(&okayWearMsg);
        }
    }

Vidíš, že je přetížené verify, aby se vyřadilo původní illogical. I když v tomto případě tam jedna podmínka je přidaná. Pak je tu také precondition, že musí hráč objekt nejprve vzít, než ho bude moci obléknout. No a nakonec je tu akce, která obleče a zobrazí výchozí hlášku, že se tak stalo.

Smyslem tedy není si pamatovat nazpaměť, že zrovna při akci rozbij je potřeba předělat verify. U různých akcí to může být různé, někdy je potřeba upravit verify, jindy check. Vždy je tedy potřeba se podívat, jak je to dělané a co to pak v action() doopravdy udělá.

Následně se objeví kaluž, u které jsem ještě musel definovat  cannotTakeMsg a protože nebyla vidět, ale šlo jí prozkoumat, ještě bylo třeba isListed = true. Teď celá akce funguje parádně.

Kaluž se nevypisuje, protože jsi zvolil Immovable, což se obvykle používá na objekty, které jsou pevnou součástí lokace a předpokládá se, že jsou posané v popisu místnosti. Tvé řešení je jedno z možných. Také bys mohl přidat specialDesc předmětu a napsat tam podrobnější popis kaluže, jak se má v místnosti napsat. Jinak když používáš Immovable, měl bys napsat celkem tři různé hlášky pro různé manipulace cannotMoveMsg, cannotPutMsg a cannotTakeMsg. Pokud stačí na vše odpovídat stejně, stačí zadat jen cannotTakeMsg a místo Immovable použít třídu CustomImmovable.

Jediná malá bolístka je v tom, že hráč musí napsat „rozbij kouli“, ovšem příkaz „rozbij kouli sekerou“ vrací „Nic takového jako „kouli sekerou“ tu nevidíš.“

Metoda dobjFor(AttackWith) útok sekerou povoluje, „zabij strážce sekerou“ je OK.

Zkus u koule dobjFor(AttackWith), protože ta koule je to, na co se útočí, tedy dobj. Pak si dej ten prázdný verify (tedy pokud prozkoumáš implementaci dobjFor(Attack) v třídě Thing, tak zjistíš, že nemusíš, že akce zakázaná není), a ta podmínka if (gIobj != sekera) v checku by měla být ok. Já myslím, že možná jen narážíš na to, že jsi nic neudělal resp nevypsal v action(). Výchozí chování je mainReport(&uselessToAttackMsg);

Využívám metodu dobjFor(Attack) asDobjFor(Break)

To je v pořádku. To zajistí, že zaútoč na kouli se bude chovat stejně jako rozbij kouli.

Napadlo mě změnit metodu dobjFor na:
dobjFor(AttackWith) asDobjFor(Break)

To ne, myslím, že nemůžeš přesměrovávat akce se dvěma objekty na akci s jedním objektem. A beztak by to nedávalo smysl, protože akci s jedním objektem beřeš jako útok holma rukama a vyzýváš hráče říci akci se dvěma objekty.

Jasně, zde je převzata metoda z definice strážce, metoda rozbij je však definována jinak a pokud uvažuji správně, musel bych jí přeprogramovat dle svých potřeb a nazvat jí např. DobjFor(BreakWith).

Než programovat celou akci s celým chováním, tak bych asi jen přidal VerbRule se slovíčky rozbiij něco něčím ke stávající akci AttackWith.

automaticky odpovědět např. textem „To nemůžeš udělat.“ - tedy přepsat standardní knihovní hlášku? Dost by to podobné situace zjednodušilo.

Jasně. To je podobné, jako když jsi napsal cannotTakeMsg u toho Immovable. Koneckonců když mrkneš na ActionMessages.PDF v http://users.ox.ac.uk/%7Emanc0049/TADSGuide/QRefs.zip, tak tam máš u každé akce na konkrétní třídě napsáno, jaké zprávy můžeš na objektu předefinovat.

Podobně je to se zapálením sena:

Přídej senu dobjFor(BurnWith) a zadej prázdné verify, aby se akce povolila. V action pak reaguje podobně, jako u Burn.

5. Namočení jehly v kaluži
Nakonec mě napadlo definovat kaluž jako RestrictedContainer, dlouho jsem uvažoval o vhodné metodě, s dobjFor(PutIn) jsem úspěšný nebyl, iobjFor(PutIn) ale funguje k plné spokojenosti. Uff, tak snad už se konečně do té tvorby dostanu. Definoval jsem také nové slovo „namoč“ pomocí:

Jasně, proč ne, to tak klidně můžeš udělat. Samořejmě si dej pozor na dobj vs. iobj. Iobj je vždy cíl nebo pomůcka a dobj to, co manipuluješ. Tady namáčíš jehlu (dobj) a kaluž je cílem (iobj).

Jen když k tomu využiješ akci PutIn, tak to znamená, že hráč tu jehlu dá do kaluže a zůstane v kaluži. To ti tady nevadí, protože jí zahodíš a místo toho mu dáš otrávenou, ale aby tě to jinde nepřekvapilo.

VerbRule(namoc)
    ('namoč' | 'namočit')
    dobjList 'do' singleIobj
    | ('namoč' | 'namočit')
    'do' singleIobj dobjList
    : PutInAction
    verbPhrase = 'namočit/namá{číš}/namočil{a} (co) (do čeho)'
    askIobjResponseProd = toSingleNoun
;

a po kompilaci ve WB fungoval příkaz „namoč jehlu v kaluži“ kromě klasického dej nebo polož jehlu do kaluže naprosto bez potíží. Ale ouha, teď jsem překompiloval Exoter.t pomocí t3make a „namoč jehlu v kaluži“ už nefunguje, polož nebo dej naštěstí ano, viz transkript. Setkal jsi se někdy s něčím podobným?

Hm, já vidím že jsi naprogramoval jen předložku 'do' a nikoliv předložku 'v'. Jo a drobnost, když píšeš verbPhrase, tak koncovky pravidelných sloves jsou eš neš ješ íš áš ne číš.

Jo a zkontroluj si tu podmínku, isLit, kterou kontroluješ na senu, takže to je nepravda. Dál Candle s vekám Cé je název třídy. Chceš-li konkrétní objekt, musíš si ho pojmenovat přes nějakou proměnnou. A nakonec mám pocit, že místo && isLit jsi spíš chtěl opak, tj. || !candle.isLit:
Kód: [Vybrat]
if (!Candle.isIn(me)&&(isLit) || !kresadlo.isIn(me))
        failCheck('Seno sám od sebe nezapálíš. ');

Kroužící orel

  • Plný člen
  • ***
  • Příspěvků: 136
    • Zobrazit profil
    • Šťastný statek
Re: Seriál o programování textových her v TADS 3
« Odpověď #109 kdy: 1. Prosinec 2017 - 13:08 »
1. Ještě jednou k metodikám a rychlým aktualizacím
Tak tady jsem špatně propojil skrumáž s agilním programováním, pokud jej dobře chápu, setkal jsem se právě s agilním stylem u doprogramování modulů IS – systém byl hotov, účetní, logistici apod. měli určité požadavky, které jsem já předával vývojářům a společně s oběma stranami testoval. Na základě této vazby jsme pak klidně změnili zadání, protože ne vždy se účetnímu podařilo správně definovat svůj požadavek.

Na generování dokumentace ze zdrojových kódů jsem se již ptal u TADSu, zde jsi mi potvrdil, že právě Library je takto připravena. HTML dokumentace, o které píšeš, je v takovém případě samoúčelná a zase naplňuje nějaké ty tabulky výkonnosti týmu, ale buďme upřímní – nebyli bychom v práci stejní? Když si vzpomenu na svoje zaměstnanecká léta IT špecialisty, každý den jsem se setkával se záplavou požadavků, co zase nefunguje na stanicích, proč nejede tiskárna, chudákovi manažerovi se jeho nejnovější mobilák nedokáže synchronizovat s Autlukáčem, jaktože najednou nefunguje pdf výstup ze SAPu… Kolega se věčně musel vrtat na linuxovém serveru, protoře požadavky jen pršely a např. po čerstvé instalaci nové verze Oracle musel vychytávat jejich chyby… no a vývojáři eshopu postavené právě nad touto databází si museli užívat to samé, nebyli poté rádi, že si dokumentaci odbyli právě tak zjedodušeně jak píšeš a měli pocit hotové práce? Nevím… já jsem v té době vždy záviděl „starým“ správcům, kteří v devadesátých letech měli na starost jenom kontrolu terminálů připojených k síti starým kulatým ethernetem a hlavní činnost byla denní záloha dosového účetnictví na disketu…

A paradoxně teď, když dělám ouplně jinou práci a na sebe, mám mnohem více času na Linux a konečně už i na to programování, které mě dříve vůbec nechytalo, ale cítil jsem, že už mě jen správa nebaví a rád bych něco vytvořil. Proto díky za TADS a Tvůj čas, baví mě to čím dál tím více a Tvé rady mi připadají jako rady učitele, který nemá v popisu práce jen nějaký teoretický výzkum, ale učí to čerstvé, s čím denně pracuje. Už teď máš u mě pořádnou projížďku na koni, což je alespoň malý revanš za Tvůj čas.
Ha, píšeš o udržování dokumentace QT Creatoru a dere Tě rychlý vývoj – ano, vždy, když jsem nastoupil do nové práce, první činnost bylo projetí dokumentace a její aktualizace plus někdy úsměvná kontrola serverovny a zjištění, co tu za těch X let, kdy se na to nikdo nedíval, vlastně všechno běží… Čím jsem starší, tím dokumentaci beru jako důležitější věc, proto mě ve světě Linuxu moc zaujal Arch, který i přes čerstvé balíčky má opravdu příkladnou wiki. RHEL a SUSE také, ta v Archu je ale dle mého názoru určena spíše pro praktické použití a krásně vysvětluje konkrétní problémy, na které bych se jako uživatel ptal. Klobouk dolů tohle v takové kvalitě udržovat. Tady např. Debian nebo Slackware nemají návody tak dobře zpracované. Otázka zní, zda to tým Archu bude i v budoucnu zvládat, systémy nám stále bobtnají, s čímž se asi nedá nic dělat. Další zajímavost je OpenBSD, zde je mi velmi sympatická jejich snaha nepřidávat bezhlavně nové funkce, ale spíše zeštíhlovat kód a integrovat pouze aplikace podrobně zdokumentované. Prošel jsem si jejich manuálové stránky, na server paráda, pro desktop mi samozřejmě více sednou ty z Archu, což je dáno směrováním obou systémů. Jenomže v OpenBSD nemám Wine a nabídka aplikací je menší plus jejich naprostá ignorace českého jazyka mi zatím nedovoluje s ním aktivně pracovat. Mnoho jejich myšlenek mi ovšem sedne, takže uvidím – naštěstí je zde na výběr a do budoucnosti nevidím a ani nechci.

2. Akce a referenční příručka plus vhodný styl hledání
Ano, určitě souhlasím, že na každou akci bych se měl podívat do Library, to se snažím dělat. Pro mě je zatím největší problém nalézt jak je daná akce pojmenována, pár jich už v paměti držím a další budou přibývat s postupem času, to asi jinak nelze.
Ano, akci PutBehind krásně popisuješ, objekt, který chci za něco umístit, musím držet a protože verify je prázdné, lze tak učinit. Ale každý objekt má implicitně verify nastaven na illogical, které v takovém případě musím potlačit, přesně tak jak jsi psal minule u příkladu se senem. U oblékání musím přetížit verify, které mělo původně implicitně nastavenou hodnotu illogical a ještě musím kontrolovat, zda konkrétní věc na sobě nemám. Teprve poté se může provést akce s oblékáním. A je zde precondition, která je stejná, jako u prvního příkladu PutBehind.

Tak a díky tomuto příkladu už vidím svojí chybu, kterou provádám při hledání v příručce. Dejme tomu, že se potřebuji podívat na definici právě PutBehind:

V Library si klidnu v levém sloupci na P a naleznu řádek PutBehindAction - class in actions.t[1939]. Rozkliknu si PutBehindAction a následně příklad pod číslem 1939. Vidím však jen následující definici:

DefineTIAction(PutBehind)
;

která mi toho opravdu mnoho neřekne, takže začnu laborovat, co s tím. A teď babo raď, jak se mám doklikat k Tebou popisovanému kódu "PutBehind" action? Je mi jasné, že vše naleznu v popisu třídy Thing, to ale vím až teď od Tebe a samotného by mě nenapadlo spávné hledání. OK, zatím si tedy grepnu řetězec "PutBehind" action a nechám se překvapit, co získám. Konečně mě příkaz:

grep -Ri ""PutBehind action"" ./

odkazuje na

www.tads.org/t3doc/doc/libref/source/thing.t.html

Když si nyní v Library rozkliknu vlevo thing.t a následně vpravo source file, Tebou popsaný PutBehind je zde. A jak jsem předpokládal, můj skromný jednořádkový příkaz naleznu také, pokud si vlevo nahoře rozklidnu actions.t .
Takže suma sumárum si také musím zapamatovat, že na akce, které se vážou k předmětu, je nutno juknout se taky do popisu Thing. To je cenná zkušenost, bezva.

3. Kaluž
Právě Immovable mě napadlo jako ideální řešení pro věc, kterou nemohu sebrat. Žádný podobný příklad v referenčních hrách nebo na fóru jsem nenalezl. Bezva nápad s přidáním specialDesc, popis místnosti je hnedle živější, budu jej používat mnohem častěji v další hře. Tak a už se dívám na popis class Immovable: NonPortable a klik na 1998 mě správně odkáže na object.t. A tady v popisu mimo jiné vidím „ The messages to use for the failure messages.“ a hnedle pod tím CustomImmovable přesně tak jak píšeš. Bezva příklad jsem našel i v Základně, soubor regionShip.t – objekt ship: MultiLoc, Fixture. Vše jsem doplnil také ke střepům a senu.
Dokumentace k TADSu je tedy parádní, budu s ní ještě podrobněji pracovat, teď už si v ní připadám skoro jako doma na wiki Archu.

4. Rozbití koule
Ano, tohle také musím prozkoumat v Library. Už se dívám na dobjFor(AttackWith), ano, vidím zde verify() { }, takže akci mohu provést a mažu jej ze své definice. V action zprávu mám, to by mělo být OK. Zde jsem zkoušel různé konstrukce, zatím se mi však nepodařilo zprovoznit jak příkaz „rozbij kouli sekerou“, tak „zaútoč na kouli sekerou“.
Pokusil jsem se dle Tvého doporučení využít:

    dobjFor(AttackWith)
    {
        check()
        {
            if (gIobj != sekera)
                failCheck('Kouli holýma rukama nerozbiješ. ');
        }
        action()
        {
            "Rozbil jsi skleněnou kouli sekerou, kapalina vytekla na zem a všude kolem jsou rozházené střepy. ";
            sklenena_koule.moveInto(nil);
            kaluz.discover();
            strepy.discover();
         }
    }

      dobjFor(Break) asDobjFor(Attack)

Zde ovšem mohu využít pouze příkaz „rozbij kouli sekerou“, jiný nelze.

Nejlépe mým potřebám zatím vyhovuje definice:

    dobjFor(Break)
    {
        verify() {}
        check()
        {
            if (!sekera.isIn(me))
                failCheck('Kouli holýma rukama nerozbiješ. ');
        }
        action()
        {
            "Rozbil jsi skleněnou kouli sekerou, kapalina vytekla na zem a všude kolem jsou rozházené střepy. ";
            sklenena_koule.moveInto(nil);
            kaluz.discover();
            strepy.discover();
         }
    }
      dobjFor(Attack) asDobjFor(Break)

Kde mohu použít „rozbij kouli“ nebo „zaútoč na kouli sekerou“, nefunguje pouze „rozbij kouli sekerou“.
Píšeš ovšem, že definice dobjFor(AttackWith) asDobjFor(Break) není správná, protože melze přesměrovávat akce se dvěma objekty na akci s jedním objektem. Pokud řádek zakomentuji, mohu využít „rozbij kouli“, ovšem „zaútoč na kouli sekerou“ už samozřejmě nefunguje.
Pokud využiji u své konstrukce dobjFor(Break) řádek dobjFor(Attack) asDobjFor(Break), nyní mohu použít „rozbij kouli“ a také „zaútoč na kouli“, jen nesmím vypisovat čím.
Pokud využiji Tvojí konstrukci dobjFor(AttackWith) a řádek dobjFor(Attack) asDobjFor(Break), mohu pouze „zautoc na kouli sekerou“.

Zkoušel jsem definovat nové Verbrule a vyzkoušel u své i Tvé konstrukce:

VerbRule(rozbij_cim)
    ('rozbij' | 'rozbít') singleDobj singleIobj
    | ('rozbij' | 'rozbít') singleIobj singleDobj
    : AttackWithAction
    verbPhrase = 'rozbít/rozbíj{íš}/rozbil{a} (co) (čím)'
    isPrepositionalPhrasing = nil
    omitIobjInDobjQuery = true
    askDobjResponseProd = singleNoun
    askIobjResponseProd = singleNoun
;

Výsledek je trochu jiný, ale stále neuspokojí:

rozbij kouli sekerou
Skleněnou koulí nemůžeš zaútočit.

Zatím tedy ponechám mojí konstrukci dobjFor(Break), která umožňuje rozbít nebo zaútočit na kouli v případě, že má hráč v inventáři sekeru, jen není možné napsat „rozbij kouli sekerou“ nebo „zautoc na kouli sekerou“. Sakra, tohle není sranda, budeš-li mít možnost, prosím jukni na mojí definici.

5. Templates
Výborně, soubor TemplatesQref.PDF se bude hodit, mám uloženo.

6. Seno
Metodu dobjFor(Burn) jsem změnil na dobjFor(BurnWith) a musel jsem trochu upravit definici pochodně. Ano, používat název třídy určitě není vhodné, pojmenoval jsem jí jako pochodeň a přidal PreferredIobj podobně jako u křesadla. Metodu iobjFor(BurnWith) jsem prozkoumal v Library, v tomto případě je na rozdíl od křesadla vhodné použít preCond = [objHeld, objBurning], zkoušel jsem do hranatých závorek „isLit“, to ale není možné a manuál vše krásně popisuje. Díky za upozornění, měl jsem chybu v definici dobjFor(BurnWith) s sena – chybné pochoden.isLit jsem změnil  na !pochoden.isLit a nyní je zapálení sena konečně v suchu.

7. Namočení jehly a VerbRule
V mém případě namáčím co - jehlu, což je dobj kam - do kaluže, což je iobj, jasné. Ano, vím že akce PutIn předmět položí a hráč jej musí zdvihnout, což mi zde nevadí, píšeš, aby mě to u jiných situací nepřekvapilo, myslíš tedy např. tehdy, když budu potřebovat, aby hráč předmět z inventáře nepoložil a po např. po namočení v jedu mu tam zůstal?
Definoval jsem 3 nové VerbRule, nyní mohu využít příkaz „namoč jehlu“, „namoč jehlu v kaluži“ a „namoč jehlu do kaluže “ - jasné, zapomněl jsem definovat prázdnou předložku, kterou si TADS doplní, v a do, upravil jsem i předchozí  VerbRule, které jsem dříve definoval se správnými koncovkami.

Je toho jak vidím ještě stále dosti k dodělání, to ale nevadí, na téhle hře se mnohé naučím a tak to má být.

Krásný víkend přeje Orel

Kroužící orel

  • Plný člen
  • ***
  • Příspěvků: 136
    • Zobrazit profil
    • Šťastný statek
Re: Seriál o programování textových her v TADS 3
« Odpověď #110 kdy: 5. Prosinec 2017 - 12:08 »

Nyní pokračuji v tvorbě, hru už lze po sebrání všech čtyř drahokamů a projitím branou dokončit, závěr hry jsem připravil pomocí kombinace TravelMessage a finishGameMsg. Kromě malé bolístky u rozbití koule, kterou jsem popisoval v minulém příspěvku se nyní setkávám s pro mě zatím zvláštním chováním u draka i psa:


1. pokud je drak zabitý pomocí otrávené jehly, vše je OK:

>přeřízni draka
Čím ho chceš uříznout?

>sekerou
Odsekl jsi dračí hlavy od těla.

>vezmi hlavy
Hotovo.


2. pokud drak žije a snažím se použít příkaz přeřízni, zde je zvláštní chyba:

>přeřízni draka
Řezat spícího draka určitě není dobrý nápad. Řezat spícího draka určitě není dobrý nápad. Řezat spícího draka určitě není dobrý nápad. Řezat spícího draka určitě není dobrý nápad. Čím ho chceš uříznout?


>přeřízni draka sekerou
Řezat spícího draka určitě není dobrý nápad. Řezat spícího draka určitě není dobrý nápad. Řezat spícího draka určitě není dobrý nápad. Řezat spícího draka určitě není dobrý nápad. Odsekl jsi dračí hlavy od těla.

// a hráč získá hlavy, což by neměl


3. pokud je drak správně zabit otrávenou jehlou, odříznu mu hlavy a nabízím je psovi, vše je OK - viz příklad č. 1:

>dej psovi hlavy
Pes se lačně vrhl na otrávené dračí hlavy. Na jeho předsmrtnou agánii nebyl pěkný pohled.

>v
Opatrně procházíš kolem mrtvého psa dál na východ.


4. pokud drak není správně zabit a tak jako tak seberu dračí hlavy, zde je zvláštní chyba - viz příklad č. 2:

>dej hlavy psovi
Tím se psovi určitě nezavděčíš. Tím se psovi určitě nezavděčíš. Tím se psovi určitě nezavděčíš.
Pes se lačně vrhl na otrávené dračí hlavy. Na jeho předsmrtnou agánii nebyl pěkný pohled.

>v
Opatrně procházíš kolem mrtvého psa dál na východ.

5. pokud psovi v každém případě dám něco jiného a je jedno, zda v inventáři mám nebo nemám dračí hlavy, je zde také zvláštní chyba:

>dej psovi křesadlo
Tím se psovi určitě nezavděčíš. Pes se lačně vrhl na otrávené dračí hlavy. Na jeho předsmrtnou agánii nebyl pěkný pohled.


Tady pravděpodobně nesprávně definuji ty 2 ify v metodě dobjFor(CutWith) u draka a iobjFor(GiveTo) u psa, vše jsem kontroloval v Library a lze bez chyb přeložil. V příloze zasílám aktuální zdroják včetně transkriptu, který končí severně od místnosti s drakem, můžeš se prosím juknout na mé definice? Nevím, zda ty dva ify jsou správné řešení a také psa nechci zabít, ale odstranit jej pomocí podání otrávených dračích hlav, napadla mě metoda dobjFor(CutWith).

gaspoda

  • Plný člen
  • ***
  • Příspěvků: 114
    • Zobrazit profil
Re: Seriál o programování textových her v TADS 3
« Odpověď #111 kdy: 5. Prosinec 2017 - 13:01 »
1. pokud je drak zabitý pomocí otrávené jehly, vše je OK:
2. pokud drak žije a snažím se použít příkaz přeřízni, zde je zvláštní chyba:
3. pokud je drak správně zabit otrávenou jehlou, odříznu mu hlavy a nabízím je psovi, vše je OK - viz příklad č. 1:
4. pokud drak není správně zabit a tak jako tak seberu dračí hlavy, zde je zvláštní chyba - viz příklad č. 2:

To bysis tedy měl zopakovat, co jsem psal v kapitole o verify v článku http://www.textovky.cz/clanky/programovani-textovych-her-v-tads-3-cast-5-akce/. Tohle bys už měl umět!

5. pokud psovi v každém případě dám něco jiného a je jedno, zda v inventáři mám nebo nemám dračí hlavy, je zde také zvláštní chyba:

Tohle bude pro tebe asi trochu nové. S postavami se zachází jinak, témata konverzace (a předání předmětu stejně, jako zeptání se nebo mluvení o něčem je také konverzací) se přidávají do postav ve formě Topic objektů. Celý mechanismus interakce s postavami je už naprogramovaný, takže není důvod snažit se akci GiveTo apod. přeprogramovat po svém. Začti se do http://www.textovky.cz/clanky/programovani-textovych-her-v-tads-3-cast-6-npc-postavy/ a tam uvidíš příklady, jak s postavou komunikovat. Chceš-li postavě dát nějaký předmět, tak naprogramuješ objekt třídy GiveTopic, tedy téma konverace, které odehrává předání předmětu. Tento objekt pomocí plus umístíš do postavy, která má objekt dostat.

gaspoda

  • Plný člen
  • ***
  • Příspěvků: 114
    • Zobrazit profil
Re: Seriál o programování textových her v TADS 3
« Odpověď #112 kdy: 5. Prosinec 2017 - 22:43 »
1. Ještě jednou k metodikám a rychlým aktualizacím

...nebyli poté rádi, že si dokumentaci odbyli právě tak zjedodušeně jak píšeš a měli pocit hotové práce?

Možná byli rádi a já bych to i dovedl pochopit - v komerční sféře bývá frmol a musí se prioritizovat, je přeci potřeba vydělat na platy managorů a tak dokumentace musí ustoupit do pozadí. Ale takový je svět, já to chápu, jen mě zaráží, když narazím na nějakého free cool a in týpka, který do světa hlášá, že je to tak správně a že ta jeho dokumentace je příkladná :-)

V Library si klidnu v levém sloupci na P a naleznu řádek PutBehindAction - class in actions.t[1939]. Rozkliknu si PutBehindAction a následně příklad pod číslem 1939. Vidím však jen následující definici:

DefineTIAction(PutBehind)
;

Když půjdeš na akce tudy, tak se moc nedozvíš. To, co tam vidíš, je interní struktura parseru, ale ta není pro programátora hry vůbec zajímavá a k ničemu užitečnému se v ní nedoklikáš. Je to zkrátka ta část programu, která říká, jak poznat ten příkaz, když ho hráč napíše, ale už se tam neřeší, jak příkaz vykonat.

Je mi jasné, že vše naleznu v popisu třídy Thing, to ale vím až teď od Tebe a samotného by mě nenapadlo spávné hledání. OK, zatím si tedy grepnu řetězec "PutBehind" action a nechám se překvapit, co získám. Konečně mě příkaz:

grep -Ri ""PutBehind action"" ./

Fulltextové hledání se někdy také hodí. Ale u těch akcí tě zajímá hlavně implemetace dobjFor resp iobjFor a to můžeš v referenční příručce také najít. Chce to si najít třídu objektu, který tě zajímá. Třeba když máš obyčejný Thing objekt, tak si v manuálu najdeš třídu Thing a budeš hledat na stránce dobjFor(PutBehind). Nebo když je to nějaký konkrétní potomek, třeba kaluž je Hidden, Immovable, RestrictedContainer, tak postupně projdeš uvedené třídy od leva a hledáš, kde najdeš patřičný dobj nebo iobjFor akce, která tě zajímá. Ono ti to ukáže, jestli je to zpracování akce udělané na té třídě nebo zděděné z některého rodiče a můžeš se rovnou prokliknout na definici.

Třeba v tomto konkrétním případě bys viděl, že Hidden sice dědí dobjFor(PutBehind) z Thing, to je ta základní implementace, která povoluje, že každý objekt může být něčím, co vložíš za něco, ale Immovable přímo definuje dobjFor(PutBehind) (tudíž to dostane přednost před zděděným chováním) a to říká, že nepřemístitelný objekt nemůžeš dát za něco, protože je nepřemístitelný.

zatím se mi však nepodařilo zprovoznit jak příkaz „rozbij kouli sekerou“, tak „zaútoč na kouli sekerou“.
Pokusil jsem se dle Tvého doporučení využít:

    dobjFor(AttackWith)
    {
        check()
        {
            if (gIobj != sekera)
                failCheck('Kouli holýma rukama nerozbiješ. ');
        }
        action()
        {
            "Rozbil jsi skleněnou kouli sekerou, kapalina vytekla na zem a všude kolem jsou rozházené střepy. ";
            sklenena_koule.moveInto(nil);
            kaluz.discover();
            strepy.discover();
         }
    }

      dobjFor(Break) asDobjFor(Attack)

Zde ovšem mohu využít pouze příkaz „rozbij kouli sekerou“, jiný nelze.

Když se dostaneš do situace, že hra řekne:

>rozbij kouli sekerou
Skleněnou koulí nemůžeš zaútočit.

tak je to jenom o tom problému, o kterém jsme se tu už bavili. Hra u příkazů, kde role bobj a iobj není přesně určena předložkou, tak potřebuje označkovat, který objekt je přímým a který nepřímým objektem akce. Takže to, co je výše ukázáno je v pořádku, jen si ještě sekeru označ jako nepřímý objekt tím, že ji přidáš třídu PreferredIobj. Potom bude hra správně reagovat na "rozbij kouli sekerou" i "rozbij sekerou kouli".

Nejlépe mým potřebám zatím vyhovuje definice:

    dobjFor(Break)
    {
        verify() {}
        check()
        {
            if (!sekera.isIn(me))
                failCheck('Kouli holýma rukama nerozbiješ. ');
        }
        action()
        {
            "Rozbil jsi skleněnou kouli sekerou, kapalina vytekla na zem a všude kolem jsou rozházené střepy. ";
            sklenena_koule.moveInto(nil);
            kaluz.discover();
            strepy.discover();
         }
    }
      dobjFor(Attack) asDobjFor(Break)

Kde mohu použít „rozbij kouli“ nebo „zaútoč na kouli sekerou“, nefunguje pouze „rozbij kouli sekerou“.

Použij oboje dohromady, tedy dobjFor(AttackWith) i dobjFor(Break) a  dobjFor(Attack) asDobjFor(Break).

Píšeš ovšem, že definice dobjFor(AttackWith) asDobjFor(Break) není správná, protože melze přesměrovávat akce se dvěma objekty na akci s jedním objektem.

TADS má pak i jiné způsoby přesměrování, kterými to lze udělat.

Zkoušel jsem definovat nové Verbrule a vyzkoušel u své i Tvé konstrukce:

VerbRule(rozbij_cim)
    ('rozbij' | 'rozbít') singleDobj singleIobj
    | ('rozbij' | 'rozbít') singleIobj singleDobj
    : AttackWithAction
    verbPhrase = 'rozbít/rozbíj{íš}/rozbil{a} (co) (čím)'
    isPrepositionalPhrasing = nil
    omitIobjInDobjQuery = true
    askDobjResponseProd = singleNoun
    askIobjResponseProd = singleNoun
;

Jasně, to je dobře, ale viz výše. Akorát z toho vyhoď | ('rozbij' | 'rozbít') singleIobj singleDobj, stačí to tam jen jedním způsobem.

7. Namočení jehly a VerbRule
V mém případě namáčím co - jehlu, což je dobj kam - do kaluže, což je iobj, jasné. Ano, vím že akce PutIn předmět položí a hráč jej musí zdvihnout, což mi zde nevadí, píšeš, aby mě to u jiných situací nepřekvapilo, myslíš tedy např. tehdy, když budu potřebovat, aby hráč předmět z inventáře nepoložil a po např. po namočení v jedu mu tam zůstal?

Resp. ono by k tomu v tomto případě došlo - hráč by nepoložil jehlu, tedy nezůstala by v kaluži a to z toho důvodu, že jsi nezavolal inherited() ze své action(). Právě v action() metodě iobjFor(PutIn) kontejneru je naprogramováno to přemístění objektu a ty to překrýváš svojí verzí action(), která dělá něco jiného. Tím standardní chování potlačuješ. Za normálních okolností bys měl inherited volat skoro vždy s výjímkou těch situací, kdy ho chceš potlačit úmyslně.
[/quote]

Kroužící orel

  • Plný člen
  • ***
  • Příspěvků: 136
    • Zobrazit profil
    • Šťastný statek
Re: Seriál o programování textových her v TADS 3
« Odpověď #113 kdy: 7. Prosinec 2017 - 12:11 »
1. Dračí hlavy a verify
Jé, tak tady jsem se teda opravdu vyznamenal jako nepozornej študák, já vím, popel na mojí hlavu. Omlouvám se za nepozornost, naštěstí oba pracujeme s dětmi, takže snad jsme zvyklí opakovat několikrát jasné věci, sakra, zrovna před mým dotazem jsem si celou kapitolu Akce ve Tvém návodu prošel. Se začátkem tvorby jsem vůbec netušil, o čem je řeč, teď už konečně vnímám např. rozdíl mezi dobjFor a iobjFor a tato kapitola je jasnější, jenže ouha, stále mi vše kolem akcí nedochází. Pro mě je to tedy ta nejobtížnější část tvorby, verify každopádně popisuješ parádně a příklad jsem upravil dle Tvých doporučení, v mém případě je ideální kombinace prázdného verify, které nepřetěžuji, hlavní podmínku, aby byl drak po smrti, kontroluje check a řezat mohu pouze objekty, které to mají povolené pomocí metody iobjFor(CutWith). Zavolal jsem inherited pro volání zdědeného chování a po projití všech těchto kontrol může action() vykonat to, co má.

Tak a hóóóu, já pořádně zpomalím, už se sice těším, až Exotera dokončím, to ale nemá smysl, protože pokud celou metodiku, kterou je naprogramován nepochopím, u další hry se budu jen zbytečně zasekávat. Také jsem zjistil, že u TADSu bude téměř nemožné zcela opisovat styl tvorby textovky, kde univerzální příkaz použij vše řeší – použiju jehlu na draka, on umře, pak použiju sekeru a useknu mu hlavy, jak krásně jednoduché…

Tady ale přeci nemohu nechat draka jen tak, když mu useknu hlavy, musí se objevit jeho bezhlavá mrtvola, která samozřejmě bude mít atribut Immovable se všemi náležitostmi, na které jsi mě upozornil minule, už je to zařízeno. Další věc mě tu pálí, příkaz uřízni/rozřízni draka sekerou opravdu není něco, co by hráče napadlo, i když si u popisu psa může přečíst že k smrti miluje dračí hlavy. Takže budu potřebovat něco jako „uřízni hlavy sekerou“, díval jsem se na zajímavou konstrukci do souboru roomTunnel.t v Základně, kde dvě vozítka rozlišuješ direktivou
disambigName
ta však rozlišuje dvě stejná vozítka, což není můj případ. Takže jsem to vyřešil jednoduše, manipulace drakem = manipulace dračíma hlavama, drak po uříznutí hlav stejně zmizí, takže vše funguje jak má:

      gcName = 'spící drak, spícímu drakovi, spící drak, spícímu drakovi, spícím drakem, dračí hlava, dračím hlavám, dračí hlava, dračím hlavám, dračíma hlavama'
      gcVocab = 'spícímu spícím spícího dračím dračíma dračí drakovi/drakem/draka/hlavám/hlavama/hlavy'

Tímle malým trikem jsem vše vyřešil, jen příkaz:
>uřízni dračí hlavy

Spícím drakem nic nepřeřízneš.
// to chápu, zde parser myslí, že chci něco přeříznout drakem

>uřízni dračí hlavy sekerou
Odsekl jsi dračí hlavy od těla.
// zde je vše OK

Zde mě tedy jako řešení napadá definovat draka jako kontejner podobně jako u strážce, zde ale pravděpodobně také narazím, protože dračí hlavy nemohou jen tak vypadnout z draka po jeho smrti. Druhá možnost je ta, že draka definuji jako dva samostatné objekty např. „dračí hlavy“ a „drak“, pamatuji si, že něco podobného jsem viděl v Erikově manuálu u příkladu s třídou Attachable – jednalo se o kostičky různých barev, které se na sebe skládají jako lego, juknu na ně a zkusím vymyslet vhodnější řešení.

A poslední věc je zabití draka jehlou, zde bych rád také příkaz píchni draka jehlou. Ten ve slovníku zaveden není, přidáním:
VerbRule(pichni_koho)
    ('píchni' | 'píchnout') singleDobj singleIobj
     : AttackWithAction
    verbPhrase = 'píchnout/pích{áš}/píchnul{a} (koho) (čím)'
    askDobjResponseProd = onSingleNoun
    askIobjResponseProd = singleNoun
;

mohu využít „píchni draka jehlou“.

Ha, mohlo by zde třeba být i zrcadlo, do kterého se drak dívá, když si zkouší ty svoje zářící šperky nebo čarovné zbraně a hra by mohla simulovat jak v něm vypadá, když chrní nebo když už je bez hlav plus třída Vaporous bude krásně popisovat ty spráné odlesky od šupin, to si ale opravdu už nechám na příští hru, která se těmito věcmi bude jen hemžit.

2. Pes – příkazy Give to a topic
Aha, nečekal jsem, že příkaz Giveto je spojen s konverzačními tématy, jak vidno, mám dost potíží pochopit akce, upřímně řečeno hovor s postavami jsem chtěl řešit až ve své další hře. To ale nevadí, naštujuji vše už teď.
Ano, už první příklad „Vem Borisův kafovak“ jasně dává najevo, že zde nejde jen o témata rozhovoru, ale o mnohem více. Také s CurState jsem se u postav samozřejmě setkal, netušil jsem, že HermitActorState bude probírán právě v této kapitole, já jsem se k němu dostal přes Erikův manuál… Tak mám dočteno, bezva příklad s tím senilním čarodějem, systém je parádně propracovaný, to jsem ostatně obdivoval už v Základně. Textovkám se přeci jen už v dobách ZX Spektra říkalo konverzační hry, takže toho budu využívat o sto šest. 

Tady musím vše okolo postav pořádně pochopit, v další hře bude klisna, se kterou si chci vysloveně vyhrát, hráč jí bude muset správně vyhřebelcovat, nasedlat, nauzdit a nakonec si s ní i pokecat – možná se z řechtandy vyklube pořádná fešanda, ještě tedy nevím, zda to bude princezna, vědma, stará babička nebo třeba zakletá dračice, určitě ale bude stát za to.

Systém rozhovorů bude nebude tedy o mnoho jednodušší, než akce, ufff, možnosti krásné, ale za jakou cenu. Nyní v T3TourGuide čtu na straně 219 přesně to, o čem píšeš, příkaz iobjFor(GiveTo) není vhodný při interakaci s NPC postavami.
V Learning T3 je poměrně jednoduchý příklad na straně 220, jen u mě bude jeho použití náročnější, protože postřebuji psa, který si vezme otrávené dračí hlavy, zabít. Nejvhodnější mi přijde definice:

+ GiveTopic
     matchObj = draci_hlavy
    topicResponse()
    {
      "Pes se lačně vrhl na otrávené dračí hlavy. Na jeho předsmrtnou agánii nebyl pěkný pohled. ";
      draci_hlavy.moveInto(nil);
      setCurState(pesDeadState);
    }
;

která ovšem vylučuje použití canObjReachSelf(obj), canObjReachContents(obj) a cannotReachFromOutsideMsg(dest), debugger v takovém případě píše, že „The symbol "canObjReachSelf" is already defined“ a u ostatních rovněž. Pokud tyto tři direktivy vymažu, kompilace proběhne, ale pes o dračí hlavy nejeví zájem „Obrovský pes vypadáš nezaujatě.“.

Příklad včetně transkriptu zasílám v příloze, ještě se juknu na definici GiveTopic do Library, snad zjistím, proč se toto děje, hledal jsem např. situaci, kdy hráč někomu podá jed a NPC jej sní a umře, něco podobného je např. ve hře Trollbridge, její definice mi však nepomohla.

3. Hledání v Library
Ano, nyní vždy budu hledat třídu objektu, o který mám zájem, v levém sloupci jsem si zobrazil Classes a např. Hidden, po kliknutí na odkaz 906 si mohu prohlížet dojb akce. Vidím, že Hidden dědí pouze z Thing a Immovable z NonPortable a ta zase z Thing. Rozkliknul jsem si např. UntakeableActor, který mě zajímá, dědí z Actor a ten z Thing.
U Hidden a Immovable jsem si vyhledal všechny metody dobjFor(PutBehind), vše mě odkazuje do object.t, jasně, Immovable dostane přednost před jakýmkoliv zděděným chováním.

4. Rozbití koule sekerou
Ano, o PreferredIobj jsme se bavili, využil jsem jej u pochodně i křesadla. Nyní jsem předefinoval sekeru a příkaz rozbij sekerou kouli i naopak už parádně funguje. Všechnu naši konverzaci mám uloženou, jak vidno, je nejvyšší čas vypíchnout z ní zajímavé dotazy a odpovědi a vše si vytisknout stejně jako Tvůj manuál a Learning T3, udělám to hned.

5. Jehla
Ano, moje action překrývá standardních chování, já chci, aby stará jehla zmizela a objevila se nová otrávená, kterou hráč musí sebrat. Pokud bych chtěl jehlu nechat v inventáři, vymažu řádky jehla.moveInto(nil), otravena_jehla.discover(); a zavolám inherited pro zdědění standardního chování, jasné.

Nebudu tedy spěchat, ještě si pohraju s drakem a pořádně nastuduji konverzaci s NPC. Dál bych si rád zprovoznil lokální webový server, konečně jsem i v Archu zprovoznil QTADS, stabilní verze s novějším kompilátorem mi nechodí, ale ta z gitu ano, přidám si jeho volání do Creatoru tak jak popisuješ. Díval jsem se na sebové hraní, manuál popisuje umístění binárního t3 souboru nejprve na IFArchive, nalezl jsem odkaz:

https://www.ifarchive.org/cgi-bin/upload.py

a následně prolinkování v mém profilu na IFDB, který jsem si vytvořil. To bude paráda, čeká mě doladění draka a psa, slovníky, překlepy, intro, možná malá nápověda apod., ale také bych rád doplnil komentáře tak jako to máš u Základny. Nakonec si vše rozběhám na lokálním webu a poté vložím do IFArchivu, při tom mohu tvořit nejprve kostru nové hry, ostatně jak píšeš v Tipech pro tvorbu vlastní hry, musím si připravit osnovu a strukturu příběhu a na to si některý ten pátek nechat, to nelze uspěchat. 

gaspoda

  • Plný člen
  • ***
  • Příspěvků: 114
    • Zobrazit profil
Re: Seriál o programování textových her v TADS 3
« Odpověď #114 kdy: 8. Prosinec 2017 - 21:00 »
1. Dračí hlavy a verify
...v mém případě je ideální kombinace prázdného verify, které nepřetěžuji, hlavní podmínku, aby byl drak po smrti, kontroluje check a řezat mohu pouze objekty, které to mají povolené pomocí metody iobjFor(CutWith). Zavolal jsem inherited pro volání zdědeného chování a po projití všech těchto kontrol může action() vykonat to, co má.

Jen ještě poznámka k tomu minulému, narážel jsem na to, že ve verify se nikdy nesmí zobrazovat žádný text napřímo, veškerá odmítnutí se provádjí jen přes volání některého illogical makra.

Tady ale přeci nemohu nechat draka jen tak, když mu useknu hlavy, musí se objevit jeho bezhlavá mrtvola, která samozřejmě bude mít atribut Immovable se všemi náležitostmi, na které jsi mě upozornil minule, už je to zařízeno.

Také se dá použít např. jednoduše Heavy, pokud bys nechtěl se psát s těmi cannotTakeMsg.

Takže jsem to vyřešil jednoduše, manipulace drakem = manipulace dračíma hlavama, drak po uříznutí hlav stejně zmizí, takže vše funguje jak má:

      gcName = 'spící drak, spícímu drakovi, spící drak, spícímu drakovi, spícím drakem, dračí hlava, dračím hlavám, dračí hlava, dračím hlavám, dračíma hlavama'
      gcVocab = 'spícímu spícím spícího dračím dračíma dračí drakovi/drakem/draka/hlavám/hlavama/hlavy'

V gcName bys neměl kombinovat dva objekty, je třeba zadat právě šest pádů oddělených čárkou, na nic dalšího nebude brán zřetel. Lepší bude hlavy udělat jako samostatný objekt třídy Component a pomocí kontejnerové hierarchie je dát do draka, čili použít u hlav o jedno plus více, než u draka.

Tímle malým trikem jsem vše vyřešil, jen příkaz:
>uřízni dračí hlavy

Spícím drakem nic nepřeřízneš.
// to chápu, zde parser myslí, že chci něco přeříznout drakem

Protože to je uřízni co čím, tak se slovo dračí pochopí jako co budeš řezat a slovo hlavy jako čím budeš řezat. Asi. Protože v TADSu je jen akce CutWith, co chce dva objekty a ne Cut s jedním objektem. Hm, musel bych to víc prozkoumat. Někdy to definování akcí může být trochu neprakticky složité a pro jednorázové výjimky pak mlže být praktičtější využít String PreParser, jak jsem naznačoval myslím v posledním článku.

Zde mě tedy jako řešení napadá definovat draka jako kontejner podobně jako u strážce, zde ale pravděpodobně také narazím, protože dračí hlavy nemohou jen tak vypadnout z draka po jeho smrti. Druhá možnost je ta, že draka definuji jako dva samostatné objekty např. „dračí hlavy“ a „drak“, pamatuji si, že něco podobného jsem viděl v Erikově manuálu u příkladu s třídou Attachable – jednalo se o kostičky různých barev, které se na sebe skládají jako lego, juknu na ně a zkusím vymyslet vhodnější řešení.

Já bych asi useknuté hlavy definoval jako samostatný objekt, který se v místnosti objeví po té, co draka zabiješ. Attachable bych se asi vyhnul, to je v tomto případě kanón na vrabce.

Systém rozhovorů bude nebude tedy o mnoho jednodušší, než akce, ufff, možnosti krásné, ale za jakou cenu.

Za cenu, že bude tvůj mozek trochu zavařenější a z uší budou stoupat obláčky páry - já jsem ochotný to risknout :-) Možnosti rozhovorů jsou dost rozsáhlé, takže bude chvíli trvat, než všechny části pochopíš, ale zase na druhou stranu si z velké části vystačíš s deklarativním přístupem, nebudeš muset psát skoro žádné příkazy, jen definuješ hodnoty vlastností.

V Learning T3 je poměrně jednoduchý příklad na straně 220, jen u mě bude jeho použití náročnější, protože postřebuji psa, který si vezme otrávené dračí hlavy, zabít. Nejvhodnější mi přijde definice:

+ GiveTopic
     matchObj = draci_hlavy
    topicResponse()
    {
      "Pes se lačně vrhl na otrávené dračí hlavy. Na jeho předsmrtnou agánii nebyl pěkný pohled. ";
      draci_hlavy.moveInto(nil);
      setCurState(pesDeadState);
    }
;

která ovšem vylučuje použití canObjReachSelf(obj), canObjReachContents(obj) a cannotReachFromOutsideMsg(dest), debugger v takovém případě píše, že „The symbol "canObjReachSelf" is already defined“ a u ostatních rovněž.

Na to b téma konverzace stejně nereagovalo, aktivita témat se řeší jinak, viz níže. Ale uvedenou chybu to hlásí asi proto, že jsi udělal nějakou jinou chybu, kterou teď přímo nevidím, možná jsi měl objekt ukončený středníkem a ty canObj... za ním.

Pokud tyto tři direktivy vymažu, kompilace proběhne, ale pes o dračí hlavy nejeví zájem „Obrovský pes vypadáš nezaujatě.“.

Musíš dodržet správnou kontejnerovou hierarchii. Máš místnost a v ní je pes. Tedy před psem máš jedno znaménko plus, což psa umisťuje do místnosti, před kterou znaménko plus není. A podle stejné logiky musíš pokračovat dál - téma konverzace potřebuješ umístit do postavy. Tedy musíš mu dát dvě znaménka plus, ale ty tam máš jen jedno. Když dáš jedno, je téma umístěno v místnosti vedle psa, ale tam nijak nereaguje. (A co hůř, rozbil sis tím navíc i stavy psa, protože ty jsou teď umístěné nikoliv ve psovi, ale v GiveTopicu.)

Kód: [Vybrat]
podzemi: Room 'Podzemí' 'do podzemí';
+ pes : UntakeableActor 'obrovský pes' 'obrovský pes' *1;
++ GiveTopic;

Potom by se to mělo ještě trochu vylepšit, to téma totiž bude fungovat i po smrti psa, což není správné. Ty bys chtěl, aby fungovalo, jen dokud je pes živý. Mohl bys do tématu přidat vlastnost isActive = pes.curState == pesGuardState, ale ještě lepší a elegantnější řešení bude GiveTopic dát rovnou do toho stavu, ve kterém má být aktivní a tím pádem pak ve stavu mrtvoly vůbec aktivní nebude:

Kód: [Vybrat]
podzemi: Room 'Podzemí' 'do podzemí';
+ pes : UntakeableActor 'obrovský pes' 'obrovský pes' *1;
++ pesGuardState: HermitActorState;
+++ GiveTopic;
++ pesDeadState: HermitActorState;

No a nakonec to bude chtít vyřešit ještě jeden problém a sice že HermitActorState má za úkol, aby postava na žádné konverzace nereagovala. Bude potřeba to změnit na normální ActorState a asi pak přidat DefaultAnyTopic, který bude reagovat na všechny jiné konverzační příazy, než ten GiveTopic.

Kroužící orel

  • Plný člen
  • ***
  • Příspěvků: 136
    • Zobrazit profil
    • Šťastný statek
Re: Seriál o programování textových her v TADS 3
« Odpověď #115 kdy: 11. Prosinec 2017 - 12:53 »
Dračí hlavy a CutWith:

Ano, já jsem se opravdu snažil do verify přímo vkládat text, což není dovolené, v Library vždy vidím definici makra illogical, kterou se budu řídit. Zkusil jsem definovat mrtvolu draka jako Heavy, paráda, vše funguje tak jak má, mohu tedy využít Immovable s cannotXXXMsg zprávami nebo Heavy, to je přesně ten typ komentáře, který chci vložit do zdrojového kódu, poznačit si, že jsou zde 2 způsoby. Podobně budu postupovat i u dalších objektů a metod, moc se mi líbí komentáře u Základny a rád bych nejen pro sebe něco podobného vytvořil i u Exotera.

Hm, definovat v gcName dva objekty je nesmysl, já jsem si to také myslel, jen jsem zatím neviděl jiné řešení.
Zkoušel jsem v historii naší komunikace dohledat zmínku o String PreParser, nebyl jsem úspěšný, používáš jej v Základně u componentPreparser.t a roomAlien.t pro to, aby parser poznal znaky obsahující číslici zadanou od hráče společně s textovým řetězcem s nebo bez mezer, jinde jsem se s ním opravdu nesetkal.

Ale je mi jasné, že definice akce CutWith počítá se dvěma objekty, proto získávám odpověď „Spícím drakem nic nepřeřízneš.“. Attachable určitě využívat nebudu, jen jsem si vzpoměl na tam uvedený příklad s kostičky lega, který bych rád využit v kombinaci drak – mrtvý drak – dračí hlavy.

Stále si mi však nedaří akci s CutWith zdárně doknočit. Spící drak má jediné +, rozumím, že hlavy by měly být definované jako komponenta uvnitř draka, takže definuji:

++ draci_hlavy : Hidden, Component
++ drak_bez_hlavy : Hidden, Immovable

Akci CutWith jsem zkoušel definovat jak u spícího draka, tak u sebe:
    dobjFor(CutWith)
    {
        preCond = []
              verify() {}

            check()
            {
                        if (gIobj != sekera)
                        failCheck('To bude chtít něco jiného. ');
            }

                action()   

                {
                        "Odsekl jsi dračí hlavy od těla. ";
                        spici_drak.moveInto(nil);
              drak_bez_hlavy.discover();
                        draci_hlavy.moveInto(me);
                }
    }

Plus testuji příkaz draci_hlavy.moveInto(me); stejně jako využíváš objekt Syringe u základny, aby se vrátil do hráčová inventáře, po vykonání ovšem získávám:

>prozkoumej hlavy
Dokud byly živé, vypadaly hrozivě.

>vezmi hlavy
Nemůžeš ji vzít, protože je součástí tebe.

>polož hlavy
Hlavy je součástí tebe.

Dračí hlavy definované jako Component hlásí po sebrání hlášky typu „nemůžeš je sebrat, protože jsou součástí místnoti/draka“ a draci_hlavy.moveInto(me).

Tady opravdu metodu dobjFor(CutWith) nechápu, asi potřebuji reálný příklad, hledal jsem ve zdrojácích TADS her, které zde mám fulltextem, Základna, Heidi, Tell, Cloak_of_Darkness, Return to Ditch Day, The Elysium Enigma, The Plant, nikdo řezání něčeho něčím nevyužívá a ani s iobjFor(CutWith) to není jiné, opravdu zvláštní.
Našel jsem jen:

http://tads3.gerynarsabode.org/index.php?title=Fuses_Example_Game&action=edit

zde je příklad s drátem, jehož definice není zrovna to, co potřebuji

a

http://users.ox.ac.uk/~manc0049/TADSGuide/trollbridge2.htm

využívající adv3lite, což nemohu použít.

Koukám na čas, tímhle se trápím už přes 4 hodiny, ufff, musím na vzduch si vydechnout, nabrat síly a zařídit dětské akce. Jestli budeš mít čas a chuť na to juknout, můžeš využít minulý transkript a aktuální zdroják zasílám v příloze. Večer nebo zítra juknu na ty konverzace u psa a dám vědět jak dopadly.

Kroužící orel

  • Plný člen
  • ***
  • Příspěvků: 136
    • Zobrazit profil
    • Šťastný statek
Re: Seriál o programování textových her v TADS 3
« Odpověď #116 kdy: 15. Prosinec 2017 - 10:11 »
Rozhovory a GiveTopic

Ještě jsem si trochu hrál s řezáním hlav a zatím se dál nemohu dostat, nevadí, nyní to nechám být a hlavy dočasně upravím tak, aby je bylo možné bez potíží sebrat a než mě napadnou další možnosti, dám si „rozhovor“ se psem.

Vrhnul jsem se na velké téma rozhovory, jen ať mi ze škeble kouří jako z toho milíře u Heidi, určitě za to budou stát. Díky těm navrhnutým tématům a možnostem zeptat se na něco jiného hra vypadá mnohem realističtěji, všiml jsem si, že pár her, které zde mám i se zdrojáky, jsou právě rozhovorem zakončeny. Tady bude obrovská možnost hru notně oživit, u Základny i Heidi jsou parádní příklady, které využiji při další tvorbě.

Zkoušel jsem si ještě před změnami u rozhovoru pohrát s chybou u třech "can", která vypisovala „The symbol "canObjReachSelf" is already defined“, skutečně se jednalo jen o středník navíc, který jsem měl umístěný po řádku

+ GiveTopic @draci_hlavy

Chybu už vidím, středník zde opravdu nesmí být, ani např. po tom pro mě zatím oříšku dobjFor(CutWith) u draka není, sakra, na ty středníky musím dát bacha, parser poté celou definici chápe ouplně jinak.

Ano, také u rozhovorů musím dodržet hierarchii kontejnerů, jdu to upravit. Mám zde místnost a objekt psa s jedním znaménkem, OK. Aha, pokud v tomto případě dám jediné plus do téma konverzace, je toto umístěno v místnosti a já tedy komunikuji s místností… jasně, tam tedy musí být znaménka dvě. Hmm, teď po vymazání středníku u GiveTopis vidím, že stavy psa by v případě jediného plus byly na něj navázány, to samozřejmě nechci. Hmm, teď už mi rozhovory konečně začínají zacvakávat a chápu, že není tak špatný nápad je u GiveTo používat, jsou krásně zapasované do objektu NPC a ty zase díky kontejnerové hierarchii do místností, což obstarává objektový model, tady asi zatím nejlépe vidím jeho výhodu – jo, právě čtu ve Tvém manuálu, že někteří autoři textovek uváděli zbytečnost kontejnerové hierarchie a zde konečně v praxi vidím opak, super, přesně to jsem potřeboval.

Vyzkoušel jsem první způsob s isActive:

++ GiveTopic
     isActive = pes.curState == pesGuardState
     matchObj = draci_hlavy
    topicResponse()
    {
      "Pes se lačně vrhl na otrávené dračí hlavy. Na jeho předsmrtnou agánii nebyl pěkný pohled. ";
      draci_hlavy.moveInto(nil);
      setCurState(pesDeadState);
    }


canObjReachSelf(obj) { return curState != pesDeadState; }
canObjReachContents(obj) { return curState != pesDeadState; }
cannotReachFromOutsideMsg(dest) { return 'Nepřipadá Ti vhodné dotýkat se psí mrtvoly!'; }
;

++ pesGuardState: HermitActorState
    noResponse = "Vrčí na Tebe a viditelně Tě nechce pustit dál. "
    stateDesc = "Vrčí na Tebe a viditelně Tě nechce pustit dál. "
    isInitState = true
;

++ pesDeadState: HermitActorState
noResponse = "Je mrtvý a už se nehýbe. "
specialDesc = "Pes leží mrtvý na podlaze. "
stateDesc = "Pes je mrtvý. "
;


nebo druhý, kde je pouze pozměněn ++ GiveTopic @draci_hlavy


a zatím získávám stále hlášku:

>dej hlavy psovi
Vrčí na Tebe a viditelně Tě nechce pustit dál.

>dej psovi hlavy
Vrčí na Tebe a viditelně Tě nechce pustit dál.


Nyní zkouším odstranit HermitActorState z PesGuardState, potřebuji, aby pes reagoval na konverzaci. Kód vypadá takto:


+ pes : UntakeableActor 'obrovský pes' 'obrovský pes' *1
      "Vidíš mu na očích, že kromě lidského masa k smrti miluje dračí hlavy. "
     
      gcName = 'obrovský pes, obrovskému psovi, obrovský pes, obrovském psovi, obrovským psem'
      gcVocab = 'obrovskému obrovským obrovského psovi/psem/psa'

      allowAttack = nil

canObjReachSelf(obj) { return curState != pesDeadState; }
canObjReachContents(obj) { return curState != pesDeadState; }
cannotReachFromOutsideMsg(dest) { return 'Nepřipadá Ti vhodné dotýkat se psí mrtvoly!'; }
;

++ pesGuardState: ActorState
    noResponse = "Vrčí na Tebe a viditelně Tě nechce pustit dál. "
    stateDesc = "Vrčí na Tebe a viditelně Tě nechce pustit dál. "
    isInitState = true
;

+++ GiveTopic
     matchObj = draci_hlavy
//     isActive = pes.curState == pesGuardState
    topicResponse()
    {
      "Pes se lačně vrhl na otrávené dračí hlavy. Na jeho předsmrtnou agánii nebyl pěkný pohled. ";
      draci_hlavy.moveInto(nil);
      setCurState(pesDeadState);
    }
;

++ pesDeadState: HermitActorState
noResponse = "Je mrtvý a už se nehýbe. "
specialDesc = "Pes leží mrtvý na podlaze. "
stateDesc = "Pes je mrtvý. "
;

Po nabídnutí dračích hlav se objeví správné hlášení „Pes se lačně vrhl na otrávené dračí hlavy. Na jeho předsmrtnou agánii nebyl pěkný pohled.“, ale stále žije i přes direktivu setCurState(pesDeadState) nebo pes.curState == pesDeadState uvnitř topicResponse(). Překladač ovšem chybu nevyhodí, vypadá to, že jí v topic nebudu moci využít. Zkusil jsem si přidat:

+++ DefaultAnyTopic
    "<.p>Pes nereaguje. "

    deferToEntry(entry) { return !entry.ofKind(DefaultTopic); }
;

který správně funguje, pokud chci psovi dát něco jiného, než dračí hlavy.

Napadlo mě ještě jedno řešení, přidal jsem objekt mrtvolu psa do GiveTopic direktivy pes.moveInto(nil) a mrtvola_psa.discover(). Nyní je vše OK, jen mě pes stále zabíjí vzhledem k definované TravelMessage v místnosti. Tu samozřejmě mohu změnit, není zde nějaký elegantnější způsob, jak mrtvolu psa ponechat v místnosti a moci odejít na východ?

Uff, podle zdrojáků mi vše přijde OK, tady se asi snažím o něco, co GiveTopic nedovoluje. Ještě počkám na Tvůj názor, pokud to jinak nepůjde, upravím  TravelMessage a nechám psa být, vše zasílám v příloze včetně transkriptu. Nečekal jsem, že se tak seknu u draka i psa, když se dívám na Tvá kouzla v Základně, které parádně komentuješ, ale samozřejmě mi často nejsou jasné, kdo ví, kdy e k jejich pochopení dostanu, to ale určitě časem.

Až budeš mít chvilku, prosím koukni na zdroják a pokud to půjde také na to useknutí dračí hlavy. Je tu ještě spousta věcí k doladění, to ale k programování patří, tuším, že ten TADS nebude o moc jednodušší, než klasické C++…

Krásný víkend přeje
Orel

gaspoda

  • Plný člen
  • ***
  • Příspěvků: 114
    • Zobrazit profil
Re: Seriál o programování textových her v TADS 3
« Odpověď #117 kdy: 15. Prosinec 2017 - 19:35 »
++ GiveTopic
     isActive = pes.curState == pesGuardState
     matchObj = draci_hlavy
    topicResponse()
    {
      "Pes se lačně vrhl na otrávené dračí hlavy. Na jeho předsmrtnou agánii nebyl pěkný pohled. ";
      draci_hlavy.moveInto(nil);
      setCurState(pesDeadState);
    }

canObjReachSelf(obj) { return curState != pesDeadState; }
canObjReachContents(obj) { return curState != pesDeadState; }
cannotReachFromOutsideMsg(dest) { return 'Nepřipadá Ti vhodné dotýkat se psí mrtvoly!'; }
;

No zde by to opět chtělo pes.setCurState(pesDeadState); jak jsme o tom už mluvili, to všechny další problémy vyřeší. Je to pes, jehož stav chceš změnit. Měnit stav můžeš u objektu nějaké postavy. Topic, i když je připojený k postavě, tak nemá takovou metodu.

Jo a canNěco metody na Topicu nemají žádnou funkci, ty mají roli jen u objektů dědících z OutOfReach.

Nyní zkouším odstranit HermitActorState z PesGuardState, potřebuji, aby pes reagoval na konverzaci. Kód vypadá takto:

+ pes : UntakeableActor 'obrovský pes' 'obrovský pes' *1
      "Vidíš mu na očích, že kromě lidského masa k smrti miluje dračí hlavy. "
     
      gcName = 'obrovský pes, obrovskému psovi, obrovský pes, obrovském psovi, obrovským psem'
      gcVocab = 'obrovskému obrovským obrovského psovi/psem/psa'

      allowAttack = nil

canObjReachSelf(obj) { return curState != pesDeadState; }
canObjReachContents(obj) { return curState != pesDeadState; }
cannotReachFromOutsideMsg(dest) { return 'Nepřipadá Ti vhodné dotýkat se psí mrtvoly!'; }
;

++ pesGuardState: ActorState
    noResponse = "Vrčí na Tebe a viditelně Tě nechce pustit dál. "
    stateDesc = "Vrčí na Tebe a viditelně Tě nechce pustit dál. "
    isInitState = true
;

Pokud už to není HermitActorState, tak noResponse je zbytečné, už nemá žádnou funkci. Navíc opět canNěco metody nemají funkci, protože jsi nepřidal OutOfReach do seznamu tříd, ze kterých objekt dědí. Zde to možné je na rozdíl od Topicu výše, který není fyzicky viditelným objektem v rámci světa.

Napadlo mě ještě jedno řešení, přidal jsem objekt mrtvolu psa do GiveTopic direktivy pes.moveInto(nil) a mrtvola_psa.discover(). Nyní je vše OK, jen mě pes stále zabíjí vzhledem k definované TravelMessage v místnosti. Tu samozřejmě mohu změnit, není zde nějaký elegantnější způsob, jak mrtvolu psa ponechat v místnosti a moci odejít na východ?

Jasně, můžeš psa vyměnit za mrtvolu. To může být někdy i výhodné, protože třeba právě mrtvola má ve slovníku slovo "mrtvolu", takže reaguje na prozkoumej mrtvolu. Pokud to uděláš, tak pak podmínka v canTravellerPass může být formulována třeba jako { return !pes.location; } tedy jinými slovy pokud pes byl přemístěn do nil, tak může projít. Pak jsou i jiné možnosti, můžeš si sám pomocí gReveal('pes-mrtvy'); označit, že pes je už kaput a pak se na to ptát { return gRevealed('pes-mrtvy'); } v canTravellerPass.

gaspoda

  • Plný člen
  • ***
  • Příspěvků: 114
    • Zobrazit profil
Re: Seriál o programování textových her v TADS 3
« Odpověď #118 kdy: 15. Prosinec 2017 - 20:12 »
Zkoušel jsem v historii naší komunikace dohledat zmínku o String PreParser, nebyl jsem úspěšný, používáš jej v Základně u componentPreparser.t a roomAlien.t pro to, aby parser poznal znaky obsahující číslici zadanou od hráče společně s textovým řetězcem s nebo bez mezer, jinde jsem se s ním opravdu nesetkal.

StringPreParser je zmíněn v kapitole o akcích mého seriálu http://www.textovky.cz/clanky/programovani-textovych-her-v-tads-3-cast-5-akce/ jako možné řešení pro obejití některých složitých situací, které parser chápe špatně.

Plus testuji příkaz draci_hlavy.moveInto(me); stejně jako využíváš objekt Syringe u základny, aby se vrátil do hráčová inventáře, po vykonání ovšem získávám:

>vezmi hlavy
Nemůžeš ji vzít, protože je součástí tebe.

Jako Component by měly být hlavy jen u živého draka. Jakmile je vyměníš za mrtvolu draka, tak odseknuté hlavy musejí být samostatným objekt, prostě Thing, který se dá sebrat. To je právě smysl třídy Component, aby objekt nešel samostatně sebrat a byl považován za součást toho, v čem je pomocí kontejnerové hierarchie umístěn. Tak např. když mám v základně běžecký pás, jehož součástí je ovládací pultík a součástí pultíku je ovládací knoflík, tak je to nějak takhle:

Kód: [Vybrat]
+ treadmill: Platform, Heavy, OnOffControl 'posuvný běžecký pás' 'běžecký pás' *2;
++ Component 'přítlačné popruhy' 'přítlačné popruhy' *2;
++ treadmillPanel: Component 'ovládací pultík (s) pultík/ovládání/ovládáním' 'pultík s ovládáním' *2;
+++ Component 'počítadlo uběhnuté vzdálenosti/počítadlo' 'počítadlo' *4;
+++ treadmillDial: NumberedDial, Component, OnOffControl 'otočný regulátor' 'regulátor' *2;

A díky tomu, když řekneš seber počítadlo, tak dostaneš odpověď, že to nemůžeš, protože počítadlo je součástí pultíku. Problém tedy v tomto případě není s akcí CutWith, ale s hlavou samotnou.

Tady opravdu metodu dobjFor(CutWith) nechápu, asi potřebuji reálný příklad, hledal jsem ve zdrojácích TADS her, které zde mám fulltextem, Základna, Heidi, Tell, Cloak_of_Darkness, Return to Ditch Day, The Elysium Enigma, The Plant, nikdo řezání něčeho něčím nevyužívá a ani s iobjFor(CutWith) to není jiné, opravdu zvláštní.

Jen čistě ze zvědavosti, Elysium Enigma pokud vím, tak nemá publikované zdrojáky, nepřeřekl ses? Ještě se dají najít zdrojáky k All Hope Abandon a Mrs. Pepper's Nasty Secret.

Koukám na čas, tímhle se trápím už přes 4 hodiny, ufff, musím na vzduch si vydechnout, nabrat síly a zařídit dětské akce. Jestli budeš mít čas a chuť na to juknout, můžeš využít minulý transkript a aktuální zdroják zasílám v příloze. Večer nebo zítra juknu na ty konverzace u psa a dám vědět jak dopadly.

Kroužící orel

  • Plný člen
  • ***
  • Příspěvků: 136
    • Zobrazit profil
    • Šťastný statek
Re: Seriál o programování textových her v TADS 3
« Odpověď #119 kdy: 19. Prosinec 2017 - 17:28 »
1. Pes
Tak konečně se zase dostávám k tvorbě, mám za sebou Ubuntí školení a už se těším na TADS. Já jsem fakt škeble dubová, nastavit

pes.setCurState(pesDeadState);

mě neťuklo, hmm, nerozpoznal jsem, že u Topicu není metoda změnit stav, na kterou jsem zvyklý u postav. Metody canObjxxx mám nyní umístěné u objektu psa, píšeš, že mají smysl pouze u objektů, které dědí z mix-in OutOfReach, jasné, už se na ní koukám do Library a zkoumám dědičnost.

Direktivu noResponse jsem vymazal, ano, mám GuardState nastaven jako ActorState, aby bylo možné se psem komunikovat. U psa jsem také doplnil OutOfReach stejně jako u strážce, už se koukám na naší starší konverzaci, kde mi toto doporučuješ u draka i psa – sakra, u draka jsem na tuto definici nezapomněl, už ani nevím, proč jsem jí nevyužil také u psa.

Výborně, řešení s mrtvolou psa mi také přijde ideální podobně jako u strážce, možnost reakce hráče na slovo mrtvola se určitě hodí. Takže zde ponechám objekt mrtvola_psa. Otestoval jsem si

{return !pes.location; }

v canTravelerPass a funguje stejně dobře jako moje stávající definice

{return pes.curState == pesDeadState; }

viz aktuální zdroják. Definici gReveal jak vidno hojně využíváš v Základně, použití v kombinaci s isInInitState v roomTunnel.t je dost výmluvné a u pronásledovatelů v canTravelerPass stejné jak nyní popisuješ, paráda.

Psa jsem si ještě označil jako PreferredIobj, aby správně fungoval příkaz „dej hlavy psovi“ i „dej psovi hlavy“, ještě musím doladit „dej sekeru psovi“, který vyhazuje „Nemůžeš sekeře nic dát.“, „dej psovi sekeru“ je OK – zde se pokusím o jiné řešení.
Nyní je tedy vše se psem celkem v pořádku a já si uvědomuji zase další souvislosti plus mám parádně vysvětlený příklad, který využiji i v budoucnu.

2. StringPreParser
Hm, na toto řešení jsem nějak zapomněl, i když jsem Tvůj manuál přečetl několikrát. Vidím jej u podkapitole Předzpracování příkazů, velmi zajímavé, přesně toto bych v budoucnu potřeboval od testerů, zjistit příkazy, které by mě třeba nenapadly a Ty jsi samozřejmě postupoval stejně, "zapni zápis" a případné komentáře po zadání hvězdičky si moc dobře pamatuji. Zrovna zde vidím příklad, já bych např. u psa vždy zadal „dej hlavy“ a parser si sám doplní komu, nenapadlo by mě využít slovo podej, které další člověk ale třeba využije. A jak perfektně uvádíš, potřebuji přepsat hráčem zadaný příkaz ještě dříve, než se k němu dostane parser, tohle v budoucnu určitě využiji, třeba i u nápovědy pro hráče podobně jako v Základně.

3. Zapeklité dračí hlavy
Ano, zase jsem u kontejnerové hierarchie a tentokrát přidáváme Component. Udělal jsem jak radíš a výsledek se konečně dostavil, po zabití draka se nestane nic, protože hlavy už jsou s ním v místnosti a hráč to z popisu draka může zjistit, po zabití draka je možné tyto uříznout a následně mrtvola draka a původní dračí hlavy zmizí a objeví se useknuté dračí hlavy s bezhlavým drakem, podobně jsem situaci řešil u strážce, jen bez Component. Uff, to mi teda dalo práce, ale díky Tvým radám jsem zase dál, hurá.

4. Zdrojáky dalších TADS her
Moc se omlouvám za nepřesnost, ke hře Elysium Enigma opravdu zdrojáky nemám, jen jsem při spuštěném fulltextu hledal i tam a nevšimnul si, že mám pouze t3 soubor. All Hope Abandon a Mrs. Pepper's Nasty Secret jsem si už stáhnul a pořádně prozkoumám, sakra, to už je let ten FbeginnersComp 2008…

5. Algoritmus
Už nějaký čas bych se rád zeptal na algoritmus v programování v podání zkušeného programátora. Vím, že je to vlastně kuchařka, kterou si v hlavě nebo v podobě nějakých postupů či diagramů připravím a dle nich poté zapisuji kód pomocí programovacího jazyka. Existují i knihy věnující se pouze algoritmizaci, jak bychom mohli nejlépe definovat algoritmus v TADSu – v tomto případě se jedná o definici místností, kontejnerové hierarchie a akcí, kterou poté pomocí jazyka implementuji do hry? Nevím, zda se nejedná o hloupou otázku, když už se programováním konečně prokousávám, rád bych se ujistil i v tomto.

6. Závěr hry
Je to neuvěřitelné, ale hra je nyní nahrubo hotová a dohratelná. Ještě, než si zprovozním lokální webový server, kde hru otestuji a vložím do IFDB je ale nutné, abych doladil především gramatické chyby, na které reaguje parser a věci, které zbytečně hru kazí, pro zajímavost zasílám v příloze s aktuálním zdrojákem a transkriptem. Na tyto se zaměřím nyní, věřím, že pomocí dosavadních zkušeností, Tvého překladu a Základny je vyřeším. Poté upravím zdrojový kód tak, aby vše bylo čitelné a sjednotím styl syntaxe plus doplním dokumentaci, která bude lépe vysvětlovat jednotlivé kroky.

Některé akce bych měl barevně nebo jiným písmem zvýraznit, např. informaci o pádu do kupky sena by si nepozorný hráč nemusel všimnout:

Vidis tu truhlu.
>d
Spadl jsi primo doprostred kupky sena, ktera idealne ztlumila tvuj pad.

Severovychodni cast nadvori
Stojis v tmavem koute nadvori. Na severu se do vysky nekolika metru tyci
kamenna vez.

Zkusím něco vymyslet, ideálně vhodného pro řádkového klienta i qtads dohromady plus časem pro webovou hru, to uvádíš i ve svém manuálu.

Jsem strašně rád, že jsem vytrval, hlavně, žes mě furt držel, protože jako neprogramátor musím být hrozný žák… Většinu naučených konstrukcí určitě využiji při další tvorbě a Library mi stále více leze pod kůži, což je paráda. S C++ ještě moment počkám, nejprve vše dokončím a pomocí QT Creatoru vytvořím hru novou, tím si znalosti upevním a učit se dalšímu jazku bude mnohem snažší.