Diskuze o textových hrách

Seriál o programování textových her v TADS 3

gaspoda

  • Plný člen
  • ***
    • Příspěvků: 141
    • Zobrazit profil
Pokud by měl být zároveň přímým objektem v nějaké akci, která vyžaduje přímé i nepřímé objekty, a proto nebylo vhodné, že se vždy prohazuje do role nepřímého objektu, tak je tu vždy možnost vykašlat se na mix-in třídu PreferredIobj. Místo toho můžeš nůž podědit z Thing do nějaké své třídy (řekněme class Knife: Thing; + knife: Knife;), aby byl snadno identifikovatelný a modifikovat akci CutWith tak, že ji nastavíš preferredIobj = Knife. Mrkni třeba na konverzační akce GiveTo, ShowTo. Ty mají nastaveno preferredIobj = Actor.


tekket

  • Mladší člen
  • **
    • Příspěvků: 68
    • Zobrazit profil
To je bezva řešení. Elegantnější, než PreferredIobj - kde vždy může nastat problém s tím, že se u každé TI akce přehodí na nepřímý objekt.


tekket

  • Mladší člen
  • **
    • Příspěvků: 68
    • Zobrazit profil
Mimochodem, kdy bude další díl seriálu?

Řeším teď nové VerbRule. Mám dle CutWith, CutWithWhat z knihovny vytvořené RepairWith, RepairWithWhat.

Normálně neúplný příkaz např. oprav nůž hra(přes RepairWithWhat) automaticky doplní vhodným objektem, pokud je přítomný. Já bych spíše chtěl, aby se v podobném případě parser pouze zeptal, pomocí čeho chci nůž opravit.

Když úplně odstraním RepairWithWhat, tak neúplný příkaz vede k tomu, že hra ho nepozná vůbec. Napadlo mě jediné řešení, že bych vytvořil obecnější TAction Repair, ale nejlepší by bylo mít pro příkazy zahrnující přímý a nepřímý předmět řešení s doplňujícím dotazem, ale u některých neumožnit automatické doplnění nepřímého předmětu.


tekket

  • Mladší člen
  • **
    • Příspěvků: 68
    • Zobrazit profil
Ve VerbRule se také často nastavují různé vlastnosti, ale nic, z čeho bych byl moudrý, jsem o nich nenašel. Možná jsem špatně hledal, ale třeba v Library Reference k některým je krátká věta, k některým nic.


gaspoda

  • Plný člen
  • ***
    • Příspěvků: 141
    • Zobrazit profil
Jako obvykle ve čtvrtek v 6.00 ráno. A jak je to s automatickým doplněním nebo naopak nedoplněním objektů v různých situacích a akcích bude vysvětleno ;-)



tekket

  • Mladší člen
  • **
    • Příspěvků: 68
    • Zobrazit profil
Ok, takže nonObvious ve verify objektu to vcelku řeší. Další varianta je mít verzi akce pouze s dobj a vypsat u ní pouze nápovědu/upřesnění jak je příkaz třeba zadat.


gaspoda

  • Plný člen
  • ***
    • Příspěvků: 141
    • Zobrazit profil
Přesně tak, řeší se to pomocí nonObvious. Třeba v naší hře jsme měli tablet, který bylo možné >rozsvítit tablet, ale nechtěli jsme, aby se to stalo jen když hráč napíše samotné >rozsviť:
Kód: [Vybrat]
    dobjFor(Light)
    {
        verify() { nonObvious; }
        action() { replaceAction(Push, tabletButton); }
    }
U akcí se to pak řeší tak, že k jedné akci jsou dvě VerbRule, jedna s badness a místo slotu na iobj dělá v konstruktoru iobjMatch = new EmptyNounPhraseProd(), snadno to někde zahlédneš. Ale úplně do těchhle vnitřností zatím moc nevidím.


tekket

  • Mladší člen
  • **
    • Příspěvků: 68
    • Zobrazit profil
Jo, zkopíroval jsem si pro druhé VerbRule k opravit právě z CutWithWhat, které má badness 500 a řeší to, že když se příkaz nezadá celý, tak se parser hráče dotáže(nebo automaticky doplní, když nemám u akce v objektu nonObvious).

Jen mi přijde zvláštní, že kód v constructu se dle mého tváří, že doplní pouze chybějící iobj, ale podle hry doplní i dobj, pokud chybí.

