Programování pro hračičky/Lekce 3

Z Wikiverzity
Skočit na navigaci Skočit na vyhledávání
Jak používat klasifikační nálepkuTato stránka je součástí kurzu:
středoškolská
Příslušnost: skupinová

Opakování a probrání domácího úkolu[editovat]

Projděme si místnosti, které jsme kdo vytvořili (zejména máme-li něco, co bychom rádi ukázali ostatním).

Podívejme se, jaký herní předmět hodlá kdo vytvořit. (Dnes se do tohoto tvoření dáme.)

Základní programátorské rozlišení objektů[editovat]

Objekty, s nimiž se setkáváme ve hře, mohou být trojího základního druhu: Mohou to být jednak jedinečné objekty, které se dále neklonují a ve hře proto vystupuje přímo jejich prototyp, jednak klonované objekty, jejichž prototyp zůstává mimo hru, zatímco ve hře se vyskytují jeho klony, jednak virtuální objekty, které se hráčům jako objekty jeví, ale z programátorského hlediska nejsou objekty, nýbrž víceméně jen soubory hlášek, jimiž se má reagovat na určité hráčské příkazy.

Jedinečné objekty[editovat]

Některé objekty se ve hře z logiky věcí mají vyskytovat jen jednou a jsou natolik složité, že se vyplatí mít jejich program uložen ve zvláštním souboru. Týká se to jednak všech místností, které nejsou generovány automaticky podle map (například celý Dům, a za prvním Prahem téměř všechny místnosti ve městech, mimořádná zákoutí atd.), jednak nehráčských postav majících nějakou osobnost (například Josef nebo Lída, které můžeme potkat hned na začátku hry), jednak předmětů nadaných mimořádnými možnostmi, jejichž častější výskyt ve hře by mohl dost změnit její náladu (například buřič trempů Kundrápa a Žahoura, který může být velmi mocnou zbraní). K těmto objektům vystupujícím přímo ve hře pak můžeme připočítat též objekty, které nějakou část hry spravují nebo koordinují, aniž by byly pro hráče přímo viditelné (tzv. mastery neboli řídicí objekty, například řídicí objekt počasí pro tu či onu doménu, řídicí objekt legionářských hlídek v Oikúmené atd.).

Takovéto objekty jsou pak přímým obrazem svého programového souboru, který se načte do paměti buď v rámci programu funkcí touch(), nebo ve hře andělským příkazem probudiž. Tím vznikne prototyp, ze kterého se ovšem už nevyrábějí žádné další klony, nýbrž který sám účinkuje ve hře.

Aby byly jedinečné objekty odlišeny od klonovatelných, umisťujeme je v mudové knihovně zpravidla do podsložek touch (v hráčské oblasti tedy třeba /d/oikumene/jeruzalem/touch, ve vlastní programátorské složce třeba /w/mojejmeno/touch nebo /w/mojejmeno/bludiste/touch). Místnosti jsou však uloženy přímo ve složkách odpovídajících různým oblastem hry[1] — jednak zde nehrozí záměna s klonovatelným objektem, protože klonování místností je omezeno jen na jasně rozpoznatelné případy,[2] jednak místnosti tvoří jakousi základní kostru mudového světa, takže jich je mnohem více než jiných jedinečných objektů, a jejich kratší směrník (tj. třeba /d/oikumene/jeruzalem/novemesto místo pomyslného /d/oikumene/jeruzalem/novemesto/touch) je pak prostě praktičtější.

Klonované objekty[editovat]

Jiné objekty se ve hře vyskytují opakovaně. Typicky jsou to běžné předměty (batoh, pochodeň, meč), ale také druhové bytosti (vlk, skřet, hlídkující legionář) a mapové místnosti (tedy například jednotlivé vzájemně podobné místnosti v určité krajině). Takovéto objekty pak mají společný programový soubor a z něho vytvořený prototyp, který ovšem sám ve hře nevystupuje, nýbrž se z něho vždy nejprve vyrobí klon, který se pak přemístí jako objekt do hry.

Klon objektu vyrobíme v rámci programu funkcí clone_object(), ve hře pak andělským příkazem budiž. Jedná se vlastně o paměťovou kopii všech proměnných, v nichž jsou uloženy vlastnosti objektu, zatímco metody objektu jsou zapsány jedinečně v prototypu. Z toho plyne, že vlastnosti jednotlivého naklonovaného objektu můžeme po naklonování nadále měnit, zatímco množina použitelných metod je pevně dána společným prototypem.

