Neverwinter Nights by Tichondrius

Banner-Tichondrius

Toolset > Scripting


Neverwinter Nights Scripting průvodce od Mole.

Třetí kapitola scriptování v NWN

3.1 Seznámení s funkcemi a cykly

Aby jste mohli používat funkce, musíte udělat následující :
- Poskytněte definici funkce
- Poskytněte prototyp funkce
- Vyvolejte funkci

První dva body jsou již hotové a pokud nepotřebujete vytvářet vlastní funkce, nemusíte se o ně starat. Spousta funkcí je již v toolsetu hotová a v každém datadisku je několik nových. Pro naši potřebu bude dostačující, když se dozvíme něco o funkcích již předdefinovaných.
Na několika příkladech si ukážeme základní práci s funkcemi.

3.2 Příklad – Testovací modul

V tomto příkladu si vytvoříme modul, ve kterém snadno dosáhneme level 20.
V toolsetu umístíme jedno přátelské NPC (např. „Boy“), jakýkoliv placeable objekt (např. Barrel) a trigger. Na všechny tyto tři věci umístíme variaci našeho scriptu. V prvním případě se script spustí při rozhovoru, ve druhém při otevření Barrelu a ve třetím při vstupu do triggeru.

a) V properities našeho NPC dáme Edit konverzace, přidáme jakoukoliv větu a v záložce Action Taken spustíme Script Wizarda. Zaškrtneme „Give Reward“ a na dalším okně určíme sumu XP. Pojmenujeme script jménem „xp_01“. Nyní když na záložce Action Taken (script by se nám měl ukazovat v okně) klikneme na Edit, spustí se script wizard a ukáže nám naše veledílo.

void main()
{
    // Give the speaker some XP
    GiveXPToCreature(GetPCSpeaker(), 10000);
}

Jak už jsme si řekli dříve, každý script musí obsahovat funkci main. Ta nám posléze (ve hře) zajistí spuštění funkce GiveXPToCreature. Parametry (pokud zadáte jméno této funkce ve Filtru, zobrazí se vám v okně zpráv) této funkce jsou
void GiveXPToCreature(object oCreature, int nXpAmount).
void – znamená, že tato funkce nemá návratovou hodnotu
object oCreature – je objekt, který bude obohacen o sumu XP
int nXpAmount – je ona suma XP
Pokud si necháte zobrazit také funkci GetPCSpeaker(), zjistíte, že tato funkce nám navrátí hráče, který je zapojen v konverzaci.

b) Pro druhý případ, kde chceme tento script umístit (placeable), nám stačí tento script jednoduše upravit. Nejprve ale pro jistotu nastavte svoje placeable jako použitelné (zaškrtněte „Useable“) a zároveň mu dejte i inventář (Has inventory). V properities jakéhokoliv placeable, na záložce „Scripts“, se můžeme podívat jak náš script spustíme. Vybereme OnOpen kliknutím na „Edit“ a otevře se nám script editor. Opíšeme předchozí script s jednou malou úpravou.

void main()
{
    GiveXPToCreature(GetLastOpenedBy(), 10000);
}


Jelikož jsme zvolili OnOpen event, funkce na zjištění jestli hráč s placeable mluví je nám na nic, místo toho si zjistíme kdo naposledy naše placeable otevřel.

c) Takto můžeme postupovat i u triggeru. Opět trochu script pozměníme a podíváme se, kdo vstoupil do triggeru.
GiveXPToCreature(GetEnteringObject(), 10000);

3.3 Cykly for, while a do while

Často potřebujeme, aby script prováděl opakovatelné úlohy, jako například sečtení prvků pole. Nebo aby opakoval jeden příkaz po dobu platnosti podmínky. Ukážeme si nejprve jednoduchý příklad a na něm si probereme jak to funguje.

int i;
for(i=0, i<5; i++)
{tělo cyklu}