Kód: [Vybrat]
VerbRule(RepairWithWhat)
    [badness 500] ('oprav' | 'sprav') singleDobj
    : RepairWithAction
    verbPhrase = 'opravit/opravuj{eš}/opravil{a} (co) (čím)'
    construct()
    {
        iobjMatch = new EmptyNounPhraseProd();
        iobjMatch.responseProd = singleNoun;
    }
;

Pořád mi není úplně jasné askDobjResponseProd a askIobjResponseProd (je např. v CutWith, mám podle něj RepairWith) - to má pochopit v jaké podobě očekává doplnění příkazu? Znamená to, že to nějak pracuje ve spojení s CutWithWhat, bez kterého se hra na doplnění vůbec ptát nebude a příkaz prostě nepochopí?


gaspoda

  • Plný člen
  • ***
    • Příspěvků: 141
    • Zobrazit profil
Jen mi přijde zvláštní, že kód v constructu se dle mého tváří, že doplní pouze chybějící iobj, ale podle hry doplní i dobj, pokud chybí.

Kdybys do toho chtěl více nahlédnout, tak je možné kdykoliv zapnout ladění parseru pomocí příkazu libGlobal.parserDebugMode = true; a pak se třeba o příkazu "uřízni", kterému chybí oba objekty, dozvíš, že byl zachycen oběma predikáty CutWith i CutWithWhat a to díky gramatice singleNoun(empty):

Kód: [Vybrat]
>uřízni
———-
firstCommandPhrase(commandOnly) [uřízni]
  commandPhrase(definiteConj) [uřízni]
    predicate(CutWith) [uřízni]
      singleNoun(empty) []
      singleNoun(empty) []
———-
firstCommandPhrase(commandOnly) [uřízni]
  commandPhrase(definiteConj) [uřízni]
    predicate(CutWithWhat) [uřízni]
      singleNoun(empty) []
—– Winner —–
firstCommandPhrase(commandOnly) [uřízni]
  commandPhrase(definiteConj) [uřízni]
    predicate(CutWithWhat) [uřízni]
      singleNoun(empty) []
    (provaz)
Čím ho chceš uříznout?

Z obou vyhrává CutWithWhat, protože má v součtu menší badness, než když se singleNoun(empty) doplnilo dvakrát. Ta gramatika singleNoun(empty) vypadá následovně, najdeš ji někde v hlubinách cs_cz.t:

Kód: [Vybrat]
/*
 *   An empty single noun is one with no words at all.  This is matched
 *   when a command requires a noun list but the player doesn't include
 *   one; this construct has "badness" because we only want to match it
 *   when we have no choice.
 */
grammar singleNoun(empty): [badness 500] : EmptyNounPhraseProd
    /* use a nil responseProd, so that we get the phrasing from the action */
    responseProd = nil

    /* the fallback responseProd, if we can't get one from the action */
    fallbackResponseProd = singleNoun
;

Proč je to takhle udělané, že je v gramatice prázdný slot na objekt i je varianta příkazu bez jednoho objektu, ti neřeknu, nerozumím tomu do takové hloubky. Možná by bez toho nebylo jasné, který objekt chybí, a proto se preferuje chybějící iobj před dobj, ale to opravdu jen spekuluji.

Pořád mi není úplně jasné askDobjResponseProd a askIobjResponseProd (je např. v CutWith, mám podle něj RepairWith) - to má pochopit v jaké podobě očekává doplnění příkazu? Znamená to, že to nějak pracuje ve spojení s CutWithWhat, bez kterého se hra na doplnění vůbec ptát nebude a příkaz prostě nepochopí?

Vlastnosti askDobjResponseProd a askIobjResponseProd ovlivňují to, jakým způsobem bude parser zpracovávat odpověď hráče na otázku ohledně chybějícího objektu. Při odpovědi na otázku "čím chceš liánu uříznout?" hráč asi odpoví "nožem". Některé příkazy ale mohou vybízet k odpovědi obsahující kromě samotného objektu i předložku, např. na otázku "na koho chceš zaútočit?" může hráč odpovědět "na Borise". Výchozí hodnotou obou vlastností je nounList a právě příkazy, u nichž je očekávána možnost, že hráč odpoví s předložkou, mají nastavenou některou z možností inSingleNoun, onSingleNoun a dalších, které jsou někde kolem řádku 6000 v cs_cz.t. U žádné z těchto variant není předložka povinná.


tekket

  • Mladší člen
  • **
    • Příspěvků: 68
    • Zobrazit profil
Citace
Kdybys do toho chtěl více nahlédnout, tak je možné kdykoliv zapnout ladění parseru pomocí příkazu libGlobal.parserDebugMode = true; a pak se třeba o příkazu "uřízni", kterému chybí oba objekty, dozvíš, že byl zachycen oběma predikáty CutWith i CutWithWhat a to díky gramatice singleNoun(empty):

