Skriptování v ArmA 2 – 2. část – Skriptování

Úvod

K psaní skriptů je nejlepší využít externí soubory. Ty jsou umístěny jako soubor mise *.sqm. Složku, ve které nalezneme mise, které jsme v editoru vytvořili, nalezneme ve složce:

Dokumenty / ArmA 2 Other Profiles / Jméno_profilu / Missions / Název%20Mise.Ostrov

Pro popisování mise, jako se brífink, úkoly, nebo poznámky se používá soubor briefing.html. Náhled mise ve výběru z hlavního menu se užívá overview.html. Pro nastavení mise, jako parametry, respawn atd. je soubor description.ext. A závěrem naše skriptovací soubory *.sqs nebo *.sqf. Zde nám BI nabízí dva jazyky a to SQS a SQF. Zabývat se budu novějším jazykem SQS.

Vytváření souborů SQS není nic složitého, ale musíme si dát pozor na kódování. Vytvoříme si nový textový dokument a změníme příponu na sqs. A pokud budeme kdy potřebovat diakritiku, jakožto my Češi zřejmě budeme, potřebujeme soubor v kódování UTF-8. Ale pozor. Může se stát, že když budeme mít soubor v kódování UTF-8, tak pokaždé, když se na soubor odvoláme, tak nám vyjede ve hře chybová hláška. Musíme totiž použít UTF-8 without signature. Jinak budeme mít na začátku soubory 3 znaky, které jinak nevidíme, a které říkají o soubory, že je to UTF-8, ale naše milá ArmA to bere už jako začátek skriptování.

Soubory briefing.html, overview.html, description.ext a init.sqs, pokud je chceme používat, musí být ve stejném adresáři jako je soubor mise. Vyžadovány nejsou.

Než začneme psát skripty, budeme potřebovat jeden zdroj informací:

http://community.bistudio.com/wiki/Category:Scripting_Commands_ArmA2

To je nezbytná, veledůležitá stránka, kde nalezneme všechny příkazy pro ArmA 2. Je přímo od BI a občas nalezneme i užitečné komentáře a postřehy lidu.

Začínáme

Nyní snad již můžeme začít skriptovat. Začneme něčím jednoduchým. Asi znáte jakési okénko pravo nahoře, jehož objevení doprovází typický zvuk. Zkusíme si ho vyvolat sami. Otevřeme si editor a vytvoříme novou misi. Na ostrově nezáleží. Na ostrov dáme vojáka, hráče. Nyní však potřebujeme událost, která vyvolá akci. Ta událost může být třeba vojákova přítomnost, nepřítomnost, opuštění oblasti, příchod do oblasti nebo něco úplně jiného. K zavolání souboru a tudíž vykonání jeho skriptů můžeme použít buď spínač, nebo jiný soubor. Spínač může reagovat na takovéto události. Také pokud spojíme spínač synchronizací se skupinou, dostaneme jiné události, jako Celá skupina přítomen apod. Existuje jeden VIP SQS soubor, který se spouští úplně sám poté, co se načte mise. To je init.sqs. Tímto souborem můžeme odkazovat na jiné soubory, které zacyklíme, můžeme jím nastavit úkoly mise, poznámky a věci, které je dobré mít na začátku mise nastavené. Skriptováním můžeme udělat prakticky vše. Můžeme vojáka uzdravit, zabít, můžeme mu dát zbraně, přesouvat ho po mapě, dávat mu úkoly. Nyní však začněme příkazem hint.

Vytvoříme složku init.sqs a napíšeme do ní:

hint "Hello World!"

Pro nedočkavce můžeme i něco dalšího. Příkaz format umí vypsat do textu i hodnoty proměnných. A pod klíčovým slovem player se nám skrývá objekt hráčovy postavy. Píšeme do uvozovek na vlkádáme %1, %2 … %n kam budeme chcít vložit hodnotu proměnných. Ty píšeme v pořadí čísel za text. O multiplayer misích si povíme později. Tam budeme muset býti na takovéto věci více opatrní a vědět, kde kód běží. Jestli u klienta nebo na serveru.

hint format["Strana hráče: %1", side player]

A obecně:

text1 = "je"
text2 = " hra"
hint format["ArmA 2 %1 super%2", text1, text2]

Ve výsledku vyleze:

ArmA 2 je super hra

Povšimněte si, že musíme dodržovat mezery, a to jak u proměnných, tak i v format.

Do proměnných můžeme "ukládat data". Jak přešně to funguje nebudeme rozebírat. Naštěstí datový typ proměnných se odvodí sám, ale je podstatný. Přízaky musí mít správné vstupy, jinak uvidíte ve hře chybovou hlášku typu (pokud spouštíte ArmA 2 s parametrem -showScriptErrors):

typ string očekáván bool

Pokud se podíváte na odkaz jež jsme zmínil výše, tak každý příkaz má poté vysvětleno jaký vstup očekává.

Proměnné můžeme rozdělit na dva typy: globální a lokální.

_lokalni = "lokální proměnná"
globalni = "globální proměnná"

Jak vidíte lokální proměnné začínají podtržítkem. Tyto proměnné jsou platné jen v našich skriptovacích souborech. Nejlepší bude udat příklad. Máme spínač na ukončení mise a budeme chcít misi skončit, pokud bude mrtví hráč s názvem nepritel. Spínači jako podmínku udáme konec. Do editoru tedy dáme proti sobě vojáky, a jeden z nich se bude mít název nepritel. Rozhodneme se, že ke zjištění smrti nepřítele použijeme externí soubor do něhož napíšeme:

#loop
if (!alive nepritel) then { _konec = true }
~1
goto "loop"

Takto nám mise nikdy neskončí, protože používáme lokální proměnnou. Situaci spravíme jednoduše:

#loop
if (!alive nepritel) then { konec = true }
~1
goto "loop"

Můžeme si všimnout, že používáme nové prvky ve skriptu. #loop je tzv. návěští (label). Příkaz goto "loop" způsobí, že se skočí na label loop a bude se zase provádět kód podním.

A příkaz ~1 způsobí, že se na tomto místě bude čeket 1 sekundu, jinak by byl tento skript velice náročný, protože by se podmínka vyhodnocovala velice, velice často.

Tento kód:

goto "dva"

#jedna
hint "jedna"

#dva
hint "dva"

vypíše:

dva

Pomocí příkazu exit můžeme provádělí skriptů v souboru zastavit:

hint "jedna"

exit

~1
hint "dva"

Tento kód vypíše pouze:

jedna

Dále se neobejdeme bez podmínek.

#loop
if (alive nepritel) then { hintSilent "nepřítel žije" } else { hintSilent "nepřítel nežije" }
~30
goto "loop"

Tento kód nám každou půlminutu napíše, jestli nepřítel žije nebo nežije. Avšak změna hintSilent nám nebude objevení okénka signalizovat zvukem.

Takovýto kód by si však zasluhoval vlastní soubor, protože nám bude běžet během celé mise a jen těžko bychom z cyklu lezli ven. V praxi tedy ze souboru init.sqs, který se pustí po načtění mise, zavoláme takovýto soubor.

status.sqs

#loop
if (alive nepritel) then { hintSilent "nepřítel žije" } else { hintSilent "nepřítel nežije" }
~30
goto "loop"

init.sqs

[] exec "status.sqs"

A nyní se nám spustí status.sqs a v souboru init.sqs můžeme dále pracovat. A k čemu slouží ty hranaté závorky? Do nich můžeme vložit parametry. Uvedeme si příklad:

init.sqs

["Ahoj"] exec "hinter.sqs"

Nyní jsme souboru hinter.sqs připojili parametr. Ten se čte následovně:

hinter.sqs

text = _this select 0
hint text

A jak se vidět, udává se i číslo od nuly do N. Souboru můžeme přižadit více parametrů:

init.sqs

["krychle", 12] exec "hinter.sqs"

hinter.sqs

hint format["každá %1 má %2 hran", _this select 0, _this select 1]

Description.ext

Paremetry musíme přiřazovat jenom souborům, ale i misi. Tím se dostáváme k souboru description.ext. Je dobré si pamatovat: Pokud budeme něco v souboru měnit a chce projevit změny, musíme misi uložit. Do tohoto souboru se i nastavuje tzv. hlavička mise. Začněme pozvolna, tedy radši hlavičkou:

class Header
{
gameType = DM;
minPlayers = 1;
maxPlayers = 20;
};