i=0 - cyklus začíná nastavením proměnné i na nulu. To je inicializační část cyklu.
i<5 – testovací cyklus testuje, zda je i menší než 5. Je-li tomu tak, program provede následující příkaz, který se nazývá tělo cyklu.
i++ - potom cyklus použije změnovou část cyklu. V našem případě navýší hodnotu proměnné i o jednu (o to se postará výraz ++, pro lepší pochopení by se tento výraz mohl napsat jako i=i+1).

Po této inkrementaci proměnné proběhne cyklus znovu a to přesně pětkrát - dokud bude platit podmínka i<5. Pokud cyklus změní i na 5, testovací podmínka selže a provede se příkaz za cyklem. Ani jeden ze tří výrazů není povinný, povinné jsou pouze dva středníky, ale cyklus for ( ; ; ) nikdy neskončí.

Cyklus while je cyklus for zbavený inicializační a změnové části. Má pouze testovací podmínku a tělo.

while(testovací podmínka)
tělo

To znamená, že musíme testovat již deklarovanou proměnnou a musíme také tuto proměnnou změnit v těle cyklu (aby ovlivnil testovací podmínku), pokud chceme aby se cyklus konečně ukončil. Jinak se nám script zacykluje. Jestliže se testovací podmínka na začátku vyhodnotí jako FALSE, cyklus se nikdy neprovede, stejně jako u cyklu for. Příkazy for a while jsou téměř ekvivalentní a je na vás, který použijete. Existuje však jemný rozdíl, pokud tělo obsahuje příkaz continue, ale o tom snad až někdy jindy (stačí vám vědět, že continue přeskočí zbytek těla cyklu a začíná novým průběhem cyklu).

Třetím cyklem je cyklus do while, který se od ostatních liší tím, že se nejprve provede tělo cyklu a až potom je vyhodnotí testovací výraz, aby scritp viděl, zda má cyklus znovu opakovat. Takovýto cyklus se na rozdíl od předchozích provede vždy alespoň jednou.

do
tělo
while (testovací výraz)

Závěr – identifikujte podmínku, která ukončuje cyklus
- inicializujte podmínku před testem
- změňte podmínku při každém průběhu cyklu předtím, než ji znovu otestujete

3.4 Operátor if a if else

Když si Script editor musí vybrat, zda udělá určitou činnost, můžeme použít operátor if. Vyskytuje se ve dvou podobách : if a if else. Příkaz if směruje script k provedení příkazu nebo bloku příkazů (blok příkazů je několik příkazů ohraničených složenými závorkami {}), je-li testovací podmínka TRUE a tyto příkazy přeskočí, je-li FALSE. Pokud nepoužijeme složené závorky, provede se pouze první příkaz po podmínce if. U příkazu if nekončí řádek středníkem a je to jeden z mála případů kdy tomu tak je.

if (testovací podmínka)
příkaz

Zkusíme tuto podmínku použít v našem případě a budeme testovat, zda objekt, který má dostat vstoupil do triggeru je hráč. Nechceme přece rozdávat expy každému NPC, které do triggeru vstoupí.

void main()
{
    if (GetIsPC(GetEnteringObject())==TRUE) /* fce GetIsPC vraci TRUE     pokud objekt vstupujici do triggeru je hrac */
    GiveXPToCreature(GetEnteringObject (), 10000);
}

Možná jste si všimnuli použití dvou rovnítek. V tomto případě to je zcela v pořádku, protože pokud použiji dvě, tak porovnávám, pokud jedno, tak přiřazuji (například i=0 přiřadí proměnné i hodnotu nula).
Takto napsaná funkce ale může mít své mouchy a proto ji uděláme o něco lépe. Nadeklarujeme proměnnou, kterou potom budeme testovat a použijeme ji ve fci GiveXPToCreature.