Aby se klonovatelné objekty nepletly s jinými, ukládáme jejich soubory buď do složky /obj a jejích podsložek (jako třeba /obj/nabytek nebo /obj/voda), nebo do podsložek obj v příslušné části hry (například /d/stredozeme/moria/obj) nebo ve vlastním programátorském adresáři (například /w/mojejmeno/obj).

Virtuální objekty[editovat]

Některé objekty se vyskytují ve hře jen jako doplňky či detaily jiných objektů a jsou na tyto jiné objekty pevně vázané. Typicky se jedná součásti popisu místností, které je možno si zvlášť prohlédnout (stěny, strop, nebe, tráva), méně často o popisové detaily na předmětech (cenovka na svetru, držadlo u kbelíku) nebo na nehráčských postavách (vlasy, zuby).

Takovéto herní objekty pak z programátorského hlediska nejsou objekty, nýbrž jen v jiných objektech uložené seznamy základních vlastností a hlášek, které se projeví při jejich prohlížení či jiných základních úkonech. Chceme-li o takovýchto objektech-neobjektech mluvit, používáme označení virtuální objekty nebo virtuální detaily, nebo podle angličtiny vzniklé zkratky v-items.

Virtuální objekt tedy nemá žádný svůj soubor na disku ani prototyp v paměti, ale je uložen jako obsah nějaké proměnné v jiném objektu. Můžeme si už říci, že se jedná o proměnnou typu mapping, tedy tzv. asociativní pole, i když přesné vysvětlení tohoto pojmu si necháme na později.

Používáme virtuální objekty[editovat]

Ze tří zmíněných druhů objektů už jsme programovali objekty jedinečné, když jsme vytvářeli první vlastní místnost, ovšem zatím to bylo spíše jen drobné předělávání podle hotového vzoru. Pro samostatnou tvorbu začneme virtuálními objekty.

Virtuální objekt můžeme přidat do jakéhokoli objektu, ale nejjednodušší bude začít jejich přidáváním do místností. (Ve své andělské pracovně máme ostatně ohnivá písmena, která jsou takovým přidaným objektem, takže i nyní se budeme moci opřít o určitý vzor.) Zvolme si tedy místnost, do níž chceme nějaký virtuální objekt přidat, a otevřme si její soubor.

Protože Prahy se odehrávají v češtině, budeme při vytváření objektů muset dbát též na správné skloňování jejich jmen. K tomu potřebujeme někde v úvodu souboru mít vloženu řádku:

#include <sklon.h>

Pokud jsme dotyčnou místnost vytvořili okopírováním workroom.c a následným předěláním, pak takovou řádku nejspíše někde na začátku souboru už máme. Pokud ne, tak ji vložíme buď na úplný začátek, nebo za rovněž někde na začátku se nacházející řádky, které začínají slovem nebo obsahují slovo inherit (o těch budeme také podrobně mluvit až později).

Do těla funkce create(), která se automaticky spouští při vytvoření objektu (v případě jednotlivé místnosti při jejím načtení do paměti), vložíme nyní volání funkce add_v_item(), jejímž jediným parametrem bude asociativní pole popisující virtuální objekt. Za vzor si můžeme vzít třeba poháry z Horepovy krčmy:

void create()
{
  ...
  add_v_item(([
    "name":"pohár",
    "plural":CISLO_MN,
    "gender":ROD_MI,
    "vzor":VZOR_STAN,
    "id":({"pohár","pohár","číše","číše","sklenice","sklenice"}),
    "vzor_id":({VZOR_STAN,VZOR_STAN,VZOR_NUSE,VZOR_NUSE,
      VZOR_ULICE,VZOR_ULICE}),
    "plural_id":({CISLO_J,CISLO_MN,CISLO_J,CISLO_MN,CISLO_J,CISLO_MN}),
    "long":"Poháry, kterých se v této krčmě užívá, jsou zhotoveny ze skla, "
      "zajímavého materiálu, oblíbeného právě v Egyptě. "
      "Mají jednoduchý válcový tvar a dole zesílenou podstavu.",
    "smell":"Přes všepronikající vůni egyptského dýmu cítíš z použitých "
      "pohárů vůně nápojů, které v nich ještě před chvilkou byly.",
  ]));
  ...
}