Z příkladu je zřejmé, co hlavička dělá. Jenom upozorním, že hodnota proměnné gameType není libovolná. Naleztene na BI stránce zmíněné výše. Parametry jsou záležitost multiplayeru. Každý hostitel je může měnit, pokud je mapa má. V hostovacím okně se mu ukáže příslušné tlačítko. Jak tedy parametry v praxi vytvořit:

titleParam1 = "Time to capture the SCUD:";
valuesParam1[] = { 900, 1800, 2700, 3600, 4500, 5400, 6300, 7200 };
defValueParam1 = 3600;
textsParam1[] = { 15 min, 30 min, 45 min, 60 min, 75 min, 90 min, 105 min, 120 min };

Na tomto příkladu je krásně vidět co k čemu slouží. Povšimněme si, že používáme proměnnou a za ní hranaté závorky. Jedná je o tzv. pole (field). Do takovéto proměnné můžeme dát více hodnot. A ještě k příkladu:

TitleParam1 Nadpis parametru, zobrazuje se hostujícímu hráči.
valuesParam1[] Hodnoty, který má hostující hráč na výběr
defValueParam1 Výchozí hodnota. Tedy ta, které bude vybraná. Je rozumné dát jednu z valuesParam1[]
textsParam1[] To jsou jednotlivé popisy, které hostující hráč vidí.

Parametrů můžeme použít více, ale musí se dodržovat správně pojmenovávat proměnné! Tedy:

titleParam1, titleParam2, titleParam3, …

Měnit pouze koncové číslo, jinak hra nepozná, že se jedná o parametr.

Vybraný parametr získáme takto:

time = param1

Další podstatná věc pro description.ext je nastavení respawnu. Respawnovat se může hráč prostě tam, kde umřel, nebo na určeném místě (či více místech) na mapě, nebo se respawnovat tím, že převezme kontrolu nad AI ve skupině, nebo v celé straně. Kód vypadá takto:

respawn = "BASE";
respawndelay = 20;

Samozdřejmě je dobré taky udat, jak dlouho si bude muset hráč počkat. Proměnná respawn může nabývat hodnot SIDE, GROUP, BASE nebo INSTANT pro respawn na místě, kde hráč zemřel. Respanwdelay je doba v sekundách.

Pro respawn BASE se musí na mapu dát značka. Ta musí mít název respawn_west nebo respawn_east apod. Ta tuto značku bude respawnován příslušník zmíněné strany. Pokud budeme chtít více míst, kam je možné se respawnovat, přidáme značky respawn_west1, respawn_west2, … a hráč bude automaticky respawnován na náhodnout značku.

Pokud budeme chtít používat rádiové zprávy, tak budeme tento soubor potřebovat. Zprávu si nadefinujeme takto:

class CfgRadio
{
  sounds[] = {};

  class zprava1
  {
    name = "";
    sound[] = {"\sounds\zprava1.ogg", db-100, 1.0};
    title = "V Olše už nikdo není. Přesouvám se na další cíl.";
  };

  class zprava2
  {
    name = "";
    sound[] = {"", db-100, 1.0};
    title = "Cíle jsou zajištěny, můžete vyrazit!";
  };
};

Je vidět, že ke zprávě může být připojen i soubor se zvukovým záznamem. Tomu můžeme nestavit hlasitost a výšku, k čemuž slouží další dva parametry. Takto si zadefinujeme všechny zprávy, které budeme chtít v misi využít a zavolají se takto:

player sideRadio "zprava1"

Před příkazem sideRadio vidíme proměnnou player. U zprávy je totiž vidět od koho je. Takto zprávu můžeme volat do několika rádiových kanálů. Do kanálu groupRadio, což je jen pro příslušíky skupiny, ve které se nachází volající, sideRadio pro příslušníky strany, vehicleRadio pro cestující v dopravním prostředku a globalRadio pro všechny.

Briefing.html

Nadále nás čeká soubor briefing.html. Ten již svoji funkci tak plně nevyužije. Je to spíše přeživší záleživost z Operace Flashpoint. Úkoly a poznámky se lépe vkládají pomocí skriptů, potom je budeme moci mít pro každého hráče či tým jiné. Tento soubor spíše využijeme na tzv. debriefing. To jest hláška, kterou vidíte v okně po skončení mise, kde mimo jiné také vidíte své skóre. Je to vlastně jednoduchá HTML stránka:

<html>
<head>
<title>Briefing</title>
</head>
<body>

<!-- Debriefing -->
<h2>
<a name="Debriefing:End1">Mise byla úspěšná.</a>
</h2>
<p>Podařilo se vám dobýt letiště. Skvělá práce!</p>
<hr>

<h2>
<a name="Debriefing:End2">Mise nebyla úspěšná.</a>
</h2>
<p>Člen vašeho týmu byl zabit. Misi nebylo možno splnit.</p>
<hr>
</body>
</html>

Všimněte si, že mise může mít více konců. Ty určuje Debriefing:EndX. Ty odpovídají různým koncům mise. Můžeme je nastavit buď pomocí spínačů, kde vybereme typ, nebo pomocí příkazu endMission:

endMission "END1"

Příkaz endMission může mít přiděleno CONTINUE, KILLED, LOSER, END1, END2, END3, END4, END5 nebo END6. Takže scénáře vašich misí mohou být opravdu pestré.

Abychom se také dostali k přidávání úkolů. Na to slouží příkaz createSimpleTask. Můžeme však používat i složitější záležitosti, jako rodičovské úkoly apod. Těmi se tady zabývat nebudu. Úkoly vytvoříme takto:

task1 = player createSimpleTask["Dobyjte letiště"]
task1 setSimpleTaskDescription["S pomocí pěchoty dobyjte letiště.","Dobytí letiště","Letiště"]
task1 setSimpleTaskDestination(getMarkerPos "posLetiste")

Na prvním řádku vidíme, že úkol dostane player, takže každý hráč. Příkaz setSimpleTaskDescription vytvoří popisky. První řetězec je popis úkolu, druhý napdis úkolu a třejí je popis waypointu. A aby měl waypoint kam ukazovat, nastaví se setSimpleTaskDestination a příkazu getMarkerPos zadáme název značky, kterou jsme na mapě v editoru umístili.

Dále můžeme používat i ukazatele. Pokud na označený text v úkolu klikneme, tak se nám mapa posune na konkrétní místo. Dělá se to následovně. Na ono místo na mapě dáme značku s název např. vesnice. Poté se do setTaskDescription dodá:

setSimpleTaskDescription["Dojděte do <marker name='vesnice'>této vesnice</marker>", ...]

Úkolů můžeme dát hráči hned několik, jenom je třeba dát pozor na pořadí. Musíme je zadávat totiž obráceně. Ten, který chceme mít dole, musíme zadat jako první:

task2 = player createSimpleTask[ ... ]
task2 setSimpleTaskDescription[ ... ]
task2 setSimpleTaskDestination( ... )

task1 = player createSimpleTask[ ... ]
task1 setSimpleTaskDescription[ ... ]
task1 setSimpleTaskDestination( ... )

Poté můžeme nastavit aktuální úkol, který úkol bude nastavený jako aktualní pomocí příkazu setCurrentTask:

player setCurrentTask task1

A samozdřejmě jak je vidět, každý hráč může mít jiný výchozí úkol. Také jste si jistě všimli, že ve hře se objevuje okno, které oznamuje, že máte nové úkoly, splněné úkoly a nesplněné úkoly. Takové okno se volá takto:

taskHint["ÚKOL SPLNĚN:\nVyčistit Olšu", [0.59375,0.83203125,0.4609375,1], "taskDone"]

Příkaz taskHint má tři parametry. První je zobrazovaný text, druhý jeho barva a třetí ikona. Barvy se zatím nelekejte. Pokud budeme chtít psát text na další řádek, požijeme \n což je obecná značka sloužící k tomuto účelu. Barvu zatím přeskočíme. Dále nastavíme ikonu. Můžeme mít taskNew, taskDone, taskFailed nebo taskCurrent. Doporučuji k těmto jednotlivým ikonám dodržovat nepsaný standart, kterým myslím barvu. Pro nový úkol dáme bílou, dokončený zelenou, nesplněný úkol červenou a sloučasný úkol bílou. Jak tyto barvy zapsat:

taskHint ["NOVÝ ÚKOL:\nUdžte Gorku", [1, 1, 1, 1], "taskNew"]
taskHint ["ÚKOL SPLNĚN:\nUdžte Gorku", [0.59375, 0.83203125, 0.4609375, 1], "taskDone"]
taskhint ["ÚKOL NESPLNĚN:\nUdžte Gorku", [1, 0, 0, 1], "taskFailed"]
taskHint ["SOUČASNÉ ÚKOLY:\nUdžte Gorku", [1, 1, 1, 1], "taskCurrent"]

Do hranaté závorky se píšou čísla od nuly do jedné včetně a první tři čísla reprezentují zastoupení RGB a posledí číslo průhlednost textu. Zelenou barvu jsem si spočítal sám a doufám, že se blíží co nejvíce k originálu.

Neobejteme se také bez měnění stavu mise. To se koná následovně:

task1 setTaskState "Succeeded"

Tako můžeme změnit misi na Succeeded což je splněná mise, Failed nesplněná mise, Canceled zrušená mise, Created nová mise a na konec Assigned přídělená mise.

Overview.html

Když z hlavního menu hry vybíráme jakou misi bychom si zahráli, vidíme, že vpravo máme obrázek a popis. K tomu slouží overview.html

<html>
<head>
<title>Název Mise</title>
</head>
<body>

<h2 align="center">Název Mise</h2>

<p align="center"><img src="misson.jpg" width="230" height="170"></p>

<p>krátký popis</p>

</body>
</html>

Ti z vás, kteří znají HTML, tak není co vysvětlovat. Ale pro ty ostatní. Obrázek se skrývá pod <img src=... a zadává se cesta k obrázku src="..." a jeho šířka width="..." a výška height="..." v pixelech. Atribut align="center" nám obsah zarovná na střed.

Lokalizace

Pokud budeme chchtít vytvořit vícejazyčnou misi (doporučuji si to dobře rozmyslet předem), tak nám v tom nic nebrání. Musíme si udělat tabulku, která obsahuje jednotlivé výrazy či věty a různých jazycích. Takový soubor se jmenuje stringtable.csv a bude se vám asi jevit jako excelovský soubor. Nyní se podívejme co do souboru napsat:

LANGUAGE,    English, Czech
STR_WEAPON,  Weapon,  Zbraň
STR_AIRPORT, Airport, Letiště

Ve skriptovacím souboru pak text přečteme takto:

text = (localize "STR_WEAPON")

Texty můžeme taky skládat, například:

text = (localize "STR_GO") + " " + (localize "STR_HOME")

Hlavně musí v souboru stringtable.csv název výrazu začínat STR_ jinak to hra nevezme. Hra si už sama vybere jakou jazykovou verzi má použít. Podobným způsobem lze i vybírat rádiové zprávy se zvuky. To jsem však nikdy nepoužíval, takže se jen zmíním, že je to možné.

Doufám, že jste již nakoukli do říše skriptování. Nyní když si budete procházet BI stránku, zmíněnou výše, tak se v ní budete snad již lépe orientovat. na závěr této čáti dám k dispozici několik praktických skriptů a k tim i komentář. V další části se podíváme na multiplayer.

Příklady skriptů

removeAllWeapons player
{ player addMagazine "30Rnd_556x45_Stanag" } forEach [1, 2, 3, 4, 5]
{ player addMagazine "HandGrenade" } forEach [1]
player addWeapon "M16A4"
player selectWeapon "M16A4"

Pokud chceme hráči dát vlastní sadu zbraní, je dobré dodržovat výše uvedené pořadí. Jinak se může stát, že hráč nebude mít nabyto, nebo nebude mít zbraň v ruce. Příkaz forEach nám vykoná příkaz v hranatých závorkách několikrát. Takže hráči dáme pět zásobníků a jeden granát. Seznam zbraní a munice můžeme nalézt na BI stránkách:

http://community.bistudio.com/wiki/ArmA_2:_Weapons

#up

~1

if (vehicle hrac1 != hrac1) then { goto "up" }
if (vehicle hrac2 != hrac2) then { goto "up" }
if (vehicle hrac3 != hrac3) then { goto "up" }
if (vehicle hrac4 != hrac4) then { goto "up" }

isOut = true
publicVariable "isOut"

Tento skript kontroluje, zda jsou všichni hráči venku z vozidla. Pokud ano, tak se použite příkaz publicVariable, který si vysvětlíme v kapitole Multiplayer.