void main()
{
    object oPC = GetEnteringObject(); // definice objektu
    if (GetIsPC(oPC))
    /* podmínka, není nutne psat ==TRUE, pokud je navratovou hodnotou funkce TRUE nebo FALSE.
     Příklad ze života, taky neřikáte 'Pokud je pravda že prší tak..' ale řeknete 'Pokud prši tak...'
     Pokud si nechate v okne nformaci vypsat informace o funkci GetIsPC, zjistite že u této funkce tomu tak je */
    GiveXPToCreature(oPC, 10000); // odmena pripadne objektu oPC pokud je splnena podmínka.
}

Uvnitř podmínky if by se neměla deklarovat žádná proměnná. Proto jsme objekt oPC deklarovali předem. Obecně tedy příkaz if způsobí, že se script rozhodne, zda provede určitý příkaz nebo blok. Příkaz if else způsobí, že se program rozhodne, který ze dvou příkazů provede.

if (testovací podmínka)
příkaz1
else
příkaz2

Je-li testovací podmínka TRUE nebo nenulová, script provede příkaz1 a přeskočí příkaz2. Jestliže je ovšem testovací podmínka FALSE nebo nula, script příkaz1 přeskočí a místo něho provede příkaz2. Příkaz if else se dá použít několikrát za sebou.

if (testovací podmínka)
příkaz1
else
if (testovací podmínka)
příkaz2
else
příkaz3

Operátor ?: je schopný nahradit podmínku if else.
výraz1 ? výraz2 : výraz3
Je-li výraz1 pravdivý, potom je hodnota celého podmíněného výrazu hodnotou výraz2. Jinak je hodnota celého výrazu hodnotou výraz3.
int i;
i = 5 > 3 ? 65 : 7;
Je-li 5 větší než 3, vyhodnotí se i jako 65. Kdyby náhodou 5 bylo menší než 3, bude výraz 7. Ale když už se vám v této podmínce podaří, aby i bylo 7, děláte asi něco špatně.

3.5 Příkaz switch

Pokud testujete více dostupných alternativ, můžete rozšířit sekvenci if else, ale příkaz switch pracuje v tomto případě daleko lépe.


switch (celočíselný výraz)
{
    case návěstí1 : příkaz
    case návěstí1 : příkaz
    …
    default : příkaz
}

Příkaz switch funguje jako směrové zařízení, které scriptu říká, jaký řádek programováho kódu se má provést. Při průběhu příkazu switch přeskočí script na řádek programu, který má návěstí odpovídající hodnotě testovaného celočíselného výrazu (tzn pouze int). Například, má-li celočíselný výraz hodnotu 4, script jde na řádek, který má návěstí case 4:. Jestliže celočíselný výraz neodpovídá žádnému návěstí, přeskočí script na řádek označený default (default není povinný, příkaz ho nemusí vůbec obsahovat, jestliže ho vynecháte, script přeskočí na další příkaz, který následuje za switch).
Pokud vše proběhne podle plánu a provede se příkaz, script by nám proveld jednu nemilou věc (která se ovšem může někdy hodit). Pokračoval by hned na dalším řádku, nikoliv na řádku, který následuje za switch. Příkaz break umožňuje scriptu přeskočit část programového kódu. Tento příkaz můžete použít v příkazu switch, nebo v libovolném cyklu.

int i;
int y
i = d4(); // prikaz na nahodna cisla
switch (i)
{
    case 1 : y=4; break;
    case 2 : y=3; break;
    case 3 : y=2; break;
    case 4 : y=1; break;
}

Hodnota návěstí case musí být konstanta. Pokud vaše alternativy zahrnují testy ve float, string apod, použijte příkaz if else. Nebo převeďte převod pomocí fcí FloatToInt, StringToInt.

Mole


 
 

Toolset


Toolset Tutorial

Or-Horo Tutorial

První Kapitola Scriptování

Druhá Kapitola Scriptování

Třetí Kapitola Scriptování

Čtvrtá Kapitola Scriptování