12. Prosinec 2017 - 18:31

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

Kroužící orel

  • Plný člen
  • ***
  • Příspěvků: 126
    • 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ů: 109
    • 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ů: 126
    • 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ů: 109
    • 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ů: 126
    • 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ů: 126
    • 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ů: 109
    • 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ů: 109
    • 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ů: 126
    • 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ů: 109
    • 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ů: 126
    • 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.