9 Editor map
Editor map pro hru 8 Kingdoms je jednoduchým nástrojem pro vytváření nových či úpravu existujících
map včetně rozestavění jednotek, budov, a veškerého počátečního nastavení pro danou hru. Byl programován
v C++ Builderu 6 díky jeho vlastnosti tvořit rychle a snadno vizuální aplikace pro Win32 a z toho též plyne,
že je spustitelný pouze na této platformě. Původně byl vyvíjen pouze jako pracovní nástroj pro vývojáře
k pohodlné tvorbě a editaci map, poté však byl upraven a vylepšen, aby mohl být součástí finální Release
verze hry. Hráči je tedy poskytnut další nástroj k tomu, aby si hru utvořil ještě zajímavější.
9.1 Architektura vrstev
Většina aplikační logiky v editoru je implementována v rámci nějaké vrstvy. Každá vrstva je reprezentována
třídou v souboru uMapEditorLayer.cpp. Všechny třídy vrstev jsou odvozeny od společného předka
TMapEditorLayer, jenž obsahuje virtuální metody, které jsou potom hromadně volány pro všechny vrstvy.
Následující tabulka udává výčet a účel všech implementovaných vrstev:
TTerrainHeightMapLayer | Reprezentuje vrstvu převýšení terénů. Pomocí ní jsou vykreslovány různé odstíny pro různé úrovně převýšení terénů. |
TTerrainTypeMapLayer | Reprezentuje vrstvu typů terénů. Pomocí ní jsou vykreslovány různé textury pro různé typy terénů. |
THexMapLayer | Reprezentuje vrstvu hexové mapy. Vykresluje hexovou mřížku a také předává informace o daném hexu. |
TMapBoundariesLayer | Reprezentuje vrstvu ohraničení mapy. Pouze vykresluje nehratelné oblasti na okraji mapy. |
TBuildingMapLayer | Reprezentuje vrstvu editace budov. Vykresluje obrázky budov a předává informace o jejich vlastnostech. |
TUnitMapLayer | Reprezentuje vrstvu editace jednotek. Vykresluje obrázky jednotek a předává informace o jejich vlastnostech. |
TKingdomsMapLayer | Reprezentuje vrstvu rozdělení mapy na království. Pomocí ní jsou zobrazena jednotlivá království a popřípadě i jejich středy. |
TUnitOccurranceMapLayer | Reprezentuje vrstvu výskytu speciálních jednotek na hexech. Stará se o vizuální zobrazování těchto výskytů a předává o nich konkrétní informace. |
TPathfindMapLayer | Reprezentuje vrstvu pro testování vyhledávání cesty. Tato vrstva byla použita pouze pro ladící a testovací účely a v Release verzi editoru není vůbec volána. Vyhledává a vykresluje na mapě optimální cestu. |
TCityLayer | Reprezentuje vrstvu měst. Stará se o jejich zobrazování a předávání informací o jejich vlastnostech. |
THexPreselLayer | Reprezentuje vrstvu kurzoru pro hex, nad kterým má uživatel aktuálně myš. Má na starosti pouze vykreslení jeho zvýraznění. |
TCursorLayer | Reprezentuje vrstvu kurzoru aktuálně vybraného hexu. Má na starosti pouze vykreslení jeho zvýraznění. |
Tabulka 9.1: Přehled vrstev
Každá z těchto tříd má v aplikaci pouze jednu instanci, jenž jsou postupně uloženy v kontajneru TList.
Je i určeno v jakém pořadí jsou vrstvy přidávány - tím je pak určeno i pořadí volání jejich virtuálních metod.
Všechny virtuální metody, jenž může implementovat jakákoliv vrstva jsou uvedeny v následující tabulce:
Draw(TCanvas* can, TRect* rectPixels, TRect* rectHexes) | Vykresluje obsah vrstvy na canvas. Parametr rectPixels udává obdélník pixelů, jenž se mají vykreslovat, parametr rectHexes pak obdélník hexů zadaný jejich souřadnicemi. |
MouseOver(int X, int Y) | Zpracovává pro danou vrstvu událost, kdy je myš nad hexem se souřadnicemi [X,Y]. |
MouseUp(int X, int Y) | Zpracovává pro danou vrstvu událost, kdy je kliknuto myší nad hexem se souřadnicemi [X,Y]. |
MouseDown(int X, int Y) | Zpracovává pro danou vrstvu událost, kdy je stisknuto tlačítko myši nad hexem se souřadnicemi [X,Y]. |
HexSelected(int X, int Y) | Zpracovává pro danou vrstvu událost, kdy je hex vybrán uživatelem [X,Y]. |
Tabulka 9.2: Virtuální metody vrstev
Pořadí vrstev má například přímý vliv na vykreslování (volání metod Draw). Toto pořadí si však může
uživatel přímo v editoru měnit, či například i vypínat vykreslování jednotlivých vrstev a zobrazit si tak
přehledněji informace, které chce.
9.2 Napojení na Resource Manager
Editor map samozřejmě potřebuje využívat zdroje, jenž jsou používány i ve hře samotné - jedná se například
o textové zdroje, informace o jednotkách a budovách. Dále potřebuje být propojeno vytváření, načítání a ukládání
mapy.
Celá inicializace práce s Resource managerem začíná jeho inicializací (volání RMInit v Editor.cpp).
V konstruktoru hlavního formuláře editoru (viz volání TfrmMapEditor::TfrmMapEditor(TComponent* Owner)
v ufrmMapEditor.cpp) jsou pomocí Message systému instanciovány resource managery pro mapu, budovy, jednotky
a textové řetězce. Poté je vybrána angličtina jako standardní jazyk pro editor.
Při vytváření nové mapy je potřeba vyplnit požadovanými hodnotami strukturu TMapInfo
(viz common/rm/rmmap.h) - ta obsahuje rozměry, jméno a popis mapy, vybrané role a
jejich vlastnosti, jejich počáteční diplomatické nastavení. Poté je mapa standardně inicializována
sadou get metod třídy TRM_map_i. Výše popsané operace odpovídají kódu metody
TfrmMapEditor::butNewMapClick(TObject *Sender) v ufrmMapEditor.cpp. Pro otevírání
existujících map a jejich ukládání jsou vždy (po příslušných validacích) volány set metody
třídy TRM_map_i (pro načítání existující mapy pouze pokud je nějaká načtena) a poté
i get metody stejné třídy.
9.3 Generování náhodných map
Generátor náhodných map vytváří náhodný povrch v právě otevřené mapě. Jeho cílem je pouze návrh možného uspořádání převážně terénů a výšek a nezatěžovat s jejich pracným vytváření uživatele. Ten si může postupně vybrat několik návrhů a vybrat si ten, který se mu nejvíce zamlouvá.
V mapě je náhodné generován terén (jsou umisťovány všechny typy kromě terénů města, bažin a skal) a výšky (všechny stupně od výšek stupně 1 až po stupeň 4). Dále jsou také náhodně umístěny výskyty speciálních jednotek. Zbývající akce, které uživatel musí následně ještě provést, aby bylo na mapě možné hrát, je pouze umístění měst a království a určení výchozí polohy hráčů (například vložením stavitele na příslušné místo). Pro vlastní generování se volá funkce generateRandomMap.
Výšky | Jako první operace poté, kdy se nastaví všechny výšky na 1, je náhodné umístění vrcholků kopců, jejichž počet určí uživatel, v maximální výšce, kterou také uživatel určí. Následně se vytvoří vlastní pohoří, podle atributu strmosti kopců, kterou zadá uživatel. Při generování je tedy využito pole reálných hodnot, které se následně převádí do mapy s hodnotami výšek pouze celočíselnými (jejichž počet je navíc omezen na pět), avšak pole reálných hodnot se zachovává pro další použití. |
Lesy | Následuje vytvoření center lesů, jejichž počet a pravděpodobnost dalšího rozšíření zadá uživatel. Po rozšíření lesů kolem jejich center může, podle zadaných parametrů, dojít ke změně lesa na hvozd. Podmínky tvoří například počet lesů na okolních hexech. |
Řeky | Po umístění pramenů řek dochází k jejich "rozlití" do terénu. Zde se využívá ještě obraz mapy v reálných čísel, aby se našel největší spád a tedy koryto řeky. Řeka je generována tedy tak, aby tekla vždy dolů, pokud může, jinak po rovině. V nížině a nebo když řeka nemá již kam téci vytváří "jezera" (reprezentované terénem moře). |
Magické území | Nakonec se ještě umístí oblasti s výskytem speciálních jednotek a to pouze na místa, kde se nechá postavit budova. |
Tabulka 9.3: Postup při vytváření mapy.
9.4 Vykreslování
Vykreslování mapy a celého herního plánu je v editoru realizováno pomocí VCL komponenty PaintBox
(třída TPaintBox). Je použito prosté softwarové vykreslování bitmap
přímo na Canvas, žádná externí knihovna (DirectX, OpenGL) nebyla použita. Vykresluje se vždy pouze
nejmenší konvexní obdélník pixelů, jenž ohraničuje všechny hexy, ze kterých je aktuálně alespoň kousek
vidět. Vykreslování je realizováno voláním virtuálních metod Draw(TCanvas* can, TRect* rectPixels, TRect* rectHexes)
na každé vrstvě. Jak již bylo řečeno v odstavci 9.1 - pořadí vrstev naskládaných v kontajneru ovlivňuje
i pořadí vykreslování bitmap přes sebe. Metoda Draw kromě konvexního obdélníka
pixelů má další parametr - obdélník aktuálně zobrazovaných hexů zadaný jejich souřadnicemi. To zaručuje, že
entity, které nemá smysl uvažovat na pixelové úrovni (jako budovy či jednotky) budou vykreslovány pouze pokud
jsou vidět (tj. uživatel má focus mapy na oblasti, kde se jednotka skutečně nachází).
9.5 Editace království
Editor map samozřejmě podporuje i úplnou editaci hranic království. Aby uživatel nemusel
pracně klikat hex po hexu celou mapu, byla zavedena tzv. centra království. Uživatel označí hexy
center pro každé království a po kliknutí na tlačítko "Generate Area From Center" je spuštěn algoritmus
floodingu (volání funkce computeBorders v uComputeBorders.cpp), jenž postupně zaplavuje
mapu od center království než narazí na jiné zaplavené oblasti. Ve výsledku to znamená, že království
určené svým centrem se sestává z hexů, jenž jsou měřeno hexovou vzdáleností nejblíže právě k centru svého
království. Hexy, jenž mají stejnou vzdálenost od dvou či více center království připadnou tomu království,
jenž má nižší ID. Uživatel si samozřejmě poté může území takto vygenerovaných království sám upravit dle
svých požadavků. Centra království jsou též ukládána do mapy, přestože v samotné hře nemají žádný význam.
9.6 Editace měst
Jedním z netriviálních problémů v editoru map je též editace měst. Město je bráno v podstatě pouze jako
terén ale musí uchovávat nějaké další informace nejen pouze na úrovni samotného hexu - například vlastníka,
či příjem. Velikost měst je pravidly omezena na 7 hexů, jenž musí navíc tvořit souvislou oblast. Pro práci
při editaci s hexy slouží následující metody třídy TfrmMapEditor (viz ufrmMapEditor.cpp).
std::list<int> getCityHexBorderDirections(int x,int y) | Vrátí kontajner čísel určujících směry hranic celého města v rámci jednoho hexu města se souřadnicemi [x,y]. Podle toho jsou poté vykresleny hranice celého města. |
int getCityOwner(int x,int y) | Vrátí ID role, jenž je určena jako vlastník města, jenž obsahuje hex se souřadnicemi [x,y]. |
int getCityID(int x,int y) | Vrátí ID města jenž obsahuje hex se souřadnicemi [x,y]. |
void addingCityHex(int x,int y) | Přidává hex města na souřadnici [x,y]. Je vytvořen zcela nový záznam o městu, nebo je nějaké existující město rozšířeno o jeden hex (pokud není již předtím maximálně veliké a nový hex s ním sousedí). |
void removingCityHex(int x,int y) | Odebírá hex města na souřadnici [x,y]. Město může buď úplně zaniknout, nebo se může rozpadnout na více nesouvislých komponent (maximálně na tři) - odebráním hexu města může tedy paradoxně měst i přibýt. |
Tabulka 9.4: Metody pro editaci měst
9.7 Nastavení diplomacie
Editor map umožňuje pro role, jenž jsou pro mapu vybrány určit též počáteční diplomatické vztahy (více
o Diplomacii viz odstavec 4.2.2) jenž budou platné při začátku hry. Pomocí formuláře lze snadno vybrat
oficiální vztah. Protože role v editoru jsou nezávislé na konkrétním hráči (AI nebo člověk), je nutné
doplnit i další vlastnosti diplomatického vztahu (viz TDipRelation a
TRelationshipProperties v ai/diplomacy/diptypes.h), které jsou
poté ukládány do mapy. Navržené vztahy (položka offeredrs) jsou inicializovány
na stejný, jako je vybraný oficiální (položka rs). Oba vztahy jsou protihráčům
určeny jako známé (příznak offer_seen_by_enemy) a hodnoty důvěr a odhadovaných
důvěr (položky belief a guess_belief) jsou
určeny z oficiálního vztahu pomocí maker DEFAULT_BELIEF_FOR_...
v ai/diplomacy/dipcoefs.h. Hodnoty těchto maker jsou průměrem maximální a minimální hodnoty důvěr
pro daný diplomatický vztah.