Programování pro hračičky/Měniči/Lekce 12

Jak používat klasifikační nálepkuTato stránka je součástí kurzu:
středoškolská
Příslušnost: skupinová

Objekty, s nimiž nejčastěji zacházíme

editovat

Pro úspěšné měnění na mistrovské úrovni — tedy již skutečné programování — je nezbytné si vždy dobře uvědomit, jaké objekty mezi sebou komunikují a jaký objekt vykonává jakou funkci. Pokud jste dosud přeskakovali teoretické kapitoly jednotlivých kroků Programování pro hračičky, projděte si znovu celý kurs a tyto kapitoly si přečtěte.

Když měničský mistr napíše program a tento program následně spustí na nějakém objektu, pak tím vytvoří zastínění tohoto objektu, tedy nový objekt, který na přechodnou dobu (než se ozve ono nám již důvěrně známé Plop.) bude překrývat některé funkce zastíněného objektu. Měničem napsaný program tvoří programový kód objektu zastínění, tedy při psaní programu se jakoby díváme na svět zevnitř tohoto objektu:

  • Na samotný objekt zastínění můžeme ve svém programu odkazovat funkcí this_object(), která vždy zastupuje objekt, v jehož programu se nachází.[1]
  • Na zastíněný objekt můžeme odkazovat pomocí funkce query_shadow_owner().
  • Objekt zastínění nemá žádný prostorový vztah k ostatním objektům, zatímco zastíněný objekt má většinou nějaké okolí, na které můžeme odkázat funkcí environment(query_shadow_owner()). Naopak seznam objektů, které má zastíněný objekt v inventáři, získáme funkcí all_inventory(query_shadow_owner()).[2]
  • Pokud je nějaká funkce, kterou v objektu zastínění naprogramujeme, zavolána, pak funkce previous_object() (případně previous_object(0)) odkazuje na objekt, který tuto funkci zavolal. Pokud onen objekt zavolal danou funkci z funkce, která byla zavolána dalším objektem, pak tento další objekt prozrazuje previous_object(1). Pokud i ten byl zavolán ještě dalším objektem, pak tento ještě další objekt je previous_object(2), atd.
  • Bytost, která právě s objektem má co do činění (tedy například zadala nějaký příkaz, který je objektem definován, nebo právě vstoupila do místnosti, kde se objekt nachází), je this_player(). Pokud nás nezajímají bytosti naprogramované, ale jen živí hráči, pak nám pomůže funkce this_interactive(), která v případě živých hráčů odkazuje na stejný objekt jako this_player(), ale v případě nehráčských bytostí má hodnotu 0.

Vlastnosti objektů v rukou mistra

editovat

Jednotlivé objekty, jak už víme, si vzájemně mohou posílat zprávy. Nejčastěji jde o volání, tedy žádost o spuštění funkce, to znamená určitého logicky uceleného kousku programového kódu.[3] Když hráč hrac očichává objekt obj, pak objekt hrac zašle objektu obj žádost o spuštění funkce query_smell(). Pokud je objekt obj zastíněn objektem sh, pak záleží na tom, zda je funkce query_smell() definována i v programu sh. Pokud ano, spustí se tato funkce v objektu sh, pokud ne, spustí se v objektu obj.

Naprogramujeme-li v objektu zastínění funkci, kterou následně nějaký třetí objekt zavolá v zastíněném objektu, pak se místo oné funkce v zastíněném objektu provede funkce námi naprogramovaná. Máme-li tedy v měničském programu, který jsme spustili na nějakém objektu, naprogramovánu funkci query_smell() a někdo takto zastíněný objekt očichá, spustí se naše funkce a provede všechno, co si přejeme — či co ze svých přání jsme do ní uměli vložit.

Dokud byl měnič pouhým učněm, mohl příkazem změň měnit několik vybraných vlastností objektu, a to jen prostým zadáním textu popisujícího danou vlastnost. Jakmile dostoupal do hodnosti mistra, může zastíněním funkcí, které zprostředkují vlastnosti objektu jiným objektům, nejen měnit vlastnosti téměř jakékoliv, nýbrž místo zaslání samotného textu spouštět celou řadu dalších akcí.

Další akce při zjišťování vlastností

editovat

Vyjděme z příkladu, na kterém jsme si ukázali zacházení s příkazem programuj v minulém kroku:[4]