Identifikátory psané velkými písmeny jsou konstanty definované v souboru /sys/sklon.h, který jsme načetli oním řádkem #include <sklon.h>. Jsou to vlastně interní čísla různých rodů, čísel a vzorů, ale protože s čísly by se pracovalo podstatně hůř, jsou takto nahrazena aspoň přibližně zapamatovatelnými slovy. ROD_F, ROD_MA, ROD_MI, ROD_N znamenají po řadě rod ženský, mužský životný, mužský neživotný a střední, CISLO_J, CISLO_MN, CISLO_POMN, CISLO_HROM číslo jednotné, číslo množné, jméno pomnožné a jméno hromadné, identifikátory začínající na VZOR_ znamenají jednotlivé skloňovací vzory. Protože skloňovacích vzorů je v češtině ve skutečnosti mnohem více, než se běžně učí ve škole (například slova „hrad“, „stan“ a „padák“ se všechna řadí ke školskému vzoru „hrad“, ale při skloňování se v některých pádech liší), budeme se muset občas kouknout buď do souboru /sys/sklon.h (jde to například ze hry andělským příkazem more /sys/sklon.h), nebo si postupně zapamatovat možné vzory při zacházení s již hotovými virtuálními objekty.

Jednotlivé položky asociativního pole pak nastavují jednotlivé vlastnosti virtuálního objektu. Poměrně jasný nám je asi význam položek "long" a "smell" (obdobně bychom mohli definovat ještě položky "noise", "feel" a řadu dalších, které najdeme popsány v Podprahové encyklopedii v sekci Jak to funguje, podsekci Virtuální detaily). Položka "name" definuje, pod jakým hlavním jménem bude objekt vystupovat, tedy pod jakým se bude třeba objevovat v hláškách typu „Michael si prohlíží poháry.“ (jméno píšeme malými písmeny, případné velké písmeno u jmen vlastních řešíme jinak), "gender", "plural" a "vzor" udávají, jak se bude toto jméno skloňovat. Mohli bychom definovat ještě "adjektiv" pro přívlastek shodný a "attribut" pro přívlastek neshodný:

    "adjektiv":({"použitý","skleněný"}),
    "attribut":"na pultě",

Takové nastavení by pak způsobilo, že výše uvedená hláška by měla podobu „Michael si prohlíží použité skleněné poháry na pultě.“

Položky "id", "vzor_id" a "plural_id" definují, pod jakými jinými jmény může hráč na daný předmět odkazovat, přičemž jednotlivé položky všech tří seznamů (či přesněji tzv. polí) k sobě patří. V daném případě je nastaveno, že hráč si může prohlížet „pohár“, „poháry“, „číši“, „číše“, „sklenici“ i „sklenice“ (zopakované „poháry“ v množném čísle ve skutečnosti nejsou zapotřebí, protože ty zajišťuje už položka "name", ale pro některého programátora může být takovéto zopakování přehlednější a praktičtější pro případ, že by se najednou rozhodl nastavit jako hlavní jméno místo "pohár" třeba "číše"). Pokud jsou nastaveny položky "adjektiv" a "attribut", je možno je používat i se všemi těmito vedlejšími identifikátory (tedy třeba „prohlédni si použitou sklenici“). Kromě toho je možno nastavit další použitelná přídavná jména položkou "plus_adjektiv".

Podle tohoto vzoru můžeme nyní všechny své místnosti ozdobit spoustou virtuálních detailů. Při tom nejspíše narazíme na různé nejasnosti a problémy, které pak můžeme řešit ve hře s ostatními anděly, nebo na diskusní stránce této lekce nebo letošního kurzu.

Klonování objektů v místnostech[editovat]

Jakmile jsme zvládli udělat prvních pár virtuálních detailů, můžeme se se získanými zkušenostmi vrhnout do klonování skutečných programátorských objektů. I ty budeme vkládat do místností (což je, podobně jako u virtuálních detailů, zdaleka nejčastější způsob jejich použití).

Nejprve si ve složce /obj a v jejích podsložkách vybereme, který z již předpřipravených objektů chceme použít (pro následující příklad předpokládejme, že se rozhodneme pro batoh). Pak si ze svých místností zvolíme tu, do které má být objekt umístěn. S touto místností pak bude dotyčný objekt spjat jako s objektem, který jej vytvořil a do kterého byl počátečně umístěn.

