Skip to content

Naratív a interakcia: Dialóg, tok a vstup

Úvod · Index funkcií · Ťahák

Táto stránka má najširší rozptyl zrelosti v celej špecifikácii. Voľby, storylety, karty a voľný textový vstup sú experimental — vydané a dnes použiteľné. Kooperatívne čítanie a väčšina senzorových funkcií sú development alebo draft: úplne špecifikované, aby ste okolo nich mohli navrhovať, ale zatiaľ sa proti nim príbeh písať nedá. Skôr než sa na čokoľvek tu spoľahnete, pozrite si značku pod nadpisom.


16. Voľby a vetvenie

Voľby sú srdcom interaktívnych príbehov. Rea podporuje jednoduché aj zložité vetvenie.

Jednoduché voľby

experimentalod 1.0* [One-time] · + [Sticky]

Jednorazové a trvalé voľby, aj pravidlo textu pred návestím, v ňom a za ním, sú vydané a v každodennom používaní.

Použite * pre jednorazové voľby a + pre opakovateľné (trvalé) voľby:

rea
Cesta sa pred tebou rozdvojuje.

* [Vyber ľavú cestu]
  Ľavá cesta vedie hlbšie do lesa.
  -> dark_forest

* [Vyber pravú cestu]
  Pravá cesta sleduje rieku.
  -> river_bank

+ [Rozhliadni sa]
  Pozorne si prezrieš okolie.
  -> the_crossroads

{if} vo vetve ukončí celú skupinu Blokový príkaz odsadený pod voľbou zavrie skupinu volieb, takže každá ďalšia možnosť zmizne a čitateľovi zostanú len tie nad ňou. Nastavte vo vetve príznak a podmienený text dajte až za gather:

rea
{comment ZLE — „Vydať sa doprava" sa nikdy nezobrazí}
* [Vydať sa doľava]
  {if story.hrdina.lampas begin}
  Lampáš ti ukáže korene.
  {end if}

* [Vydať sa doprava]
  Rieka je tu hlasnejšia.

{comment DOBRE}
* [Vydať sa doľava]
  {set story.hrdina.smer = "vlavo"}

* [Vydať sa doprava]
  {set story.hrdina.smer = "vpravo"}

- {if story.hrdina.smer = "vlavo" and story.hrdina.lampas begin}
Lampáš ti ukázal korene.
{end if}

{set}, {give}, {take}, {earn}, {spend} aj {play} sú vo vetve v poriadku — skupinu zatvárajú blokové príkazy. :::

Pravidlá textu voľby:

text
* PRED [NÁVESTIE] PO
  ╰─┬─╯  ╰───┬───╯ ╰┬╯
    │        │      └── zobrazí sa až po výbere (rozprávanie)
    │        └── zobrazí sa ako klikateľný text voľby
    └── zobrazí sa AJ vo voľbe, AJ v rozprávaní
  • Text v [ ] sa zobrazí ako návestie voľby
  • Text za [ ] je rozprávanie zobrazené po výbere voľby
  • Text pred [ ] sa objaví vo voľbe aj v rozprávaní
rea
* „Musím si to premyslieť[."]," povedal si.
  Kupec trpezlivo čakal.

Po výbere sa zobrazí: „Musím si to premyslieť," povedal si. Kupec trpezlivo čakal.
Ako voľba sa zobrazí: „Musím si to premyslieť."

Podmienené voľby

experimentalod 1.0* {gold >= 50} [Bribe the guard]

Podmienka pred návestím rozhoduje o tom, či sa možnosť vôbec ponúkne.

Voľby môžu mať podmienky:

rea
* {has_key} [Odomkni dvere]
  Kľúč sadne dokonale. Dvere sa rozletia.

* {gold >= 50} [Podplať strážcu]
  Strážca si vrecká tvoje zlato a ustúpi nabok.

* [Odíď]
  Otočíš sa a potichu odídeš.

Skryté voľby

experimentalod 1.0* hidden [&card_id] …

Možnosť bez tlačidla, ktorá sa spustí len voľným textovým vstupom alebo aktiváciou v reálnom svete; vydané spolu s menu objavovania.

Voľba označená ako hidden nevykreslí žiadne tlačidlo. Zostáva vo výbere skupiny — podmienky, jednorazové spotrebovanie aj rozprávanie fungujú ako obvykle — ale spustiť sa dá len niečím iným než klepnutím: tým, že ju čitateľ opíše vo voľnom textovom vstupe, alebo vstupom z reálneho sveta, ktorý sedí na aktivačné polia jej karty (naskenovaný kód, odfotená značka, vyslovená fráza):

rea
* hidden [&look_under_sofa] Jozef sa zohol a pozrel pod starú pohovku, kde našiel záhadnú obálku s nápisom _Tajné!_
  {give secret_envelope}

Kľúčové slovo hidden stojí na riadku voľby ako prvé; podmienka môže nasledovať za ním:

rea
* hidden {story.player.curious} [&look_under_sofa] …

Skryté voľby sa zvyčajne viažu na kartu akcie cez [&card_id] — pole description= karty je to, s čím sa porovnáva voľný text, a jej polia scan=, mark= a listen= sú to, na čo sedia vstupy z reálneho sveta. Keďže sa návestie objaví až po spustení voľby, na skrytý obsah naznačte v okolitej próze; návestie a rozprávanie sú odmena, nie pozvánka. Skupiny zložené prevažne zo skrytých volieb popisujú Menu objavovania.

Odbočky

experimentalod 1.0-> the_clearing

Jednoduchý skok na pomenovanú kotvu.

Pomocou -> skočíte na pomenovanú sekciu (kotvu):

rea
-> the_clearing

[#the_clearing]
Prichádzaš na malú čistinku zaliatu mesačným svetlom.

Vnorené voľby

experimentalod 1.0* * [Deeper] · - gather

Vnáranie v štýle weave, kde sa vetvy opäť zbiehajú v bode zberu zodpovedajúcej hĺbky.

Voľby sa dajú vnárať pribúdajúcimi * alebo +:

rea
* [Prihovor sa cudzincovi]
  „Kto si?" opýtaš sa.
  * * [Zatlač viac]
    „Povedz mi svoje pravé meno!"
  * * [Nechaj to tak]
    „To nič. Zabudni, že som sa pýtal."
  - - Cudzinec sa nepokojne pomrví.
* [Ignoruj cudzinca]
  Prejdeš okolo bez slova.
- Noc pokračovala v tichu.

- - slúži ako bod zberu — miesto, kde sa vnorené vetvy opäť zbiehajú (inšpirované systémom weave v jazyku Ink).

Body zberu

Zbery používajú - na príslušnej úrovni vnorenia a zhromažďujú všetky vetvy späť dokopy:

rea
Čo urobíš?

* [Bojuj]
  Vytasíš zbraň!
* [Uteč]
  Otočíš sa a bežíš!
* [Vyjednávaj]
  „Nemôžeme sa o tom porozprávať?"

- Nech si zvolil čokoľvek, výsledok bol rovnaký: problémy si ťa našli.

Záložné voľby

experimentalod 1.0* ->

Možnosť bez textu, ktorá sa vyberie automaticky, keď už nič iné nie je použiteľné.

Voľba bez textu funguje ako záložná (vyberie sa automaticky, keď neostanú žiadne iné možnosti):

rea
* [Opýtaj sa na počasie]
  „Pekný deň, však?"
* [Opýtaj sa na novinky]
  „Počul si niečo zaujímavé?"
* ->
  Rozhovor vyšumel. -> leave_tavern

Rozparsuje sa, ale zatiaľ sa nevyberá sama Záložná voľba sa rozpozná ako možnosť a nevykreslí žiadne tlačidlo, ale runtime ju zatiaľ nevyberie, keď ostatné dôjdu — flow/fallback-choice-taken nemá emitor. Skupina, ktorá sa na ňu spolieha, jednoducho skončí bez čohokoľvek, na čo sa dá kliknúť. Dovtedy nechajte čitateľovi viditeľnú cestu ďalej. :::

Tunely (odboč a vráť sa)

experimentalod 1.0->-> examine_lock

Odbočka do sekcie, ktorá sa automaticky vráti volajúcemu — mechanizmus, ktorý znovu využívajú spúšťané storylety.

Tunel odbočí do sekcie a po jej skončení sa automaticky vráti volajúcemu. Do tunela vstúpite pomocou ->->:

rea
Blížiš sa k zamknutým dverám.
->-> examine_lock
Po prehliadke zvažuješ možnosti.

* [Vylom zámok]
  ->-> pick_lock_sequence
  Dvere sú otvorené!
* [Nájdi inú cestu]
  -> alternative_path

Tunelovaná sekcia sa vracia pomocou ->-> na svojom konci (alebo jednoducho tým, že dosiahne posledný riadok):

rea
[#examine_lock]
Zámok je starý a hrdzavý. Železný, s jednoduchým mechanizmom.
->->

[#pick_lock_sequence]
Vytiahneš náradie a pustíš sa do práce.
{if dexterity > 5 begin}
  Kolíčky hladko zapadnú na miesto.
{else}
  Trvá to niekoľko pokusov, ale nakoniec…
{end if}
->->

Tunely sa hodia na znovupoužiteľné pasáže (napr. opakované obhliadky, spoločné dialógové sekvencie) bez ručného smerovania späť.

Tunel a storylet riešia odlišné problémy a siahnite po každom zámerne: tunel je znovupoužiteľný blok obsahu viazaný na aktuálnu kapitolu — autor explicitne píše miesto volania (->->) a tunel sa vždy vráti presne na to miesto. Storylet vyberá jadro, autor ho nevolá: ťahá sa z celopríbehového zásobníka cez {deck} alebo ho zobudí vstup z reálneho sveta cez trigger=, na základe require/priority/cooldown, nie pevného miesta volania v texte.

Obsah pri prvej návšteve

experimentalod 1.0{once begin} … {then} … {end once}

Hlavný obsah pri prvom stretnutí, voliteľná náhrada pri každom ďalšom.

Zobrazte obsah len pri prvej návšteve pasáže, s voliteľnou náhradou pre ďalšie návštevy:

rea
[#the_tavern]
{once begin}
  V krčme je teplo a rušno. V rohu hrá bard.
  Nič také si ešte nevidel.
{then}
  Známa krčma. Bard pri tvojom príchode prikývne.
{end once}

Krčmár ti kýva, aby si prišiel.

Blok {once begin} vykreslí svoj hlavný obsah pri prvom stretnutí a náhradu {then} pri všetkých ďalších návštevách. Ak {then} chýba, po prvej návšteve sa nezobrazí nič.

Nahrádzanie textu (živé návestia)

development{label door begin}locked{end label} · {replace door = "open"}

Návestia sa dnes parsujú a sledujú, ale {replace} ich zatiaľ neprepisuje na mieste — práve sa stavia cesta aktualizácie na mieste cez renderer.

Návestia označujú text, ktorý sa dá počas príbehu nahradiť priamo na mieste:

rea
Dvere sú {label door_state begin}zamknuté{end label}.

{comment Neskôr, po odomknutí}
{replace door_state = "otvorené"}

V kombinácii s voľbami pre interaktívne odhalenie:

rea
Na stene vidíš {label clue begin}záhadný symbol{end label}.

* [Prezri si symbol]
  {replace clue = "runu ochrany"}
  Samozrejme — je to runa ochrany!

Cyklický text (klepnutím ďalej)

development{cycle color begin}red|blue|green{end cycle}

Hodnoty cyklu a sprístupnená premenná fungujú; ovládanie klepnutím na posun v čítačke sa ešte stavia.

Text v riadku, ktorým môže čitateľ klepaním prechádzať medzi možnosťami; hodí sa na prispôsobenie postavy alebo objaviteľské rozprávanie:

rea
Vybral si si {cycle color begin}červený|modrý|zelený|čierny{end cycle} plášť.

Čitateľ klepe na zvýraznené slovo a cyklí: červenýmodrýzelenýčiernyčervený → …

Vybraná hodnota je dostupná ako premenná: {color} vráti aktuálny výber.

Meniaci sa text

experimentalod 1.0{a|b|c} · {&cycle} · {!once} · {~shuffle}

Varianty sekvencie, cyklu, jedného zobrazenia a náhodného poradia riadené počtom návštev; premiešanie čerpá zo seedovaného generátora príbehu, takže opakované prehratie je reprodukovateľné.

Text sa môže meniť podľa počtu návštev pomocou | vnútri { }:

rea
{Vstúpiš do krčmy.|Vraciaš sa do krčmy.|Zase krčma. Začína to byť zvyk.}

Režimy:

PrefixSprávanie
(žiadny)Sekvencia — prehráva po poradí, zastane na poslednom
&Cyklus — opakuje sa donekonečna
!Raz — každý prehrá raz, potom nič
~Premiešanie — náhodné poradie
rea
Bol {&pondelok|utorok|streda|štvrtok|piatok|sobota|nedeľa}.

Zasmial sa. {!Úprimný smiech.|Zdvorilé zachichotanie.|Tentoraz sa nezasmial.}

Minca padla na {~hlavu|znak}.

Vzor rozbočovač a lúče

Centrálna kotva rozbočovača, do ktorej sa čitatelia vracajú po preskúmaní vetiev. V spojení s {once begin} každá vetva pridá rozbočovaču nový kontext:

rea
[#town_square]
Stojíš na námestí.

{once name=visit_market begin}
  * [Navštív trh]
    Preskúmaš rušné trhové stánky.
    {set story.flag.visited_market = true}
    -> town_square
{end once}

{once name=visit_temple begin}
  * [Vstúp do chrámu]
    V chráme je ticho a chlad.
    {set story.flag.temple_blessing = true}
    -> town_square
{end once}

{if story.flag.visited_market and story.flag.temple_blessing begin}
  * [Vyraz k hradu]
    So zásobami aj požehnaním si pripravený.
    -> castle_gates
{end if}

Paralelné dejové línie

draft{parallel begin} {thread a begin} … {end parallel}

Nezávislé vlákna, ktoré sa zbiehajú v bode stretnutia. Prediskutované pre kooperatívne čítanie, ale nepostavené — nič v parseri zatiaľ nepozná {parallel} ani {thread}.

Viaceré dejové línie, ktoré napredujú nezávisle a v kľúčových chvíľach sa zbiehajú:

rea
{parallel begin}
  {thread elena_thread begin}
    [#elena_journey]
    Elena putuje na západ cez les.
    {set story.elena.location = "forest"}
    {wait gareth_thread.reached("bridge") begin}{end wait}
    Stretnú sa pri moste.
  {end thread}

  {thread gareth_thread begin}
    [#gareth_journey]
    Gareth sa vydá horskou cestou.
    {set story.gareth.location = "mountain"}
    [#bridge]
    Dorazí k starému kamennému mostu.
  {end thread}
{end parallel}

V kooperatívnom čítaní môžu rôzni čitatelia sledovať rôzne vlákna súčasne a zažívať príbeh z pohľadu rôznych postáv.

Karty a balíčky (naratív riadený kvalitami)

neznáma funkcia

Pod identifikátorom storylets nie je v súbore .vitepress/data/features.ts zaregistrovaná žiadna funkcia.

Karta je líce plus telo a balíček je ich pomenovaná zásoba. Výber beží na when, priority, repeatable, cooldown, weight a tags; {draw} alebo {play} rozdá ruku a kartu bez balíčka môže zobudiť vstup z reálneho sveta cez trigger= ako vedľajšiu cestu, ktorá sa vráti presne tam, kde čitateľ prestal. Karty majú vlastnú stránku: pozri Karty a balíčky.

Menu objavovania

experimentalod 1.0{menu select=2 begin} … {end menu}

Skupina volieb, ktorá čaká na viacero objavov namiesto jedného výberu; politika select=all aj pravidlo priority „menu má prednosť pred storyletom" sú vydané.

Skupina volieb môže byť aj skrytým menu objavovania — množinou skrytých volieb, ktoré sa zobudia, až keď čitateľ vytvorí zodpovedajúci vstup z reálneho sveta: naskenuje QR kód, odfotí ručne kreslenú značku, vysloví frázu alebo napíše opis:

rea
{menu select=2 begin}
* hidden [&qr_door] Služobné dvere cvaknú a otvoria sa…
* hidden [&painted_tree] Namaľovaný strom sa zaligoce…
* hidden [&couch_secret] Pod gaučom nájdeš obálku…
* [Vzdaj to a pokračuj]
{end menu}

Obalenie skupiny volieb do {menu select=N begin} … {end menu} mení, na koľko objavov skupina počká, kým príbeh pôjde ďalej:

Hodnota select=Správanie
(vynechané)Bežná skupina s jedným výberom — bez zmeny oproti dnešku
NPredkladá skupinu znovu po každej aktivácii, kým sa nevyberie N možností alebo kým žiadna nezostane použiteľná
allZostáva otvorené, kým je použiteľná aspoň jedna možnosť

Každá aktivácia prehrá rozprávanie a účinky danej možnosti presne ako klepnutá voľba — {set}, {give}, odbočky, všetko ide tou istou cestou. Jednorazová možnosť (*) po výbere z výberu odchádza; opakovateľná (+) zostáva dostupná. Viditeľná možnosť môže menu ukončiť skôr odbočením inam, alebo sa jednoducho počíta do N ako každý iný výber.

Aktivačné kanály — scan=, mark=, listen= — sa deklarujú na odkazovanej karte (pozri Aktiváciu v reálnom svete v kapitole Karty), nie na samotnom riadku voľby. Ten istý odkaz [&card_id] aj príznak hidden fungujú, či sa karta zobudí klepnutím, naskenovaním, značkou alebo hlasom.

Krok späť a uloženia vnútri menu

Každý objav je samostatne zaznamenaná voľba, takže krok späť sa vracia po jednom objave — krok späť vnútri menu so select=2 vráti tesne pred poslednú aktiváciu a skorší objav zostane na mieste. Uloženia vytvorené uprostred menu pokračujú s tou istou množinou zostávajúcich použiteľných možností.

Priorita pri spúšťačoch storyletov

Jedno naskenovanie, vyslovená fráza či fotka nakreslenej značky môže znamenať len jednu vec. Ak má čitateľ vo chvíli, keď taký vstup vytvorí, otvorené čakajúce menu objavovania, kontroluje sa najprv menu; len ak v menu nič nesedí, vstup prepadne ďalej a zobudí storylet (pozri Interakcie s reálnym svetom).

Pozri Storylety a balíčky pre výber storyletov, spúšťače a prioritu/váhu.

Krok späť a spätná navigácia

draft{undo enabled=false}

Runtime si drží zásobník krokov späť na úrovni volieb, ale príkaz {undo}, ktorým autor môže krok späť pre hádankovú sekciu vypnúť, je len špecifikovaný — kooperatívne pravidlá okolo neho nie sú vyriešené.

Platforma poskytuje vstavanú spätnú navigáciu, ktorá čitateľom umožňuje vrátiť sa k predchádzajúcim pasážam. Autori toto správanie môžu riadiť:

rea
{undo enabled=false}

Predvolene je krok späť zapnutý pre sólo čítanie a vypnutý pre kooperatívne čítanie (zdieľaný stav sa nedá vrátiť). Autori ho môžu výslovne vypnúť pre hádankové sekcie, kde by krok späť poprel celý zmysel:

rea
{lock condition="has_key" begin}
  {undo enabled=false}
  Dvere za tebou zapadnú. Niet cesty späť.
  {comment Čitateľ sa nemôže vrátiť za tento bod, kým sa sekcia so zámkom neskončí}
{end lock}

Krok späť funguje na úrovni volieb — každá voľba čitateľa vytvára bod obnovenia. Krok späť vráti všetky zmeny premenných od poslednej voľby.

Krok späť pri kooperatívnom čítaní

Krok späť je v kooperatívnom režime predvolene vypnutý, pretože zdieľaný stav sa nedá vrátiť jednostranne. Ak autor krok späť v kooperatívnom čítaní výslovne zapne ({undo enabled=true}):

  • Krok späť ovplyvňuje len miestny stav jednotlivého čitateľa — jeho premenné, pozíciu a inventár.
  • Zdieľané premenné (shared.*) sa krokom späť nikdy nevracajú. Keď je zdieľaná premenná raz nastavená, zostáva nastavená pre všetkých.
  • Ak sa čitateľ vráti za blok {exclusive}, ktorý si nárokoval, výhradný zámok sa neuvoľní — závisí od neho stav ostatných čitateľov.
  • Ak sa čitateľ vráti za {vote}, ktorého sa zúčastnil, jeho hlas sa nestiahne — výsledok hlasovania platí.
  • Zásobník krokov späť je obmedzený na aktuálnu kapitolu. Čitatelia sa nemôžu vrátiť cez hranicu kapitoly.

Kontrolné body

Automatické ukladanie prebieha pri každej voľbe a autor môže výslovné body obnovenia označiť príkazom {checkpoint}. Keďže kontrolný bod je vecou stavu, nie rozprávania, je celý špecifikovaný — vrátane toho, čo presne snímka zachytáva a ako uloženia prežijú aktualizáciu príbehu — v časti Uloženie a kontrolné body.

Viacdielne príbehy

experimentalod 1.0manifest.parts + gates + cross-part links

Časti sa načítavajú na požiadanie, premenné sa hromadia pozdĺž prejdenej cesty a uložená cesta pri obnovení znovu vybuduje históriu čítania.

Dlhší príbeh sa dá rozdeliť na časti príbehu — samostatné súbory .rea uvedené v manifeste balíka ako parts (schému manifestu nájdete v časti 5). Čitateľ prechádza postupnosťou častí: živým dokumentom je len aktuálna časť a posúvanie nahor odhaľuje už navštívené časti — skutočne prejdenú cestu, nikdy nezvolenú vetvu. Medzi časťami sa dá presúvať dvoma spôsobmi.

experimentalod 1.0[[ story/0005-forest.rea ]]

Automatický, koncový prechod medzi časťami viacdielneho príbehu; vydané, pričom prípona :scene v rámci časti sa ešte ustaľuje.

Brána [[ cieľ ]] — automatický prechod bez textu. Zaberá vlastný riadok a je koncová: keď k nej tok dorazí, nič za ňou sa v aktuálnej časti nevykreslí a brána označuje, kde príbeh pokračuje. Posunutím za koniec aktuálnej časti sa odhalí ohraničená časť priamo v texte, ako plynulé pokračovanie.

rea
Prejdeš cez oblúk; niet cesty späť.

[[ story/0005-forest.rea ]]

Keďže brána časť ukončuje, obsah umiestnený za ňou je nedosiahnuteľný — editor to označí ako varovanie. Brána môže mieriť na scénu v rámci časti pomocou [[ part.rea:scene ]] a pokračovať pri kotve [#scene]. Brány vnútri {if} vyjadrujú vetvenie riadené premennou bez ručnej voľby:

rea
{if has_key begin}
[[ story/0006-castle.rea ]]
{end if}
{if not has_key begin}
[[ story/0006-bush.rea ]]
{end if}
experimentalod 1.0[enter the castle > story/0006-castle.rea]

Bežný odkaz, ktorého cieľom je súbor inej časti, takže čitateľ sa môže posunúť ďalej klepnutím.

Odkaz medzi časťami — bežný navigačný odkaz, ktorého cieľom je súbor časti, dovolí čitateľovi posunúť sa ďalej klepnutím:

rea
Pred tebou sa dvíha [vstúp do hradu > story/0006-castle.rea].

Premenné sa prenášajú medzi časťami: príkazy {set} najvyššej úrovne každej časti sa spustia raz pri jej vstupe, navrch stavu nahromadeného doteraz. Uložený postup zaznamenáva usporiadanú cestu navštívených častí plus aktuálnu časť a pozíciu v nej, takže pokračovanie prehrá navštívené časti pre históriu čítania a v aktuálnej časti nadviaže tam, kde čitateľ prestal (pozri časť 5, Stav čítania).


17. Karty: postavy, predmety a akcie

Karty sú interaktívne prvky príbehu, na ktoré môže čitateľ klepnúť a prezrieť si ich. Oživujú svet príbehu za hranicou obyčajného textu.

Karty postáv [@]

experimentalod 1.0[@elena] · {define character elena name="Elena Voss"}

Klepnuteľné odkazy na postavy podložené definičným blokom s portrétom, titulom a popisom.

rea
[@elena]
Pri fontáne vidíš stáť [@elena].

Karty postáv sa definujú v metadátach alebo vo vyhradenom bloku:

rea
{define character elena name="Elena Vossová", title="Potulná učenkyňa", image="media/elena.png", description="Vysoká žena so striebrom pretkanými vlasmi a od atramentu zafarbenými prstami."}

Keď čitateľ klepne na [@elena], uvidí kartu postavy s portrétom, menom, titulom a popisom.

Karty predmetov [$]

experimentalod 1.0[$golden_key] · {give …} · {take …}

Predmety, čitateľovo vrecko a obsluhy {on acquire item=…} / {on lose item=…}.

rea
Na zemi nájdeš [$golden_key].

{define item golden_key name="Zlatý kľúč", image="media/golden_key.png", description="Zdobený kľúč, na dotyk teplý. Zdá sa, akoby ticho hučal.", rarity=rare}

Predmety sa dajú pridať do inventára čitateľa:

rea
{give golden_key}
{take golden_key}
{if "golden_key" in story.reader.inventory begin}
  Kľúč ti vo vrecku hreje.
{end if}

Viac kusov sa zadáva cez count=. Holé číslo nie je počet — ignoruje sa a nedá sa nič:

rea
{give sip count=12}   {comment 12 šípov}
{give sip}            {comment 1 šíp}
{give sip 12}         {comment ZLE — nedá vôbec nič}

Mince a peňaženka

experimentalod 1.0{earn gold 2} · {spend bronze 3}

Trojúrovňová peňaženka s pevnými internými názvami, aby uloženia zostali prenosné, a s označeniami a pomermi, ktoré autor môže premenovať.

Príbehy, ktoré potrebujú peniaze, používajú vstavanú peňaženku mincí. Má tri úrovne — gold, silver, bronze — s pevným základným pomerom 1 gold = 10 silver = 100 bronze. Interné názvy úrovní sa nikdy nemenia (aby súbory uložení zostali prenosné), ale autori môžu premenovať označenia zobrazené čitateľovi a upraviť prepočtové pomery:

rea
{coins gold="Dukát" silver="Groš" bronze="Halier"}
{coins silver_per_gold=5 bronze_per_silver=4}

{earn gold 2}
{earn silver 5}
{spend bronze 3}

{if story.reader.coins.total >= 100 begin}
  Na začarovanú čepeľ máš.
{end if}

{spend} automaticky rozmieňa vyššie nominály, keď čitateľ nemá presnú úroveň, a odmietne (bez akejkoľvek zmeny), keď peňaženka na cenu nestačí. Zostatok sa zrkadlí do premenných dostupných čitateľovi a pretrváva medzi uloženiami:

PremennáObsah
story.reader.coinsNormalizovaný zostatok {gold, silver, bronze, total}
story.reader.coins.totalCelková hodnota v bronzových základných jednotkách
story.reader.coinNamesAutorské označenia {gold, silver, bronze}

Karty akcií [&]

experimentalod 1.0[&open_the_gate]

Body vetvenia s vizuálnym dôrazom, ktorých description: zároveň slúži ako sémantický cieľ pre voľný textový vstup.

Karty akcií predstavujú body vetvenia príbehu s vizuálnym dôrazom:

rea
[&open_the_gate] Otvor starodávnu bránu
[&climb_the_wall] Radšej prelez múr

Poznámka: Karty akcií používajú & (ampersand), aby sa odlíšili od vlastných kotiev, ktoré používajú [#nazov].

Podobne ako karty postáv a predmetov môže aj akcia niesť blok {define action} s názvom a popisom:

rea
{define action open_the_gate name="Starodávna brána", description="otvor hrdzavú bránu; násilím otvor starú bránu; pretlač sa cez vchod"}

description= sa zobrazí na karte a zároveň slúži ako sémantický cieľ pre voľný textový vstup akcie — teda to, čo môže čitateľ napísať, aby akciu pomenoval.

Aktivácia v reálnom svete

developmentscan= · mark= · listen=

Polia karty sa parsujú a porovnávajú, ale strana snímania závisí od čitateľskej aplikácie: skenovanie a reč sa stavajú a mark: podpisy potrebujú kresliaci nástroj editora, ktorý zatiaľ nie je vydaný.

Karta akcie sa môže zobudiť aj vstupom z reálneho sveta namiesto klepnutia — alebo popri ňom. Vedľa description= stoja tri voliteľné polia:

rea
{define action qr_door name="Služobné dvere", scan="^REAST-DOOR-.*"}

{define action painted_tree name="Namaľovaný strom", mark="emb1:Zk3q…                      // podpis vypočítaný editorom z kresby"}

{define action couch_secret name="Pod gaučom", description="pozri sa pod starý gauč; nadvihni pohovku; prehľadaj priestor pod sedadlom", listen="pod gaučom"}
PolePorovnáva sa sPorovnanie
scan=Obsahom naskenovaného QR alebo kóduRegulárny výraz bez ohľadu na veľkosť písmen
listen=Prepisom rečiRegulárny výraz bez ohľadu na veľkosť písmen
mark=Odfotenou ručne kreslenou značkouPresná zhoda podpisu

Karta môže tieto polia ľubovoľne kombinovať — couch_secret vyššie odpovedá na napísaný opis aj na vyslovenú frázu.

mark= je nepriehľadné. Jeho hodnota je podpis, ktorý nástroj editora „Nakresli značku" vypočíta z kresby alebo fotografie — nikdy ho nepíšte ani neupravujte ručne. Ak chcete značku vytvoriť alebo zmeniť, prekreslite ju v editore; pracovný postup nájdete v Menu objavovania v reálnom svete.

Tieto polia vyniknú, keď je možnosť prehrávajúca kartu hidden — pozri Menu objavovania v kapitole Voľby a vetvenie. Viditeľná možnosť s aktivačnými poľami odpovedá na oboje: čitateľ môže klepnúť na jej tlačidlo alebo vytvoriť zodpovedajúci vstup z reálneho sveta.

Sady a kategórie kariet

experimentalod 1.0{define cardset ability name="Ability Cards"}

Kategórie kariet deklarované autorom so spoločnými pravidlami získania, straty a použitia a s háčikmi na úrovni sady, ktoré karta môže prekryť.

character, item a action sú tri vstavané sady kariet. Autori môžu deklarovať ďalšie sady a zoskupiť tak karty, ktoré zdieľajú rovnaké pravidlá získania, straty a použitia — napríklad sadu ability, sadu attribute alebo tematickú sadu relic. Sada sa deklaruje blokom {define cardset <id> begin}:

rea
{define cardset ability name="Karty schopností", description="Karty udeľujúce vlastnosti, ktoré si hrdina môže vybaviť.", acquire="Získavajú sa dokončením úloh.", lose="Strácajú sa, keď je postava porazená.", use="Zahraním sa uplatní uvedený bonus k vlastnosti."}

Sada môže niesť ľudsky čitateľné polia pravidiel acquire, lose a use plus ľubovoľné ďalšie vlastnosti kľúč: hodnota. id sady sa stáva druhom každej karty, ktorá do nej patrí.

Karta do sady vstúpi tým, že sa použije id sady tam, kde by inak stálo character, item alebo action:

rea
{define ability spinach name=Špenát, strength=+2}

Obsluhy udalostí

To, čo karta robí, sa píše ako vrcholový blok {on <udalosť> <predmet> begin} ... {end on}. Predmet je jediný atribút — card=, item=, deck= alebo set= — takže pravidlo pre jednu kartu, pre celý balíček aj pre celú sadu sa píše rovnako a obsluha sa dá prečítať bez toho, aby čitateľ vedel, v ktorom bloku leží. Karta, balíček a sada rozumejú udalostiam acquire, lose, use a missed.

Obsluha na sade sa vykoná pri každej karte danej sady:

rea
{define cardset ability name="Karty schopností"}

{on acquire set="ability" begin}
  {set story.ability_count = story.ability_count + 1}
{end on}

{on use set="ability" begin}
  {set story.last_ability_used = event.card_id}
{end on}

Karta pridáva svoje pravidlo vedľa pravidla svojej sady, nenahrádza ho:

rea
{define ability ginko name=Ginko, intelligence=+2}

{on use card="ginko" begin}
  {set story.player.intelligence = story.player.intelligence + 2}
{end on}

Obsluha môže niesť klauzulu when, ktorá beží až po begin}, takže podmienka si ponechá vlastné čiarky a úvodzovky:

rea
{on lose set="ability" when story.act >= 3 begin}
  Vedomosť sa nadobro stratí.
{end on}

Poradie rozlíšenia: vykoná sa každá zodpovedajúca obsluha, od najmenej konkrétnej — najprv sada, potom balíček, nakoniec samotná karta. Prekrytie sa píše ako stráž when, nie ako predefinícia.

Zahranie karty

experimentalod 1.0{play ginko}

Vykoná každú obsluhu {on use}, ktorá karte zodpovedá — obsluhu sady pred vlastnou — a vyšle udalosť card-played, ktorú môžu hostitelia sledovať.

{play <card_id>} spustí použitie karty. Vykoná každú obsluhu {on use}, ktorá karte zodpovedá — najprv obsluhu jej sady, potom jej vlastnú — takže karta vlastnosti uplatní svoju vlastnosť a karta akcie spustí svoj účinok tým istým príkazom:

rea
{play ginko}        Spustí obsluhu sady ability, potom vlastnú obsluhu karty ginko
{play spinach}      Spustí samotnú obsluhu sady ability

play= rozhoduje, či sa karta dá zahrať znovu. Karta, jej balíček aj jej sada môžu deklarovať jeden z troch životných cyklov a vyhráva vlastný cyklus karty, potom balíčka, potom sady:

HodnotaČo sa stane po zahraní
reusableKarta sa vráti a dá sa zahrať ľubovoľne veľakrát (predvolené)
exhaustedKarta je odložená, kým ju niečo nevráti — nové kolo, nová scéna
consumedKarta je pre toto čítanie preč

Opakované zahranie spotrebovanej alebo odloženej karty nespraví nič a udalosť card-played nesie životný cyklus, takže hostiteľ môže kartu ukázať ako minutú. consumed znamená preč pre toto čítanie, nikdy nie navždy: príbeh, ktorý schová obsah za neopakovateľné ťahanie, je vzorec, ktorý čitatelia neznášajú najviac, a preto prehľad zbierky ukazuje, čo existuje, vedľa toho, s čím sa tento čitateľ stretol.

Identifikátory kariet môžu obsahovať písmená, číslice, spojovníky a podčiarkovníky. Zahranie neznámej karty nespraví nič. Každé úspešné zahranie vyšle behovú udalosť card-played nesúcu id karty a druh jej sady, ktorú môžu hostitelia sledovať a aktualizovať podľa nej rozhranie.

Predefinovanie vstavaných sád

Tri vstavané sady sa dajú predefinovať a pripojiť im spoločné pravidlá bez zmeny toho, ako sa ich karty píšu. Predefinovanie action s pridaním ceny za použitie platí pre každú kartu akcie [&]:

rea
{define cardset action name="Bojové akcie", use="Na zahranie minie akčný bod."}

{on use set="action" begin}
  {set story.actions_played = story.actions_played + 1}
{end on}

Keď sa autorská redefinícia zrazí s implicitnou vstavanou sadou, vyhráva deklarácia autora.

Hodnoty vlastností kariet

Vlastnosť karty je jedna z dvoch vecí a rozhoduje o tom úvodzovkovanie. Hodnota bez úvodzoviek, ktorá je úplným literálom jazyka Rea — číslo, pravdivostná hodnota, bod @(lat, lng), pole — je typovaná vlastnosť: skutočná hodnota, s ktorou vie príbeh porovnávať a počítať. Všetko ostatné je doslovný text, uložený presne tak, ako je napísaný, s jedinou úpravou: zástupný symbol {premenná} sa pri každom dopyte na kartu nahradí aktuálnou hodnotou tejto premennej — práve to umožňuje karte zobraziť živú štatistiku alebo odomknutú úroveň ilustrácie.

rea
{define character elena name="Elena Voss", level="{story.elena.level}", home="@(48.14, 17.10)"}

level je text vzniknutý nahradením premennej, nie číslo. home je v úvodzovkách, takže je to text @(48.14, 17.10), nie bod — pripomína literál súradnice bez toho, aby ním bol. Bez úvodzoviek by ním bol.

Typované vlastnosti kariet

experimentalod 1.5weight=3 · home=@(48.14, 17.10) · story.card.lantern.weight

Atribút karty bez úvodzoviek, ktorý je úplným literálom jazyka Rea, nesie skutočné číslo, pravdivostnú hodnotu, bod alebo pole a číta sa späť ako story.card.<id>.<prop>. Úvodzovky nechajú hodnotu zobrazovaným textom, takže sa obe čítania nikdy neprekrývajú. Kópia je iba na čítanie a drží len typované vlastnosti.

Karta je pre príbeh jediným zdrojom pravdy o narratívnej entite a typovaná vlastnosť je spôsob, ako si príbeh tú pravdu prečíta späť. weight=3 na predmete je číslo 3, nie text 3, takže vstúpi do porovnania alebo do súčtu bez toho, aby sa najprv zduplikovalo do premennej — a zduplikovaná hodnota je hodnota, ktorá sa rozíde: karta a logika si prestanú odpovedať v momente, keď sa upraví jedna z nich.

Rozhoduje úvodzovkovanie a obe čítania sa nikdy neprekrývajú.

NapísanéVýsledok
weight=3číslo 3 — typované
weight=-1.5číslo -1.5 — typované
lit=falsepravdivostná hodnota false — typované
home=@(48.14, 17.10)bod — typované
sizes=[1, 2, 3]pole čísel — typované
traits=[brave, literate]zoznam reťazcov — traits/tags spracúva jazyk sám ako zoznam
weight="3"text 3 — iba na zobrazenie
level="{story.elena.level}"text, nahrádzaný pri každom čítaní
rarity=raretext rare — holé slovo je identifikátor, nie literál
home="@(48.14, 17.10)"text, ktorý pripomína súradnicu

Zástupný symbol {premenná} nie je literál, takže hodnota, ktorá ho nesie, nemôže byť nikdy typovaná a typovaná hodnota ho nemôže nikdy niesť. Neexistuje hodnota, na ktorú by platili obe čítania, a teda ani pravidlo prednosti, ktoré by sa bolo treba učiť. Úvodzovky okolo čísla sú zároveň spôsob, ako si autor udrží zobrazovaný reťazec ako "007" alebo "3+".

Typovaná vlastnosť sa číta ako story.card.<id>.<prop>, rovnako ako sa počítadlá balíčka čítajú ako story.deck.<id>.remaining:

rea
{define item lantern name="Brass Lantern", weight=3, lit=false}
{define character elena name="Elena Voss", home=@(48.14, 17.10), traits=[brave, literate]}

{if story.card.lantern.weight > 2 begin} Ťahá ti opasok nadol. {end if}
{set story.load = story.load + story.card.lantern.weight}
{if distance(story.reader.position, story.card.elena.home) < 500 begin} Je blízko. {end if}
{if "brave" in story.card.elena.traits begin} Ide prvá. {end if}

Pre túto cestu platia tri pravidlá:

  • Iba na čítanie. {set story.card.…} je odmietnutý a ohlásený. Zdrojom pravdy je definícia; zapisovateľná kópia by znovu otvorila presne to rozchádzanie, ktoré táto funkcia zatvára.
  • Iba typované vlastnosti. Textová vlastnosť nemá cestu: story.card.elena.name neexistuje. Zobrazovaný text karty je zobrazovaný text a jeho sprístupnenie by spravilo z prózy každej karty premennú a z každého premenovania rozbitý výraz.
  • Meno sa overuje voči karte, nie voči vzoru. Neznáme id karty aj karta bez takej typovanej vlastnosti sa hlásia samostatne, takže story.card.lantern.wieght sa zachytí a nerozplynie sa ticho do prázdna.

Karta definovaná až nižšie v súbore sa stále vyrieši: vlastnosti sa zrkadlia pri registrácii karty, čo je priechod v poradí dokumentu a beží pred čítaním.

Typovaná hodnota je literál, nie výraz. Je pevne daná v čase definície: weight={story.base_weight} je text a weight=story.base_weight je tiež text, pretože holý identifikátor nie je literál. Karta, ktorá musí ukazovať živé číslo, si ponechá textový tvar so {premennou} alebo blok {face begin}, ktorý sa vyhodnocuje priamo pri každom čítaní. Holý príznak (mandatory) je pravdivostná hodnota true, ako bol vždy. Hodnota, ktorá má tvar literálu, ale nedá sa z nej hodnotu postaviť — zemepisná šírka mimo rozsahu — zostáva textom.

Vlastné polia jazyka sa nikdy netypujú: name, title, image, description, scan, mark, listen, play, deck, role, require, trigger a match majú vlastné spracovanie, takže name=3 je reťazec 3. traits a tags sa dostanú do story.card.<id>.traits (alebo .tags) zo zoznamu, ktorý jazyk už spracoval, pod menom, ktoré napísal autor.

Pripísanie repliky

experimentalod 1.0@elena: "Follow me."

Spája riadok reči s kartou postavy, čo je zároveň to, čo umožňuje automatické priradenie hlasu.

Repliku pripíšete pomocou @id_postavy: na začiatku riadku. Spája reč s kartou postavy a umožňuje automatické priradenie hlasu:

rea
@elena: „Mapa vedie k severnej veži. Musíme sa ponáhľať."
@gareth: „Si si tým istá? Tú oblasť hliadkujú strážcovia."
@elena: „Ver mi. Poznám cestu cez záhrady."

Platforma použije nastavenia hlasu definované pri postave (z {define character}) a syntézu reči vykreslí automaticky. Keď hlas definovaný nie je, platforma priradí odlišný hlas na základe vlastností postavy.

Pripísanie repliky funguje aj s rozprávaním v tom istom riadku:

rea
@elena: „Poď za mnou," zašepkala a vkĺzla do tieňov.
@gareth: Zaváhal. „Mám z toho zlý pocit."

Anonymní alebo nepomenovaní hovoriaci používajú opis:

rea
@stranger: „Tu by si nemal byť."
@crowd: „Nech žije kráľ!"

18. Hlas a zvuk

Prevod textu na reč

development{voice speaker="elena", emotion="whisper" begin}

Príkaz sa parsuje a dorazí k hostiteľovi ako požiadavka, ale samotnú syntézu reči dodáva čitateľská aplikácia, ktorá sa ešte stavia — dnes sa nič nevysloví.

Príkaz {voice} riadi vykreslenie syntézy reči:

rea
{voice speaker="narrator", speed=5, pitch=5 begin}
  Na počiatku nebolo nič, len ticho.
{end voice}

{voice speaker="elena", emotion="whisper", speed=3 begin}
  Počuješ to? Steny počúvajú.
{end voice}

Atribúty hlasu:

AtribútRozsah/HodnotyPredvolenéPopis
descriptionreťazecVoľný opis želaného hlasu (napr. "Hlboký, melancholický mužský hlas")
speakerreťazec"narrator"Identita hlasu alebo opis postavy
speed1–95Tempo reči
volume1–95Hlasitosť
pitch1–95Výška hlasu
emotionreťazec(neutrálne)Emocionálne zafarbenie (v jazyku textu): whisper, shout, sad, excited, angry, calm
tonereťazecCelkový tón reči: formal, informal, friendly, authoritative, narrative
pause1–9Pauza pred prehovorením (1 = najkratšia, 9 = najdlhšia)

Na predvolené hodnoty sa vrátite zavolaním {voice} bez atribútov.

Prehrávanie zvuku

draft{audio src=…, loop} · {stop ambient_music}

Pomenovaný, ovládateľný zvuk na pozadí. Vložený zvuk cez [?caption < src] funguje už dnes; príkazová forma, ktorá spúšťa a zastavuje pomenovanú stopu, je len špecifikovaná.

rea
{audio src="media/thunder.ogg", volume=0.8}
{audio src="media/ambient.ogg", loop, volume=0.3, name=ambient_music}
{stop ambient_music}

19. Vstup a interakcia

Textový vstup

experimentalod 1.0{input name=guess, type="number", min=1, max=100}

Vykonávanie sa pozastaví, kým čitateľ neodošle odpoveď; číselný vstup sa oreže na svoje hranice.

rea
{input name=player_name, placeholder="Zadaj svoje meno"}
Ahoj, {player_name}!

Správanie vstupu: Vykonávanie sa pri {input} pozastaví, kým čitateľ neodošle hodnotu. Hodnota sa uloží do premennej určenej v name. Ak čitateľ odošle prázdnu hodnotu, premenná sa nastaví na prázdny reťazec "".

Číselný vstup

rea
{input name=guess, type="number", min=1, max=100, placeholder="Hádaj číslo"}

Číselný vstup sa overuje proti obmedzeniam min a max. Hodnoty mimo rozsahu sa orežú na najbližšiu hranicu. Nečíselný vstup padne na 0.

Voľný textový vstup akcie

experimentalod 1.0{input type="action", placeholder="What do you do?"}

Čitateľ napíše, čo chce urobiť, a spustí sa najlepšie zodpovedajúca možnosť; porovnávanie prebieha výhradne na zariadení, takže veta ho nikdy neopustí.

{input type="action"} mení scénu z menu na miesto: čitateľ vlastnými slovami napíše, čo chce urobiť, a príbeh aktivuje voľbu, ktorá to najlepšie vystihuje — aj keď sa formulácia líši od čohokoľvek na obrazovke:

rea
Izba je malá a zaprášená. V rohu sa prehýba starý gauč.

{input type="action", placeholder="Čo urobíš?"}

* [Otvor okno]
  Dnu prúdi čerstvý vzduch.
* hidden [&look_under_sofa] Jozef sa zohol a pozrel pod starú pohovku, kde našiel záhadnú obálku s nápisom _Tajné!_

{define action look_under_sofa name="Pozri sa pod pohovku", description="nadvihni alebo sa pozri pod starý gauč v rohu; skontroluj priestor pod pohovkou; prehľadaj pod sedadlom"}

Na rozdiel od obyčajného textového vstupu sa odoslaný text neuloží do premennej — porovná sa s použiteľnými možnosťami čakajúcej skupiny volieb, viditeľnými aj skrytými, s už uplatnenými podmienkami a bez spotrebovaných jednorazových možností. Pri možnostiach viazaných na kartu akcie je sémantickým cieľom description= karty — píšte ju ako stručný zoznam zámerov, synonymá vítané, v jazyku príbehu; do úvahy sa berie aj name= karty a návestie možnosti.

Zhoda aktivuje možnosť presne tou istou cestou ako klepnutie — rozprávanie, účinky, krok späť, uloženia aj analytika sa správajú rovnako. Odoslanie, ktorému nič nesedí, zobrazí v poli jemnú správu o nezhode a skupina zostane otvorená, takže hádať je vždy bezpečné.

Porovnávanie beží výhradne na zariadení čitateľa: čitateľská aplikácia poskytuje malý viacjazyčný model vnorení a vstavané porovnávanie prekryvu slov odpovie vtedy, keď model nie je dostupný (alebo kým sa ešte načítava), takže voľný textový vstup funguje vždy — offline, súkromne, bez nákladov na jednotlivú interakciu. Keďže je model viacjazyčný, formulácia čitateľa sa v rozumnej miere môže odchýliť aj od jazyka autorovho popisu.

Samotná napísaná veta nikdy neopustí zariadenie a neukladá sa do stavu príbehu; zaznamená sa len výsledná voľba. To viaže aj autorský kanál: odoslanie, ktoré sa s ničím nezhoduje, vyvolá env/no-match a záznam nenesie žiadne argumenty — ani vetu, ani jej dĺžku, ani to, s čím sa porovnávala. Diagnostika je dáta, ktoré opustia zariadenie vo chvíli, keď autor spustí reast validate v CI, takže platí pre ňu to isté pravidlo ako pre stav príbehu. Pozri Spracovanie chýb.

Tlačidlá

draft{button label="Continue", target=next_chapter}

Navigačný prvok oddelený od voľby. Špecifikované, ale nepostavené — voľby a odkazy pokrývajú to isté, takže to zostalo na konci zoznamu.

rea
{button label="Pokračovať v ceste", target=next_chapter}
{button label="Otvoriť inventár", action=show_inventory}

Tlačidlá s target navigujú na kotvy (ekvivalent -> kotva). Tlačidlá s action spúšťajú pomenované udalosti, ktoré môžu obslúžiť bloky {on nazov_akcie begin}.

Časovač

development{timer duration=30, on_expire="-> times_up" begin}

Runtime časovače spúšťa, zastavuje a nechá vypršať; odpočet viditeľný pre čitateľa a sprístupňujúce prvky, ktoré musia časovanú voľbu sprevádzať, sa ešte stavajú.

rea
{timer duration=30, on_expire="-> times_up" begin}
  Máš 30 sekúnd na rozhodnutie!
  * [Prestrihni červený drôt]
    -> red_wire
  * [Prestrihni modrý drôt]
    -> blue_wire
{end timer}

Správanie časovača: Keď časovač vyprší, odbočka on_expire sa spustí okamžite — aj keď je čitateľ uprostred voľby. Čakajúce voľby sa zrušia a príbeh pokračuje na cieli odbočky. Ak on_expire nastavené nie je, blok časovača sa jednoducho skončí a čítanie pokračuje za {end timer}. Časovače sa pozastavia, keď aplikácia prejde na pozadie, a pokračujú po návrate do popredia. Vnorené časovače nie sú povolené — nový {timer} vnútri aktívneho časovača nahradí ten vonkajší.

Interakcia sloveso — cieľ

draft{verbs begin} · {target chest begin}

Ťahanie slova akcie na zvýraznené podstatné meno. Obľúbený nápad z Texture, špecifikovaný celý, ale nezačatý; pred stavbou potrebuje alternatívu k ťahaniu pre používateľov klávesnice a čítačiek obrazovky.

Interakcia sloveso — cieľ, inšpirovaná mechanikou slovo na slovo z jazyka Texture, dovoľuje čitateľom ťahať slová akcií na zvýraznené ciele v texte. Vytvára hmatateľný zážitok poháňaný objavovaním:

rea
{verbs begin}
  examine: "Pozri sa zblízka na"
  use: "Použi"
  talk: "Prihovor sa"
{end verbs}

Vidíš {target chest begin}drevenú truhlicu{end target} a
{target old_man begin}starého muža{end target} sediaceho neďaleko.

{on use chest begin}
  Otvoríš truhlicu a nájdeš v nej striebornú dýku.
  {give silver_dagger}
{end on}

{on examine chest begin}
  Truhlica je zo starého duba, obitá železnými pásmi. Zvnútra presakuje slabá žiara.
{end on}

{on talk old_man begin}
  „Ale, dobrodruh! Tá truhlica čakala na niekoho odvážneho."
{end on}

{on examine old_man begin}
  Napriek veku má bystré oči. Z vrecka kabáta vykúka mapa.
{end on}

Ako to funguje: Dostupné slovesá sa vznášajú ako ťahateľné prvky. Čitateľ ťahá sloveso na zvýraznené cieľové slovo. Spustí sa zodpovedajúci blok {on sloveso ciel begin}. Nespárované kombinácie zobrazia predvolenú odpoveď:

rea
{on default begin}
  Zdá sa, že to nefunguje.
{end on}

Slovesá môžu byť podmienené a citlivé na kontext:

rea
{verbs begin}
  unlock: "Odomkni" {if has_key}
  pick: "Vylom zámok" {if dexterity > 5}
{end verbs}

20. Kooperatívne čítanie

Rea natívne podporuje zážitky pre viacerých čitateľov, kde ten istý príbeh číta viac ľudí súčasne.

Roly čitateľov

development{define role captain max=1}

Roly a obsah viazaný na rolu sa parsujú a vyhodnocujú; priraďovanie a prerozdeľovanie potrebujú službu skupinových relácií, ktorá sa stavia.

rea
{define role captain name=Kapitán, description="Vodca výpravy. Robí konečné rozhodnutia.", max=1}

{define role crew name="Člen posádky", description="Plní rozkazy. Má jedinečné zručnosti.", max=4}

Obsah pre konkrétnu rolu

rea
{if context.group.role = "captain" begin}
  Tajnú mapu vidíš len ty. Čo povieš posádke?
{else}
  Kapitán niečo študuje. Čakáš na rozkazy.
{end if}

Synchronizované voľby

development{vote timeout=60 begin} … {end vote}

Parsuje sa a cesta degradácie na sólo čítanie už funguje; sčítavanie medzi reálnymi čitateľmi čaká na ten istý realtime kanál.

rea
{vote timeout=60 begin}
  Posádka sa musí rozhodnúť spoločne:
  * [Na sever cez hory]
  * [Na juh pozdĺž pobrežia]
  * [Zostať a postaviť tábor]
{end vote}

Väčšina si zvolila: {vote.result}

Komunikácia medzi čitateľmi

development{whisper to="captain" begin} · {broadcast begin}

Správy cielené na rolu a správy pre celú skupinu; obe sa parsujú a obe degradujú na bežný text pri sólo čítaní, doručovanie čaká na službu skupinových relácií.

rea
{whisper to="captain" begin}
  Toto vidí len kapitán: poklad je ukrytý pod tretím kameňom.
{end whisper}

{broadcast begin}
  Toto vidia všetci: blíži sa búrka!
{end broadcast}

Čakanie na čitateľov

development{wait readers=all begin} … {end wait}

Blokuje, kým nedorazia ostatní čitatelia, alebo prejde okamžite pri sólo čítaní; viacčitateľská polovica nebeží.

rea
{wait readers=all begin}
  Čaká sa, kým na toto miesto dorazia všetci čitatelia…
{end wait}

Zdieľaný stav

development{set shared.torch_lit = true}

Rozsah shared. je rozpoznávaný a synchronizačná vrstva v jadre existuje, ale šírenie medzi čitateľmi potrebuje realtime kanál platformy, ktorý zatiaľ nebeží.

Čitatelia zdieľajú spoločný menný priestor stavu. Zdieľané premenné môže meniť ktorýkoľvek čitateľ a zmeny sa šíria k ostatným:

rea
{set shared.torch_lit = true}
{set shared.door_opened_by = context.reader.name}

{if shared.torch_lit begin}
  Fakľa osvetľuje chodbu pre všetkých.
  (Zapálil ju {shared.door_opened_by})
{end if}

Synchronizácia stavu

draft{synchronize out} · {synchronize auto="on", interval=5}

Ručné ovládanie odoslania a načítania zdieľaného stavu pre ťahové príbehy. Špecifikované, ale zmysel dostane až vtedy, keď bude existovať automatická synchronizácia, takže nič nie je postavené.

Predvolene sa zmeny zdieľaných premenných šíria automaticky v reálnom čase. Príkaz {synchronize} dáva autorom výslovnú kontrolu nad tým, kedy sa stav odosiela a prijíma:

rea
{synchronize out}

Odošle zdieľaný stav aktuálneho čitateľa na server — ostatní čitatelia dostanú aktualizáciu.

rea
{synchronize in}

Načíta najnovší zdieľaný stav zo servera do pohľadu aktuálneho čitateľa.

Režim automatickej synchronizácie sa dá zapnúť alebo vypnúť. Keď je zapnutý, platforma synchronizuje v pravidelných intervaloch bez výslovných volaní {synchronize}:

rea
{synchronize auto="on", interval=5}

Zapne automatickú synchronizáciu každých 5 sekúnd. Návrat k ručnému riadeniu:

rea
{synchronize auto="off"}

Po auto=off sa zmeny šíria len vtedy, keď sa výslovne zavolá {synchronize out} alebo {synchronize in}.

AtribútPopisPredvolené
outOdoslať miestny zdieľaný stav na server
inNačítať najnovší zdieľaný stav zo servera
autoZapnúť alebo vypnúť pravidelnú synchronizáciu (on/off)on
intervalSekundy medzi automatickými synchronizáciami (pri auto=on)určuje platforma

Vzory použitia:

  • Ťahové hry: auto=off, výslovné {synchronize out} po ťahu každého hráča
  • Spolupráca v reálnom čase: auto=on s krátkym intervalom (predvolené správanie)
  • Kritické sekcie: {synchronize out} po blokoch {exclusive}, aby sa zmena rozšírila okamžite

Riešenie konfliktov

draft{exclusive action="open_chest" begin} · {race timeout=10 begin}

Primitíva typu „vyhráva prvý čitateľ" s definovaným správaním pri odpojení. Podrobne špecifikované; implementácia čaká na vrstvu zdieľaného stavu, od ktorej závisia.

Keď sa viacerí čitatelia pokúsia o protichodné akcie súčasne, platforma konflikty vyrieši:

rea
{exclusive action="open_chest" begin}
  {comment Truhlicu môže otvoriť len jeden čitateľ}
  K truhlici sa dostaneš prvý a vypáčiš ju.
  {set shared.chest_opened = true}
{end exclusive}

{race timeout=10 begin}
  {comment Vyhráva prvý čitateľ, ktorý to dokončí}
  * [Chyť drahokam]
    Schmatneš drahokam skôr než ktokoľvek iný!
    {give ruby}
{end race}

Živá prítomnosť

draft{presence show="cursor" begin} · {react options=[…] begin}

Vidieť, kde sa nachádzajú ostatní čitatelia, a reagovať emotikonmi. Špecifikované ako neskorší doplnok kooperatívneho čítania; nič nie je postavené.

Čitatelia môžu v reálnom čase vidieť polohu a reakcie ostatných:

rea
{presence show="cursor" begin}
  {comment Ukáž, kde sa v texte nachádza každý čitateľ}
{end presence}

{react options=["😮", "😂", "😢", "❤️"] begin}
  {comment Plávajúce emoji reakcie viditeľné pre všetkých čitateľov}
{end react}

Udalosti čitateľov

rea
{on reader_join begin}
  {broadcast begin}K družine sa pridal nový dobrodruh!{end broadcast}
{end on}

{on reader_leave begin}
  {broadcast begin}{event.reader_name} opustil družinu.{end broadcast}
{end on}

{on reader_idle, timeout=120 begin}
  {whisper to=event.reader begin}Si tam ešte?{end whisper}
{end on}

Hraničné prípady a správanie platformy

Odpojenie

Keď sa čitateľ odpojí (strata siete, zatvorenie aplikácie, pád):

  • Počas {wait}: platforma upraví požadovaný počet čitateľov. Pri readers=all sa odpojení čitatelia po ochrannej lehote (predvolene 30 sekúnd) vylúčia. Zvyšní pokračujú.
  • Počas {vote}: hlas odpojeného čitateľa sa zo sčítania vylúči. Ak už hlasoval, jeho hlas platí.
  • Počas {race}: odpojený čitateľ je diskvalifikovaný. Ak neostane nikto, preteky sa skončia bez víťaza a platforma vykoná vetvu {else} (ak nejaká je) alebo blok preskočí.
  • Počas {exclusive}: ak odpojený čitateľ držal výhradný zámok, zámok sa po ochrannej lehote uvoľní a môže si ho nárokovať iný čitateľ.
  • Vo všeobecnosti: platforma spustí {on reader_leave begin} a zachová miestny stav odpojeného čitateľa. Ak sa pripojí do okna relácie (nastaviteľné v metadátach, predvolene 5 minút), pokračuje z poslednej pozície s nedotknutým stavom.

Konflikty zdieľaných premenných

Keď viacerí čitatelia menia zdieľanú premennú súčasne:

  • Vyhráva posledný zápis je predvolená stratégia riešenia. Platforma určuje poradie podľa serverových časových značiek.
  • Pri číselnom hromadení (napr. {set shared.gold = shared.gold + 10}) platforma uplatní atomické zvýšenie+10 každého čitateľa sa uplatní nezávisle, nie na základe zastaraného načítania.
  • Autori si môžu pre kritické sekcie vyžiadať výslovné zamknutie:
rea
{exclusive action="modify_treasury" begin}
  {set shared.gold = shared.gold + story.player.contribution}
{end exclusive}

Hraničné prípady hlasovania

  • Vypršanie času bez hlasov: blok {vote} sa vyhodnotí ako undefined. Autori by to mali ošetriť:
rea
{if vote.result = undefined begin}
  Nepadlo žiadne rozhodnutie. Rozhodne kapitán.
{end if}
  • Remíza: platforma vyberie náhodne spomedzi remizujúcich možností. vote.result odráža zvolenú možnosť; vote.tied je true.
  • Jediný čitateľ: ak je prítomný len jeden čitateľ, jeho voľba vyhráva okamžite bez čakania na vypršanie času.

Hraničné prípady pretekov

  • Vypršanie času bez dokončenia: race.winner je undefined. Obsah bloku sa preskočí.
  • Súčasné dokončenie: víťaza určí serverová časová značka.

Prerozdelenie rolí

Roly sa pri odpojení čitateľa neprerozdeľujú automaticky. Ak kapitán odíde, príbeh pokračuje bez kapitána, kým:

  • to autor neošetrí cez {on reader_leave begin} s výslovnou logikou prerozdelenia, alebo
  • sa nepripojí nový čitateľ a neprihlási sa o uvoľnenú rolu

Autori by mali vždy písať obranné kontroly rolí:

rea
{if readers_in_role("captain") = 0 begin}
  Posádka je bez vodcu. Niekto sa musí ujať velenia.
{end if}

Správanie v sólo režime

experimentalod 1.0automatic

Každá kooperatívna konštrukcia má definované správanie pre jediného čitateľa, takže kooperatívny príbeh je vždy hrateľný osamote — táto časť funguje už dnes aj tam, kde viacčitateľská polovica nie.

Kooperatívne príbehy musia byť hrateľné jediným čitateľom bez úprav. Platforma automaticky uplatňuje tieto pravidlá degradácie:

Príkaz / vlastnosťSprávanie pri viacerých čitateľochDegradácia pri sólo čítaní
{vote timeout=N begin}Všetci hlasujú, vyhráva väčšinaVoľba čitateľa vyhráva okamžite (bez čakania)
{wait readers=all begin}...{end wait}Blokuje, kým nedorazia všetciOkamžitý prechod
{wait VÝRAZ begin}...{end wait}Blokuje, kým výraz nie je pravdivýBez zmeny — podmienka môže závisieť od času či stavu
{exclusive begin}Akciu si môže nárokovať len jeden čitateľVždy dostupné — čitateľ si ju nárokuje okamžite
{race timeout=N begin}Vyhráva prvý, kto to dokončíČitateľ vždy vyhráva okamžite (bez čakania)
{whisper to=ROLA begin}Text vidí len cieľová rolaZobrazí sa ako bežný text
{broadcast begin}Správu vidia všetciZobrazí sa ako bežný text
{presence show=... begin}Ukazuje pozície ostatných čitateľovSkryté (nič neurobí)
{react options=[...] begin}Emoji reakcie viditeľné pre všetkýchSkryté (niet ďalších čitateľov)
{synchronize out/in}Odoslanie a načítanie zdieľaného stavuNič neurobí — jediný čitateľ, netreba server
{synchronize auto=on/off}Prepína automatickú synchronizáciuNič neurobí — stav je vždy miestny
{on reader_join begin}Spustí sa pri pripojení čitateľaNikdy sa nespustí
{on reader_leave begin}Spustí sa pri odchode čitateľaNikdy sa nespustí
{on reader_idle begin}Spustí sa pri nečinnosti čitateľaMôže sa spustiť — aj sólo čitateľ môže byť nečinný
context.group.sizePočet pripojených čitateľovVráti 1
context.group.readersZoznam objektov čitateľovVráti [self]
context.group.roleRola aktuálneho čitateľaVráti prvú definovanú rolu
readers_in_role(R)Počet čitateľov v role RVráti 1 pre všetky roly

Zásady sólo režimu:

  1. Žiadne čakanie na neprítomných čitateľov — vypršania a čakania na počet čitateľov sa preskočia okamžite
  2. Žiadny skrytý obsah — sólo čitateľ vidí všetok obsah viazaný na roly (hrá všetky roly)
  3. Žiadny rozbitý stavgroup.* vracia platné údaje (size=1, readers=[self])
  4. Prepísanie autorom — príbehy sa môžu cez metadáta prihlásiť do režimu jednej roly

Zaobchádzanie s rolami v sólo režime

Predvolene je sólo čitateľ priradený ku všetkým rolám naraz. Bloky viazané na rolu ({if context.group.role = "captain" begin}) sa vyhodnotia ako pravdivé, a keď pre tú istú pasáž existuje viac blokov rolí, zobrazia sa všetky s vizuálnym označením roly (napr. [Kapitán], [Posádka]).

Autori, ktorí chcú sólo hru s jednou rolou (čitateľ si vyberie jednu rolu a pre ostatné príbeh prehrá znovu), sa môžu prihlásiť cez manifest:

json
{ "solo_mode": "single_role" }

21. Interakcie s reálnym svetom

Rea sa cez menný priestor context.* prepája so senzormi a rozhraniami reálneho sveta, čím umožňuje príbehy reagujúce na fyzický kontext čitateľa. Každý prístup k senzoru vyžaduje povolenie čitateľa a elegantne degraduje — ak senzor nie je dostupný, príbeh pokračuje aj bez neho.

Požiadavky na schopnosti

experimentalod 1.0{require gps} · {require nfc optional} · world.has("nfc")

Deklarovanie toho, čo príbeh potrebuje, a overenie za behu, čo skutočne dostal, aby každá senzorová cesta mala napísanú náhradu.

Deklarujte, ktoré funkcie reálneho sveta príbeh potrebuje. Čitateľská aplikácia dostupnosť overí pred spustením:

rea
{require gps}
{require camera}
{require accelerometer}
{require nfc optional}

Pridanie optional znamená, že funkcia príbeh obohatí, ale nie je nutná. Funkcia has() overuje za behu:

rea
{if has("nfc") begin}
  Prilož zariadenie k NFC štítku ukrytému pod lavičkou.
{else}
  Napíš kód vytlačený na lavičke: {input type="text", name=bench_code}
{end if}

Tri slovesá, jeden jazyk

experimentalod 1.1{wait escape=…, escape_to=… when EXPR begin} … {end wait}

Príbeh sa zastaví, kým výraz nezmení hodnotu na pravdivú. Telo je to, čo čitateľ vidí počas čakania; escape= sa po uplynutí trvania vzdá a escape_to= ho pošle inam. Povinné vždy, keď výraz číta context.*.

Každá brána v príbehu — {if}, condition voľby, when storyletu, stráž when stavového automatu, visible: špendlíka na mape, oblasť zastávky — sa píše v jednom jazyku výrazov a rozhoduje o nej jeden podsystém. Nelíši sa podmienka, ale kedy sa na ňu engine pozrie, a to vyjadruje blok, ktorý autor zvolí, nie druhá syntax:

RežimZápisVýznamVyžaduje únik
teraz{if}, condition voľby, visible: špendlíkavyhodnotí sa vo chvíli, keď k nej čitateľ dôjdenie
kým{wait EXPR begin} … {end wait}, {waypoint}príbeh sa tu zastaví a pokračuje, keď sa výraz stane pravdivýmáno, keď výraz číta context.*
kedykoľvek{on EVENT when GUARD}, when storyletu, {zone} on enter / on exitspúšťa sa na hrane, môže sa spustiť opakovaneneaplikuje sa

Autor volí sloveso jedinou otázkou — zastaví sa tu príbeh? — a vo všetkých troch prípadoch píše ten istý jazyk výrazov. Nové schopnosti preto prichádzajú ako nové podstromy context. a nové funkcie, nikdy nie ako nová gramatika.

Čakanie na podmienku

experimentalod 1.1{wait escape=…, escape_to=… when EXPR begin} … {end wait}

Príbeh sa zastaví, kým výraz nezmení hodnotu na pravdivú. Telo je to, čo čitateľ vidí počas čakania; escape= sa po uplynutí trvania vzdá a escape_to= ho pošle inam. Povinné vždy, keď výraz číta context.*.

{wait EXPR begin} … {end wait} zastaví príbeh, kým sa EXPR nestane pravdivým. Telo je to, čo čitateľ vidí počas čakania; keď sa brána otvorí, telo sa nahradí a príbeh pokračuje za {end wait}.

rea
{wait escape=duration("PT3H"), escape_to="dry_night" when context.weather = "rain" and context.time.hour >= 20 begin}
  Sadneš si na lavičku pod podlubím a pozeráš na oblohu.
{end wait}

Prvé kvapky dopadajú na dlažbu. Pod podlubím už niekto čaká.
AtribútPopis
escapeTrvanie, po ktorom sa čakanie samo vzdá (escape=duration("PT3H"))
escape_toKotva, na ktorú sa čitateľ presunie namiesto nekonečného čakania

Čakanie, ktorého výraz číta context.* — stav zariadenia, polohy alebo počasia mimo kontroly autora — MUSÍ deklarovať escape= alebo escape_to=; autor, ktorý vynechá oboje, dostane link/wait-no-escape (varovanie, nie chyba: zámerná tvrdá brána je legitímny návrh). Je to to isté pravidlo, aké {waypoint} mal vždy, vyslovené raz pre každú čakajúcu podmienku.

Zo sémantiky vyplývajú tri veci a autor potrebuje všetky tri:

  • Podmienka môže byť unknown. Keď je zdroj, ktorý číta, zamietnutý, nedostupný alebo zastaraný, výraz nie je ani pravdivý, ani nepravdivý. Čakanie berie unknown ako čakaj ďalej a nechá rozhodnúť únik — zamietnutý senzor nesmie potichu odpovedať „nie“ a zavrieť bránu, o ktorej sa čitateľ nikdy nedozvedel. {if} ho berie ako nepravdu, a preto link/context-no-fallback pýta {else}.
  • Termíny sú absolútne a zmeškané okno sa ráta. Príbeh sa zavrie na lavičke a otvorí o tri hodiny neskôr; escape=duration("PT3H") je dovtedy vyčerpaný bez ohľadu na to, či aplikácia bežala. Navše: čakanie, ktoré sa stalo pravdivým, kým bol príbeh zatvorený, sa všimne pri návrate — {wait context.time.hour = 22} sa spustí čitateľovi, ktorý bol preč od deviatej do pol dvanástej, lebo engine prehrá hodiny, ktoré prespal, namiesto toho, aby sa pýtal len na okamih prebudenia. Platí to pre všetko odvodené od hodín; včerajšie počasie si nikto nezaznamenal, takže dopytovaný zdroj sa rozhodne až v okamihu návratu.
  • Čakanie posunie príbeh, keď ho nabudúce otvoríš, nie skôr. Žiadne čakanie nebeží pri zatvorenej aplikácii — web nemá určovanie polohy na pozadí ani spoľahlivé naplánované miestne upozornenie — takže príbeh nikdy nie je pred čitateľom, len dobehnutý k okamihu jeho návratu. O čakaní sa nikam nič neposiela: rozhoduje sa na zariadení, podľa jeho hodín. Čitateľ teda nedostane ťuknutie po pleci a escape= je to, čo chráni príbeh pred tým, kto sa už nevráti.

Pre podmienky, s ktorými sa čakanie zvyčajne píše, existujú tri funkcie:

rea
{wait between(context.time, "22:00", "06:00") begin}     {comment po desiatej večer, vrátane po polnoci}
{wait elapsed(story.started) >= duration("PT30M") begin} {comment o pol hodinu čítania neskôr}
{wait within(context.location, "old_bridge") begin}      {comment vnútri oblasti pomenovanej zastávky}

Holé {wait begin} … {end wait} bez výrazu ostáva nezmenené: je to pauza, nie brána.

Zdroje kontextu

experimentalod 1.1context.time · context.location · context.weather · context.ext.<ns>

Každý podstrom context. je zdroj s vlastnou kadenciou: čas je odvodený a presne povie, kedy sa nabudúce zmení, poloha je prúd s doručovaním a počasie je zdieľané dopytovanie s obmedzenou frekvenciou. Zdroj sa spustí, keď naň začne čakať prvá podmienka, a zastaví sa, keď odíde posledná.

Každý podstrom context. je zdroj a zdroje nemajú rovnakú cenu: GPS je prúd, ktorý vyčerpáva batériu, počasie je sieťové volanie s obmedzenou frekvenciou, hodinový čas je zadarmo a presne predvídateľný. Engine si z výrazu podmienky sám odvodí, ktoré zdroje potrebuje — príbeh to nikdy nedeklaruje a obrazovka súhlasu sa počíta z toho, nie z manifestu.

ZdrojDruhKadencia
context.time.*odvodenýnikdy sa nedopytuje — engine sa zobudí presne raz, v najbližšej sekunde, minúte, hodine alebo o polnoci, ktorú podmienka vie zbadať
context.location, context.heading, context.speeddoručovanýzariadenie ho doručuje, kým naň nejaká podmienka čaká
context.weather, .temperature, .wind, .humiditydopytovanýjedna zdieľaná požiadavka za interval, nech ho číta koľkokoľvek podmienok
context.ext.<ns>.*hostiteľčokoľvek poskytuje rozšírenie hostiteľa, ktoré ho zaregistrovalo
{scan}, {listen}, {capture}, NFCručnýiba z podnetu čitateľa

Zdroj sa spustí, keď začne čakať prvá podmienka, ktorá ho sleduje, a zastaví sa, keď odíde posledná — príbeh si teda pýta povolenie presne vtedy, keď ho potrebuje, a nikdy nedrží senzor otvorený počas kapitoly, ktorá ho nepoužíva.

Riadok ručný je pravidlo, nie opomenutie: podmienka smie čítať premennú, ktorú vytvoril {scan} alebo {listen}, ale engine nikdy nespustí fotoaparát ani mikrofón preto, že ich výraz spomenul. Pasívne čakanie na úkon, o ktorý nikto čitateľa nepožiadal, je neviditeľná brána.

Podmienka, ktorá číta podstrom context., aký neposkytuje žiadna platforma, dostane pri linkovaní link/unknown-context-source — nikdy by sa nemohla stať pravdivou.

Poloha

experimentalcontext.location matches circle(@(lat, lng), metres)

Testovanie polohy čitateľa proti oblasti v bežnom výraze. Meranie polohy od hostiteľa sa prevedie na bod, takže ho matches berie priamo proti kruhu, mnohouholníku alebo obalenej trase.

Poloha sa zapisuje bodovým literálom @(lat, lng) a operátor matches sa pýta, či leží v oblasti:

rea
{if context.location matches circle(@(48.14, 17.10), 500) begin}
  Cítiš zvláštnu rezonanciu. Toto je to miesto z príbehu!
{end if}

Vlastnosti polohy:

VlastnosťTypPopis
context.locationbodAktuálna pozícia (lat, lng)
context.location.latfloatZemepisná šírka
context.location.lngfloatZemepisná dĺžka
context.location.altfloatNadmorská výška v metroch (ak je známa)
context.location.accfloatPresnosť v metroch
context.headingfloatSmer kompasu v stupňoch (0–360)
context.speedfloatRýchlosť pohybu v m/s

Zastávky

experimentalod 1.1{waypoint old_bridge, circle(@(48.14, 17.10), 50) begin}

Pomenované miesta v štýle geocachingu podmienené príchodom. Zastávka je {wait} plus miesto na mape: hint= je jej text počas čakania, telo je obsah po príchode a o oboch rozhoduje ten istý plánovač.

Zastávky, inšpirované geocachingom, definujú pomenované miesta, ktoré musí čitateľ navštíviť. Zastávka je {wait} plus miesto na mape — {waypoint name, AREA, require=EXPR} je {wait context.location matches AREA and EXPR} s metaúdajmi mapy — takže hint= je jej text počas čakania, telo je obsah po príchode a o oboch rozhoduje ten istý plánovač a to isté pravidlo úniku:

rea
{waypoint old_bridge, circle(@(48.1432, 17.1056), 50) begin}
  Starý most ti pod nohami zavŕzga. Pod tretím doskou
  nájdeš koženú mešec so zvláštnym symbolom.
  {set story.symbol_found = true}
{end waypoint}

{waypoint castle_ruins, circle(@(48.1510, 17.1120), 100), require=story.symbol_found begin}
  Symbol sa pri približovaní k ruinám rozžiari.
  Vo východnej stene sa odhalí skrytá chodba.
{end waypoint}

Zastávky majú voliteľné atribúty:

AtribútPopis
requirePodmienka, ktorá musí platiť na aktiváciu
hintText pomáhajúci čitateľovi nájsť miesto
proximityVzdialenosť v km, pri ktorej sa nápoveda zobrazí
iconIkona značky na mape
hiddenZastávka je na mape neviditeľná, kým nie je splnené require
escapeČasový limit (napr. duration("PT30M")), po ktorom sa zastávka preskočí
escape_toNáhradná kotva, kam sa čitateľ pošle namiesto nekonečného čakania

Zastávka, ktorej podmienka prechodu závisí od context.* (stav zariadenia, polohy alebo času mimo kontroly autora), MUSÍ deklarovať escape= alebo escape_to= — autor, ktorý vynechá oboje, dostane link/waypoint-no-escape (varovanie, nie chyba: zámerná tvrdá fyzická brána bez digitálneho obchádzania je legitímny návrh). To isté platí pre zastávky, z ktorých sa skladá {route}, keď závisia od context.*.

rea
{waypoint museum_door, circle(@(48.1486, 17.1077), 30), require=context.device.gps, escape=duration("PT2H") begin}
  Dvere povolia. Vnútri je výstava presne taká, ako ju opísal ten list.
{end waypoint}

Ide o podmienku, ktorú napísal autor. Každú zastávku beh programu porovnáva s polohou čitateľa, takže samotné toto porovnanie varovanie nespustí — spustí ho až require=context.… alebo oblasť postavená zo živého čítania, napríklad circle(context.location, 50). escape_to= pomenúva kotvu rovnako ako odbočka: tú, ktorú nedefinuje žiadne [#kotva] ani {label}, hlási link/undefined-anchor, a kotva, ku ktorej vedie len escape_to=, sa nepovažuje za nepoužitú.

Obrázky máp a špendlíky

development{map id bounds="…" begin} … {pin id at=… begin} … {end map}

Obrázok dodaný autorom ukotvený na reálne GPS hranice dvoma bodovými literálmi, pričom polohu každého špendlíka určuje bodový výraz at:. Parsuje sa na štruktúrované uzly; zostávajúcim dielom je renderer projekcie v čítačke.

Príbeh zasadený do reálneho miesta môže ukázať vlastnú mapu namiesto všeobecnej: obrázok dodaný autorom ukotvený na reálne hranice GPS, so špendlíkmi umiestnenými na súradniciach.

rea
{map old_town bounds="@(48.152, 17.100), @(48.140, 17.120)" begin}
  image [!Staré mesto < assets/old-town.webp]
  {pin bridge at="@(48.1432, 17.1056)" begin}
    label "Starý most"
  {end pin}
  {pin reader at=context.location begin}
    label "Ty"
  {end pin}
{end map}

bounds= udáva severozápadný a juhovýchodný roh obrázka ako dva bodové literály a jadro každý špendlík naň premietne ekvidištantne. at: špendlíka prijíma ľubovoľný bodový výraz — literál alebo context.location pre špendlík, ktorý sleduje čitateľa — takže sa špendlík môže pohybovať s čítaním alebo sa objaviť až po nastavení premennej (visible).

Nič z tohto bloku sa zatiaľ nevykresľuje — parser mu rozumie, výpočet projekcie je napísaný a zostávajúcim dielom je plátno na strane čitateľa.

Viacetapové trasy

experimentalod 1.1{route treasure_hunt, waypoints="a, b", sequential begin}

Chodník cez zastávky deklarované inde. Postup sa odvodzuje z týchto zastávok, nevedie sa osobitne, a riadok complete: sa vykreslí tam, kde stojí blok, keď sú hotové všetky etapy.

Zreťazte zastávky do postupných alebo voľných trás:

rea
{route treasure_hunt, waypoints="old_bridge, castle_ruins, hidden_cave", complete="Dokončil si hľadanie pokladu!", sequential begin}
{end route}

Trasa pomenúva zastávky deklarované inde; neobsahuje ich, takže zastávka ostáva jednou vecou na jednom mieste a trasa je chodník cez ne. Jej riadok complete= sa vykreslí tam, kde stojí samotný blok {route}, a až keď sú hotové všetky etapy — autor teda blok umiestni tam, kam patrí odmena, spravidla za chodník. Dovtedy blok neukazuje nič.

sequential zaznamenáva, že etapy sa majú navštíviť v poradí. Keďže každá {waypoint} zastaví príbeh tam, kde stojí, je toto poradie zároveň poradím čítania; atribút hovorí mape, ukazovateľu postupu či rozhraniu hostiteľa, že preskakovanie nie je zámerom.

Postup čitateľa sa dá čítať ako story.<trasa>.done, .total a .complete a odvodzuje sa zo samotných zastávok, nie z osobitnej evidencie — trasa nedrží stav, s ktorým by uloženie mohlo nesúhlasiť.

Etapa pomenúvajúca zastávku, akú nedeklaruje žiadna časť príbehu, je link/unknown-route-waypoint: chodník by sa nikdy nedal dokončiť.

Geografické zóny

experimentalod 1.1{zone dark_forest, area(…)} · {on enter zone="dark_forest" begin}

Forma kedykoľvek bloku wait: ten istý výraz oblasti, rozhodovaný na každej hrane. Blok vykreslí obsah hrany, ktorú čitateľ naposledy prekročil, a príkazy hrany sa vykonajú pri jej spustení.

Zóna je forma kedykoľvek bloku {wait} — ten istý jazyk výrazov nad tou istou hodnotou oblasti, len sa rozhoduje na každej hrane, nie raz. Príbeh nikdy nezastaví: čitateľ prejde okolo bloku a ten prehovorí, keď vstúpi do oblasti alebo z nej vyjde.

rea
{zone dark_forest, area(@(48.14, 17.10), @(48.15, 17.10), @(48.15, 17.11))}

{on enter zone="dark_forest" begin}
  Stromy sa okolo teba zomknú. Les pôsobí živo.
  {set story.ui.ambient = "forest"}
{end on}

{on exit zone="dark_forest" begin}
  Vynoríš sa z lesa a žmúriš do slnka.
  {set story.ui.ambient = "default"}
{end on}

Zóna vykreslí obsah tej hrany, ktorú čitateľ naposledy prekročil, a to na mieste samotného bloku: obsah vstupu, kým je vnútri, a obsah odchodu, keď odíde. Jedna ohraničená odpoveď namiesto záznamu — čitateľ, ktorý sa vráti späť do lesa, uvidí, ako sa stromy zase zovrú, nie rastúci prepis každého prechodu. Kým neprekročí ani jednu hranu, blok neukazuje nič.

Samotná zóna je jediný nepárový príkaz: deklaruje oblasť a označuje miesto, kde sa vykreslí prekročená hrana. Každá hrana je vrcholový blok {on enter zone="..."} alebo {on exit zone="..."} — tá istá plochá forma, akú má každá iná udalosť v jazyku — takže hrana sa dá prečítať aj bez zóny nad ňou.

Príkazy hrany sa vykonajú vo chvíli, keď sa spustí, presne ako dôsledky zvolenej možnosti — {set} vnútri {on enter} teda zaberie pri vstupe, nie vtedy, keď sa vykreslí jeho text. Hrana môže niesť stráž ako každé iné kedykoľvek{on enter zone="dark_forest" when story.has_key begin} — a story.<zóna>.inside sa dá čítať kdekoľvek v príbehu, takže zóna môže podmieniť obsah ďaleko od miesta, kde je deklarovaná, bez opakovania svojej oblasti.

Ako každá podmienka sledujúca polohu čitateľa aj zóna spustí zdroj polohy, keď k nej príbeh dôjde, a uvoľní ho, keď ho už nič nepotrebuje.

Denná doba

experimentalod 1.0world.hour · world.weekday · world.season

Jediný vstup z reálneho sveta, ktorý nepotrebuje povolenie ani senzor, takže je najbezpečnejšie na ňom stavať príbeh.

rea
{if context.time.hour >= 22 or context.time.hour < 6 begin}
  Tma okolo teba dnes v noci pôsobí skutočne.
{else}
  Denné svetlo robí príbeh menej desivým.
{end if}

Vlastnosti času:

VlastnosťTypPopis
context.time.hourintegerAktuálna hodina (0–23)
context.time.minuteintegerAktuálna minúta (0–59)
context.time.weekdaystringNázov dňa (malými písmenami)
context.time.datestringDátum ako reťazec ISO
context.time.seasonstringRočné obdobie podľa pologule

Nočný režim

Skombinujte čas a svetelný senzor pre atmosféru:

rea
{if context.time.hour >= 22 and context.light < 50 begin}
  {set story.ui.theme = "dark"}
  Táto kapitola sa dá čítať len v tme. Zhasni svetlá.
{end if}

Počasie

draftworld.weather · world.temperature

Špecifikované až po úroveň povolenia (približná poloha podľa IP — nikdy nie GPS), ale nie je zapojený žiadny poskytovateľ počasia, takže hodnoty sa nikdy nenaplnia.

rea
{if context.weather = "rain" begin}
  Aké príhodné — prší v príbehu aj za tvojím oknom.
{end if}

Vlastnosti počasia:

VlastnosťTypPopis
context.weatherstringAktuálny stav (clear, rain, snow, fog, storm)
context.temperaturefloatTeplota v stupňoch Celzia
context.windfloatRýchlosť vetra v m/s
context.humidityfloatVlhkosť v percentách (0–100)

Skenovanie QR a čiarových kódov

draft{scan type="qr", pattern="^REAST-.*" begin}

Blokujúca forma, ktorá zastaví príbeh, kým nedorazí kód. Špecifikovaná; namiesto nej sa stavajú neblokujúce cesty — pole karty scan: alebo trigger: scan — pretože blokujúce skenovanie uviazne čitateľa, ktorý kód nenájde.

rea
{scan type="qr", target="REAST-SECRET-42" begin}
  Naskenuj QR kód ukrytý v reálnom svete a odomkni túto kapitolu.
{end scan}

Podporované typy skenovania:

TypPopis
qrQR kód (najbežnejší)
barcodeĽubovoľný podporovaný čiarový kód (EAN, UPC atď.)
aztecKód Aztec (palubné lístky)
datamatrixKód Data Matrix

Atribút target sa porovnáva s naskenovanou hodnotou. Na zhodu regulárnym výrazom použite pattern:

rea
{scan type="qr", pattern="^REAST-.*" begin}
  Našiel si jeden zo skrytých kódov! {set story.codes_found = story.codes_found + 1}
{end scan}

Blok {scan} je blokujúci — príbeh sa na tom mieste zastaví a čaká na kód. Pre kódy, na ktoré čitateľ môže naraziť kdekoľvek cestou, radšej použite spúšťané storylety (trigger: scan) alebo možnosť menu objavovania s poľom karty scan=: tie sú dobrovoľnými prerušeniami, ktoré sa spustia vtedy, keď vstup dorazí.

NFC štítky

development{nfc target="reast:chapter5" begin} · {nfc read, name=tag begin}

Príkaz dorazí k hostiteľovi ako požiadavka na schopnosť; polovica v čitateľskej aplikácii a QR náhrada pre zariadenia bez NFC sa stavajú.

rea
{nfc target="reast:chapter5" begin}
  Prilož zariadenie k NFC štítku a odhaľ skrytú správu.
{end nfc}

{nfc read, name=tag_data begin}
  Štítok obsahuje: {tag_data}
{end nfc}

Fotoaparát a fotografia

draft{capture type="photo", name=reader_photo begin}

Špecifikované s úrovňou povolenia na jedno použitie a požiadavkou na náhľad. Nezačaté — kontrola súkromia musí prísť pred kódom.

rea
{capture type="photo", name=reader_photo begin}
  Odfoť svoje okolie a pokračuj.
{end capture}
TypPopis
photoJedna fotografia
videoKrátke video (atribút maximálneho trvania)
selfieFotografia predným fotoaparátom

Pohyb a orientácia

draft{on shake, intensity=2 begin} · world.tilt.x

Udalosti zatrasenia, náklonu a kompasu. Špecifikované so záložnými riešeniami pre podporu prehliadačov; implementácia zatiaľ žiadna.

Prístup k senzorom zariadenia pre fyzické interakcie:

rea
{on shake, intensity=2 begin}
  Zatrasieš čarovnou guľou. Objaví sa odpoveď: {~Áno|Nie|Možno|Spýtaj sa znovu}
{end on}

{on tilt, direction="north", threshold=15 begin}
  Ihla kompasu sa otočí na sever. Skryté dvere sa otvoria.
{end on}

Vlastnosti pohybu:

VlastnosťTypPopis
context.tilt.xfloatNáklon dopredu a dozadu (−180 až 180)
context.tilt.yfloatNáklon doľava a doprava (−90 až 90)
context.orientationfloatOtočenie zariadenia (0–360, kompas)
context.acceleration.xfloatZrýchlenie pozdĺž osi X
context.acceleration.yfloatZrýchlenie pozdĺž osi Y
context.acceleration.zfloatZrýchlenie pozdĺž osi Z

Úroveň svetla

draftworld.light

Čítanie jasu miestnosti v luxoch pre scény podmienené tmou. Špecifikované, ale podpora v prehliadačoch je taká slabá, že celou funkciou môže nakoniec zostať náhrada podľa dennej doby.

rea
{if context.light < 10 begin}
  V úplnej tme začne fosforeskujúci text svietiť.
{end if}

{if context.light > 500 begin}
  Jasné slnko odhalí na strane neviditeľný atrament.
{end if}

context.light vracia okolité svetlo v luxoch (0 = tma, 500 a viac = jasné denné svetlo).

Vibrácie a haptika

development{vibrate 200} · {vibrate pattern=[100, 50, 100]}

Príkaz sa parsuje a vysiela požiadavku hostiteľovi; čitateľská aplikácia ju musí splniť a jej vizuálna náhrada pulzom pre zariadenia bez motorčeka sa ešte stavia.

rea
{vibrate 200}
{vibrate pattern=[100, 50, 100, 50, 300]}

Vzor: pole striedajúcich sa dĺžok vibrácie a pauzy v milisekundách.

Blízkosť

draft{on proximity "near" begin}

Priblíženie zariadenia k predmetu. Špecifikované; nezačaté.

rea
{on proximity "near" begin}
  Priblížiš zariadenie k predmetu. Objaví sa tajná správa.
{end on}

Hlasový vstup

development{listen language="en", name=spoken_word begin}

Príkaz dorazí k hostiteľovi ako požiadavka o prepis. Rozpoznávanie beží na zariadení čitateľa a nikdy sa neukladá ani neprenáša — práve tá čitateľská aplikácia sa stavia.

rea
{listen language="sk", name=spoken_word begin}
  Vyslov čarovné slovo a otvor dvere.
{end listen}

{if spoken_word = "abrakadabra" begin}
  Dvere sa pomaly so škrípaním otvoria.
{end if}

Rovnako ako {scan} aj blok {listen} zastaví a čaká na jednom mieste. Pre frázy, ktoré čitateľ môže vysloviť kedykoľvek, použite spúšťané storylety (trigger: listen) alebo možnosť menu objavovania s poľom karty listen=.

Priorita: menu objavovania verzus spúšťače storyletov

Naskenovanie, vyslovená fráza či odfotená značka je jediná fyzická udalosť — nemôže znamenať dve veci naraz. Ak má čitateľ vo chvíli, keď taký vstup vytvorí, otvorené čakajúce menu objavovania, jadro najprv skontroluje možnosti menu s poľami scan=, mark= a listen=. Len keď v menu nič nesedí, ten istý vstup prepadne ďalej a zobudí spúšťač storyletu.

Pozri Storylety a balíčky pre výber storyletov, spúšťače a prioritu/váhu.

Kocky a náhodnosť

draftdice("2d6+3") · d20adv · 4d6kh3

Kompaktný zápis zo stolových hier je špecifikovaný, ale vstavaná funkcia dice() nie je implementovaná. Použite namiesto nej {use "std/dice"}, ktorá je vydaná a pokrýva bežné hody.

Rea podporuje zápis hodov kockou inšpirovaný zvyklosťami stolových hier na hrdinov:

rea
{set story.combat.roll = dice("2d6+3")}
Hodil si {story.combat.roll}!

{if story.combat.roll >= 10 begin}
  Kritický úspech! Drak uteká.
{else if story.combat.roll >= 7}
  Draka zraníš.
{else}
  Drak ťa odhodí bokom.
{end if}

Zápis hodov kockou:

ZápisPopis
d6Jedna šesťstenná kocka
2d6Dve šesťstenné kocky, súčet
2d6+3Dve k6 plus modifikátor
d20advHod s výhodou (lepší z dvoch k20)
d20disHod s nevýhodou (horší z dvoch k20)
4d6kh3Hoď 4k6, ponechaj tri najvyššie
d100Percentuálna kocka

Výzvy v reálnom svete

draft{challenge night_vigil begin} require: … timeout: 30m

Viacero senzorových podmienok spojených do jedného podmieneného momentu. Špecifikované ako vrchol interakcie s reálnym svetom; nemôže sa začať skôr než senzory, ktoré spája.

Skombinujte viacero senzorov do interakcií v štýle výziev, inšpirovaných geocachingom a dobrodružnými hrami:

rea
{challenge night_vigil timeout=30m, hint="Nájdi starú kaplnku po polnoci. Neber si svetlo."
            when context.time.hour >= 23 and context.light < 20
                 and context.location matches circle(@(48.14, 17.10), 200) begin}
  Stojíš v tme pred starodávnou kaplnkou.
  Hviezdy nad tebou skladajú odkaz viditeľný len v túto hodinu.
  {set story.star_message = "VERITAS"}
{end challenge}

Atribúty výzvy:

AtribútPopis
when …Jedna alebo viac podmienok, spojených cez and
timeoutČasový limit (napr. 30m, 2h)
hintUsmernenie zobrazené pri čiastočnom splnení podmienok
retryPovolí opakovanie po neúspechu (predvolene true)
rewardPremenná nastavená po dokončení

Súkromie a nakladanie s údajmi

experimentalod 1.0none · low · medium · high

Štvorúrovňový model, pravidlo „predvolene pominuteľné" a záruka, že „autori nikdy nevidia surové súradnice", sú záväzné pre každú senzorovú funkciu vyššie, nech je jej vlastný stav akýkoľvek.

Príbehy Rea môžu pristupovať k GPS, fotoaparátu, mikrofónu a pohybovým senzorom. Platforma presadzuje prísne pravidlá súkromia:

Úrovne povolení:

ÚroveňSenzorySprávanie
Žiadnačas, dátum, ročné obdobieNetreba povolenie — neidentifikuje
Nízkapočasie, svetlo, vibrácieJediná výzva, len približné údaje
StrednáGPS (približné), akcelerometer, gyroskopVýslovné povolenie, len počas otvoreného príbehu
VysokáGPS (presné), fotoaparát, mikrofón, NFCPovolenie na jedno použitie s náhľadom snímaného

Pravidlá nakladania s údajmi:

  1. Predvolene pominuteľné. Hodnoty senzorov existujú len počas aktuálnej relácie čítania. Žiadna trvalá história polohy, žiadne záznamy senzorov
  2. Autori nemajú prístup k surovým údajom. Autori dostávajú logické výsledky a udalosti (context.location matches circle(…)true/false), nie presné súradnice. Výnimka: {capture} poskytuje fotografie len na zobrazenie v príbehu
  3. Presná poloha sa neprenáša na server. V kooperatívnom režime vidia ostatní čitatelia udalosti („Čitateľ A dorazil na waypoint_X"), nikdy nie surové súradnice
  4. Mikrofón len počas relácie. {listen} prepisuje miestne. Zvuk sa nikdy neukladá ani neprenáša — ako premenná je dostupný len rozpoznaný text
  5. Počasie cez približnú geolokáciu. Volania rozhrania počasia používajú polohu podľa IP adresy, nie súradnice GPS
  6. Diagnostika nenesie dáta čitateľa. Každé pravidlo vyššie viaže autorský kanál rovnako ako stav príbehu. Záznam smie pomenovať premennú, odcitovať to, čo autor doslova napísal do súboru .rea, a opísať typ hodnoty za behu — nikdy nie hodnotu. Neexistuje cesta v kóde, ktorou by sa prepis z {listen}, fotka z {capture}, reader.* alebo context.location stali argumentom diagnostiky; konštruktory, ktoré ich stavajú, odmietnu reťazec od volajúceho rovno. Pozri Spracovanie chýb

Záruky pre čitateľa:

  • Pred spustením príbehu: zobrazia sa požiadavky na senzory (z poľa metadát sensors:)
  • Každá žiadosť o senzor zobrazí opis účelu (dodaný autorom cez atribút hint)
  • Čitateľ môže ktorýkoľvek senzor odmietnuť — príbeh elegantne degraduje
  • Čitateľ môže povolenia odvolať aj počas príbehu
  • Všetky zachytené médiá a stav relácie môže čitateľ zmazať

Dostupnosť senzorov

Nie všetky zariadenia podporujú všetky senzory. Čitateľská aplikácia Reast poskytuje náhradné riešenia:

SenzorPodpora v prehliadačochNáhrada
Poloha GPSVšetky prehliadačeRučné zadanie mesta či regiónu
Fotoaparát / QRVšetky prehliadačeRučné zadanie kódu textom
AkcelerometerChrome, EdgeGestá klepnutia a potiahnutia
GyroskopChrome, EdgeTlačidlá smeru kompasu
Svetelný senzorObmedzenáOdhad podľa dennej doby
NFCAndroid ChromeAlternatíva cez QR kód
VibrácieChrome, FirefoxEfekt vizuálneho pulzu
Hlasový vstupChromeTextový vstup
PočasieCez rozhranie APIČitateľ zadá sám alebo preskočí