string query_smell()
{
  return wrap("Au! "+Ten(1,this_object())+" tě "+dan("kousl",this_object())+" do nosu!");
}

Takovýto program zatím nedělá nic, co by nebyl zvládl už učeň příkazem změň (jen má tu výhodu, že je natrvalo uložen, takže není nutno jej při opakovaném používání vždy znovu psát). Jakožto mistři ovšem už dokážeme více. Do funkce query_smell(), která se volá pokaždé, když nějaká bytost očichá zastíněný objekt, můžeme naprogramovat rozličné další akce, které se mají v takovou chvíli provést. Musíme je samozřejmě vložit před příkaz return, protože ten provádění funkce ukončí:

string query_smell()
{
  previous_object()->random_move();
  return wrap("To byl TAK HROZNÝ smrad, že nešlo neutéct!");
}

Funkce previous_object() nám poskytne odkaz na objekt, který zavolal funkci query_smell(), tedy zpravidla na bytost, která očichala zastíněný objekt. Tomuto objektu pošleme žádost o zavolání funkce random_move(), která v případě, že se jedná o živou bytost, způsobí její útěk náhodným směrem. Funkce query_smell() následně vrátí popis zápachu zastíněného objektu, který dostane čichající bytost. Protože bytost nejprve náhodně uteče a teprve potom dostane popis zápachu, je tento popis naformulován již jako dodatečné vysvětlení útěku.

Teprve dodatečně obdržený popis zápachu se nám ovšem nemusí úplně hodit. Útěk bytosti by sice bylo také možno spustit se zpožděním, tedy poskytnout bytosti nejprve popis zápachu předmětu a teprve následně ji donutit k útěku, avšak tato možnost vyžaduje složitější nástroje, které probereme až v některém z příštích kroků. Zatím vycházejme z toho, že návratová hodnota funkce query_smell() doputuje k bytosti až po všech ostatních naprogramovaných akcích, a pokusme se už pomocí těchto akcí dosáhnout kýženého efektu:

#include <message.h>

string query_smell()
{
  query_shadow_owner()->send_message_to(previous_object(),
    MT_SMELL,MA_SMELL,
    wrap(Ten(1,this_object())
      +" smrdí tak strašně, že se dáváš na panický útěk!"));
  previous_object()->send_message(MT_LOOK,MA_SMELL,
    wrap(Ten(1,previous_object())
      +" očicháv"+plural("á","ají",previous_object())+" "
      +ten(4,this_object())+"."));
  previous_object()->random_move();
  return wrap("Uf, to bylo jak pěstí do nosu!");
}

Naše funkce query_smell() nyní postupně provádí čtyři akce. Nejprve vyzve zastíněný objekt ke spuštění funkce send_message_to(), která pošle objektu, jenž naši funkci zavolal, tedy očichávající bytosti, hlášku o strašném smradu a započatém útěku. Potom vyzve očichávající bytost ke spuštění funkce send_message(), která rozešle okolním objektům hlášku, že bytost očichává zastíněný předmět; implicitní hláška o tom, že bytost očichává předmět, se totiž neodešle, protože ve chvíli, kdy by byla odeslána (poté, co funkce query_smell() příkazem return vrátila očichávající bytosti zprávu o zápachu), už bytost není ve stejné místnosti jako předmět. Pak následuje nám již známý náhodný pohyb, při němž bytost automaticky rozešle do okolí hlášku o svém útěku, a závěrečné vrácení zprávy o zápachu, která se ovšem v našem případě stala už jen dodatečným komentářem.

Řádka #include <message.h>, kterou nově vidíme na začátku programu, způsobí načtení souboru /sys/message.h, v němž jsou definovány zkratky pro různé typy posílaných hlášek. Z těchto zkratek následně používáme MT_SMELL, MA_SMELL, MT_LOOK. Zkratky začínající MT (z anglického „message type“) popisují, jakým smyslem vnímáme hlášenou skutečnost (MT_SMELL znamená, že ji čicháme, MT_LOOK, že ji vidíme, ), zkratky začínající MA (z anglického „message action“) sdělují, jakým úkonem byla hlášená skutečnost vyvolána (MA_SMELL znamená, že čicháním).[5]

