16. Prosinec 2018 - 05:12

Zobrazit příspěvky

Tato sekce Vám umožňuje zobrazit všechny příspěvky tohoto uživatele. Prosím uvědomte si, že můžete vidět příspěvky pouze z oblastí Vám přístupných.


Příspěvky - gaspoda

Stran: [1] 2 3 ... 9
1
TADS / Re: akce po určitém počtu tahů
« kdy: 14. Listopad 2018 - 20:06 »
Ano, určitě po inherited, protože ve zděděném chování se provede právě to přemístění na oltář, které následně testuješ. Občas je docela fajn nakouknout do Library Reference Manual (http://www.tads.org/t3doc/doc/libref/index.html), jak jsou naprogramované ty různé akce, tj. d/iobjFor, abys věděl, co vlastně přetěžuješ a kolikrát se tam dočteš zajímavé souvislosti. Např. jestli je něco zakázané checkem nebo verify, jestli je to někam přemapované, jak se to provádí atp.

Jsem rád, že se ti to povedlo a i když jsi se nám snažil namluvit, že nejsi programátor, tak je vidět, že určitě zkušenosti máš. Smím se optat, jestli jsi narazil na nějaké věci, které by potřebovaly lépe vysvětlit, nebo na něco, co tě překvapilo, zaujalo atp.? A pokud tě to ještě úplně neodradilo, tak někdy trošku koukni na pokročilejší možnosti, jako třeba konverzační systém, které dokážou hry posunout přeci jen dál, než bylo zvykem za starých časů.

2
TADS / Re: akce po určitém počtu tahů
« kdy: 13. Listopad 2018 - 20:24 »
DefaultSky jsem neodstranil záměrně. To byla moje chyba. Nevěděl jsem že pokud nadefinuji jedno, musím nadefinovat i ostatní (doufám že si to myslím správně).

Pokud přepíšeš vlastnost roomParts vlastním polem, tak se tím zahodí předchozí hodnota (předchozí pole) zděděné z třídy OutdoorRoom, obsahující výchozí části místnosti. OutdoorRoom má právě jen zem a oblohu na rozdíl od běžné místnosti. Jestliže chceš některou součást místnosti vyměnit za svojí upravenou, pak jsou dva způsoby. Buď napíšeš nové pole a uvedeš všechny části, tj. tvojí zem i původní oblohu a nebo je i jiný zápis, kterým přidáš svojí zem a odebereš tu výchozí, přičemž obloha zůstane zachovaná:

Kód: [Vybrat]
roomParts = [m_Zem_v_oaze, defaultSky]
roomParts = static inherited - defaultGround + m_Zem_v_oaze

Předmět "p_Baterie" nadefinovaný jako "Wearable" je blbost. Opravil jsem. (zůstalo mi to tam při kopírování z jiného předmětu)

To já samozřejmě chápu: http://hackles.org/cgi-bin/archives.pl?request=37  :D

U dveří "d_Hrobka" je iobjFor(PutOn) opravdu navíc. Ale afterAction() způsobí otevření cesty směrem dolů. Když to dám jinam tak to nejde. Asi by mělo být něco jiného než afterAction(), ale nevím co. Každopádně takto je to funkční. Nebo to způsobuje chybu, kterou jsem nenašel?

Jasně, ale asi bych to afterAction čekal spíš v tom objektu oltáře, pocitově mi to tam spíš patří, protože se toho týká. To by také fungovalo, ne? Resp. já bych to celé zjištění, jestli jsou všechny diamanty napsal asi rovnou do action() metody iobjFor(PutOn), ve které mimochodem máš if(gActionIn(PutOn)), který se mi zdá zbytečný - jsi v iobjFor(PutOn), tak to asi jiná akce být nemůže a pomocí validContents máš omezeno, že tam nikdo nedá nic jiného, tak mě nenapadá, za jakých okolností nenapsat hlášku o záři.

Ale to jsou takové drobnosti, většinou je více cest, jak různé věci udělat a komentuju je spíš proto, abys pro zajímavost viděl některé souvislosti, ale jde ti to pěkně od ruky.

3
TADS / Re: akce po určitém počtu tahů
« kdy: 10. Listopad 2018 - 21:20 »
Jen pár drobností, ani to většinou nejsou chyby, spíš různé tipy, co jde ještě udělat trochu jinak:

Kód: [Vybrat]
setGameTitle () {
"<i>Indiana Jones II.</i>\b";
}

Tohle je torchu podezřelé, odřízl jsi <title> tag, to asi může trochu rozbít některé interpretry, ani nevím. Obvykle se tohle nemění.

Kód: [Vybrat]
havaruj() {
/* Pokud uz jsem se z letadla dostal, neudelame nic. */
if(me.isIn(m_V_letadle)) {

Dá se také napsat me.isIn(self), self totiž je odkaz na současný objekt, prostě taková zkratka.

Kód: [Vybrat]
+ p_Padak: Hidden, Wearable 'bílý americký padák' 'padák' *2
gcVocab = 'bílý americký padák'

Nemusíš opakovat ta slova slovníku, co jsi napsal výše. Spíš bych doplnil ostatní pády pro jistotu, 'bílého bílému bílém bílým amerického americkému americkém ameryckým padáku/padákem'.

Kód: [Vybrat]
+ p_Prazdna_sedadla: Chair, CustomImmovable 'prázdná prázdné sedadla/sedadlo' 'prázdná sedadla' *4
Nemusíš pojmenovávat objekty, pokud na ně nikde jinde neukazuješ. Lecjakou dekoraci můžeš prostě vytvořit anonymně, tedy jen + Chair...

Kód: [Vybrat]
m_V_oaze: OutdoorRoom 'V oáze' 'k oáze'
"Jsi v malé oáze uprostřed pouště. U malého jezírka tu stojí <b>palma</b> s několika kokosovými ořechy. Nic moc.
\nZajímavější je, že kousek odtud směrem na jih je plot z ostnatého drátu s bránou a strážní věží.
<<if p_Lopata.isIn(me) || d_Pisek_podlaha.isOpen>><<d_Pisek_podlaha.isOpen ? '\nV písku je vidět vchod do podzemí. ' : '\nV <b>písku</b> pod palmou to vypadá jakoby se před nějakou dobou trochu propadla zem. '>>"

Možná trochu jednodušeji:

Kód: [Vybrat]
    <<if d_Pisek_podlaha.isOpen>>\nV písku je vidět vchod do podzemí.
    <<else if p_Lopata.isIn(me)>>\nV <b>písku</b> pod palmou to vypadá jakoby se před nějakou dobou trochu propadla zem. "

Kód: [Vybrat]
roomParts = [m_Zem_v_oaze]
Tady jsi odstranil defaultSky, nevím, jestli záměrně.

Kód: [Vybrat]
+ p_Palma: Fixture, CustomFixture 'kokosová palma' 'palma' *3
Jen CustomFixture, ne obě třídy. To je buď jedno nebo druhé, liší se to jen tím, jestli cannotTakeMsg reaguje na všechny druhy hýbání stejně, nebo se píší tři různé hlášky.

Kód: [Vybrat]
+ p_Auto: Container, CustomFixture 'německý automobil/auto/auta' 'německý automobil' *2
"Mercedes Benz, ale bohužel v nepojízdném stavu. "

// je tu pro zobrazeni nepřenosných objektů v popisu mistnosti
isListed = true

Tohle děláš na více místech. isListed zařídí takové obyčejné listování, což možná chceš, protože to pak vypadá, jako ve starých hrách, ale v TADSu se na vypíchnutí objektu v rámci místnosti častěji používá vlastnost specialDesc, která věnuje objektu zvláštní odstaveček po rozhlédnutí v místnosti, používá se to většinou u pár důležitých objektů a většinou se pak napíše trochu víc, než jen že objekt vidíš.

Kód: [Vybrat]
++ p_Baterie: Hidden, Wearable 'baterie' 'baterie' *3
Asi ne Wearable ;-)

Kód: [Vybrat]
action() {
"Otevřel jsi truhlu, a našel v ní zajímavé věci! ";
inherited;
makeOpen(true);

Inherited by myslím měl udělat makeOpen(true) automaticky.

Kód: [Vybrat]
<<if d_Hrobka.isOpen>>\nUprostřed oltáře je otvor vedoucí do podzemí! <<end>>"
End na konci řetězce je nepovinný.

Kód: [Vybrat]
+ d_Hrobka: SecretDoor 'otvor/díra/vchod' 'otvor' *2
"<<isOpen ? 'Uprostřed oltáře je otvor vedoucí do podzemí!' : 'Nic takového tu nevidíš.'>> "

// otevře se směr dolů do uvedené místnosti
destination = m_Hrobka

iobjFor(PutOn) {
action() {
if(gActionIn(PutOn)) {
"Záření oltáře ještě zesílilo! ";
}
inherited;
}
}

afterAction() {
if(p_Diamant1.isHeldBy(p_Oltar) && p_Diamant2.isHeldBy(p_Oltar) && p_Diamant3.isHeldBy(p_Oltar) && p_Diamant4.isHeldBy(p_Oltar) && p_Diamant5.isHeldBy(p_Oltar)) {
"\b<<if !d_Hrobka.isOpen>>Uprostřed oltáře se otevřel otvor vedoucí do podzemí! <<end>>";
if (!d_Hrobka.isOpen) {
makeOpen(!d_Hrobka.isOpen);
}
}
inherited;
}

// tedy napíšeme postupně 2. koho? čeho? 3. komu? čemu? 4. koho? co? 6. (o) kom? (o) čem? 7. kým? čím? pád oddělené čárkou
gcName = 'otvor, otvoru, otvor, otvoru, otvorem'
gcVocab = 'otvor/otvorem/díra/dírou/vchod/vchodem'
;

To iobjPutOn a afterAction je nějaké divné, dole u oltáře beru, ale tady je to nějak navíc asi omylem.

4
TADS / Re: akce po určitém počtu tahů
« kdy: 10. Listopad 2018 - 15:06 »
Mimochodem kde se používájí slova z gcVocab?

gcVocab resp. vocabWords, který se obvykle zapisuje pomocí šablony jako ta první sada slovíček, se používá pro naplnění slovníku parseru, tj. té části programu, která se snaží analyzovat, co hráč napsal, a dát tomu nějaký význam a provést jeho příkaz. Použijí se tedy k porovnání s tím, co hráč napsal a z toho se odvodí, jestli s tímto objektem hráč chce manipulovat.

Naproti tomu název objektu, tj. vlastnost gcName a name, která se obvykle píše jako druhý řetězec v apostrofech na začátku objektu, je název, který hra použije při výpisu zpráv na obrazovce, ať už se jedná o hlášení, že jsi něco zvednul, výpisu inventáře atd., zkrátka kdykoliv hra tiskne název objektu na obrazovku.

Kód: [Vybrat]
+ cDoor: Door 'vnitřní tlakové dveře dveře/těsnění*dveře'
    'vnitřní tlakové dveře' *3
    "Jsou to mohutné dveře schopné udržet atmosféru v oddělené části lodi. Po
        obvodu je měkké těsnění, které občas prověřuješ. Z obou stran jsou
        výrazné tlakoměry ukazující tlak na druhé straně dveří. V případě
        potřeby tlakový uzávěr může lehce zaklapnout a udrží i velmi prudkou
        změnu tlaku. "

    /* Dveře jsou pomnožné, tj. mají vždy tvar množného čísla. */
    isPlural = true

    gcName = 'vnitřních tlakových dveří, vnitřním tlakovým dveřím, vnitřní
        tlakové dveře, vnitřních tlakových dveřích, vnitřními tlakovými dveřmi'
    gcVocab = 'vnitřních vnitřním tlakových vnitřními tlakovým tlakové tlakovými
        dveří/dveřím/dveřích/dveřmi'
;

Tyhle dvě role se důsledně rozlišují a ve slovníku pochopitelně člověk uvede i mnoho různých synonym, aby hráč nemusel uhodnout jeden přesný název. A pochopitelně i všechny důležité pády, nemusejí se však opakovat ani být seřazené, což naopak u názvu potřeba je, aby si TADS uměl vybrat. U slovníku parseru stačí jen poznat.

Jak tedy zadat správně "diamant u stropu"? A kam zapsat "u"? Nebo ho mám vynechat?

Kód: [Vybrat]
gcVocab = 'diamant diamantu diamantem (u) (stropu)/diamant/diamantu/diamantem'
Tohle je trochu složitější název objektu a jak jsem řekl přídavné a podstatné jméno, tak zde úplně neodpovídá. Řekl jsem to trochu zjednodušeně a teď je příležitost dovysvětlit. Pro TADS není úplně důležité, co to je v rámci mluvnice českého jazyka, ale jak slovo chápat a kde se může vyskytnout. Tedy základní pravidla zní, že poznáme objekt tehdy, když:

  • Hráč použije jen jedno slovo ze slovníku podstatných jmen.
  • Hráč použije jedno či více slov za sebou ze slovníku přídavných jmen v libovolném pořadí.
  • Hráč použije kombinaci předchozích, ale přídavná jména musí vždy stát před jediným z podstatných jmen, ne naopak.

Tedy když mám velký červený míč, napíšu asi 'velký červený míč/míček' a z toho se vygenerují všechny smysluplné kombinace, např. bude fungovat >vem červený i >vem míček, libovolné pořadí, třeba >vem červený velký i >vem velký červený míč. Nebude ale fungovat >vem míč červený, protože přídavné jméno má vždy stát před podstatným.

Protože diamant i strop je podstatné jméno, tak ten diamant zopakuji i jako přídavné (že může stát před druhým podstatným jménem) a i zvlášť jako skutečné podstatné jméno, když hráč jen řekne >vem diamant. To opakování není úplně nezbytné, fungovalo by to i bez něj, ale TADS vždy bere samotné přídavné jméno jako méně hodnotnou shodu, když posuzuje více možností, o jaký objekt jde.

Ta slova v závorce pak jsou taková, která mohou být v příkazu zadaná, ale pokud jsou samotná, tak je bude TADS ignorovat. Jde o to, že třeba "u" je tak obecné slovíčko, že by ho hráč nenapsal samo o sobě a že je příliš nevýznamné, než aby se měl parser domnívat, že hráč hovoří o daném objektu. Podobně i to "stropu", je mnohem větší šance, že hráč chce prozkoumat strop a ne diamant u stropu, když napíše >prozkoumej strop.

Ještě poznámka - gcVocab je duplicitní a záměnné se slovníkem, který lze zapsat rovnou pomocí šablony v úvodu definice objektu. Ale při všech těch pádech je to takové delší, tak se hodí mít na to samostatný řádek - já třeba v záhlaví objektu uvedu jen první pád a ty ostatní pak dávám do gcVocab, ale mohl bych to nacpat všechno jen nahoru.

A ještě otázka k akci MoveWith. Jak je zapsat aby bylo možné použít obě jak MoveWith tak i AttackWith?
Mám je napsat pod sebe jako akci AttackWith nebo je lze nějak spojit?

Jednu uděláš tak, jak potřebuješ a u druhé přesměruješ, např.:

Kód: [Vybrat]
    dobjFor(LookThrough)
    {
        verify()
        {
            if(location.isOpen())
                illogicalNow('Dveře jsou zasunuty ve stěně komory, takže okénko
                    je teď schované. ');
        }
        action ()
            { "Odsud je jím vidět do kupole. "; }

    }
    dobjFor(LookIn) asDobjFor(LookThrough)

5
TADS / Re: akce po určitém počtu tahů
« kdy: 9. Listopad 2018 - 09:37 »
Jinak jsem ještě zápasil s akcí sraž něco něčím.
Žádnou správnou akci jsem na to nenašel (možná jsem špatně hledal).
Po pročtení dlouhého vlákna zde jsem to vyřešil akcí "AttackWith" s nadefinováním vlastních slov.
Akorát kazí dojem že lze na předmět i úspěšně zaútočit. A to asi hráče nenapadne.

Samozřejmě vždy je možnost si zadefinovat úplně novou akci, která nebude navázaná na stávající, ale s každou takovou neobvyklou akcí hrozí, že hráč neuhodne správné sloveso. Proto by obvykle autor měl spíš sledovat opačný cíl, umožnit akci provést více slovesy a více synonymy, přesměrovat i méně pravděpodobné akce na tu správnou, aby průbeh hry byl hladší. Když bude hra reagovat na příkaz "zaútoč na diamant bičem", tak to dojem toho, koho napadne "sraž" nezkazí a toho, koho to nenapadne a místo toho "zaútočí", tak tomu to pomůže se posunout dál, tak jeho dojem to také nezkazí. Určitě bych přesmeroval také akci MoveWith, tedy posuň něco něčím, ta je pro nějaké štouchnutí do něčeho asi přímo určená, ale jak říkám, čím více synonym, tím lépe.

A ještě lépe, pokud hráče na neobvyklou akci někde předem připravíš. Třeba v naší hře jsme hráče předem důkladně procvičili, že může namířit tablet na něco, aby to později mohl použít k řešení problému.

Kód: [Vybrat]
gcVocab = 'diamant u stropu/diamantu u stropu/diamantem u stropu'
A samotnou akci takto:
Kód: [Vybrat]
gcVocab = 'druhý druhému druhým diamant/diamantu/diamantem'

Pozor na formát gcVocab, tedy zápisu slovníku. V tom prvním případě nemáš správně, zapisuj se 'přídavné přídavné podstatné/podstatné', tj. neoddělují se lomítkem skupiny slov, ale jenotlivá podstatná jména. V tom druhém případě to máš správně napsané, takže jsi se asi jen překoukl nebo si to neuvědomil.

6
TADS / Re: akce po určitém počtu tahů
« kdy: 1. Listopad 2018 - 23:57 »
Já si v popisu místnosti kód provádějící načasování volám. Do textového výpisu se dá vložit spousta různých věcí prostřednictvím těch zdvojených špičatých závorek. Třeba podmínit část výpisu <<if něco>>text<<end>> ale i jakýkoliv příkaz, který se vykoná a vloží do textu svůj výstup. V příkladu výše žádný výstup není, ale akce načasování se spustí.

Šlo by to napsat i jinak. Ten popisek místnosti uvedený ve dvojitých uvozovkách na začátku místnosti je taková zkratka - použije se šablona, která vloží daný text do vlastnosti desc, takže bys také mohl napsat desc = "Popis. " Vtip je v tom, že desc je metoda, která se spustí a má úkol vytisknout popis místnosti na obrazovku, nejde tedy o obyčejnou proměnnou, do které se uloží nějaký text. Takže místo výše uvedeného by šlo napsat:

Kód: [Vybrat]
m_V_letadle: Room 'V letadle'
    desc()
    {
        "Popis. ";
        if(casovac == nil) casovac = new Fuse(self, &havaruj, 5);
    }
;

Pokud se ti bude zdát divné, že míchám pojmy proměnné (data) a metody (funkce, příkazy), tak je to tím, že se to v TADSu zase tolik nerozlišuje. TADS je hodně dynamický jazyk a text napsaný v dvojitých uvozovkách není řetězcová konstanta, nejsou to data, je to *příkaz* k provedení tisku.

7
TADS / Re: akce po určitém počtu tahů
« kdy: 1. Listopad 2018 - 21:27 »
Protože nejsem žádný programátor pátral jsem po nějakém jednoduchém enginu a našel hru "Základna na asteroidu"
a sní i skvělý návod na programování v TADS 3 od gaspody.

Nó, TADS je obvykle vnímán spíš jako složitější, protože nemaskuje skutečnost, že je nástrojem pro programátory :-)
Ale zase se umí odměnit svými schopnostmi vytvořit opravdu uhlazenou a dobře fungující hru. Takže držím palce!

A má otázka zní: Jak udělat aby pokud je hráč ještě v letadle a provede 5 tahů tak hra skončí.

Pro vytváření časovaných událostí jsou k dispozici třídy Fuse a Daemon. Ty chceš tu první, která načasuje událost
jednorázově na zadaný počet tahů dopředu. Daemon spouští událost opakovaně. Příklad je v páté kapitole návodu
k TADSu (http://www.textovky.cz/clanky/programovani-textovych-her-v-tads-3-cast-5-akce/).  Tedy něco na tenhle způsob, ale netestoval jsem to:

Kód: [Vybrat]
m_V_letadle: Room 'V letadle'
"Probudil ses v malém dvoumotorovém letadle...<<nacasuj()>> "

casovac = nil
nacasuj()
{
/* Nacasuj jen jednou, i kdyz se popisek mistnosti zobrazi vicekrat. */
        if(casovac == nil) casovac = new Fuse(self, &havaruj, 5);
        }
havaruj()
{
/* Pokud uz jsem se z letadla dostal, neudelame nic. */
if(me.isIn(m_V_letadle))
{
"Bum! ";
finishGameMsg(ftDeath, [finishOptionUndo] );
}
}
;

8
TADS / Re: Otěžemi zimní noci v TADS3
« kdy: 19. Únor 2018 - 17:53 »
No vida, to je chyba v překladu, jestli si chceš opravit soubor msg_neu.t, tak od řádku 2842, chybí tam jen to obj ve složené závorce:

Kód: [Vybrat]
    /* generic "no can do" message for intangibles */
    notWithIntangibleMsg(obj)
    {
        gMessageParams(obj);
        return 'To {|[jsi]} {s/se} {kýmčím obj} nem{ůž[eš]|ohl[a]} udělat. ';
    }

    /* generic failure message for varporous objects */
    notWithVaporousMsg(obj)
    {
        gMessageParams(obj);
        return 'To {|[jsi]} {s/se} {kýmčím obj} nem{ůž[eš]|ohl[a]} udělat. ';
    }

Při té příležitosti se dá nadhodit, že pro objekty, jako je třeba Měsíc, který je ten jeden a stejný Měsíc vidět z více lokací, tak na to se používá MultiLoc třída, která se přimíchá před ostatní běžné třídy.

9
TADS / Re: Seriál o programování textových her v TADS 3
« kdy: 31. Leden 2018 - 21:59 »
Hardwarová kalibrace je určitě záležitostí pouze drahých grafických monitorů a použití speciální sondy, tyhle levné, které nejsou určeny na profi práci to neumí. V OSD mého monitoru jde nastavit jas, teplota, gamma, saturace a zisk jednotlivých barevných složek, ale neměl jsem potřebu to měnit.

10
TADS / Re: Seriál o programování textových her v TADS 3
« kdy: 29. Leden 2018 - 20:07 »
To Eizo má ještě mj. i zajímavý stojan (myslím, že je to jeden z těch typů umožňující monitor sklopit do polohy jako knížku drženou v rukou) a tradičně pětiletou záruku na rozdíl od tříleté. Na druhou stranu, i když je uvnitř v podstatě ekvivalentní panel od Samsungu (AH IPS), tak u daného Eiza je jen 16:9 a ne 16:10 (Eizo má i druhou variantu, ale stojí kolem 10 tis.).

A pokud se teď vrátíme k topicu, tak na logy jsem se krátce díval a nic mě zatím nenapadlo, bylo to takové matoucí. Já na to myslím, jen teď mám dost málo času :-(

11
TADS / Re: Seriál o programování textových her v TADS 3
« kdy: 26. Leden 2018 - 23:34 »
Teď se ještě dívám na Alzu a popis dvou levnějších EIZ:
https://www.alza.cz/24-eizo-ev2450-bk-d2268875.htm?o=1
https://www.alza.cz/24-eizo-flexscan-ev2451-bk-d4725652.htm?o=2

Paper Mode se zdá zajímavý a např. recenze na typ EIZO FlexScan EV2450-BK vypadají velmi, velmi dobře. Tenhle kousek se mi tedy začíná líbit čím dál tím víc.

V dané cenové kategorii to není špatné, i ty Delly jsou docela fajn. V Kapse máme starší Dell 2412 za cenu těsně přes 6000,- a má eIPS panel. Ty levnější IPS panely jsou myslím jen 6 bitové, ale dobré je, že to na nich kupodivu není znát. Samozřejmě IPS panely trpí na jemné glow v černé obrazovce, mají však velmi slušné pozorovací úhly a barvy neurazí. Osobně je považuji za docela doporučení hodné, pokud nechceš dát více.

Nejlepší je projít si nějaký skutečný test, doporučuji mrknout na:

http://www.tftcentral.co.uk/reviews/eizo_ev2450.htm
http://www.tftcentral.co.uk/reviews/dell_u2715h.htm
http://www.tftcentral.co.uk/reviews/benq_bl3200pt.htm

a porovnat si je. A pokud máš možnost je vidět naživo, tak také, jen je dobré se trochu vyzbrojit zkušenostmi a vědět, co je na těch monitorech potřeba vidět (glow, kontrast, úroveň černé, rovnoměrnost podsvětlení, pohled ze spodu, blikající pwm podsvícení při nižším jasu, jak moc jde jas snížit pro práci po tmě, ghosting při rolování textu nebo obrazu s jemným šedým vzorkem, jemný šedý gradient apod.) To Eizo za osmičku je o trochu lepší. Samozřejmě pak j vyšší cenová kategorie, kde jsou lepší a dražší panely, tuším, že Dell dělal něco lehce přes 12 tis.

12
TADS / Re: Seriál o programování textových her v TADS 3
« kdy: 25. Leden 2018 - 20:02 »
Dveře jsem upravil dle Tvého doporučení, funkcionalita zůstala stejná. Já se inšpiroval právě u Heidi, kde jsou plně definovány také jen na jednom místě, viz cottageDoor na řádku 425 a cottageDoorInside na řádku 706. Budu ale používat syntaxi, kterou uvádíš, určitě je přehlednější.

Aha, to bude opomenutí, dveře by měly být skutečně definovány plnohodnotně z obou stran, aby se daly prozkoumat atp.

Když už jsme u těch dveří, podíval jsem se na mříž, která je definována stejně a pokud hráč prolézá nahoru a je otevřená, přidal jsem TravelMessage s popisem, což vypadá zase o chlup reálněji. Jen mě napadá, že po jejím přepilování nemají smysl příkazy otevři/zavři mříž, které sice hráč zapsat může a parser mu odpoví, efekt zde však není žádný a hlášení nevypadá dobře.

Dobrá, ale pak asi nemá cenu modelovat mříž jako dveře, pokud to není taková mříž, která se jako dveře otevírá. Konektorů, které spojují místnosti existuje více. Co kdybys vyzkoušel ThroughPassage, ze které konec konců dveře dědí? Je to podobné, dveře navíc dědí ještě od Openable. Tj. ThoughPassage je něco, skr co se dá projít do jiné místnosti, jako např. díra ve zdi, tunel apod., skrátka cokoliv, u čeho by hráč chtěl zadat příkaz projdi/prolez něčím. Podobně existuje PathPassage, která funguje podobně, ale popisuje pohyb po objekty, tedy např. jdi po cestě apod. Na rozdíl od dveří se tyto objekty nedají zavírat a otevírat, ale stejně jako jiné konektory u nich funguje canTravellerPass/explainTravelBarrier, takže by neměl být problém je použít.
V Základně jsem našel parádní příklad, regionBase.t definuje IndirectDoor s využitím IndirectLockable a následné direktivy, které jsem si upravil na:

Dnes budu mít jen jeden technický dotaz ohledně monitoru vhodného pro programování. Vývojařina samozřejmě unavuje oči, displej na mém armádním notebooku není zrovna dvakrát šetrný. Napadlo mě situaci řešit e-ink displejem, jediný dostupný monitor nabízí Dasung E-Ink PaperLike Pro s šílenou cenou a ne zrovna jednoduchým objednáním z ciziny.

No to teda nevím, na čtečku knih, kde jednou za minutu obrátíš stránku asi ok, ale byl bych hodně skeptický, že by se něco takového dalo použít jako normální monitor...

možná by byl vhodný ten BenQ EW2775ZH s přeci jenom rozumnější cenou. Jaké máš zkušenosti jako vývojář Ty, můžeš mi vhodné zobrazovadlo doporučit?

BenQ patří k lowendu. Panely vždy vyrábí někdo jiný, ale elektronika asi nebude nejkvalitnější. Ale doporučení, to je těžké. No u toho BenQu píší, že to je AMVA+ panel, takže to asi není tak strašlivé, jako TNka, ale zkušenost s tím žádnou nemám. Pokud by byla pravda, že panl je osmibitový a má pozorovací úhlu 178 stupňů a nativní kontrast 3000:1‎, jako píší na http://www.relaxedtech.com/reviews/benq/ew2775zh/1, tak by to nemuselo být až tak zlé, ale na druhou stranu cena je podezřelá - předpokládám, že to má nějaký háček, jak už to tak bývá. Snad jen jedinou poznámku, že když už širokoúhlý, tak programátor ocení spíš 16:10, aby nebyl panel tak ošizený na výšku. Jenže tím se výběr prudce sníží.

Já osobně mám doma ještě dnes už stařičké Eizo S1910, které ve své době stálo asi 19 tisíc. Z dnešního pohledu je to už trochu malé, ale má to SPVA panel, který je docela dobrý kompromis, má dobré úhly, kontrast 1000:1 není největší, ale stačí a sice ne kalibrované, ale docela hezké barvy. Neprojevují se u něj žádné zásadní vady, jako u TN a byl výrazně levnější, než profi IPSka. V práci mám na stole nějaké levné Eizo s TN panelem, které mě děsně štve, protože má šestibitové barvy a ditheruje...

13
TADS / Re: Seriál o programování textových her v TADS 3
« kdy: 23. Leden 2018 - 19:59 »
1. Pochodeň, křesadlo, seno a pes
Jasně, otrávenou jehlu musím označit jako PreferredIobj a draka ani PreferredIobj, ani PreferredDobj, poté vše krásně funguje.

Setkal jsi se a potrápil s tímto typem řetězení i u Základny?

Při té poslední úpravě jsem to právě udělal tak, aby Actor mel v konverzačních příkazech přednost. Ne, na základně to bylo docela jednoduché. Ale docela zajímavý problém byl, když jsem se snažil modelovat konverzaci se dvěma postavami zároveň, aby nevypadala jako rozhovor jen s jednou postavou.

2. Výklenek
>stoupni na
(na malý výklenek)
Fajn, teď stojíš na malému výklenku.
>stoupni si do výklenku
Už stojíš na malému výklenku.
Popis místnosti: Ve výklenku (stojíš na malému výklenku)

Tady by asi bylo vhodnější, aby hra mluvila o to, že stojíš ve výklenku a ne na výklenku. To samozřejmě není problém, podívej se, jak v Základně je nadefinovaný objekt spacáku. Vlastnosti objInPrep, objIntoPrep, objIntoCase a objOutOfPrep definují, jaké předložky a pád použít.

3. Dveře
Ano, zde mě zmátlo fungující „Jsou otevřené.“ z jedné strany a „Je otevřené.“ ze strany druhé. Sákryš, samozřejmě, že isPlural = true znám, vždyť jej mám u dveří na konci chodby, doplnil jsem je ještě ke dveřím druhým a tady k dračím hlavám.
 
Potíž ovšem stále přetrvává, je zajímavé, že z jedné strany vše funguje jak má a ze druhé nikoliv – nejde jen o množné číslo, ale právě z jedné strany je lze otevřít i zavřít, po zavření pokud chci projít parser automaticky doplní otevření dveří. Ze strany druhé ovšem nikoliv – množné číslo je i přes direktivu isPlural špatné a mohu projít i při zavřených dveřích, aniž by se parser o cokoliv pokusil.

Hele já nevím, které dveře máš na mysli, ale asi to bude u všech stejné a nemyslím, že bys to měl opravené. Níže máš jednu půlku dveří dobře a tu druhou máš úplně bez definice. Ta by měla být stejná, nejen isPlural, ale i keyList a slovník a jakékoliv další vlastnosti. To, že jsou obě půlky mezi seou prolinkované, aby se dalo procházet ještě neznamená, že bys nemusel uvést všechno ostatní.

Kód: [Vybrat]
chodba_se_zeleznymi_dvermi: Room 'Chodba' 'do chodby'
    "Stojíš v chodbě. Na jihu jsou masivní železné dveře. \bMůžeš jít na sever. "
    north = chodba_se_zebrikem
    south = dvere_chodba_se_zeleznymi_dvermi
;

+ dvere_chodba_se_zeleznymi_dvermi : LockableWithKey, Door 'dveře' 'dveře' *3
  "Masivní železné dveře. "

     isPlural = true
     keyList = [svazek_klicu]

     gcName = 'dveří, dveřím, dveře, dveřích, dveřmi'
     gcVocab = 'dveří/dveřím/dveřích/dveřmi'
;

//po použití svazku klíčů od strážce: "Podařilo se ti odemknout železné dveře. "

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

zatuchla_komurka: Room 'Zatuchlá komůrka' 'do zatuchlé komůrky'
    "Nacházíš se v malé zatuchlé komůrce. Na severu jsou vidět masivní železné dveře.
    \bMůžeš jít na sever. "
    north = chodba_se_zeleznymi_dvermi
;

+ dvere_zatuchla_komurka : LockableWithKey, Door -> dvere_chodba_se_zeleznymi_dvermi 'dveře' 'dveře' *3
    // Tady chybí uvést to samé, co u prvních dveří
;

14
TADS / Re: Seriál o programování textových her v TADS 3
« kdy: 8. Leden 2018 - 20:19 »
Tebou popsané příklady mají hlavu a patu a jsou krásně srozumitelné, neuvažuješ o tvorbě nějaké učebnice?
Myslím to vážně, ideálně určené starším dětěm nebo študentíkům např. ve stylu knihy Python for Kids.

Haha, dobrý vtip.

3. PreferredIobj
Podíval jsem se do knihovny, kde se s PreferredIobj vůbec nesetkám a celý svůj TADS adresář jsem si tímto výrazem grepnul, používáš jej ve svém překladu,
u Základny a já u Exoteru, ano, už si vzpomínám, že jsme se o něm a úpravou Dobj bavili v souvislosti s novou verzí češtiny.

PreferredIobj s velkým P na začátku je mix-in třída, která slouží jen jako označení, je to jen deklarované a pak v jednom ifu.

Teď je mi také jasné, že u křesadla a pochodně budou stejné potíže, hned jsem to vyzkoušel a hnedle u zapálení samotné pochodně křesadlem a využití těchto předmětů u psa je to horší:

>zapal pochoden
Čím ji chceš zapálit?
>kresadlem
To není něco, co by mohlo hořet.

>zapal kresadlem pochoden
To není něco, co by mohlo hořet.

>zapal pochoden kresadlem
Zapaluješ pochodeň.

Tady se vytvořila řetězová závislost, kdy křesadlem zapaluješ pochodeň a pochodní zapaluješ seno. Tedy pochodeň je jednou přímým objektem a podruhé je nepřímým. Proto pochodeň nemůžeš označit ani jako PreferredDobj, ani PreferredIobj. Protože je to řetězec jen na tři, tak pochodeň, která je uprostřed, nech bez zakéhokoliv označení (tedy uber PreferredDobj u pochodně), křesadlo označ jako PreferredIobj, protože křesadlo je vždy to, co zapaluje něco jiného a kupku sena označ jako PreferredDobj (to už máš), protože to se vždy nechává zapálit něčím jiným.

>dej pochoden psovi
Nemůžeš pochodeň nic dát.

>dej kresadlo psovi
Nemůžeš křesadlu nic dát.

Vše jsem vyřešil stejným způsobem, vše funguje krom:
>zapal pochodni seno
{Tím iobj} nemůžeš nic zapálit

Zkoušel jsem ke kupce sena dát dle diskuze s Tekketem direktivu:
preferredIobj = pochoden

ale zatím bez úspěchu, budu na tom ještě pracovat, PreferredDobj u pochodně potřebuji. Jak vidno, tyto předměty budu muset doladit – kombinace
křesadlo, pochodeň, zapálení sena a správné hlášky, pokud tyto předměty chci umožnit ve hře cizí postavě dát, není zrovna triviální.

Tohle opět koliduje s výše uvedeným řešením, ale upavil jsem knihovnu, aby dávala přednost tomu, že pro konverzační akce je Actor před těmi ad hoc vlajkami. Mimochodem ten nedosazený parametr {Tím iobj} jsem v nové verzi překladu také opravil.

6. TADS Web server
Neodolal jsem a podle návodu Setting up a custom TADS Web server jsem si nainstaloval lokální server s frobem. Po trošce laborování jsem byl úspěšný, v Ubuntu 16.04 je možné pro Apache povolit a2enmod a mohu v prohlížeči spustit t3launch, v Archu už a2enmod není, v souboru .htaccess v podadresáři t3launch stačí zakomentovat všechny 3 řádky a vše je OK. Test, který popisují, proběhne v pořádku, pokud v prohlížeči zadám
http://localhost/t3launch/

dostávám hlášení a dále viz můj dotaz níže:

Missing Parameters
The link you used to reach this page is missing some required information.

Jasně. Ty skripty, které sis nainstaloval slouží ke spuštění webového hraní a fungují jako univerzální gameserver pro TADSové webové hry. Ta chybová hláška znamená, že jsi neřekl, jakou hru chceš spustit. Jako minimum musíš předat parametr storyfile s odkazem na hru. Měl bysis tedy vyrobit třeba nějakou startovací stránku a hru spustit odkazem ve tvaru:

<a href="http://localhost/t3launch/?storyfile=http%3A%2F%2Flocalhost%2Fasteroid-web.t3">spustit základnu</a>

Gameserver je dělaný univerzálně, aby si hru stáhnul z internetu, proto se tam dává HTTP odkaz a ten může věst kamkoliv na internet, kde je soubor se hrou vystavený. Pokud si to chceš hostovat sám, tak si dej soubor se hrou např. do kořenového adresáře webu, tj. zkontroluj si, že když zadáš http://localhost/asteroid-web.t3, tak že ti to nabídne soubor ke stažení a pak bude ten odkaz na t3launch výše fungovat.

V konfiguráku config.php jsem nastavil cestu k lokální databázi, využívám mariadb a vytvořeného uživatele t3launch s databází t3launch a heslem password. Ten se např. pomocí phpmyadmina do databáze dostane, při příkazu uložit se mi ovšem pozice nabídne ke stažení na lokální počítač a v databázi není nic.

Ne, takhle je to v pořádku, skripty t3launch nedělají nic jiného, než spuštění hry a do databáze si zaznamenávají jen jedinou tabulku s informacema o stažených hrách, aby věděly, jestli při příštím stažení mohou použít nakešovanou kopii nebo musí stahovat hru znovu.

Ty máš na ostrém serveru rozhraní, které zadá jméno a heslo nového uživatele do databáze, se kterou pracuje instance spuštěného frobu s adresou např. 127.0.0.1:4417, která je přesměrována na venkovní adresu.

To je funkcionalita storage serveru, která je k dispozici na stránkách ifdb.tads.org. Když máš zaregistrovaný účet na IFDB, tak si můžeš spouštět TADSové hry a při ukládání místo aby se nabídl save file ke stažení, tak se pozice uloží online. Tahle funkcionalita je k dispozici pro každého, i ty si můžeš nahrát svou webovou hru na IFDB a vůbec nemusíš řešit vlastní server, protože dobrovolníci provozují několik veřejných TADS gameserverů.

Já jsem si podobnou funkcionalitu vytvořil sám pro sebe, tj. provozuji vastní gameserver i storageserver, takže i když IFDB nevyužívám, mám také možnost ukládání pozic online pod jménem a heslem. Potřeboval jsem to totiž propojit s uživatelskými účty na Technoplanetě a IFDB mi neposkytne tu bezešvou integraci. Navíc jsem měl naprogramováno odesílání událostí ze hry (změna skóre atp.) do online žebříčku a to by na IFDB také nešlo.

Naprogramování a nastavení storage serveru není nikde zdokumentované a zveřejněné a je to už pokročilá záležitost. Možná to někdy zveřejním, ale nebude to brzy, protože bych to musel pořádně dotáhnout a vyseparovat z jiného kódu.

7. Dokončování hry
Výklenek v místnosti se sekerou jsem prvně chtěl definovat jako Decoration, chybí mi však vhodná zpráva u sebrání, takže si dekoraci nechám jinam a využiji CustomImmovable.

Může být. Přesnější by bylo CustomFixture než CustomImmovable, liší se to v malém detailu, že Fixture je něco, co je pro hráče zcela evidentně součástí lokace a tudíž nepřenosné, kdežto Immovable se z pohledu hráče nemusí jevit nepřenosně, byť nepřenosné je. Samozřejmě se to dá dál rozvíjet, třeba v Return to ditch day je výklenek součástí jednoho puzzlu. Tam je deklarovaný jako Fixture, Platform, aby se dalo stoupnout do výklenku. Ale to už je vyšší dívčí :-)

Jdu-li na sever a dveře zase prozkoumám, dostávám:
>prozkoumej dveře
Je otevřené.

No musíš si dveře označit isPlural = true, vždyť to víš, jen nesmíš zapomenout.

Tady si pořádně nastuduji vše o množném číslu, podobná potíž se vyskytuje také u drahokamů, pokud jich držím více:
>prozkoumej drahokamy
Slovo "drahokamy" v tomto pribehu neni dulezite.

Stačí přidat množné číslo do slovníku, plurál se odděluje hvězdičkou 'první drahokam*drahokamy', koukni v Základně.

Další zajímavost je mixování mužského a ženského rodu:
>prozkoumej provaz
Je pevný.
>prozkoumej lano
Je pevný.

No jasně, o tom jsme v tomhle vláknu už také myslím mluvili. Když jsou synonyma, která jsou v jiném rodu, tak se dají ty další rody zadat pomocí vlastnosti changeGender, např. changeGender = 'lan:4'.

Namočit jehlu mohu jen příkazem „namoč jehlu v kaluž“, pro využití „v kaluži“ bude určitě vhodné něco jako {komučemu xxx}, to už jsem viděl v Heidi i v Základně.

Příkaz "namoč jehlu v kaluži" řekne, že Slovo „kaluži“ v tomto příběhu není důležité, protože jsi slova v gcVocab objektu kaluz oddělil čárkami, což není správné. Změn to na lomítka gcVocab = 'kaluže/kaluži/kaluží'

Užij si Vánoční čas, zkoušel jsem posílat své koňské přání na Tvůj kapsový email uvedený v překladu TADSu, ale bez úspěchu, tak to napravuji zde společně s aktuálním zdrojákem a plánovaným webem.

Díky, kapsácký mail by měl fungovat normálně, nevím, jestli jsi neposlal moc velkou fotku. Jo a je na něm greylisting, takže poprvé mail od serveru odmítne a pokud to poštovní server za chvíli zkusí znovu, tak mail od něj už přijme. To je obrana proti masovému spamu, ale běžný poštovní server by si s tím měl poradit.

15
TADS / Re: Seriál o programování textových her v TADS 3
« kdy: 20. Prosinec 2017 - 11:51 »
1. Pes
hmm, nerozpoznal jsem, že u Topicu není metoda změnit stav, na kterou jsem zvyklý u postav.

Jeden fyzický objekt ve hře, jako třeba postava, se může skládat z několika programových objektů, jako třeba samotný actor, jeho jednotlivé stavy, téma konverzace. Když chceš měnit stav, tak to musíš dělat se správným objektem, tedy s postavou a ne s tématem. Je to podobné, jako když se Lego autíčko skládá z několika dílků zacvaknutých do sebe. Když chceš něčím otáčet, tak můžeš otáčet kolečkama, ale ne blatníkem. Každá kostička má svůj jasně daný účel a možnosti manipulace.

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.

OutOfReach žádnou knihovní dědičnost nemá, jde o to, jaký svůj objekt od ní podědíš ty. Když bych se podržel příkladů s kostičkama stavebnice, tak ty si tvoříš svojí kostičku tím, že dědíš od různých tříd a to pak dává kostičce různé vlastnosti, jako třeba barva, velikost atp. Třeba budu hledat červenou kostičku velikosti 2x1. Najdu ji a teď si vzpomenu fajn, ale já ještě chci, aby měla díru. To, že přidáš nějakou třídu je jako když přidáš nějakou vlastnost. Najdu tedy kostičku s dírou a můžu protáhnout hřídelku. Mít objekt bez třídy OutOfReach je jako mít kostičku bez díry, tedy nemá cenu snažit se protáhnout hřídelku / nemá cenu psát vlastnosti canNěco, nemají žádnou funkci.

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í.

První by mělo fungovat samo, protože u všech konverzačních akcí je automaticky každá postava označena jako preferovaný iobj už v knihovně. (Podobně je u akce prostrč objekt jiným objektem označeno, že NonPortable objekt je ten, skrz který se protlačuje.) Třída PreferredIobj je určená k využití jen u těch akcí, u kterých to nejde takhle předvídat dopředu.

Ve druhé situaci se jedná o konflikt, protože sekera je už z důvodu využití v jiné akci označená jako PreferredIobj a to je pak nevhodné v konverzačních akcích, kde postava má být iobj, tudíž se to pere mezi sebou - máme dva objekty a oba mají nastveno, že jsou iobj. Jedna možnost, jak to řešit je nenastavit sekeru jako PreferredIobj a místo toho nastavit ten druhý objekt v té druhé akci se sekerou jako PreferredDobj. Nevím, co to je za akci, sekání hlav? Pak sekaným hlaván nastavit PreferredDobj.

Úplně obecné řešení jsem rozebíral s Tekketem na pomezí mezi druhou a třetí stránkou této diskuse.

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.

Algoritmus je obecně postup vyřešení nějaké úlohy a vlastně není závislý na konkrétním programovacím jazyce. Když si představíš recept na jídlo v kuchařce, tak algoritmus je ten postup výroby - nejprve oddělíme žloutek od bílku, tohle přidáme do tohohle, mícháme dokud není taková konsistence, pečeme do růžova,... Pouhý výčet surovin v receptu ale algoritmem není, že potřebujeme dvě vajíčka a půl kilo mouky, to je deklarace, ne algoritmus. Algoritmus je postup řešení, to kde říkáš věci jako nejprve tohle, potom tohle. Dokud něco, tak dělej tohle apod.

Takže definice místností, hierarchie objektů, to jsou pouhé deklarace (a tyto deklarace jsou pak nějakým algoritmem v knihovně následně zpracovávány). Algoritmus je až to, kdy píšeš nějaké příkazy, které jsou vykonávány, tedy když píšeš příkazy v nějaké action() apod. A ačkoliv tedy deklarace dat není algoritmem, může být vymyšlení vhdného uspořádání dat kolikrát i důležitější, než vymyšlení algoritmu, který s nimi pracuje.

O algoritmech a algoritmizaci se má cenu bavit tehdy, když jde o nějaký netriviální postup, který lze dělat třeba různými způsoby, např. učebnicový problém je řazení podle velikosti. Mám řadu různě dlouhých tyček a chci je srovnat podle velikosti. Tak jdu z leva do prava a porovnám každé dvě sousední a pokud jsou v nesprávném pořadí, tak je prohodím. Tento celý postup zopakuji tolikrát, kolik je tyček a pak budou srovnané všechny. To je sice docela snadno pochopitelné, ale je to také jeden z nejpomalejších způsobů (Bubble Sort). Jsou i jiné způsoby, ale jejich pochopení je náročnější. Navíc neexistuje jediný správný a nejlepší způsob, trochu se to liší podle vstupních dat - jsou data úplně náhodná? Jsou už trochu srovnaná? Jsou mezi nimy stejné hodnoty, nebo je každá řazená hodnota jiná, než ostatní? https://www.youtube.com/watch?v=ZZuD6iUe3Pc

Algoritmizace je tedy o schopnosti vymýšlet postupy řešení úloh, schopnosti rozpoznat, že pro některý problém se hodí některý ze známých algoritmů a umět jim porozumět a správně vybrat, který je vhodnější v určité situaci, když je více možností.

6. Závěr hry
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.

Lze lecos, třeba i po zobrazení hlášky hru zapauzovat a pokračovat ve výpisu až po stisknutí klávesy, ale osobně to považjí spíše za kontraproduktivní, protože to narušuje plynulost čtení.

Stran: [1] 2 3 ... 9