Ikarus? Uniony? O co w tym wszystkim chodzi?

,

Wstęp

Ja nazywam się Emu, w Silver Ore Team jestem „tym od Ikarusa” i zajmuje się głównie programowaniem gameplayu i szeroko pojętych mechanik. Poza zespołem staram się dokumentować ZenGin (silnik Gothica) czego efekty można zobaczyć na stronie Gothic Modding Community.

Jeśli mieliście styczność z moddingiem Gothica z pewnością spotkaliście się z takimi określeniami jak „pakiety skryptowe” lub „pluginy” jednak czym one są? Aby odpowiedzieć na zadanie wcześniej pytanie musimy cofnąć się do samego początku, czyli premiery narzędzi MDK (Mod Development Kit) do Gothica. Możliwości edycji rozgrywki były imponujące, jednak modderzy szybko napotkali ograniczenia związane z brakiem bezpośredniego wpływu na kod gry.

A mówiąc po ludzku, nie dało się zrobić dużo ponad to co już znaliśmy z Gothica. Oczywiście nie mowa to o historii a gameplayu. Dajmy tutaj prosty przykład: Chcemy zrobić zmianę czasu podczas dialogu i całość ubrać w ładne przyciemnienie ekranu, efekt ten jest znany m.in. z Kronik Myrtany gdzie był używany podczas szybkiej podróży. Mając dostęp tylko do edytowalnych skryptów gothica coś takiego było niemożliwe do zrealizowania. Na szczecie tylko do czasu…

Rozdział 1: Era Ikarusa

Obejściem takich ograniczeń miał być wydany w 2010 roku pakiet skryptowy Ikarus wykorzystujący lukę w parserze Daedalusa (jerzyka skryptowego Gothica) i umożliwiający dostęp do pamięci gry. Otworzyło to niemalże nieskończone możliwości, a największą wadą Ikarusa było to że można było z niego korzystać dopiero po rozpoczęciu nowej gry lub wczytaniu zapisu. Z czym wiązał się ten dostęp do pamięci? A no pozwalał na dostęp do obiektów silnika, czy wykonywanie kodu maszynowego bezpośrednio na procesorze i ci się z tym wiązało wiele innych operacji. Jedną z ważniejszych cech Ikarusa było też zapewnienie niektórych klas silnika, więc mając adres obiektu można było zamienić go na instancje i uzyskać dostęp do jego parametrów.

Stopniowo do ikarusa dodawano nowe funkcje jak na przykład wsparcie G1. Niewiele później bo w 2011 na jego bazie powstał kolejny pakiet skryptowy LeGo oferujący więcej gotowych rozwiązań do szybkiej implementacji w modyfikacjach. Każdy modder mógł teraz  łatwo dodać nowe paski statusu np. staminy, czy dodać dowolną funkcje do tzw. GameLoopa, co oznaczało że będzie ona wywoływana przez cały czas gry. To jednak tylko przykłady, możliwości były dużo większe: trialogi, obsługa kurosra, hookowanie funkcji, zapisywanie dowolnych informacji do plików, tworzenie nowych zCView czy wsparcie dla nowych typów danych (int64, hashtables, queue). Dużo tego prawda? A to nawet nie połowa rzeczy oferowanych przez ten pakiet. Oczywiście nie wszystko było dostępne od razu. Wiele rzeczy było dodawane w aktualizacjach, a po drodze naprawiano masę błędów.

Z użyciem Ikarusa i LeGo powstało wiele świetnych modyfikacji, a wzorowym przykładem wykorzystania tej technologi są Kroniki Myrtany: Archolos. Jednakże nie było to rozwiązanie bez wad, od samego początku największą bolączką była optymalizacja a w szczególności czas wykonywania kodu, który był po prostu długi…

Rozdział 2: Narodziny Uniona

Dopiero w 2016 pojawił się potencjalny rywal dla monopolu Ikarusa. AST (Agama Script Tools), który potem przerodził się w Uniona oferował dużo większe możliwości ingerencji w silnik gry, a sama praca z nim była dużo wygodniejsza i bliższa bezpośredniej edycji kodu źródłowego gry. Zapewniono API z dostępem do niemalże wszystkich klas i funkcji silnika, a dzięki inicjalizacji podczas startu gry możliwa była ingerencja w menu. Pozbyto się też największego problemu Ikarusa czyli długiego czasu potrzebnego na wykonanie kodu. Skompilowanie pluginy działały z taką samą szybkością jak kod gry.

Niestety od samego początku cierpiał Union na jeden duży problem - brak gotowych rozwiązań. Jako że była to nowa technologia nie istniała żadna baza gotowych mechanik które modder mógł by dodać do swojej modyfikacji. Same pluginy, pisane w języku C++, odstraszały niedzielnych modderów, którzy woleli dalej używać przestarzałego Ikarusa, a nie uczyć się nowej technologi mimo że jej wykorzystanie w wielu aspektach jest prostsze.