Všechny ostatní prvky použité v programu jsou nám už známy. Upozorníme jen na použití funkcí query_shadow_owner() a this_object(), z nichž první označuje zastíněný objekt a druhá objekt zastínění, který právě programujeme. V tomto konkrétním případě bychom mohli tyto funkce klidně zaměnit, protože objekt zastínění předává volání všech funkcí, které sám nedefinuje, zastíněnému objektu, a funkce send_message_to(), send_message() ani funkce interně volané funkcemi Ten(), ten() a plural() ke zjištění gramatických údajů (query_name(), query_gender(), query_plural(), query_vzor() a další) v našem programu definovány nejsou. V jiných případech by však záměna this_object() a query_shadow_owner() mohla změnit fungování programu.

Funkce s parametry

editovat

U některých vlastností, které měnič uměl nastavovat už jakožto učeň, zjistíme po prozkoumání příslušné funkce, že očekává parametry. Je tomu tak například u „označení“, tedy onoho krátkého popisu objektu, který se objevuje v inventáři hráče nebo v místnosti při rozhlédnutí. Imaginací funkce query_short(), která tento krátký popis vrací, zjistíme, že v její deklaraci je uveden vstupní parametr:

FUNKCE: query_short
DEKLARACE: varargs string query_short(object who)
POPIS:
   Vrací krátký popis objektu. Krátký popis se projevuje např. v seznamu 
   výstroje nebo v seznamu přítomných objektů v popisu místnosti. Parametr
   'who' udává, kdo přesně tento objekt vidí (není-li zadán, bere se zaň
   previous_object()).

   Krátký popis nesmí mít na konci žádná větná znaménka (jako '.', '!'
   nebo '?') a nesmí obsahovat žádný řádkový zlom ('\n').

   Na začátku krátkého popisu by mělo být velké písmeno.

VIZ: set_short
SKUPINA: basic
ZDROJ: /i/item/description.c

Slovo varargs, které vidíme na začátku deklarace funkce, je jeden z tzv. modifikátorů, které deklaraci upřesňují, a znamená, že funkce má proměnlivý počet parametrů, tedy že onen jeden deklarovaný parametr typu object můžeme při jejím zavolání vynechat. Následné string známe už z prvních pokusů s funkcí query_smell() — je to vyjádření toho, že i funkce query_short() vrací znakový řetězec, tedy text.

Konečně, zmiňovaný vstupní parametr je deklarován pomocí object who, které je vloženo do závorek za jménem funkce. Slovo object označuje typ parametru, tedy má se jednat o odkaz na nějaký herní objekt, a ne třeba o číslo nebo o řetězec.[6] Pojmenování parametru who oproti tomu slouží jen jeho označení uvnitř funkce, takže ve svém programu si můžeme zvolit označení úplně jiné; v následujícím příkladu si jej na ukázku nazvěme třeba pozorovatel[7] a využijme jej k tomu, aby se námi zastíněný předmět jevil různým pozorovatelům různě:

varargs string query_short(object pozorovatel)
{
  // když není zadán pozorovatel, pak to budiž objekt, který funkci volal:
  if (!pozorovatel)
    pozorovatel=previous_object();
  // když je to hráč s mushlí, předpokládáme, že je to měnič
  if (playerp(pozorovatel) && present("mushle",pozorovatel))
    return Ten(1,this_object())+", čekající na to, až "+ho(4,this_object())+" změníš";
  else
    return Ten(1,this_object());
}

Protože parametr funkce je nepovinný a nepředaný parametr má uvnitř funkce nulovou hodnotu, otestujeme nejprve přítomnost parametru. Právě to zjišťuje podmínka if (!pozorovatel) (! znamená negaci, potažmo nulovost), a pokud se ukáže jako pravdivá (tedy pokud má pozorovatel nulovou hodnotu), nastaví hodnotu pozorovatel na previous_object(), tedy objekt, ze kterého byla tato funkce zavolána.

Jakmile jsme si takto zajistili, že pozorovatel je určitě nějaký objekt, můžeme se ptát na jeho vlastnosti. K tomu využíváme funkce playerp(), která nám sdělí, zda daný objekt je hráčská postava, a present(), která zjistí, zda se v inventáři daného objektu nachází objekt nesoucí dané označení (&& je logická konjunkce). Protože jediný herní objekt, který nese označení „mushle“, je měničská mušle, kterou neměniči do ruky nedostanou, můžeme při splnění této podmínky předpokládat, že pozorovatel je měnič, a poslat mu zvláštní měničský krátký popis zastíněného objektu, zatímco všem ostatním bytostem pošleme krátký popis úplně obyčejný.

