by L`f

Jeśli zamierzasz właśnie stworzyć mod dodający nowe misje do gry, nowe postacie rozwijające linię fabularną, czy przyłączalnego bohatera, trafiłeś(-aś) pod dobry adres.

Tworzenie dialogów wymaga umiejętności planowania i na pierwszy rzut oka może wydawać się trudne. Jednak gdy tylko poznamy podstawowe zasady pisania plików .d wyda nam się to bardzo proste i większym problemem będzie wymyślenie tekstu, niż oskryptowanie go.

Pliki .d, które nauczymy się tworzyć, zawierają nieskompilowany skrypt dialogu oraz treść wypowiedzi. Podczas instalacji aplikacja WeiDU skompiluje je do odpowiednich plików i umieści w grze. Jednak tym zajmiemy się dopiero w punkcie piątym tutorialu, a teraz naszym głównym zmartwieniem będzie napisanie dobrego skryptu dialogu, który gra odczyta. No cóż, dość gadania, zabierajmy się do roboty.



SPIS TREŚCI:


1.   Zanim zaczniesz pisać skrypt

1.1.  Programy do ściągnięcia

1.2.  Pojęcia podstawowe


2.   Podstawy tworzenia bloku dialogu

2.1.  Prosty blok dialogowy

2.2.  Zastosowanie tokenów w dialogach

2.3.  Akcja zakończenia i linki między blokami w jednym pliku .dlg

2.4.  Rozbudowane bloki dialogowe i ich elementy

2.4.1. REPLY

2.4.2. DO

2.4.3. JOURNAL i podkomendy


3.   Linki zewnętrzne:

3.1.  Istota komendy EXTERN

3.2.  Struktura i skrypt dialogu łańcuchowego

3.3.  Aktualizacje istniejących plików .dlg, czyli komenda APPEND


4.   Komendy zaawansowane

4.1.  EXTEND_BOTTOM i EXTEND_TOP

4.2.  COPY_TRANS – kopiowanie przejść

4.3.  INTERJECT, czyli wtrącenia

4.4.  INTERJECT_COPY_TRANS

4.5.  Dodawanie nowych warunków do bloków w istniejących plikach .dlg


5.   Kompilacja i implementacja dialogu w grze



1. Zanim zaczniesz pisać skrypt


1.1. Programy do ściągnięcia

Do pisania dialogów nie potrzebujemy skomplikowanych programów, wystarczy nam Notatnik. Warto jeszcze ściągnąć WeiDU, dzięki któremu będziemy mogli zainstalować dialog.

Poleciłbym także Infinity Explorer, jest to przeglądarka silnika Infinity (na silniku Infinity pracuje m. in. Baldur’s Gate II). Dzięki temu programowi możemy poznać konstrukcję oryginalnych dialogów, a także innych elementów gry.

Aby mieć informacje potrzebne do modyfikacji istniejących plików .dlg, przyda nam się również program DLTCEP. Ma on wiele funkcji, nam jednak przyda się tylko jedna.


1.2. Pojęcia podstawowe

Plik dialog.tlk

Jest to zbiór wszystkich stringów (o teksty chodzi, erotomani ;P) występujących w grze, z których każdemu przypisany jest numer. W ‘czystym’ Baldur’s Gate II + Tron Bhaala w tym pliku znajduje się około 70000 stringów.

Pliczku tego nie można zmieniać ręcznie (spowodowałoby to niekompatybilność z niemal wszystkimi modami), ale jego bezinwazyjna modyfikacja jest możliwa za pomocą aplikacji WeiDU.

String

String to tekst przypisany do jednego numeru w pliku dialog.tlk. Może mieć dowolną ilość znaków, tak więc stringiem może być imię ‘Anomen’, albo długi wpis w dzienniku bohatera. W dialogach również je wykorzystujemy, a każdy string pojawia się w okienku dialogowym jako pojedyncza wypowiedź. Więcej o zapisywaniu stringów w dialogach dowiemy się w punkcie 2.1.

Blok dialogowy

Jest to podstawowa jednostka dialogu składająca się z jednego oskryptowanego stringu. Do stringu w dialogu można opcjonalnie przypisać wykonywaną akcję (np. odebranie przedmiotu, zaatakowanie postaci gracza, ucieczkę z obszaru), odpowiedź rozmówcy z naszej drużyny, czy dodanie wpisu do dziennika. W blokach rozpoczynających możemy dodać również warunek, kiedy ten blok ma zostać wypowiedziany.

Blok musi również mieć połączenie z innym blokiem dialogowym, lub akcję zakończenia dialogu. Wszystkie bloki dialogowe w danym pliku .dlg tworzą tak zwane drzewko dialogowe, które możemy obejrzeć, rozwijając pierwszy lepszy dialog otwarty dzięki Infinity Explorerowi.

Plik .dlg

Jest to plik dialogowy, który odczytuje gra. Zawiera on strukturę dialogu (połączenia między blokami, odpowiedzi bohatera, akcje, warunki), ale nie zawiera samego tekstu – tylko odnośniki do konkretnych stringów z pliku dialog.tlk Zawartość takiego pliku można przypisać jednemu lub wielu stworzeniom w grze.



2. Podstawy tworzenia bloku dialogowego


//tak będę zapisywał uwagi na temat danego fragmentu kodu


2.1. Elementy prostego bloku dialogowego

Nadszedł czas na skryptowanie – otwieramy Notatnik i zapisujemy pusty plik nie jako *.txt, tylko jako *.d. .d to format pliku, który aplikacja WeiDU potrafi skompilować. Poznajmy więc podstawową komendę tworzącą pusty, surowy plik .dlg:



BEGIN ~NAZWA_PLIKU_.DLG~

Komenda BEGIN ~~ tworzy plik .dlg o nazwie takiej, jak zawarta między tyldami. Na przykład, chcąc stworzyć plik dialogowy gienek.dlg wpisujemy w naszym pliku .d komendę:



BEGIN ~GIENEK~

Proste. Plik .dlg o danej nazwie zostanie dodany do zasobów gry przy instalacji modu. Nazwa może być dowolna, byleby nie zawierała tyld i miała najwyżej osiem znaków.

Teraz zajmiemy się blokiem dialogowym. Schemat prostego bloku dialogowego przedstawia się tak:



IF ~(warunki, przy których blok ma być użyty, tylko dla bloków rozpoczynających)~ THEN BEGIN (nazwa bloku)
SAY ~(wypowiedź)~
IF ~(warunki, dla których przejście nr 1 ma zostać wykonane)~ THEN (komendy przejścia nr 1)
END

W tyldach pomiędzy IF i THEN umieszczamy warunki, korzystając z oznaczeń skryptowych warunków, które gra może odczytać. Pełny ich spis znajduje się w pliku trigger.ids, który możemy otworzyć Infinity Explorerem. Zwykle po nazwach łatwo się zorientować, co warunkują.

Nazwa bloku może być dowolna. Ważne jest tylko, żeby nazwy bloków w połączeniach się zgadzały. Bloki w oryginalnych plikach .dlg są nazwane numerami od 0 wzwyż. W danym pliku .dlg danej nazwy bloku można użyć tylko raz.

W polu wypowiedź wpisujemy tekst naszej wypowiedzi. Tekst ten przy instalacji modu zostanie zapisany jako nowy string w dialog.tlk. W rodzaju znaków, które może zawierać wypowiedź, są tylko drobne ograniczenia. Tekst zapisany w tyldach, który zawiera tyldę, zostanie źle odczytany, dlatego teksty zawierające tyldy powinno się pisać w taki sposób:



SAY %Kopytko~!!%

Albo:



SAY "Kopytko~!!"

Jednak w zapisie stringu z wykorzystaniem procentów nie możemy użyć procentu w treści stringu, analogicznie rzecz się ma z cudzysłowami. Najlepiej więc korzystać z tyld.

Jeśli jednak chcemy zastosować w wypowiedzi procent, tyldę i cudzysłów (pogratuluję temu, kto wpadnie na taki pomysł), możemy zapisać taki string w jeszcze inny sposób:



SAY ~~~~~"Kopytko~!!", zawartość 74%~~~~~

Czyli zapisujemy string pomiędzy dwoma zestawami pięciu tyld. Jedynym ograniczeniem do stosowania takiego sposobu jest to, że nie wolno umieścić w stringu pięciu tyld w jednym ciągu, jednak kto, u diabła, robiłby coś takiego!? ;)

Komendy przejść dostarczają grze informacji o akcji, jaką ma wykonać, kiedy dane warunki są spełnione. Na przykład osoba postronna nie nawiąże dialogu z Minsciem, kiedy ten jest martwy, potrzebna jest alternatywna ścieżka dialogu. Takich przejść w jednym bloku dialogowym możemy stworzyć wiele, ale tymi kwestiami zajmiemy się w punkcie 2.3, gdzie omówię tworzenie skomplikowanych bloków dialogowych.

Komend przejść jest kilka, wśród nich wyróżniamy najważniejsze:

Komendy linków:

GOTO (nazwa bloku) – linki wewnętrzne, czyli połączenia wewnątrz danego pliku .dlg

EXIT – zakończenie dialogu

EXTERN (nazwa pliku .dlg) (nazwa bloku w danym pliku .dlg) – linki zewnętrzne, czyli połączenia z innymi plikami .dlg

Komendy zdarzeń:

REPLY ~(treść odpowiedzi)~ - odpowiedzi, które możemy wybierać jako rozmówca

DO ~(komendy akcji)~ - akcje, które w danej chwili mają zostać wykonane, pisane w języku skryptów Infinity

JOURNAL i jej podkomendy – wpisy w różnych kategoriach dziennika.

Na razie zajmiemy się komendami GOTO i EXIT, komendy REPLY, DO i JOURNAL poznamy w punkcie 2.4, a komendą EXTERN zajmiemy się dopiero w punkcie 3.1.

END oznacza koniec danego bloku skryptowego w pliku .dlg.


2.2. Zastosowanie tokenów w dialogach

Warto o tym wspomnieć, zanim przejdzie się do pisania dialogu. Tokeny dialogowe to specjalne wyrażenia, które można zapisać w wypowiedzi bohatera, a które w zależności od pewnych warunków wyświetlają w okienku dialogowym gry różny tekst.

Pełna lista w polskiej wersji Baldur’s Gate II i przykład ich zastosowania – opracowane przez Shaggiego – znajdują się w tym miejscu.


2.3. Akcja zakończenia i linki między blokami w jednym pliku .d

Każdy blok dialogowy musi zawierać link do innego bloku dialogowego, chyba, że jest to blok kończący dialog (w grze wywołuje on przycisk ‘koniec rozmowy’). Istnieją dialogi jednoblokowe, w których pierwszy blok jest tym kończącym. Tak skonstruowane dialogi ma np. większość statystów w miastach.

Jeżeli dla naszego Gienka będziemy chcieli stworzyć taki blok, będzie to wyglądało na przykład tak:



IF ~True()~ THEN BEGIN 0 //True() jest zawsze spełnione, wpisuje się je tam, gdzie musi być warunek, a żadnych warunków nie chcemy umieszczać
SAY ~Hej! Którędy na Wrocław?~ //wypowiedź Gienka
IF ~~ THEN EXIT //komenda powodująca zakończenie dialogu
END //zakończenie bloku dialogowego

Tak wygląda możliwie najprostszy blok dialogowy.

Jednak gra wyglądałaby średnio, gdyby dialogi składały się z pojedynczych bloków. Dialogi są rozbudowane, składają się z wielu połączonych bloków, a do łączenia dwóch takich jednostek służy komenda GOTO.

Oto przykład połączenia trzech bloków dialogowych Gienka:



IF ~NumTimesTalkedTo(0)~ THEN BEGIN powitanie //NumTimesTalkedTo(X) to warunek określający, ile razy rozmawialiśmy z daną osobą; wartość X=0 to pierwsza rozmowa
SAY ~Cześć! Jestem Gienek i nie mam tchawicy.~
IF ~~ THEN GOTO tchawica //link do bloku o nazwie 'tchawica'
END

IF ~~ THEN BEGIN tchawica //jako że nie jest to blok rozpoczynający dialog, nie ma warunków dla rozpoczęcia tego bloku
SAY ~Urwała mi się, kiedy podrapałem się w nogę.~
IF ~~ THEN GOTO koniec //link do bloku o nazwie 'koniec'
END

IF ~~ THEN BEGIN koniec
SAY ~Dobra, no nie gap się tak, . Już sobie idę.~ // to token, po szczegóły odsyłam do punktu 2.2
IF ~~ THEN EXIT //zakończenie dialogu
END

Można to zapisać tak. Zadziała. Jednak zdecydowanie prościej jest zastosować skróconą formę skryptu. Ten sam dialog w skróconej formie wygląda tak:



IF ~NumTimesTalkedTo(0)~ THEN BEGIN powitanie
SAY ~Cześć! Jestem Gienek i nie mam tchawicy.~
= //znak równości działa jak komenda GOTO do następnej wypowiedzi
~Urwała mi się, kiedy podrapałem się w nogę.~ //pomijamy komendę SAY, która już raz została użyta w tym bloku
=
~ Dobra, no nie gap się tak, . Już sobie idę.~
IF ~~ THEN EXIT
END

Jasne, że prościej. Na trzy bloki dialogowe piszemy jeden blok skryptowy, w dodatku używamy jednej, a nie trzech nazw (pozostałe dwie dopowiada sobie WeiDU, ale to już nie nasz problem).

Komenda GOTO jednak jest używana, lecz w sytuacjach bardziej skomplikowanych, kiedy w zależności od warunków może wystąpić kilka przejść z jednego bloku dialogowego.

Dzięki temu można tworzyć nie linie, a drzewka dialogowe. Oto przykład skryptu dialogu o dwóch różnych liniach. Bohaterem jest wciąż Gienek.



IF ~NumTimesTalkedTo(1)~ THEN BEGIN powrót //wartość 1 dla tego warunku oznacza, że drugi raz zaczynamy rozmowę z daną osobą
SAY ~O, wracacie, . Gienkowi miło was znowu widzieć.~
=
~Wiecie, hm, chciałbym cię zapytać...~
IF ~Gender(LastTalkedToBy,MALE)~ THEN GOTO facet //przejście nr 1, opis poniżej
IF ~Gender(LastTalkedToBy,FEMALE)~ THEN GOTO dziewczyna //przejście nr 2
END

IF ~~ THEN BEGIN facet
SAY ~... widzisz pan tego tam kretyna? To jest brat mój, Zenek. Ta szuja ukradła mi mięso. Mógłbyś mu dać po pysku?~
=
~No dobra, rozumiem. Nic za darmo. Dam dwa miedziaki i racicę kozła.~
IF ~~ THEN EXIT
END

IF ~~ THEN BEGIN dziewczyna
SAY ~Masz śliczną krtań, paniusiu. Zechciałabyś mi towarzyszyć w przechadzce po parku rządowym?~
=
~Ee... Nie musisz tak patrzeć. To może... ja już pójdę, co?~
IF ~~ THEN EXIT
END

Ten dialog ma dwie ścieżki w zależności od płci bohatera, który rozmawia z Gienkiem – wybór ścieżki dialogu określa warunek Gender(obiekt,wartość), gdzie obiektem jest LastTalkedToBy, czyli bohater rozmawiający z Gienkiem.

Jeśli rozmawia z nim mężczyzna (wartość warunku – MALE), Gienek będzie próbował wynająć bohatera na Zenka, a gdy porozmawiamy z Gienkiem kobietą (wartość – FEMALE), ten będzie usiłował ją poderwać. Z mizernym skutkiem.

Takich rozwidleń w dialogach są setki, dlatego aby dobrze pisać dialogi, należy zapoznać się z charakterystyką warunków. A tych najlepiej się uczyć, przeglądając oryginalne skrypty i dialogi z gry.

Oczywiście, jeśli sytuacja jest bardziej skomplikowana, warunków do danego przejścia może być więcej, niż jeden. Przejście zostanie wykonane tylko wtedy, kiedy wszystkie będą spełnione. Jednak, aby nie doszło do sytuacji bez wyjścia, należy uwzględniać wszystkie alternatywy. Oto, jak nie warunkować przejść:



IF ~True()~ THEN BEGIN criticalerror
SAY ~Cośtam~
IF ~Gender(LastTalkedToBy,MALE)~ THEN EXIT
END

Taki blok dla rozmówcy-mężczyzny spowoduje wywołanie przejścia (w tym wypadku zakończy dialog), natomiast dla rozmówcy-kobiety nie przewidziano wykonania żadnej akcji. Dyskryminacja! Jeśli rozmówca jest kobietą – gra się posypie. Należy uwzględniać wszystkie alternatywy i warunkować ostrożnie, aby nie było sytuacji bez wyjścia.


2.4. Rozbudowany blok dialogowy i jego elementy

Rozbudowane bloki zawierają tylko kilka elementów więcej, niż te prostsze. Podstawowe bloki dialogowe nie obędą się bez komend przejść GOTO i EXIT, bez innych mogą. I będą działać.

Jednak bez komendy REPLY dialog to właściwie monolog, a bez DO rozmówca nie może wywoływać prostych czynności typu ustalenie zmiennej, albo danie drużynie pięciuset sztuk złota. JOURNAL i jej podkomendy mają znaczenie bardziej marginalne, jednak przy tworzeniu misji się przydają.

Generalnie nie ma znaczenia, w jakiej kolejności ustawimy te trzy komendy (kiedy skorzystamy z więcej niż jednej z nich), ważne jest tylko, aby GOTO, albo EXIT, czyli komenda linku było ostatnią komendą przejścia.

Zaczniemy po kolei…


2.4.1. Komenda REPLY

Komenda REPLY (a właściwie REPLY ~tekst~) powoduje, że aby wykonać dane przejście, należy w okienku dialogowym wybrać jedną z możliwych odpowiedzi, które wypowiada nasz drużynowy rozmówca. Mówiąc prościej – pozwala ona na wybór odpowiedzi na daną wypowiedź.

Schemat bloku z odpowiedziami wygląda następująco:



IF ~(ewentualne warunki)~ THEN BEGIN (nazwa_bloku)
SAY ~(wypowiedź)~
IF ~(ew. warunki)~ THEN REPLY ~(tekst odpowiedzi nr 1)~ (dalsze komendy przejścia nr 1)
IF ~(ew. warunki)~ THEN REPLY ~(tekst odpowiedzi nr 2)~ (dalsze komendy przejścia nr 2)
IF ~(ew. warunki)~ THEN REPLY ~(tekst odpowiedzi nr n)~ (dalsze komendy przejścia nr n) //nie ma ograniczonej liczby odpowiedzi, podobnie jak nie ma ograniczonej liczby przejść w jednym bloku
END

Odpowiedzi pozwalają graczowi na wybór ścieżki dialogu. Mogą być uwarunkowane, jednak nie muszą. Ważne jest, aby możliwe do wyboru nie znalazły się jednocześnie przejście z odpowiedzią i przejście bez odpowiedzi – gra nie będzie wtedy wiedziała, co zrobić.

Jednak odkąd ukazały się nowsze wersje WeiDU, możemy zamiast długiego ‘IF ~warunek~ THEN REPLY ~tekst~ GOTO x’ użyć do zapisania przejścia z odpowiedzią prostej komendy trzech plusów. Wygląda to tak:



IF ~(ewentualne warunki)~ THEN BEGIN (nazwa_bloku)
SAY ~(wypowiedź)~
+ ~(ew. warunki)~ + ~(tekst odpowiedzi nr 1)~ + x
+ ~(ew. warunki)~ + ~(tekst odpowiedzi nr 2)~ + y
END

Pierwszy plus oznacza tutaj IF, drugi – THEN REPLY, a trzeci – GOTO.

Jeśli odpowiedź jest niczym nieuwarunkowana, dwa pierwsze plusy zapisujemy obok siebie – ++, co oznacza ‘IF ~~ THEN REPLY’. Jeżeli natomiast nie chcemy stosować komendy GOTO, zamiast trzeciego plusa podstawiamy właściwą komendę. Komenda plusowa jest zdecydowanie łatwiejsza i przyjemniejsza, a jej zastosowanie przedstawię na przykładzie.

Po raz kolejny za przykład posłuży nam Gienek.



IF ~NumTimesTalkedTo(2)~ THEN BEGIN prosba //warunek do bloku - rozmowa zaczynana trzeci raz
SAY ~Hm, ... Czy mógłbym...~
++ ~No?~ + prosba1
++ ~Dobrze, tylko się streszczaj.~ + prosba2
END

IF ~~ THEN BEGIN prosba1
SAY ~Yy... no to... co ja też chciałem...~
++ ~Błagam, myśl szybciej.~ + prosba3
++ ~(nic nie mów)~ + prosba3
END

IF ~~ THEN BEGIN prosba2
SAY ~Dobrze! No to... ee...~
IF ~~ THEN GOTO prosba3
END

IF ~~ THEN BEGIN prosba3
SAY ~Chciałem podwyższyć stawkę. Pięć złociszy, racica osioła i moja dozgonna wdzięczność za przestawienie gęby Zenkowi. Co ty na to?~
++ ~Daj mi się chwilę zastanowić.~ + wait
++ ~Nie zgadzam się. Nie znam człowieka.~ + refuse
+ ~Alignment(LastTalkedToBy,MASK_EVIL)~ + ~Dość! Znikaj stąd, albo dam ci takiego kopa, że wylądujesz w Młynku Nieświńskim!~ + runrunaway //co znaczy warunek do tego przejścia - objaśnię poniżej
END

IF ~~ THEN BEGIN wait
SAY ~Dobra. Przyjdź do mnie jak się zdecydujesz.~
=
~A mam nadzieję, że się zdecydujesz, no.~
IF ~~ THEN EXIT
END

IF ~~ THEN BEGIN refuse
SAY ~Ej, nie bądźcie źli dla mnie! Jak zmienicie decyzję to wróćcie.~
IF ~~ THEN EXIT
END

IF ~~ THEN BEGIN runrunaway
SAY ~Ee... przecież my jesteśmy w Młynku Nieświńskim, no nie?~
++ ~Czepiasz się szczegółów.~ EXIT //zamiast trzeciego plusa stosujemy EXIT, który kończy dialog
END

Nie takie trudne, prawda? Pozwala to stworzyć drzewko dialogowe, w którym sami wybieramy, co chcemy powiedzieć. To chyba wszystko związane z odpowiedziami gracza.

A warunek Alignment(obiekt,wartość) sprawdza charakter danej osoby, w tym wypadku LastTalkedToBy – gracza rozmawiającego z Gienkiem.

Warunek ten przy wartości MASK_EVIL jest spełniony, gdy charakter rozmówcy Gienka jest zły. Podobnie przy wartości MASK_GOOD jest spełniony, gdy charakter rozmówcy jest dobry. Jest jeszcze kilka innych wartości, które określają ten warunek.


2.4.2. Komenda DO

Ta komenda (właściwie DO ~akcja~) wywołuje określoną inną czynność po wyborze odpowiedzi w dialogu lub kliknięciu przycisku ‘dalej’, czy ‘koniec rozmowy’. Akcję tą określa się poprzez komendy akcji skryptów silnika Infinity. Spis wszystkich komend akcji znajduje się w pliku action.ids – również do otwarcia przez Infinity Explorer.

Schemat wygląda tak:



IF ~(ewentualne warunki)~ THEN BEGIN (nazwa_bloku)
SAY ~(wypowiedź)~
IF ~(ew. warunki)~ THEN DO ~(akcje przejścia nr 1)~ (dalsze komendy przejścia nr 1)
IF ~(ew. warunki)~ THEN DO ~(akcje przejścia nr 2)~ (dalsze komendy przejścia nr 2)
END

Komenda DO wywołuje akcje niezwiązane z dialogiem, jak na przykład ustawienie zmiennej, stworzenie przedmiotu w ekwipunku, rzucenie czaru, stanie się wrogiem itp. Jej zastosowanie pokażę na przykładzie czwartej rozmowy z Gienkiem:



IF ~NumTimesTalkedTo(3)~ THEN BEGIN decyzja
SAY ~I jak? Namyśliliście się już?~
++ ~Tak. Zenek nic mi nie zrobił. Jak chcesz, sam go zlej. Chyba się go nie boisz?~ DO ~SetGlobal("GieneksJobRefused","LOCALS",1)~ + refuse1 //komendę SetGlobal() objaśnię poniżej
++ ~Pomogę, ale musisz zapłacić więcej.~ + more
++ ~Dobra. Załatwię to szybko. I liczę na umówioną zapłatę. Gdzie znajdę tego Zenka?~ DO ~SetGlobal("GieneksJob","LOCALS",1)~ + place
END

IF ~~ THEN BEGIN refuse1
SAY ~N-nie. To znaczy... e, kurczę, dajcie mi spokój.~
IF ~~ THEN EXIT
END

IF ~~ THEN BEGIN more
SAY ~Zdzieracie z Gienka ile się da! Niech stracę, dziesięć złociszy, kij i mój kaszkiet. Tylko przetrzepcie go porządnie!~
++ ~Za mało. Nie zrobię tego. Sam mu dokop, chyba że się go boisz.~ DO ~SetGlobal("GieneksJobRefused","LOCALS",1)~ + refuse1
++ ~Dobra. Pożałuje, że ukradł ci mięso i je *zwróci*, zapewniam cię. Gdzie on jest?~ DO ~SetGlobal("GieneksJob","LOCALS",2)~ + place
END

IF ~~ THEN BEGIN place
SAY ~Świetnie! Ten kretyn siedzi tam, pod ścianą i żre moje mięso. Idźcie mu dokopać, a później wróćcie do mnie.~
IF ~~ THEN EXIT
END

No i jest. Zastosowanie DO ~akcja~ nie jest szczególnie trudne i poza warunkami nie ma praktycznie żadnych ograniczeń. A co do komend akcji zastosowanych w DO…

Komenda SetGlobal("X","Y",Z) ustawia zmienną na określoną wartość. Zmienne służą jako niestandardowe warunki, oznaczane jako Global("X","Y",Z). X w tym wypadku to nazwa zmiennej (może być dowolna), Y to charakter zmiennej – globalna (dla całej gry), lokalna (dla jednej osoby), obszarowa (dla jednego obszaru). Z to wartość liczbowa zmiennej.

Tak więc SetGlobal("GieneksJob","LOCALS",2) ustawia zmienną lokalną GieneksJob na wartość 2.

Jeśli jednak odmówiliśmy i zmienna GieneksJobRefused jest ustawiona na 1, możemy w dalszym ciągu przyjąć zadanie Gienka. Piszemy mu specjalny dialog, który uaktywni się przy kolejnej rozmowie z nim:



IF ~Global("GieneksJobRefused","LOCALS",1)~ THEN BEGIN refused2
SAY ~Hej, ludzie! Zmieniliście może zdanie? Pomożecie mi z tym czymś? On mi już zeżarł prawie całe mięso!~
++ ~Chyba nie mówisz poważnie.~ + refuse1 //omijamy akcję DO - po co drugi raz tak samo ustawiać zmienną? Linki prowadzą do bloków z poprzedniego przykładu
++ ~Zapłać więcej, to pomogę.~ DO ~SetGlobal("GieneksJobRefused","LOCALS",0)~ + more //neutralizujemy zmienną GieneksJobRefused - jeśli odmówimy mu w bloku 'more', ustawi się ona ponownie
++ ~Dobra. Gdzie znajdę tego Zenka?~ DO ~SetGlobal("GieneksJob","LOCALS",1)
SetGlobal("GieneksJobRefused","LOCALS",0)~ + place //tutaj dwie akcje następują po sobie
END

To wszystko, co związane z komendą DO. Po resztę odsyłam do action.ids, warto też poczytać sobie pliki skryptów (.bcs), żeby przyswoić co bardziej skomplikowane komendy akcji.


2.4.3. Komendy wpisów do dziennika

Komendy dodające wpisy do dziennika są trzy, ale wszystkie mają taki sam sposób działania. Różnią się tylko tym, że dodają wpisy do różnych zakładek.

Są to JOURNAL, UNSOLVED_JOURNAL i SOLVED_JOURNAL. Stosujemy je tak samo, jak DO i REPLY, czyli:



IF ~(ewentualne warunki)~ THEN BEGIN (nazwa_bloku)
SAY ~(wypowiedź)~
IF ~(ew. warunki)~ THEN JOURNAL ~wpis~ (dalsze komendy przejścia nr 1)
END

Komenda JOURNAL dodaje wpis do głównej zakładki dziennika, czyli tam, gdzie pojawiają się wpisy o postępie głównego wątku fabuły. UNSOLVED_JOURNAL dodaje do zakładki z nierozwiązanymi misjami, a SOLVED_JOURNAL – do zakładki, gdzie są wpisy z ukończenia misji.

Schemat takiego wpisu wygląda następująco:



JOURNAL ~Nagłówek wpisu (najlepiej krótki)

Treść wpisu~

Przykład podam na bloku ‘place’ z poprzedniego podpunktu:



IF ~~ THEN BEGIN place
SAY ~Świetnie! Ten kretyn siedzi tam, pod ścianą i żre moje mięso. Idźcie mu dokopać, a później wróćcie do mnie.~
IF ~~ THEN UNSOLVED_JOURNAL ~Skopać mięsokrada

Pewien facet imieniem Gienek zlecił mi zmianę kształtu nosa jego brata Zenka, który ukradł mu mięso. Obiecał w zamian nieprzebrane bogactwa, dlatego zdecydowałem się podjąć tej świętej misji. Zenek jest niedaleko ode mnie, chyba tylko kretyn nie zdoła go znaleźć. Mam nadzieję, że nie jestem kretynem.~ EXIT
END

Taka komenda spowoduje dodanie tego wpisu do zakładki niewykonanych zadań. Pozostałe komendy działają tak samo. I to wszystko na temat wpisów.

Ponadto poznaliśmy już podstawowe funkcje działań w zakresie jednego pliku .dlg – możecie już pisać dialogi pomiędzy jednym bohaterem niezależnym i graczem należącym do drużyny.



3. Linki zewnętrzne i dialogi na kilka postaci


3.1. Komenda EXTERN

Każdej postaci może być przypisany tylko jeden plik .dlg. W takim razie, aby kolejna postać włączyła się do rozmowy, musimy stworzyć link pomiędzy plikiem .dlg pierwszej i drugiej postaci.

Ta komenda (jej właściwa nazwa to EXTERN nazwa_pliku.dlg nazwa_bloku) pozwala na tworzenie połączenia z innym plikiem dialogowym. Samo EXTERN to skrót od ‘external link’, czyli ‘link zewnętrzny’ Zastosowanie tej komendy przejścia pozwala na tworzenie dialogów z więcej niż jedną postacią, a także np. wtrąceń członków drużyny.

Schemat bloku z wykorzystaniem EXTERN wygląda tak:



IF ~(ewentualne warunki)~ THEN BEGIN (nazwa_bloku)
SAY ~(wypowiedź)~
IF ~(ew. warunki)~ THEN (ew. komendy typu REPLY/DO przejścia nr n) EXTERN NAZWA_PLIKU_.DLG nazwa_docelowego_bloku
END

Wyobraźmy sobie sytuację, że Gienek nawiązuje dialog z Zenkiem. Obaj są bohaterami niezależnymi, a więc zastosowanie EXTERN będzie potrzebne.



IF ~See("Zenek")
!Dead("Zenek")~ THEN BEGIN zenek
SAY ~Ej, ty! Złodzieju!~
IF ~~ THEN EXTERN ZENEK zenek1
END

Jednak taki link jest linkiem ślepym, bo nie mamy stworzonego pliku zenek.dlg. Jednak nie jest to dla nas problemem, znamy już komendę BEGIN. Zapisujemy:



BEGIN ~ZENEK~

IF ~~ THEN BEGIN zenek1
SAY ~No? Czegoś chciał?~
IF ~~ THEN EXTERN GIENEK zenek2
END

Teraz piszemy blok dla Gienka, jednak robimy to po komendzie BEGIN ~GIENEK~, a przed BEGIN ~ZENEK~ - czyli w obszarze pliku .dlg Gienka. Zapisujemy go pod blokiem ‘zenek’.



IF ~~ THEN BEGIN zenek2
SAY ~Oddaj mi to, co ukradłeś! Słyszysz? Przyprowadziłem przyjaciół!~
IF ~~ THEN EXTERN ZENEK zenek3
END

I piszemy kolejny blok dla Zenka w obszarze zenek.dlg

Cały skrypt tego dialogu będzie wyglądał następująco:



BEGIN ~GIENEK~

(... - inne bloki dialogowe)

IF ~See("Zenek")
!Dead("Zenek")~ THEN BEGIN zenek
SAY ~Ej, ty! Złodzieju!~
IF ~~ THEN EXTERN ZENEK zenek1
END

IF ~~ THEN BEGIN zenek2
SAY ~Oddaj mi to, co ukradłeś! Słyszysz? Przyprowadziłem przyjaciół!~
IF ~~ THEN EXTERN ZENEK zenek3
END

(...)
END //zakończenie ostatniego bloku na obszarze pliku gienek.dlg

BEGIN ~ZENEK~

(...)

IF ~~ THEN BEGIN zenek1
SAY ~No? Czegoś chciał?~
IF ~~ THEN EXTERN GIENEK zenek2
END

IF ~~ THEN BEGIN zenek3
SAY ~(wypowiedź)~
IF ~~ THEN EXTERN GIENEK zenek4
END

(....)
END //zakończenie ostatniego bloku na obszarze pliku zenek.dlg

Jest to klasyczny przykład dialogu łańcuchowego – po jednej wypowiedzi bezwarunkowo następuje inna.

Jeżeli takie dialogi ciągną się łańcuchowo, istnieje znacznie prostszy sposób zapisania ich. Zaraz objaśnię to w punkcie 3.2.

Komenda EXTERN działa podobnie, jak komenda GOTO i stosujemy ją w taki sam sposób, jeśli tylko chcemy stworzyć link zewnętrzny.


3.2. Skrypt bloku łańcuchowego

Dialogi łańcuchowe, jak ten z przykładu w 3.1, można zapisać w pliku .d w prostszy sposób za pomocą bloku łańcuchowego. Taki blok należy umieścić poza obszarami plików .dlg.

Bloki łańcuchowe rządzą się nieco innymi prawami, niż zwykłe zespoły bloków dialogowych typu:



IF ~~ THEN BEGIN x
SAY ~(wypowiedź)~
=
~(dalsza część wypowiedzi)~
=
~(ostatnia część wypowiedzi)~
IF ~~ THEN (komendy przejścia)
END

Choć mimo to są do nich podobne. Oto przykład prostego bloku łańcuchowego – rozmowa między Lianevą i Raldonionem, dwoma postaciami z Pieśni Władającej. CRGOR02 to nazwa pliku .dlg Lianevy, a CRGOR04 to plik Raldoniona:



CHAIN CRGOR02 eastnothcalm //komenda CHAIN rozpoczyna blok łańcuchowy, a nazwa pliku dialogowego informuje, kto rozpoczyna dialog; 'eastnothcalm' to nazwa bloku dialogowego
~Przyjrzyj się uważnie, . Ani jednego zaplutego potworka, poza tym w zielonym, która przy mnie stoi.~ //nawet w pierwszej wypowiedzi pominięte zostaje SAY
== CRGOR04 //przełączenie rozmawiającego na Raldoniona, '==' działa jak komenda EXTERN; po znakach równości piszemy nazwę pliku .dlg
~Och, niech cię szlag trafi... Czuję się, jakby w mojej głowie trwało powstanie zbrojne elfów.~ //wypowiedź Raldoniona
== CRGOR02 //przełączenie na Lianevę
~Nie użalaj się nad sobą. To nie wygląda szczególnie reprezentatywnie.~ //wypowiedź Lianevy
== CRGOR04 //przełączenie na Raldoniona
~...~
== CRGOR02
~Hah, to mi się nawet podoba, Raldonion. Z taką migreną możesz co najwyżej strzelić we mnie magicznym pociskiem.~
= //pojedynczy znak równości działa jak GOTO - link w zakresie pliku .dlg Lianevy
~Choć bardziej prawdopodobne, że próbując, podpalisz sobie szatę na tyłku.~
== CRGOR04
~(ugh...) Zaprawdę, jesteś złą kobietą, Lianeva...~
EXIT //koniec rozmowy; pomijamy IF ~~ THEN, oraz końcowy END

Struktura dość przejrzysta. Na końcu bloku łańcuchowego możemy umieścić komendy przejścia:

EXIT;

• komendę GOTO x (jeśli blok x należy do tego samego pliku .dlg, co ostatnia wypowiedź w bloku łańcuchowym);

EXTERN nazwa_pliku_.dlg x (jeżeli blok x nie należy do tego samego pliku .dlg, co ostatnia wypowiedź w bloku łańcuchowym)

END nazwa_pliku_.dlg x (działa tak samo, jak EXTERN)

COPY_TRANS (zastosowanie tej komendy opisane jest w punkcie 4.2.)

W bloku łańcuchowym nie możemy stosować REPLY ani JOURNAL (można jednak na końcu stworzyć przejście do zwykłego bloku dialogowego, w którym zostanie zastosowana odpowiedź, albo wpis).

Możemy jednak zastosować komendę DO i to przed każdym z linków w bloku łańcuchowym, np.:



CHAIN GIENEK blok_x
~(wypowiedź)~
DO ~(akcja nr 1)~
== ZENEK
~(odpowiedź)~
(...)
== GIENEK
~(ostatni tekst)~
DO ~Attack("Zenek")~ //po zakończeniu dialogu Gienek atakuje Zenka
EXIT

Jednak powyższy blok łańcuchowy był uruchamiany z przejścia i nie był on uwarunkowany. Jeśli jednak pierwsza wypowiedź bloku łańcuchowego ma zawierać warunek rozpoczęcia, początek będzie wyglądał na przykład tak:



CHAIN
IF ~NumTimesTalkedTo(0)~ THEN ILLANITH beginning
~(wypowiedź)~
(...)
EXIT

Aktorów może być oczywiście więcej niż dwóch, jednak cały dialog musi być tak uwarunkowany, aby wszyscy żyli i mogli rozmawiać (tj. nie byli nieprzytomni itp.). I wszyscy muszą być na tym samym obszarze. W innym wypadku gra się posypie.

A oto prostszy zapis dialogu między Zenkiem i Gienkiem z punktu 3.1:



CHAIN
IF ~ See("Zenek")
!Dead("Zenek")~ THEN GIENEK zenek
~Ej, ty! Złodzieju!~
== ZENEK
~No? Czegoś chciał?~
== GIENEK
~Oddaj mi to, co ukradłeś! Słyszysz? Przyprowadziłem przyjaciół!~
(...)

3.3. APPEND – aktualizacje istniejących plików .dlg

Jeśli chcemy nie tylko dodawać, ale również modyfikować istniejące już pliki .dlg, komenda APPEND będzie niezastąpiona. Nie jest to komenda przejścia, ogranicza ona w pliku .d bloki dodawane do danego pliku .dlg.

Można ją zastosować do pisania wtrąceń bohaterów z drużyny.

W grze istnieje plik keldorj.dlg i jest on przypisany Keldornowi, kiedy ten jest w naszej drużynie. Załóżmy, że w piszemy plik illanith.dlg. Jeśli w pisanym przez nas nowym dialogu umieścimy taki blok:



IF ~~ THEN BEGIN illamove
SAY ~Słyszałem, że w tym mieście jest stowarzyszenie paladynów... jakieś Promienne Serce, czy coś takiego. Może słyszeli o mojej organizacji... Będę czekał na ciebie w ich kwaterze.~
IF ~IsValidForPartyDialog("Keldorn")~ THEN EXTERN KELDORJ illakeldpaladins //warunek sprawdza, czy Keldorn jest w drużynie i czy może rozmawiać
IF ~!IsValidForPartyDialog("Keldorn")~ THEN GOTO illamove1 //wykrzyknik przed warunkiem to negacja danego warunku, czyli jeśli Keldorna nie ma w drużynie lub nie może on rozmawiać, rozmowa przejdzie do bloku 'illamove1'
END

Musimy dodać blok ‘illakeldpaladins’ w pliku keldorj.dlg. Możemy zrobić to za pomocą komendy APPEND. W tym samym pliku dialogowym poza obszarem illanith.dlg wpisujemy:



APPEND KELDORJ //komenda aktualizacji danego pliku .dlg
IF ~~ THEN BEGIN illakeldpaladins
SAY ~Z pewnością ciepło cię przyjmą, elfie. Gościmy każdego, kto ma w cenie honor i służy dobru.~
=
~A tobie nie można odmówić żadnej z tych dwóch rzeczy.~
IF ~~ THEN EXTERN ILLANITH illakeldpaladins1 //link do bloku w pliku illanith.dlg
END //zakończenie danego bloku dialogowego
END //drugie END to zakończenie bloku APPEND

W ten sposób Keldorn komentuje wypowiedź Illanitha.

Tak budowane są wtrącenia do nowych dialogów. W zakresie jednego bloku APPEND możemy umieścić dowolną liczbę bloków dialogowych.

APPEND jest wykorzystywane również w blokach łańcuchowych, lecz jest ono ukryte w znaku przełączenia, czyli ‘==’.

Dzięki temu pliki .dlg poszczególnych aktorów bloku łańcuchowego mogą już być w grze, a przez skrypt dialogu łańcuchowego zostaną zmodyfikowane.

Teraz wiesz już wszystko, co potrzebne jest do pisania nowych dialogów. Warto jednak nauczyć się też bardziej szczegółowej modyfikacji istniejących.



4. Zaawansowane komendy i bloki


Komendy te, takie jak COPY_TRANS, EXTEND_TOP, czy INTERJECT są przydatne do modyfikacji plików dialogowych istniejących już w grze. Przydadzą się one, gdy będziemy chcieli tworzyć moda NPC z wtrąceniami czy komentarzami pojawiającymi się w różnych dialogach, lecz nie tylko.

O ile INTERJECT lub COPY_TRANS nie przydają się zbyt często w pisaniu dialogów (chyba, że tworzymy wtrącenia nowemu przyłączalnemu NPCowi), to komendy EXTEND_BOTTOM lub EXTEND_TOP warto znać, aby dodać niezależne przejście w istniejącym bloku dialogowym. Właśnie nimi zajmiemy się najpierw:


4.1. Dodawanie nowych przejść w blokach dialogowych, czyli EXTEND_TOP i EXTEND_BOTTOM

Te dwie komendy mają podobne zastosowanie – dodają one całkiem nowe przejścia w blokach dialogowych plików .dlg, które istnieją już w grze. Jednak aby określić blok, w którym zechcemy umieścić nowe przejście, trzeba użyć edytora DLTCEP.

Otwieramy zainstalowany DLTCEP i ustawiamy w nim setup, klikając na przycisk ‘Setup’ w lewym dolnym rogu okna (wystarczy podać ścieżkę pliku chitin.key, czyli rejestru całej gry BG II, do tego zaznaczyć typ gry i podać nazwę ustawień – obojętnie jaką, może być BG2. Zapisujemy i wychodzimy).

Następnie na pasku menu wybieramy Edit/Dialog (DLG), a gdy pokaże się okienko przeglądania pliku .dlg, klikamy ‘Load Dialog’ i wybieramy interesujący nas plik. Podczas rozwijania poszczególnych gałęzi drzewka dialogowego odnajdziemy oznaczenie interesującego nas bloku.

Bloki dialogowe plików .dlg mają nazwy od 0 wzwyż.

Komendy EXTEND_TOP i EXTEND_BOTTOM mają podobny schemat, a wygląda on tak:



EXTEND_BOTTOM (nazwa modyfikowanego pliku .dlg) (nazwa modyfikowanego bloku)
IF ~(ew. warunki przejścia nr 1)~ THEN (komendy zdarzeń) (komenda linku)
IF ~(ew. warunki przejścia nr 2)~ THEN (komendy zdarzeń) (komenda linku)
END //zakończenie bloku EXTEND_BOTTOM

Zastosowania tej komendy są różne, od budowy wtrąceń NPCów po dodawanie nowej linii dialogu. Jednego z zastosowań nauczymy się na przykładzie dialogu Księgarza – opowiadacza z gospody na Wzgórzach Umar. Nazwa jego pliku .dlg to imnbook1.dlg. Oryginalny skrypt jego bloku dialogowego o nazwie 0 wygląda tak:



IF ~True()~ THEN BEGIN 0
SAY ~ Przykro mi, ostatnimi czasy nie mam zbyt wiele ksiąg. Interes marnie się kręcił, więc przeniosłem większość towaru do Athkatli. Właściwie to nie rozmawiałem z nikim od tygodni.~
++ ~Nie powodziło ci się w handlu przez te wszystkie nieprzyjemne wydarzenia w okolicy?~ + 2
++ ~Przepraszam za kłopot.~ + 1
+ ~(oryginalne warunki)~ + ~Szukam informacji o księdze opisującej szczegóły leczenia wampiryzmu.~ + 4
END

W tym bloku są tylko trzy przejścia. Jeśli chcemy dodać czwarte, w pliku .d piszemy taką komendę:



EXTEND_BOTTOM IMNBOOK1 0
+ ~Global("KaatjeIavScroll","GLOBAL",1)~ + ~Szukamy potężnego zwoju magicznego. Handlujesz też zaklęciami?~ + bookscrolls
END //zakończenie bloku

APPEND IMNBOOK1 //komenda APPEND dodaje do modyfikowanego pliku blok, do którego prowadzi link z nowego przejścia
IF ~~ THEN BEGIN bookscrolls
SAY ~Rzeczywiście, mogę coś takiego mieć. Jakie konkretnie zwoje was interesują?~
IF ~~ THEN REPLY ~Szukamy zwoju zaklęcia przeciwnego do zaklęcia Uwięzienie. Masz coś takiego?~ GOTO bookscrolls1 //link do kolejnego bloku
END
(...) //blok bookscrolls1 i inne
END //zakończenie bloku APPEND

Tak do bloku 0 dodaliśmy kolejne przejście, do którego stworzyliśmy kolejny blok i kolejny wątek rozmowy. Skrypt bloku 0 w pliku imnbook1.dlg po instalacji takiego dialogu wygląda następująco:



IF ~True()~ THEN BEGIN 0
SAY ~ Przykro mi, ostatnimi czasy nie mam zbyt wiele ksiąg. Interes marnie się kręcił, więc przeniosłem większość towaru do Athkatli. Właściwie to nie rozmawiałem z nikim od tygodni.~
++ ~Nie powodziło ci się w handlu przez te wszystkie nieprzyjemne wydarzenia w okolicy?~ + 2
++ ~Przepraszam za kłopot.~ + 1
+ ~(oryginalne warunki)~ + ~Szukam informacji o księdze opisującej szczegóły leczenia wampiryzmu.~ + 4
+ ~Global("KaatjeIavScroll","GLOBAL",1)~ + ~Szukamy potężnego zwoju magicznego. Handlujesz też zaklęciami?~ + bookscrolls
END

I jako przejście numer cztery pojawia się to dodane przez EXTEND_BOTTOM. Jeśli zmienna globalna KaatjeIavScroll jest ustawiona na wartość 1, podczas rozmowy będziemy mogli wybrać tą odpowiedź.

Dzięki temu uzyskamy u Księgarza informacje na temat kupna zaklęcia Uwolnienie.

Komenda EXTEND_TOP różni się od EXTEND_BOTTOM tylko nieznacznie, ponieważ dodaje dane przejście nie jako ostatnie z przejść w danym bloku, tylko jako pierwsze.

Kolejne podpunkty punktu 4. polecam modderom, którzy mają już doświadczenie w pisaniu dialogów i ogólnie w modowaniu. Tych zaś, którzy znajdują się na początku drogi pisania skryptów dialogów zapraszam do przeczytania punktu 5., z którego dowiedzą się, jak zainstalować napisany dialog.


4.2. Kopiowanie listy przejść – komenda COPY_TRANS

Tej komendy używa się przy tworzeniu wtrąceń w ważnych momentach gry przy tworzeniu własnego przyłączalnego NPCa. Kopiuje ona całą listę przejść danego bloku w danym pliku dialogowym do bloku dialogowego, w którym COPY_TRANS użyjemy. Pokażę to na przykładzie dialogu przy zdradzie Yoshimo.



EXTEND_BOTTOM YOSHJ 113 //tekst Yoshimo, po którym następują komentarze członków drużyny
IF ~IsValidForPartyDialogue("ILLANITH")~ THEN EXTERN ILLANIJ yoshibetrayilla //jeśli w drużynie jest Illanith, wypowie on swoje zdanie na temat Yoshimo
END

APPEND ILLANIJ //aktualizacja pliku .dlg Illanitha będącego w drużynie
IF ~~ THEN BEGIN yoshibetrayilla
SAY ~Zatem, "przyjacielu", zamierzasz poznać tajemnice śmierci? Niech tak się stanie, jeśli to jest twym życzeniem.~
COPY_TRANS YOSHJ 113 //skopiowanie oryginalnej listy przejść bloku 113 pliku yoshj.dlg
END
END

Oznacza to, że blok dialogowy, w którym Illanith grozi złodziejowi śmiercią będzie miał takie same przejścia, jak te zawarte w oryginalnym bloku 113 pliku yoshj.dlg.

Dokładnie aż 15 różnych przejść. Zainteresowani mogą przyjrzeć się danemu blokowi w DLTCEP, a następnie zaimplementować dany skrypt w grze (patrz punkt 5.) i zaobserwować zmianę.


4.3. Budowa wtrąceń – INTERJECT

Wtrącenia można budować przez zastosowanie EXTEND_BOTTOM i APPEND, tak jak to poniższe:



EXTEND_BOTTOM C6ELHAN2 28
IF ~IsValidForPartyDialog("Illanith")~ THEN EXTERN ILLANIJ illasuld
END

APPEND ILLANIJ
IF ~~ THEN BEGIN illasuld
SAY ~Zniszczył całe elfie miasto? Będę pierwszym, który nabije tego mordercę na klingę.~
IF ~~ THEN EXTERN C6ELHAN 29
END
END

W tym wypadku Illanith komentuje wypowiedź Elhana zniszczeniach w Suldanessenarze. Między blokami 28 i 29 w pliku dialogowym Elhana nie występują żadne skomplikowane przejścia, dlatego obywamy się bez COPY_TRANS.

Taki sposób działa, jednak jest bardziej pracochłonny, a musimy się oszczędzać na następne dialogi, prawda? Lepiej zastosować blok INTERJECT. To samo wtrącenie zapisane za jej pomocą wygląda tak:



INTERJECT C6ELHAN2 28 illasuld //oznaczenie podobne jak w EXTEND_TOP/BOTTOM
== ILLANIJ IF ~IsValidForPartyDialog("Illanith")~ THEN //konstrukcja tego dialogu przypomina blok łańcuchowy, jednak po przełączeniu aktora dodajemy IF ~(warunek)~ THEN
~Zniszczył całe elfie miasto? Auessie... Pozwól mi być pierwszym, który nabije tego mordercę na klingę.~ //wtrącenie Illanitha, pomijamy SAY
EXTERN C6ELHAN 29 //zakończenie charakterystyczne dla bloku CHAIN

Bloki INTERJECT są skonstruowane podobnie, jak bloki CHAIN i mogą być zakończone tak jak one, czyli przez komendę EXTERN (używaną zamiennie z END), GOTO lub EXIT. Po szczegóły odsyłam do punktu 3.2.

Możemy też rozbudować wtrącenie Illanitha, dodając do tego komentarz Elhana:



INTERJECT C6ELHAN2 28 illasuld
== ILLANIJ IF ~IsValidForPartyDialog("Illanith")~ THEN
~Zniszczył całe elfie miasto? Auessie... Pozwól mi być pierwszym, który nabije tego mordercę na klingę.~
== C6ELHAN IF ~IsValidForPartyDialog("Illanith")~ THEN //powtarzamy warunek dla każdej kolejnej wypowiedzi tego wątku
~Mam nadzieję, że miasto nie zostało jeszcze zniszczone, elfie. Na razie mamy tylko doniesienia o atakach.~
GOTO 29 //ostatnią wypowiedź słyszymy od Elhana, dlatego stosujemy link wewnętrzny zamiast zewnętrznego

Całkiem, jak w CHAIN.

Jednak bloki INTERJECT tworzymy wtedy, kiedy wtrącenie danego NPCa będzie jedynym, albo kiedy chcemy pominąć pozostałe wtrącenia innych bohaterów. Tak, jak w przypadku pochlebstw wobec Adalon:



INTERJECT UDSILVER 9 illaadalon //wtrącenie przy bloku 9 pliku dialogowego Adalon
== ILLANIJ IF ~IsValidForPartyDialog("Illanith")~ THEN
~Teraz już wiem, czemu srebrne smoczyce uznaje się za najpiękniejsze istoty. Twój urok przewyższa wszystko, co dotąd widziałem.~
EXTERN UDSILVER 58

Pomimo tego, że na temat urody smoczycy mogą wypowiedzieć się również jeden z czworga innych bohaterów, chcemy, aby wypowiedź Illanitha była jedyną. Jednak gdyby Illanitha nie było w drużynie, tylko jeden z pozostałych czworga mógłby zacząć kadzić Adalon, a następnie, z pominięciem trzech innych, dialog przeskoczyłby do bloku 58.


4.4. Połączenie komend INTERJECT i COPY_TRANS

To połączenie było tak popularne w modach, że do WeiDU dodano obsługę nowego sposobu dodawania bloków wtrącenia – bloku INTERJECT_COPY_TRANS.

Jej zapis jest taki sam, jak INTERJECT, tylko, że podczas gdy INTERJECT wywoływał akcję EXTERN, GOTO, EXITINTERJECT_COPY_TRANS zawsze wywołuje akcję COPY_TRANS.

Taki sposób pisania dialogu przydaje się, gdy dodajemy wtrącenie w to samo miejsce, gdzie mogą mówić inni bohaterowie. Na przykład w przypadku dialogu, gdy bohaterowie zauważają, że są w Piekle. Tam wypowiadają się *wszyscy*. W takim wypadku można zastosować ten blok. Będzie to wyglądało tak:



INTERJECT_COPY_TRANS PLAYER1 25 helilla //blok o Piekle, po którym wszyscy się wypowiadają to właśnie blok nr 25 pliku player1.dlg
== ILLANIJ IF ~IsValidForPartyDialog("Illanith")~ THEN //warunek
~Nie sposób się z tobą nudzić, ...~
END //zamiast GOTO, EXTERN lub EXIT, kończymy blok samym END - przecież wykonywaną akcją i tak jest COPY_TRANS, nie?

W ten sposób po Illanithu wypowiedzą się inni członkowie naszej drużyny, którzy spadli do Piekła za nami.

Podobnie rozpiszemy sprzeciw Illanitha po zawarciu układu z Firkraagiem:



INTERJECT_COPY_TRANS FIRKRA02 32 illaattack //blok 32 pliku firkra02.dlg to ten, w którym podpisaniu układu sprzeciwia się 'dobra' część naszej drużyny
== ILLANIJ IF ~IsValidForPartyDialog("Illanith")~ THEN
~Nie tym razem, . Nie pozwolę, aby to doszło do skutku. Stawaj, a kiedy cię zabiję, sam obronię dziecko Garrena!~
DO ~LeaveParty()
Enemy()
Attack(Player1)~ //akcja - Illanith opuszcza drużynę, staje się wrogiem i atakuje głównego bohatera; pomijamy 'IF ~~ THEN', jak w blokach CHAIN
END

Gdybyśmy zastosowali zwykłe INTERJECT, inni członkowie drużyny nie mogliby się wypowiedzieć. Stosując INTERJECT_COPY_TRANS – mogą wszyscy.


4.5. Dodawanie nowych warunków do bloków w istniejących plikach .dlg

Ten punkt, w odróżnieniu od trzech poprzednich, ma niewiele wspólnego z wtrąceniami. Jednak przy tworzeniu modu sam miałem problem z tym, jak dodać warunek do istniejącego już bloku bez nadgrywania zmodyfikowanego pliku .dlg.

Do dodawania warunków dla całego bloku służy komenda ADD_STATE_TRIGGER. Przydaje się ona do dodawania zaprzeczeń jakiegoś warunku, by (gdy dany warunek jest spełniony) rozmowa rozpoczęła się w określony sposób.

Komenda dodająca nowy warunek jest prosta:



ADD_STATE_TRIGGER (nazwa modyfikowanego pliku .dlg) (nazwa modyfikowanego bloku) ~(dodawany warunek lub warunki)~

Czyli jeśli do dialogu z Solarem w finale gry zechcemy dodać alternatywną linię dialogową, warunkowaną jakąś zmienną, do istniejącej linii dialogowej musimy wstawić zaprzeczenie takiego warunku.

Załóżmy, że zmienną sprawdza warunek Global("SolarAlternativeBranch","GLOBAL",1) – skrypt dodania zaprzeczenia tego warunku w istniejącej linii będzie wyglądał tak:



ADD_STATE_TRIGGER FINSOL01 4 //blok zaczynający rozmowę po 'upadku' Melissany ~!Global("SolarAlternativeBranch","GLOBAL",1)~

Dzięki temu, jeśli SolarAlternativeBranch ma wartość 1, rozmowa przebiegnie alternatywnym torem, jeśli jednak ta zmienna ma wartość 0 – pozostanie po staremu.



5. Kompilacja i implementacja dialogu w grze


Kiedy skończymy pisać nasz dialog, będziemy chcieli go umieścić w grze. Przyjmijmy, że nazwa tego pliku .d to ‘dialog.d’.

Przygotowujemy zestaw instalacyjny naszego modu-dialogu. Składa się on z trzech elementów, czyli folderu o nazwie ‘dialog’, który zawiera w sobie pusty folder ‘backup’ i plik ‘dialog.d’. Poza tym folderem musimy mieć jeszcze pliki setup-dialog.exe (czyli aplikację WeiDU.exe o zmienionej nazwie), oraz plik setup-dialog.tp2.

Aby zaimplementować nasz dialog w plikach gry będziemy potrzebowali WeiDU (najlepiej najnowszej wersji). Zmieniamy jego nazwę na setup-dialog.exe. Ta aplikacja zajmie się kompilacją pliku lub plików .d.

Jednak musimy utworzyć plik z instrukcjami dla WeiDU. Plik ten nazwiemy setup-nazwa_folderu_z_modem.tp2, w tym wypadku setup-dialog.tp2. Plik .tp2 to zwykły plik tekstowy o zmienionym rozszerzeniu.

Otwieramy Notatnik i wpisujemy trzy linie, które zakwalifikują nasz dialog jako oddzielny mod, a następnie instrukcję skompilowania go:



BACKUP ~dialog\backup~
AUTHOR ~(tu podaj swój nick i adres e-mail)~
BEGIN ~Mój dialog~ //ta nazwa będzie oficjalną nazwą danego komponentu modu

COMPILE ~dialog\dialog.d~
USING ~~ //ta komenda służy do używania plików .tra, ale w tym tutorialu tym się nie zajmujemy; pozostaje pusta

Plik zapisujemy jako setup-dialog.tp2.

Nasz zestaw instalacyjny powinien wyglądać tak:

- dialog //folder

-- backup //folder

-- dialog.d

- setup-dialog.exe

- setup-dialog.tp2

Następnie przenosimy folder dialog i pliki setup-dialog.exe i setup-dialog.tp2, po czym włączamy aplikację setup-dialog.exe i klikamy ‘y’, kiedy program zapyta nas, czy instalować mod.

Jeżeli program wskaże błędy, spójrz na dane przez WeiDU linie i sprawdź, gdzie w kodzie dialogu jest błąd. Popraw go, zapisz plik dialog.d i spróbuj zainstalować mod jeszcze raz.

Jeśli aplikacja nie znajdzie błędów w kodzie (bo ich nie będzie ;)) i wszystko dobrze zinterpretuje, instalacja przebiegnie pomyślnie. Gratuluję, właśnie dodałeś(aś) do gry całkiem nowy dialog!


Jednak aby dialog mógł funkcjonować w grze, musi być on przypisany do jakiejś osoby. Można to zrobić, edytując plik .cre za pomocą DLTCEP. Ale to już inna historia…

Wszystkie działy modowania są ze sobą powiązane. Jednak można się nauczyć każdego z osobna. Umiejętność pisania kodu dialogu leży nie w znajomości samego kodu, a w zrozumieniu całego logicznego mechanizmu dialogu. Należy umieć planować, co pozwoli stworzyć całe drzewko dialogowe.

Przy pisaniu kodu trzeba myśleć jak maszyna. Przy pisaniu samych wypowiedzi dialogowych – jak pisarz. Przy pisaniu kodu dialogu – jak jedno i drugie jednocześnie. I to chyba wszystko na ten temat.


W razie pytania dotyczącego któregoś punktu proszę zgłosić je na moje PW.


Źródła, z których korzystałem, pisząc ten tutorial:

- Oryginalne pliki BG II + ToB

- ReadMe WeiDU

- skrypty modów 'Er'vonyrah: Pieśń Władającej' i 'Historia pewnego Kronikarza'

- skrypty mojego porzuconego projektu o nazwie 'Illanith NPC Mod'

- moja własna wiedza i wyobraźnia...

I nic więcej. - L`f




Podziękowania (za pomocą przycisku POMÓGŁ pod postem), opinie i komentarze