Dobrý tip, vyzkouším. Asi si rovnou přečtu něco o ladění v Tads. Čtu vše víceméně podle aktuální potřeby na přeskáčku.

Citace
Vlastnosti askDobjResponseProd a askIobjResponseProd ovlivňují to, jakým způsobem bude parser zpracovávat odpověď hráče na otázku ohledně chybějícího objektu. Při odpovědi na otázku "čím chceš liánu uříznout?" hráč asi odpoví "nožem". Některé příkazy ale mohou vybízet k odpovědi obsahující kromě samotného objektu i předložku, např. na otázku "na koho chceš zaútočit?" může hráč odpovědět "na Borise". Výchozí hodnotou obou vlastností je nounList a právě příkazy, u nichž je očekávána možnost, že hráč odpoví s předložkou, mají nastavenou některou z možností inSingleNoun, onSingleNoun a dalších, které jsou někde kolem řádku 6000 v cs_cz.t. U žádné z těchto variant není předložka povinná.

To jsem víceméně odhadl dobře, jen mi to nesedělo s tím, že obě vlastnosti jsou definovány v CutWith, ale to pokládání upřesňujícího dotazu funguje díky CutWithWhat - no nechám to zatím být a budu předpokládat, že obě VerbRule prostě nějak pracují dohromady.



gaspoda

  • Plný člen
  • ***
    • Příspěvků: 141
    • Zobrazit profil
Tak zítra následuje neobvykle dlouhý díl o NPC postavách. Doufám, že zaujme, protože konverzační systém v TADS je velice vychytaný.


gaspoda

  • Plný člen
  • ***
    • Příspěvků: 141
    • Zobrazit profil
Tamtadadáá! Dnes konečně vyšel poslední díl a s ním jsem zveřejnil i kompletní zdrojové kódy Základny na asteroidu, první plnohodnotné české textové hry, která v TADS 3 vznikla.


tekket

  • Mladší člen
  • **
    • Příspěvků: 68
    • Zobrazit profil
Super, už jsem si zdroják stáhnul, jen jsem to narychlo prosvištěl, je to hodně rozsáhlé. Na tads teď nějak nezbývá čas, tak snad mezitím všechno nezapomenu.


Kroužící orel

  • Plný člen
  • ***
    • Příspěvků: 205
    • Zobrazit profil
    • Šťastný statek
Je to sice více, než rok od zveřejnění článku pro TADS a kódů pro Základnu, teprve nyní jsem se však dostal k tomu, abych si jej vytiskl a pořádně pročetl. Musím se přiznat, že návod je psaný perfektně a otevřel mi oči, možnosti TADSu jsou opravdu rozsáhlé a rozhodně stojí za to v něm textovku naprogramovat.

Zkoušel jsem nainstalovat kompletní prostředí TADS v Linuxu i Windows XP, poslední verze Workbenche 3.1.3 je alespoň na mých počítadlech dosti nestabilní a ani WINE v Linuxu na tom není jinak, s trochou trpělivosti se však použít dá. Naštěstí Frobtads funguje parádně a i v připraveném Debianu 9 je k dispozici v repozitářích, takže není co řešit. A mimo jiné mě donutí konečně se vrhnout na ovládnutí editoru VIM, poměrně nedávno vyšel balík pro zvýrazňování syntaxe pro TADS, tato kombinace mi umožní programovat i v příkazové řádce téměř na jakémkoliv počítadle.

Po přečtení článku jsem chca nechca musel přehodnotit svůj postoj k tvorbě příběhu i programování, poslední závěrečná kapitola parádně popisuje doporučenou možnost tvorby, je pravda, že jsem zatím žádnou cizojazyčnou textovku nehrál a neměl jsem tedy možnost srovnávání. Ale je to tak v pořádku, člověk se stále učí a bez přiznání svých chyb se zkrátka dále nedostanu.

Takže se vrhnu na to samé, co jsem měl v plánu před několika lety, v TADSu naprogramuji Exotera tak jak byl na Spektru a poté tu samou hru s využitím více jeho možností, následně Kroužícího orla, kde se ještě hodně zamyslím nad úpravou příběhu. Pedrův engine je prostě super a moc se mi líbí, cítím ovšem, že s TADSem budu moci využít nové možnosti plus se mnohé naučit, takže jdu na to.

Moc děkuji za článek, přesně ten jsem potřeboval.