Další vlastnosti

editovat

Herní objekty mají ovšem mnohem více vlastností, než které jsou měničskému učni přístupny pomocí příkazu změň. Můžeme si prohlédnout symboly v Matici a jednotlivě je kontemplovat a pak imaginovat jednotlivé funkce, jejichž jména nám zní zajímavě, nebo i naslepo zkoušet meditovat různá slova nebo shluky písmen, které by možná mohly pojmenovávat něco, co nás zajímá, a postupně odhalit funkce, které určují například pohyblivost nebo hmotnost předmětů, průchody mezi místnostmi, sytnost potravin či jedovatost jedů, sílu zbraní či tloušťku stromů.

Tak se například dobereme k funkci query_invis(), podle níž okolní objekty rozpoznávají, zda a nakolik je daný objekt viditelný. Imaginací získaný popis této funkce samotné nám neřekne mnoho:

FUNKCE: query_invis
DEKLARACE: int query_invis()
POPIS: 
   Vrátí hodnotu zadanou pomocí set_invis().
   Pokud je query_invis() nulové, znamená to, že objekt je plně viditelný.
VIZ: set_invis, query_short
SKUPINA: basic
ZDROJ: /i/item/name.c

Obsažnější je popis odkazované funkce set_invis():

FUNKCE: set_invis
DEKLARACE: void set_invis(int flag)
POPIS:
   Podle obsahu flagu objekt učiní:
      viditelným           ... flag == V_VIS    
      neuvedeným           ... flag == V_NOLIST
      skrytým              ... flag == V_HIDDEN
      třpytně neviditelným ... flag == V_SHIMMER
      zcela neviditelným   ... flag == V_INVIS 

   Tyto konstanty jsou definovány v <invis.h>.

   Viditelné objekty jsou ty, které jsou hned vidět v seznamu objektů
   při prohlédnutí místnosti, hráče nebo nádoby. 
   Neuvedené jsou ty, které se v těchto seznamech neobjevují, ale jinak 
   se chovají úplně jako viditelné.
   Je-li objekt skrytý, pak je neuvedený a navíc, když s ním jiný hráč
   něco dělá, objeví se v hláškách ostatním jen "cosi" nebo "kdosi".
   Třpytně neviditelné objekty jsou skryté, gramatické funkce vracejí
   v jejich případě "něco" nebo "někdo", a objekt v místnosti, kde je 
   přítomen, vytváří lehký třpyt. 
   Zcela neviditelné objekty ani nevytvářejí třpyt; gramatické funkce sice
   vracejí "něco" nebo "někdo", ale parse_com() objekt nenajde.

VIZ: query_invis, query_short
SKUPINA: basic
ZDROJ: /i/item/name.c

Na základě těchto informací už můžeme sestavit jednoduchý program, který změní zastíněný předmět tak, že se nebude vypisovat v inventáři objektu, v němž se nachází, tedy bude-li ležet v místnosti, příchozí jej neuvidí, a bude-li jej mít v rukou hráč, při jeho prohlédnutí se přítomnost předmětu ničím neprozradí:

#include <invis.h>

int query_invis()
{
  return V_NOLIST;
}

Podobné pokusy můžeme nyní provádět se všemi dalšími vlastnostmi, jejichž funkce se nám podaří v dokumentaci nalézt.

Přímé nastavování vlastností

editovat

Dosavadní příklady se omezovaly na překrývání funkcí, které na požádání sdělují nějakou vlastnost objektu. Pohled na funkci set_invis() nám ovšem připomněl, že vlastnosti je možno též přímo nastavovat. Zatímco překrytím funkce query_invis() začne objekt navenek tvrdit něco o své viditelnosti, zatímco uvnitř zůstane jeho viditelnost nastavena na původní hodnotu, přímým zavoláním funkce set_invis() se změní nastavení viditelnosti uvnitř samotného objektu.