V programu místnosti nyní provedeme několik úprav. Především někde v úvodu souboru (ale až za různá inherit a #include) deklarujeme proměnnou typu objekt, která bude odkazovat na vytvářený objekt. Tuto proměnnou bychom mohli nazvat klidně Franta nebo x3356762, ale je dobrým zvykem nazývat věci tak, aby byly podle názvu rozpoznatelné, tedy ji nazvěme třeba batoh:

object batoh;

Od této řádky dále pak můžeme v programu místnosti používat proměnnou batoh a ukládat do ní odkazy na objekty.

Jako další úpravu vložíme do programu místnosti novou funkci reset(). Funkce tohoto jména se u objektů, které se právě nacházejí ve hře, volá automaticky přibližně jednou za 40 minut, a slouží k tomu, aby se tyto objekty mohly vždy znovu uvést do nějakého kýženého základního stavu. Základním stavem naší místnosti ovšem bude, aby obsahovala batoh, a proto, pokud objekt batoh neexistuje nebo už není v místnosti přítomen, naklonujeme nový batoh z /obj/batoh.c a přemístíme ho do této místnosti:

void reset()
{
  if (!batoh || !present(batoh))
    {
      batoh=clone_object("/obj/batoh");
      batoh->move(this_object());
    }
}

Tato funkce reset() se musí nacházet za deklarací proměnné batoh (protože v ní tuto proměnnou používáme) a před funkcí create().

Jako třetí a poslední úpravu nyní do funkce create() přidáme zavolání funkce reset(), aby se náš batoh vytvořil hned při vytvoření místnosti:

void create()
{
  ...
  reset();
}

Velmi často se samozřejmě stává, že bychom rádi vytvořili objekt, který je sice podobný nějakému předpřipravenému objektu (tedy, jinak řečeno, je téže třídy), ale některé vlastnosti má mít jiné. Pak musíme vlastnosti objektu po naklonování ještě změnit funkcemi, jejichž použití snadno pochopíme z toho, co už známe z vytváření místností a virtuálních objektů:

void reset()
{
  if (!stud)
    {
      stud=clone_object("/obj/voda/studna");
      stud->set_name("kašna");
      stud->set_gender(ROD_F);
      stud->set_vzor(VZOR_PANNA);
      stud->set_id(({"skruž","sloupek","socha","nymfa","víla","džbán",
          "ornament","ornament"}),
        ({VZOR_JABLON,VZOR_STOLEK,VZOR_ZENA,VZOR_ZENA,VZOR_ZENA,VZOR_HRAD,
          VZOR_STAN,VZOR_STAN}),
        ({CISLO_J,CISLO_J,CISLO_J,CISLO_J,CISLO_J,CISLO_J,
          CISLO_J,CISLO_MN}));
      stud->set_adjektiv("kamenný");
      stud->set_long("Kašna, kterou vidíš před sebou, sestává z velké "
        "kamenné skruže, uprostřed níž se tyčí právětak kamenný sloupek, "
        "nesoucí sochu nymfy, tedy vodní víly. Víla drží v rukou veliký "
        "džbán, z něhož vytéká voda a naplňuje kašnu. Sloupek i skruž "
        "jsou zdobeny tesanými ornamenty.");
      stud->move(this_object());
    }
}

Někde v úvodu programu pak samozřejmě budeme muset načíst definice gramatických kategorií a deklarovat proměnnou stud:

#include <sklon.h>

object stud;

Za upozornění zde ještě stojí funkce set_id(), která nastavuje nejen vedlejší identifikátory, ale také jejich skloňovací vzory a mluvnická čísla. Zatímco u virtuálního objektu jsme na to potřebovali tři různé položky, nyní můžeme místo položek použít funkci, kterážto může mít i více parametrů — a v případě funkce set_id() jsou parametry právě ona tři pole, která jsme u virtuálního objektu museli uvést zvlášť.

Práce na vlastním objektu[editovat]

Objekt, na kterém budeme nadále pracovat, vytvoříme jako jedinečný, tedy založíme si ve své programátorské složce podsložku touch, a v ní založíme nový soubor s příponou .c (pro náš příklad jej nazvěme třeba bublator.c).

Na začátek souboru napišme do programátorských komentářů, oč se v souboru bude jednat (ani my sami to možná nebudeme za nějaký čas vědět přesně):

// Salátův bublátor: když se do něj kopne, vypouští bublinky

Popis můžeme případně (časem) rozšířit o další smysluplné informace (pro ukázku nyní použijeme druhý typ zápisu komentářů):

/* proměnné: 
    bubcount ... kolik bublinek se vypustí na jedno kopnutí
    onlypl   ... je-li nenulové, pak reaguje pouze na hráče

   TODO: nastavení barvy bublinek 
*/

Nyní využijeme již hotových tříd, jejichž programy nalezneme ve složce i a jejích podsložkách, abychom nemuseli všechno programovat sami. Pokud má být bublátor krom bublání (ježto budeme muset určitě sami naprogramovat) obyčejným předmětem, který se dá též přemisťovat a případně i prodat, napíšeme na následující řádky:

inherit "/i/item";
inherit "/i/move";
inherit "/i/value";

Pokud bychom se rozhodli, že bublátor má být zároveň použitelný jako zbraň pro boj zblízka, pak využijeme příslušné třídy (která již dědí všechny tři výše zmíněné a má nějaké ty metody a vlastnosti navíc) a místo těchto tří řádků napíšeme:

inherit "/i/weapon/zblizka";

Poté, co jsme takto deklarovali, od kterých tříd bude náš objekt dědit vlastnosti a metody, načteme si definice konstant, které budeme využívat. Budou to přinejmenším konstanty označující nám již známé gramatické kategorie (neukončuje se středníkem, protože to vlastně není příkaz LPC, nýbrž preprocesoru, o kterém si něco více povíme později):

#include <sklon.h>

Nyní již můžeme dát svému objektu počáteční tvar, tedy definovat základní funkci (konstruktor) create() a v ní pomocí nám již známých funkcí nastavit vlastnosti:

void create()
{
  set_name("bublátor");
  set_gender(ROD_MI);
  ...
}

V případě, že by bublátor dědil /i/weapon/zblizka nebo další programové moduly, které už obsahují funkci create(), je potřeba v naší funkci create() spustit též toto zděděné create(), aby se provedla nutná nastavení. Pokud objekt dědí jen jednu třídu, pak stačí napsat:

void create()
{
  ::create();
  set_name("bublátor");
  set_gender(ROD_MI);
  ...
}

Pokud objekt dědí více tříd, pak můžeme zavolat všechna v nich definovaná create() zápisem:

void create()
{
  "*"::create();
  set_name("bublátor");
  set_gender(ROD_MI);
  ...
}

Volání děděných funkcí je možno specifikovat i podrobněji, ale to si podrobně probereme až v jedné z příštích lekcí.

Program, který dosud máme hotový, stačí k tomu, aby náš objekt (zatím bez jakýchkoli speciálních funkcí) vstoupil do hry. Protože není klonovatelný, nemůžeme jej vytvořit andělským příkazem budiž, ale načteme jej do paměti příkazem probudiž (či zkráceně probu), a pak jej k sobě přemístíme příkazem sem:

> probu /w/salatdo/touch/bublator
Ok.
> sem /w/salatdo/touch/bublator
Ok.
> r
Jsi v prázdné místnosti. Není tu vůbec nic zajímavého, ba i tento
popis je tu jen proto, aby tu na pohled bylo něco vidět.
        Dále: jih.
Bublátor.

Na tomto příkladě si můžeme také hned uvědomit, že i ve hře se stále nacházíme někde v souborovém systému (pročež můžeme používat i relativní směrníky):

> pwd
/w/salatdo
> cd touch
/w/salatdo/touch
> probu bublator
Ok.
> sem bublator
Ok.

Poznámky[editovat]

  1. Konkrétně v mudu Prahy jsou základní oblast hry (Dům) a oblasti zvláštního určení (Nebesa, Podsvětí, prázdná místnost, posmrtná místnost a pod.) umístěny v podsložkách složky /room, zatímco herní domény (Oikúmené, Středozemě, Stínadla, Narnie, Pásmo) jsou umístěny v podsložkách složky /d (tedy /d/oikumene atd.).
  2. Ke klonování místností podle určitého jednotného prototypu dochází v mapových objektech a ve virtuálních kompilátorech. První případ vidíme kdekoli v otevřené krajině, druhý třeba v některých bludištích. Podrobně si oba případy probereme později.

Úkoly do příští lekce[editovat]

  • Projděme si své místnosti, podle libosti si je ozdobme virtuálními objekty a některé vyzbrojme též klonovanými objekty.
  • Vytvořme svůj předmět v rozsahu popsaném v této lekci.

Pomocné stránky[editovat]