Jednego jednak Unionowi odmówić nie można, przyczynił się on do załatania mnóstwa bugów silnika oraz umożliwił powstanie uniwersalnych pluginów, znacząco unowocześniających gameplay i działających z niemal każdą modyfikacją. Przykładem mogą być chociażby zUtilities (quicksave, przyspieszanie czasu i wiele innych usprawnień), QuickLoot (szybkie podnoszenie przedmiotów, czy Advanced Inventory (obsługa myszy w ekwipunku, dodawanie ulubionych przedmiotów).

No dobra ale co z modyfikacjami używającymi Uniona? Czy takie istnieją? Owszem istnieją jednak z uwagi na wcześniej wymienione przyczyny są one raczej rzadkością. Dużym projektem który postawił na Uniona miały być Dzieje Khorinis, ale wszyscy wiemy jak potoczyła się historia. Nie oznacza to że takich modyfikacji nie da się tworzyć, czego przykładem jest właśnie Historia Neka.

Rozdział 3: Czasy obecne

Jaki z tego wniosek? Ikarus - ten zły, Union - wybawca? Niekoniecznie.

Wiadomo, że tam gdzie się tylko da, najlepiej stosować Uniona, ale obecnie najlepszym w mojej opinii rozwiązaniem jest używanie obu tych systemów.

Zastosowania Ikarusa

Trialogi
W oryginalnej grze rozmowy mogą być prowadzone tylko z jedną osobą na raz. Jednak jeden z pakietów wchodzących w skład LeGo oferuje funkcje pozwalające obejść to ograniczenie. Dlatego też w Hisotrii Neka będzie można uświadczyć rozmów z wieloma postaciami na raz.

Kolejkowanie funkcji w AI
Czyli tak zwane AI_Function pozwala na dodanie dowolnej funkcji skryptowej do kolejki AI wybranego NPC. Pozwala to nam na tworzenie bardziej zaawansowanych scenek i cutscenek na silniku gry. Niedługo to zastosowanie może zostać wyparte przez Uniona za sprawą plugina zParserExtender.

func void test()
{
	// Normalna Funkcja: 
	// Mdl_ApplyOverlayMds(hero, "HUMANS_SPRINT.MDS");
	
	// hero czeka 2s
	AI_Wait(hero, 2.0);
	
	// Zakolejkownaie funckji żeby wykonała sie dopiero po AI_Wait
	AI_Function_NS(hero, Mdl_ApplyOverlayMds, hero, "HUMANS_SPRINT.MDS"); 
};

Funkcje cykliczne
Jak mówi nazwa są to funkcje które wykonują się co określony czas (lub co każdą klatkę). Umożliwia to pakiet FrameFunctions którego wykorzystanie można zobaczyć poniżej. Takie rozwiązanie umożliwia na wywołanie jakiejś akcji bez ingerencji gracza (np. zagadania do NPC), tylko przykładowo na podstawie odległości do określonego miejsca na mapie

func void Przyklad()
{
    // Funkcja wywoływana będzie 5 razy w odstępach 3000ms = 3s
    FF_ApplyOnceExt(MojaFunckja, 3000, 5);
};

func void MojaFunckja()
{
    Print ("Gothic jest super");
};

Zastosowania Uniona

Złożone mechaniki
Bardziej złożone systemy nie powinny być pisane z wykorzystaniem Ikarusa, ponieważ jak już wspominałem, jest on wolny. Przy implementacji takich rzeczy jak na przykład nowe menu czy minigry warto postawić na Uniona.

Hooki - zmiana działania funkcji
Jeśli chcemy zmienić sposób działania jakiejś funkcji silnika albo podczas jej działania musimy napisać tzw. Hooka. Ikarus również oferuje taką możliwość jednak o wiele wydajniejsze i łatwiejsze jest użycie Uniona. Dzięki takiemu Hookowi możemy na przykład zaktualizować dodane przez nas statystyki w menu postaci. Jeśli chcecie dowiedzieć się czegoś więcej na temat tej techniki to zapraszam tutaj.

Externale
Funkcje dostępne w daedalusie, które nie są zdefiniowane w skryptach nazywamy externalami. Służą one do wywoływania kodu silnika z poziomu skryptów i są lepszą alternatywą pakietu CALL znanego z Ikarusa. Nie będę opisywał tutaj ich szczegółowego działania i procesu tworzenia dlatego zainteresowanym polecam dedykowany artykuł. W naszej modyfikacji takie externale wykorzystywane są na każdym kroku od podnoszenia przedmiotu przez NPC do zablokowania graczowi dostępu do menu (oczywiście podczas cutscenek).

Rozdział 4: Co dalej?

No właśnie, co dalej? Czy modding Gothica już zawsze będzie rozbity na Ikarusa i Uniona?

Ostatnio sytuacja zaczęła się powoli zmieniać. Ikarus już jakiś czas temu przestał otrzymywać aktualizacje. I mimo powstawania nowych pakietów takich jak AFSP, część modderów postanawia przesiąść się na Uniona.

Tymczasem wspomniany Union ma się świetnie i jest podczas przejścia na nowe union-api i gothic-api co jednocześnie uniezależnia go od wykorzystania Union SDK i starszego standardu C++. Powstaje także uniwersalny loader, który umożliwi uruchamianie pluginów z samym SystemPackiem. 

Za sprawa plugina zParserExtender Ikarus zaczyna tracić swoje ekskluzywne funkcje. Zastąpienie go w całości nie jest takie proste więc wykorzystywanie obydwu narzędzi będzie najlepszym rozwiązaniem jeszcze przez jakiś czas, ale można się spodziewać, że w przyszłości Ikarus przestanie być powszechnie używany i pozostanie tylko (nie dla wszystkich miłym) wspomnieniem.