Obdobné nastavovací funkce s předponou set jsou k dispozici téměř u všech vlastností, na které se můžeme zeptat funkcemi s předponou query. Pro vlastnosti vyjádřitelné čísly navíc zpravidla existují i funkce s předponou add, které příslušnou vlastnost změní o dané množství. Vedle nám již známé funkce query_short() tak najdeme i funkci set_short(), funkci query_fuel(), která informuje o tom, jak dlouho ještě bude hořet lampa nebo pochodeň, zase doprovázejí funkce set_fuel() a add_fuel().

Protože volání nastavovacích funkcí mění vlastnosti objektu víceméně natrvalo, je při jejich užívání namístě opatrnost. Některá volání nastavovacích funkcí jsou proto úplně nebo částečně zablokována, ba odpovědní andělé v současnosti (duben 2017) zvažují případné úplné zablokování všech přímých volání nastavovacích funkcí z měničských programů. Proto, pokud se pustíte do experimentování s těmito funkcemi, dodržujte několik základních pravidel, abyste zbytečně nepřivolávali hněv nebes:

  1. nepřenastavujte žádné vlastnosti přímo v objektech hráčů,
  2. nejraději omezte volání nastavovacích funkcí na ty případy, kdy dojde k následnému zničení objektu, resp. spotřebování nastavené vlastnosti (shoření pochodně, vyhoření oleje v lampě, snědení potravy, vypití vody z lahve apod.)
  3. pokud z nějakého herního důvodu přece jen voláte nastavovací funkci v objektu, který nejspíše nebude v nejbližší době zničen, pak po odeznění tohoto důvodu nastavte opět původní hodnotu.

Struktura programu

editovat

Výše popsané funkce (a u nich uvedené příklady) nám poskytují docela slušnou zásobu kamenů pro stavbu programů. Abychom je však bez přílišného tápání dokázali spojit ve funkční celek, musíme si uvědomit, jak je program v LPC uvnitř uspořádán.

Komentáře

editovat

V celém textu programu se tu a tam mohou vyskytovat programátorské komentáře, určené k vysvětlení toho, co určitým úsekem programu zamýšlíme. Takové komentáře vidíme i v příkladech výše, a to ve dvou provedeních:

  • dvojznak // prohlásí za komentář zbytek řádku, tedy překladač LPC bude zbytek řádku včetně tohoto dvojznaku ignorovat
  • dvojznak /* otevře komentář, který může být víceřádkový, tedy překladač LPC bude ignorovat veškerý text, než narazí na dvojznak */, jímž se komentář opět uzavře

Komentáře nepíšeme jen pro jiné programátory, kteří by měli naše programy číst (v případě měničského programování dokonce nikdo jiný naše programy číst ani nemůže), ale také abychom po nějakém čase sami věděli, proč jsme to či ono v programu napsali právě tak a ne jinak.

Deklarace dědičnosti

editovat

Na začátku programu se může vyskytovat jedna či více deklarací, od jakých jiných programových modulů má náš objekt dědit své vlastnosti. Tyto deklarace jsou vždy uvozeny klíčovým slovem inherit, případně ještě doplněným předsazenými modifikátory (například virtual inherit). Co přesně znamenají tyto deklarace i případné modifikátory, to si probereme později. Pro začátek si vystačíme bez nich — to znamená, že všechny vlastnosti, které má námi programované zastínění mít, do něho musíme naprogramovat my sami.

Vložené soubory definic

editovat

Zpravidla na začátku programu (ale zpravidla až za případnou deklarací dědičnosti) se mohou vyskytovat řádky začínající příkazem#include.[8] Ty říkají, že na místo daného řádku se má do programu vložit obsah souboru, který je uveden za samotným #include(). Obsahem takového vloženého souboru jsou nejčastěji definice konstant, jejichž jména (podle tradiční konvence psaná velkými písmeny, aby se na první pohled odlišila ode jmen proměnných) jsou snadněji zapamatovatelná i přenositelná než interní kódy, které zastupují.

Chceme kupříkladu v rámci svého programu posílat hlášky do okolí a najdeme si k tomu funkci send_message().[9] V jejím popisu (který si přečteme tím, že v Matici imaginujeme její název) je uvedeno, že funkci bychom při volání měli sdělit, jakým typem akce je hláška vyvolána a jakými smysly je obsah hlášky vnímatelný, a že pro toto sdělení bychom měli použít konstant definovaných v /sys/message.h (jejich seznam nám prozradí imaginace souvisících MT_LIST a MA_LIST).

Takříkajíc hlavní hmotou programu jsou definice funkcí, kterých program může obsahovat prakticky neomezeně. Jak jsme viděli v příkladech výše, definice funkcí začíná vždy její deklarací neboli hlavičkou, z níž se dozvíme, jak se funkce jmenuje, jaké parametry očekává a jakou hodnotu vrací, a pokračuje jejím tělem, tedy do složených závorek uzavřenou posloupností jednotlivých programových příkazů, které popisují, co má funkce postupně udělat.

V příkladech výše nalézáme hlavičku funkce vždy na samostatné řádce, stejně jako složené závorky, do kterých je uzavřeno tělo funkce, a příkazy v těle funkce tu více, tu méně odsazené od levého okraje. Veškeré tyto grafické úpravy slouží jen lepší přehlednosti programu v očích programátora, samotné LPC na dodatečné mezery ani na zlomy řádky nehledí. Místo úhledně zapsané funkce:

varargs string query_short(object pozorovatel)
{
  if (!pozorovatel)
    pozorovatel=previous_object();
  if (playerp(pozorovatel) && present("mushle",pozorovatel))
    return Ten(1,this_object())+", čekající na to, až "+ho(4,this_object())+" změníš";
  else
    return Ten(1,this_object());
}

bychom klidně mohli napsat:

varargs string query_short(object pozorovatel){if(!pozorovatel)
pozorovatel=previous_object();if(playerp(pozorovatel)&&
present("mushle",pozorovatel))return Ten(1,this_object())
+", čekající na to, až "+ho(4,this_object())+" změníš";else return
Ten(1,this_object());}

a funkce by fungovala úplně stejně. Velmi důrazně však doporučujeme uspořádávat programy do úhledných řádek se smysluplným odsazováním, protože jinak se v nich po čase nevyznají nejen jiní programátoři, nýbrž ani sám autor. Možných způsobů řádkování a odsazování je povícero, můžete převzít ten, jejž používáme v tomto kurzu, nebo si vyvinout nějaký vlastní, ale snažte se jej následně důsledně uplatňovat ve všech programech, které píšete.

Drobnosti okolo

editovat

V prostoru mezi definicemi jednotlivých funkcí se mohou nacházet ještě některé drobnosti, jejichž význam si vysvětlíme až později, ale které si na tomto místě zmíníme, kdybychom se s nimi někde setkali. Kromě programátorských komentářů popsaných výše to mohou být deklarace proměnných, které se používají ve více funkcích (například int hmotnost; nebo private object hrac; nebo nosave string napis="NEVSTUPOVAT";), a další preprocesorové příkazy (řádky začínající znakem #), z nichž jsme zatím poznali #include, ale jichž existuje mnohem více.

I v programech zkušených programátorů se občas vyskytují chyby, natož když se člověk teprve učí programovat. Když hráč Prahů narazí na chybu, kterou udělal někdo jiný, pak v horším případě fungují věci jinak, než mají, a v lepším případě se před ním otevře časoprostorová díra. Když měničský mistr udělá ve svém programu chybu sám, pak v onom horším případě musí prostě hledat na všech místech programu, která se na akci podílela, ale v lepším případě se mu objeví ona hláška o časoprostorové díře, před níž se ovšem vypíše údaj o programovém souboru, v němž se chyba vyskytla, údaj o řádku, na němž byla zjištěna, a stručný text popisující, v čem chyba spočívá. Například když měnič Šnek udělal chybu v programu pokus na řádce 7, může spatřit třeba toto:

z/cechy/bratrstvo_zmeny/obj/players/snek_pokus.c line 7 :syntax error
Před tebou se otevřela časoprostorová díra.

Ono závěrečné syntax error je jedna z mnoha možných chybových hlášek. Jejich nejčastější typy shrnuje a stručně vysvětluje následující přehled:

Bad argument 1 to call_other
Došlo k pokusu zavolat obj->fun(), kde ovšem obj nebyl objekt ani jméno načitatelného souboru. (Většinou je obj nulové, například protože objekt byl mezitím zničen.)
Calling a vanished simul_efun
Něco nefunguje v mudových programových knihovnách. Takováto chyba určitě není způsobena tím, že by měnič něco chybně napsal do svého programu, a měla by být proto co nejrychleji ohlášena někomu z andělů (například pomocí příkazu chyba).
Can't load objects when no effective user,
Illegal to call clone_object() with effective user 0,
Illegal use of restore_object(),
Illegal use of save_object()
Objekt nemůže načítat a klonovat jiné objekty a číst žádné soubory, pokud nemá efektivní uživatelský identifikátor (euid). Tato chyba by se už vyskytovat neměla, proto její případné výskyty hlaste andělům (například pomocí příkazu chyba).
Error in loading object,
Failed to load file
Chyba v objektu, který měl být načten. Případně se může jednat o chybu v jednom z děděných modulů, nebo o chybějící děděný modul.
Illegal array size
Příliš velké pole. Pole smějí mít nejvýše 3000 položek.
Illegal character constant
Neplatný znak ve zdrojovém kódu. Vzniká nejčastěji vlivem zapomenutého " na začátku nebo na konci řetězce.
Illegal to shadow 'nomask' function
Pokus zastínit objekt zastíněním, které obsahuje deklaraci funkce, která je v zastiňovaném objektu deklarována jako nomask.
Indexing on illegal type.
Operátor indexu [] byl použit na jiný typ proměnné než pole, asociativní pole nebo řetězec.
Newline in string
Někde na řádce chybí " uzavírající řetězec.
Object the closure was bound to has been destructed
Použití klauzury vázané na objekt, který byl mezitím zničen (takže klauzura nemůže být vyhodnocena).
Return type not matching
Návratová hodnota funkce, vypočtená z výrazu uvedeného po return, neodpovídá typu deklarovanému v hlavičce funkce.
Too many local variables
Byl překročen maximální počet 20 parametrů funkce, deklarovaných v její hlavičce.
Unexpected end of file
V souboru něco chybí už na předkompilační úrovni, např. uzavírací uvozovka řetězce nebo uzavírací závorka v preprocesorovém makru (#define).
Unrecognized inheritance modifier qualifier
Neznámý modifikátor před názvem typu proměnné nebo funkce v její deklaraci. Většinou se jedná o překlep (pirvate místo private, varags místo varargs apod.).
add_action from object that was not present.
Volání add_action() z objektu, který není v okolí bytosti, pro niž se mají příkazy definovat. Nejspíše došlo k přesunu objektu v init() nebo v nějaké podobné nestandardní situaci.
Function not defined by inheritance as specified
Deklarace překrývané funkce nebo volání zděděné funkce nesouhlasí (co do počtu a typu parametrů) s deklarací funkce v děděném modulu.
Privilege violation : ...
Došlo k volání funkce, kterou hlavní objekt povoluje volat jen některým objektům, a volající objekt nepatří mezi tyto objekty.
Syntax error
Chybí závorka nebo středník, přebývá znaménko a podobně. Někdy může také nastat, když je proměnná deklarována podruhé. Pozor, občas se chyba projeví až o několik řádek později, než je chybné místo v textu programu.

Povinné

editovat
  • Většina mudových klientů (včetně MUSHclientu a KildClientu) umožňuje přímo zkopírovat program z Wikiverzity do programátorského editoru v Prazích. Vyzkoušejte si to:
    1. spusťte měničský příkaz programuj "okopirovano" (nebo jinak nazvaný nový program, na tom nesejde)
    2. příkazem a přejděte do režimu přidávání řádků (po napsání a nezapomeňte stisknout Enter)
    3. překopírujte vybraný program z výše uvedených příkladů na vstup mudového klientu
    4. znakem . (a odřádkováním) ukončete zadávání textu programu
    5. příkazem 1z („vypiš program od řádky 1“) se přesvědčte, že se vám podařilo program zdárně překopírovat
    6. příkazem x program uložte a opusťte editor
    7. spusťte měničský příkaz spusť "okopirovano" na ... na nějakém vhodném objektu poblíž a podívejte se, jak to funguje
  • Takto vytvořený program zkuste postupně pomocí programátorského editoru v Prazích změnit, tedy třeba přepište hlášky z příkladu na nějaké svoje vtipnější, místo vůně zkuste pracovat s hmatem, místo testování přítomnosti měničské mušle testujte přítomnost potravy apod. Nahrazujte řádky příkazem c, mažte je příkazem d, vkládejte nové příkazem i, nahrazujte jen určité kousky řádek příkazem s atd. (nápovědu vám poskytne příkaz h).
  • Zkuste napsat program měnící nějakou vlastnost, která není přístupná učňovskému změň ani použitá ve výše uvedených příkladech — například proměňte předmět, který od vás na tržišti nikdo nechtěl koupit ani za nejmenší minci, na předmět mimořádně cenný, nebo přinuťte ostružiník, jejž jste právě otrhali do mrtě, aby se okamžitě obsypal novými plody. K nalezení vhodných funkcí použijte příkazů kontempluj, medituj a imaginuj v Matici.

Dobrovolné

editovat
  • Sepište program, který změní některou z vlastností měnitelných pomocí příkazu změň, např. vydávaný zvuk. Vyzkoušejte si, jaký je rozdíl mezi změň a spouštěním programu, jež mění danou vlastnost stejným způsobem. Porovnejte nejen složitost zápisu, ale i náročnost takové proměny (množství spotřebovaných duševních bodů) a délku jejího trvání.
  • Projděte si typy zpráv v seznamech MA_LIST a MT_LIST. Sepište dva programy, které budou simulovat efekty přeonačení self a others pomocí funkcí send_message() a send_message_to(). Použijte vhodný typ zprávy podle toho, jakým smyslem je vnímatelná a jakou akcí je způsobena.

Pomocné stránky

editovat

Poznámky

editovat
  1. Jak již bylo zmíněno v minulém kroku, podrobný popis této i každé další zmíněné funkce získáme, pokud v matici imaginujeme její jméno, zde tedy imaginuj this_object.
  2. Funkce all_inventory() nevrací jeden objekt, nýbrž pole objektů, což je datová struktura, se kterou jsme se zatím neučili pracovat. Kdo se to naučil někde jinde, může svých dovedností využít, ostatní si musí na zdárné využívání této funkce ještě počkat.
  3. V teorii objektově orientovaného programování takovéto na pokyn zvenčí spustitelné funkce nazýváme metody. V programovém kódu objektu se mohou vyskytovat — a zpravidla také vyskytují — i funkce, které metodami nejsou, tedy nejsou určeny k tomu, aby se popsaným způsobem volaly zvenčí.
  4. Pro výpis tohoto programu jsme použili syntaktického analyzátoru, který patří k softvérovému vybavení Mediawiki. Díky tomu jsou pro lepší orientaci vyhrazená slova jazyka LPC vyznačena zeleně, označení datového typu červeně, název funkce modře, programátorské poznámky azurově. U dřívějších příkladů jsme této možnosti využít nemohli, protože jsme ještě nepracovali s čistě programovým textem. Od nynějška jí naopak budeme využívat hojně.
  5. Úplný seznam těchto zkratek zjistíme imaginací MT_LIST a MA_LIST v Matici.
  6. Přesný přehled typů si podáme v některém z pozdějších kroků.
  7. Mohli bychom jej nazvat klidně třeba polonedorobek, Lucie nebo x3465, ale zkušenost praví, že je vhodné nazývat parametry (stejně jako funkce a všechno ostatní, co v programu musíme nějak pojmenovat) aspoň trochu popisně, abychom si i my sami po nějaké době i bez podrobného zkoumání programu vzpomněli, co jsme tím či oním identifikátorem označili.
  8. Řádky začínající # nejsou v plném smyslu slova součástí programu. Jsou to příkazy pro preprocesor, tedy pomocný program, který předkousává námi napsaný text programu a doplňuje do něho předdefinované části, než je tento text následně předložen překladači LPC. Proto musí tyto příkazy začínat vždy na začátku řádku a na jejich řádku už nesmí být nic dalšího (podle počátečního znaku # preprocesor pozná, že daný řádek má zpracovat).
  9. Při hledání vhodné funkce si můžeme jednak prohlédnout symboly v Matici, všimnout si, že jeden je též message, a následně zkusit kontempluj message, jednak na základě povědomí, že „hláška“ se anglicky řekne „message“, zkusit naslepo medituj message. V případě tohoto slova se jako vhodnější ukáže kontemplace, protože nabídne užší výběr funkcí. Jejich jména pak postupně imaginujeme, až nám konečně imaginuj send_message prozradí, že je to funkce, kterou hledáme.