Tutorial w 7 krokach dla początkujących

by Zed Nocear



Celem niniejszego tutoriala jest przystępne objaśnienie podstaw tworzenia plików .D WeiDU umożliwiających dodawanie i modyfikację dialogów w grach opartych na Infinity Engine (gry IE). Tutorial jest w miarę uniwersalny dla wszystkich gier IE, ale pisany był w oparciu o zasady panujące w BG I. Pomiędzy grami istnieją pewne drobne różnice w funkcjonowaniu dialogów, co niniejszy tutorial stara się uwzględnić. Z założenia nie jest to poradnik prowadzący za rączkę i podający gotowe schematy how to do it.

SPIS TREŚCI

Zamiast wstępu

Podstawowe pojęcia i potrzebne narzędzia


Informacje podstawowe

1. Blok dialogu, czyli najmniejsza cegiełka, z których buduje się dialogi – informacje podstawowe

2. Najprostszy dialog – wymiana zdań między postacią niezależną a postacią gracza

2.1. Najprostsza sytuacja – naprzemienne wypowiedzi BN-a i odpowiedzi gracza do wyboru, które warunkują dalszy przebieg dialogu

2.2. Zróżnicowanie listy odpowiedzi postaci gracza przy użyciu warunków odpowiedzi, np. w zależności od cech postaci, posiadanego przedmiotu lub sumy pieniędzy

2.3. Zróżnicowanie wypowiedzi BN-a w reakcji na tą samą odpowiedź postaci gracza przy użyciu warunków odpowiedzi

2.4. Podział tekstu niemieszczącego się w okienku dialogowym na kilka części (Multisay)

3. Różnica między pojęciem dialogu/rozmowy a plikiem dialogu DLG, czyli umieszczenie więcej niż jednej rozmowy w pliku dialogu


Informacje mniej podstawowe

4. Stworzenie dialogu dwóch lub więcej postaci niezależnych, którym postać gracza "przysłuchuje się". Zakończenie odpowiedzi w bloku instrukcją EXTERN, praktyczna instrukcja CHAIN

5. Struktura i funkcjonowanie bloku – informacje zaawansowane

5.1. Pełna składania ciągu tekstowego (tzw. "string"):

5.1.1. Formy męskie i żeńskie tekstów w dialogach

5.1.2. Tekst z podkładem dźwiękowym

5.1.3. Użycie tzw. tokenów w dialogach

5.2. Pełna struktura linii odpowiedzi: warunek, tekst odpowiedzi, akcje skryptowe, wpis do dziennika, zakończenie odpowiedzi

5.3. Zasady obsługi linii odpowiedzi w bloku przez engine gier IE

5.4. Wagi bloków – zmiana kolejności sprawdzania warunków aktywacji rozmów

6. Możliwości i znaczenie pliku D

6.1. Reprezentacji tekstów w pliku D, traifikacja dialogów

6.2. Idea pliku D, wybrane instrukcje w plikach D: tworzenie nowego DLG, modyfikacja istniejących plików DLG

7. Informacje dodatkowe (warunki i akcje skryptowe, uproszczony zapis składni, specyficzne uwagi do funkcjonowania dialogów grze, użycie pliku D w instalce WeiDU)

7.1. Wybrane, najczęściej używane warunki i akcje skryptowe w dialogach

7.2. Uproszczony zapis dialogów

7.3. Aktywacja dialogów w grze, specyficzne dialogi NPC-ów przyłączalnych do drużyny

7.3.1. Plik dialogu zawierający "zwykłe" rozmowy dostępny dla każdej postaci z gry (także zwierząt i potworów)

7.3.2. Plik dialogu zawierający "bantery", czyli pogaduchy między członkami drużyny

7.3.3. Plik dialogu zawierający "rumors", czyli pogłoski wyświetlane przy piciu alkoholi w karczmach i ofiarach na tacę w światyniach

7.4. Wstawianie dialogów (plików D) do gry

7.4.1. Kompilacja pliku D z linii komend Windows

7.4.2. Komendy pliku TP2 w instalce WeiDU

7.4.3. Minimalistyczna ale kompletna, działająca instalka moda WeiDU



Zamiast wstępu

Podstawowe pojęcia dotyczące przechowywania tekstów w grach IE (plik TLK, string, strref):


W grach IE wszystkie teksty (nie tylko dialogi, ale też nazwy stworzeń, nazwy i opisy przedmiotów i czarów a nawet teksty interfejsu gry) zebrane są w jednym pliku Dialog.TLK. Pojedynczy tekst wyświetlany w grze jako funkcjonalna całość, czy będzie to krótki wyraz, czy wielozdaniowa treść książki z gry, określany jest jako ciąg tekstowy, czyli z angielskiego string. Stringi w Dialog.TLK są ponumerowane. Pliki definiujące stworzenia, przedmioty, czary i dialogi nie zawierają tekstów a tylko ich numery z Dialog.TLK czyli string references, albo w skrócie strrefy.


Ważne pojęcia, często na zasadzie skrótu myślowego określane jednakowo "dialogiem", a które należy rozróżniać:

Rozmowa/dialog – wymiana tekstów między postaciami z gry (niekoniecznie tylko dwoma i niekoniecznie jeden z rozmówców musi reprezentować gracza)


Plik dialogu DLG – format pliku gier Infinity Engine (IE) zawierający dialog/i i bezpośrednio używany w grze (nieczytelny dla człowieka, zawiera wyłącznie informacje sterujące [kolejność pojawiania tekstów, warunki aktywacji rozmów i inne] oraz numery tekstów z pliku Dialog.TLK, tzw. strrefy, podczas gdy same teksty zapisane są w Dialog.TLK


Plik źródłowy dialogu D – plik instalacyjny zawierający czytelny dla człowieka format zapisu instrukcji stworzenia nowego lub modyfikacji istniejącego pliku dialogu DLG, używany przez program WeiDU (oprócz instrukcji kompilacji do DLG i struktury dialogu zawiera zazwyczaj pełne teksty, ale może zawierać także tylko strrefy tekstów z Dialog.TLK lub numery tekstów zapisanych w osobnym pliku instalacyjnym TRA)


Wymagane narzędzia:

Do tworzenia dialogów w grach IE wystarczą: 1/ dowolny edytor tekstowy, 2/ program WeiDU oraz 3/ jakieś narzędzie do podglądania zasobów gry.

  1. Dialogi, jako pliki typu TXT, można pisać nawet w notatniku Windows, ale zdecydowanie wygodniejszy będzie jakiś zaawansowany zastępnik, np. Notepad2, m.in. pokazujący numery linii tekstu (nieocenione przy wyszukiwaniu błędów). W przypadku, kiedy plik dialogu osiąga gigantyczne rozmiary (np. pliki dialogów NPC-ów przyłączalnych do drużyny), pisanie tekstu źródłowego dialogu w edytorze MS Word w formacie DOC lub jego odpowiedniku  z Open Office pozwala na zastosowanie wyróżnień fragmentów tekstów przez różne kolory czcionek, tła, pogrubienie, kursywę itp., dzięki czemu łatwiej ogarnąć całość tekstu. Aby użyć dalej takiego pliku, trzeba go najpierw wyeksportować do formatu TXT, kiedy całe to dodatkowe formatowanie automatycznie znika, i zmienić rozszerzenie pliku na D.

  2. Program WeiDU posłuży nam do wstawienia (skompilowania) dialogu do gry, oraz w drugą stronę - wypakowania istniejących dialogów w grze do czytelnej dla człowieka postaci celem ich obejrzenia/przeanalizowania. Stosowany przez WeiDU format źródłowy dialogów D jest o wiele czytelniejszy niż prezentacja dialogów w programach do podglądania zasobów gry, ale by to zrobić, musimy jednak znać nazwę pliku DLG w zasobach gry, czyli jakieś narzędzie do podglądania zasobów gry i tak jest nam potrzebne.

  3. Infinity Explorer, Near Infinity lub DLTCEP według gustu i przyzwyczajeń. NI lub DLTCEP pozwala także wprowadzić drobne modyfikacje plików dialogów "na gorąco" (bez konieczności reinstalacji dialogu), co ułatwia proces wyszukiwania i usuwania błędów, bo rzadko kiedy stworzony dialog działa od początku zgodnie z oczekiwaniami.

Wyobraźmy sobie, że chcemy stworzyć następującą sytuację w grze - podchodzimy we Wrotach Baldura do jakiegoś nieszczęśnika, który na nasz widok oniemieje ze strachu, a może podziwu:


Napotkana osoba: Aaaaaaach!

Postać gracza: Jak dotrzeć do gospody Trzy Baryłki?

Napotkana osoba: Ba... ba... ba...

Postać gracza: Język ci kołkiem stanął? Odpowiadaj!

Napotkana osoba: Cooo?... (długa cisza) Cooo?

Postać gracza: Chyba się nie dogadamy. Miłego dnia.


Otwieramy Notatnik i wpisujemy nasz dialog w formie zrozumiałej dla instalatora WeiDU:


IF ~NumTimesTalkedTo(0)~ THEN BEGIN 0

SAY ~Aaaaaaach!

IF ~~ THEN REPLY ~Jak dotrzeć do gospody Trzy Baryłki?~ GOTO 1

END


IF ~~ THEN BEGIN 1

SAY ~Ba... ba... ba...

IF ~~ THEN REPLY ~Język ci kołkiem stanął? Odpowiadaj!~ GOTO 2

END


IF ~~ THEN BEGIN 2

SAY ~Cooo?... (długa cisza) Cooo?

IF ~~ THEN REPLY ~Chyba się nie dogadamy. Miłego dnia.~ EXIT

END


Dialog składa się z bloków, numerowanych od zera, z których każdy zawiera wypowiedź postaci niezależnej i odpowiedź gracza. Pierwszy blok zawiera warunek zaistnienia dialogu (~NumTimesTalkedTo(0)~), pozostałe stanowią kontynuację dialogu i go nie potrzebują. Odpowiedź gracza w dwóch pierwszych blokach zawiera na końcu  instrukcję (GOTO), informującą, który blok będzie stanowił kontynuacje dialogu. Odpowiedź gracza w ostatnim bloku zawiera na końcu instrukcję zakończenia dialogu (EXIT). Znaczenie zastosowanych elementów dialogu (jak i tych licznych niewykorzystanych w powyższym przykładzie), a także jak wstawić tak zapisany dialog do działającej gry, omówimy po malutku w kolejnych rozdziałach poradnika.


1. Blok dialogu, czyli najmniejsza cegiełka, z których buduje się dialogi – informacje podstawowe

Dialog każdej postaci w grach IE składa się z elementarnych cegiełek, czyli bloków. Podstawowy schemat struktury bloku jest następujący:


IF ~warunki aktywacji dialogu~ THEN BEGIN identyfikator_bloku0

SAY ~wypowiedź postaci niezależnej, z którą rozmawiamy~

IF ~warunki odpowiedzi0~ THEN REPLY ~odpowiedź0 postaci gracza~ GOTO identyfikator_bloku1 // kontynuacja dialogu – przeskok do kolejnego bloku

IF ~warunki odpowiedzi1~ THEN REPLY ~odpowiedź1 postaci gracza~ GOTO identyfikator_bloku2 // kontynuacja dialogu – przeskok do kolejnego bloku

IF ~warunki odpowiedzi2~ THEN REPLY ~odpowiedź2 postaci gracza~ EXIT // zakończenie dialogu

END


A w praktyce moźe to wyglądać tak (fragment dialogu Tamoko z BG I):


IF ~Global("TamokoMove","GLOBAL",0)~ THEN BEGIN 0

SAY ~Przepraszam za to, że skrywam pewne sprawy, ale czasy są niespokojne. Gdybyście znaleźli dla mnie chwilę czasu może usłyszelibyście coś, co... chcielibyście usłyszeć. Może moglibyśmy sobie pomóc nawzajem?~

IF ~~ THEN REPLY ~Skąd wiesz co chcemy wiedzieć? Nie chcemy informacji od szpiegów kryjących się w mroku.~ GOTO 1

IF ~~ THEN REPLY ~Jeśli twoja informacja jest coś warta, to owszem, chętnie posłucham.~ GOTO 3

IF ~~ THEN REPLY ~Chciałbym wiedzieć z kim mówię. "Nie biorę cukierków od obcych", jak to powiadają.~ GOTO 2

END


Lub tak (startowy blok dialogu Keldatha Ormlyra ze Świątyni Poranka w BG I):


IF ~True()~ THEN BEGIN 0

SAY ~Witaj! Poszukiwacz przygód zawsze znajdzie schronienie w domu Lathandera. Staramy się pomóc wszystkim tym, którzy chcą uczynić Krainy bezpieczniejszymi. Jeżeli zmęczyły was trudy walki, możecie tutaj skorzystać z nekromanckich regeneracji, ile tylko dusza zapragnie. W zamian chcemy tylko małego datku. Jeżeli niczego nie potrzebujecie, to pozwólcie że opowiem wam o szalonym Bassilusie.~

IF ~~ THEN REPLY ~Jaką pomoc możecie nam zaproponować?~ DO ~StartStore("tem3402",LastTalkedToBy())~ EXIT

IF ~!Dead("BASSILUS")~ THEN REPLY ~Obecnie nie potrzebujemy pomocy.~ GOTO 8

IF ~Dead("BASSILUS")~ THEN REPLY ~Obecnie nie potrzebujemy pomocy.~ EXIT

END


Powyższy schemat dla jasności pomija kilka opcjonalnych elementów bloku, które omówię później.


Każdy blok składa się z jednej wypowiedzi postaci niezależnej i dowolnej liczby linii odpowiedzi członka naszej drużyny do wyboru. Na końcu linii odpowiedzi następuje przejście do kolejnego bloku (kolejnej wypowiedzi BN-a) instrukcją GOTO lub opcjonalnie zakończenie dialogu instrukcją EXIT. BN (bohater niezależny) to postać w grze, którą gracz nie może sterować, czyli w BG wszyscy poza członkami drużyny.


Identyfikator_bloku to unikalna liczba lub nazwa, która jednoznacznie określa konkretny blok (dwa bloki nie mogą mieć tego samego identyfikatora). Bloki w dialogach i odpowiedzi w blokach numerowane są standardowo od zera. Napisałem "identyfikator_bloku" zamiast "numer_bloku" ponieważ WeiDU akceptuje też etykiety tekstowe zamiast numerów do identyfikacji bloków. Bardzo to ułatwia sprawę przy pisaniu dialogów, bo nie trzeba się martwić o renumerację bloków i odwołań do nich (GOTO), jeśli w trakcie pisania wstawimy jakieś bloki w środek pliku.


Tekst po znakach "//" oznacza komentarz, który nie będzie interpretowany przez WeiDU jako część struktury dialogu. Komentarz po znaku "//" ciągnie się do końca wiersza. Jeżeli chcemy stworzyć komentarz zajmujący kilka wierszy (lub "wyłączyć" z interpretacji WeiDU jakiś fragment dialogu, ale nie chcemy go skasować) to umieszczamy go między znakami "/*" a "*/". Weidu dekompilując plik .DLG z gry do postaci .D sam wstawia swoje komentarze ułatwiające odczytanie struktury dialogu.


Składnia warunków bloku i odpowiedzi jest identyczna jak w skryptach gier IE. Kilka rodzajów warunków można znaleźć w prezentowanych poniżej przykładach. Nieco obszerniej warunki zostaną omówione w podrozdziale 7.1. tutoriala.


Słowa kluczowe IF, THEN, BEGIN, SAY, REPLY, GOTO, EXIT, END są "case sensitive", czyli zawsze muszą być pisane dużą literą, inaczej WeiDU nie zinterpretuje ich prawidłowo.


Każdy blok musi kończyć się słowem kluczowym END


Treści warunków, wypowiedzi po SAY i odpowiedzi po REPLY muszą obowiązkowo być zamknięte w tyldach (np. ~TEKST~) i nie mogą zawierać tyldy w swoim tekście. Jeśli mimo wszystko chcemy w tekście zawrzeć znak tyldy, zamiast tyldy można użyć cudzysłowów (np. "TEKST"), znaków procentu (np. %TEKST%) lub pięciu kolejnych tyld (np. ~~~~~TEKST~~~~~). Analogicznie nie można wtedy użyć tych znaków w środku tekstu. W ostatnim przypadku można użyć w tekście tyldy, cudzysłowu i procentu, nawet jednocześnie, byle nie ciągu pięciu tyld.


Pewne elementy bloku są obowiązkowe, inne tylko opcjonalne. Blok musi zawierać wypowiedź i przynajmniej jedną odpowiedź! Wypowiedź i warunki mogą być "puste", ale nie można ich całkowicie pominąć w składni, wstawiamy wtedy same tyldy "~~". Najprostszy, poprawny pod względem składni blok będzie wyglądał następująco:


IF ~~ THEN BEGIN blok_00

SAY ~~

IF ~~ THEN EXIT

END


Taki blok stworzy pustą linię w okienku dialogu i zakończy dialog.


Ponieważ nie ma własnego warunku aktywacji, zostanie aktywowany tylko wtedy, gdy nastąpi przeskok do niego z innego bloku.


Większość odpowiedzi w dialogach nie ma warunku, czy też mówiąc dokładnie posiada pusty warunek (~~). Jeżeli odpowiedź nie posiada warunku pojawia się automatycznie, jeśli posiada warunek, to pojawi się w grze tylko wtedy, gdy warunek będzie spełniony (prawdziwy).


Zauważmy, że struktura bloku dialogu wymusza określoną kolejność. Jeśli zaczepiamy BN-a, zawsze najpierw pojawi się wypowiedź BN-a, a potem dopiero odpowiedź członka naszej drużyny. I tak jest zawsze w grze, bez wyjątku, ale wcale nie musi. Wstawiając pustą wypowiedź (SAY ~~) do bloku, jako pierwsze w grze zobaczymy nasze wypowiedzi do wyboru (zależnie od liczby odpowiedzi w bloku). Tym niemniej efekt nie jest zbyt naturalny, gdyż nawet jeśli będzie tylko jedna nasza odpowiedź i tak trzeba będzie ją zatwierdzić kliknięciem.


2. Najprostszy dialog – wymiana zdań między postacią niezależną a postacią gracza

2.1. Najprostsza sytuacja – naprzemienne wypowiedzi BN-a i odpowiedzi gracza do wyboru, które warunkują dalszy przebieg dialogu

Przejdźmy teraz od bloku do całej rozmowy/dialogu, który składa się z wielu bloków:


IF ~warunki aktywacji dialogu~ THEN BEGIN identyfikator_bloku0

SAY ~wypowiedź postaci niezależnej, z którą rozmawiamy~

IF ~~ THEN REPLY ~odpowiedź0 postaci gracza~ GOTO identyfikator_bloku1 // kontynuacja dialogu – przeskok do bloku 1

IF ~~ THEN REPLY ~odpowiedź1 postaci gracza~ GOTO identyfikator_bloku2 // kontynuacja dialogu – przeskok do bloku 2

IF ~~ THEN REPLY ~odpowiedź2 postaci gracza~ EXIT // zakończenie dialogu

...

END


IF ~~ THEN BEGIN identyfikator_bloku1

SAY ~wypowiedź postaci niezależnej, z którą rozmawiamy~

IF ~~ THEN REPLY ~odpowiedź0 postaci gracza~ GOTO identyfikator_bloku3 // kontynuacja dialogu – przeskok do kolejnego bloku (pominiętego w przykładzie)

IF ~~ THEN REPLY ~odpowiedź1 postaci gracza~ GOTO identyfikator_bloku4 // kontynuacja dialogu – przeskok do kolejnego bloku (pominiętego w przykładzie)

IF ~~ THEN REPLY ~odpowiedź2 postaci gracza~ EXIT // zakończenie dialogu

...

END


IF ~~ THEN BEGIN identyfikator_bloku2

SAY ~wypowiedź postaci niezależnej, z którą rozmawiamy~

IF ~~ THEN REPLY ~odpowiedź0 postaci gracza~ GOTO identyfikator_bloku5 // kontynuacja dialogu – przeskok do kolejnego bloku (pominiętego w przykładzie)

IF ~~ THEN REPLY ~odpowiedź1 postaci gracza~ GOTO identyfikator_bloku6 // kontynuacja dialogu – przeskok do kolejnego bloku (pominiętego w przykładzie)

IF ~~ THEN REPLY ~odpowiedź2 postaci gracza~ EXIT // zakończenie dialogu

...

END


...


Tylko blok inicjujący dialog posiada warunek jego aktywacji, pozostałe go nie potrzebują. Kolejność bloków jest teoretycznie dowolna, bo o ich kolejności wystąpienia w grze decydują warunki bloku (dla pierwszego) i instrukcje skoku (dla kolejnych bloków), ale lepiej dla osoby piszącej lub sprawdzającej dialog, aby były ułożone w kolejności występowania, inaczej można się nie połapać w strukturze dialogu. Wszystkie "ścieżki" i warianty dialogu powinny prowadzić ostatecznie do instrukcji EXIT. Łatwo stworzyć semantycznie poprawny dialog, akceptowany przez grę, ale tworzący zamkniętą pętlę, z której nie da się wyjść, tylko jaki sens ma w grze tworzenie dialogu, którego nie da się zakończyć.


Taka konstrukcja obowiązuje nie tylko w rozmowie między postacią z drużyny gracza a napotkaną postacią w grze (BN-em), ale także przy rozmowie naszej głównej postaci (Player1, CHARNAME) z innym członkiem naszej drużyny (np. romans z Viconią w BG II).


2.2. Zróżnicowanie listy odpowiedzi postaci gracza przy użyciu warunków odpowiedzi, np. w zależności od cech postaci, posiadanego przedmiotu lub sumy pieniędzy.

IF ~NumTimesTalkedTo(0)~ THEN BEGIN kupiec_00 //postać jeszcze nie rozmawiała z nami

SAY ~Witajcie, przemili klienci. Interesują was mikstury uzdrawiające? Mogę sprzedać miksturę uzdrowienia za 150 sz lub wspaniały eliksir zdrowia, neutralizujący każdą truciznę za 500 sz. Na co się decydujecie?~

IF ~~ THEN REPLY ~Nie interesują mnie twoje towary.~ EXIT // 1) bez warunku

IF ~PartyGoldGT(499)~ THEN REPLY ~Wezmę eliksir zdrowia.~ GOTO kupiec_01 // 2) złoto drużyny > 499 (czyli 500 lub więcej)

IF ~PartyGoldGT(149)~ THEN REPLY ~Wezmę miksturę uzdrawiającą.~ GOTO kupiec_02 // 3) złoto drużyny > 149 (czyli 150 lub więcej)

IF ~PartyGoldLT(150)~ THEN REPLY ~Nie stać mnie na żadną. Przyjdę później.~ GOTO kupiec_03 // 4) złoto drużyny < 150 (nie stać na nic)

IF ~CheckStatGT(LastTalkedToBy,14,INT)~ THEN REPLY ~Może się potargujemy?...~ GOTO kupiec_04 // 5) inteligencja 15 lub wyższa

IF ~PartyHasItem("MISC97")~ THEN REPLY ~Na nic, ale mamy coś, co ciebie zainteresuje.~ GOTO kupiec_05 // 6) w ekwipunku drużyny jest przedmiot MISC97.ITM (kufel piwa)

END


...


Przykładowy początkowy blok dialogu z kupcem. Zawiera 6 odpowiedzi, ale w grze nigdy nie pojawią się wszystkie razem. Odpowiedź 1) będzie występować zawsze. Jeżeli drużyna ma 500sz lub więcej pojawią się odpowiedzi 2) i 3) ("więcej niż 499" spełnia też warunek "więcej niż 149"), jeśli między 150 a 499sz tylko 3), gdy drużyna ma mniej niż 150sz tylko 4). Niezależnie, jeśli postać rozmawiająca z kupcem ma inteligencję ponad 14, pojawi się odpowiedź 5), otwierająca dodatkową ścieżkę dialogową. Niezależnie, jeśli w ekwipunku dowolnego członka drużyny znajduje się piwo Baalor De'Traniona (czyli MISC97.ITM według zasobów gry), pojawi się odpowiedź 6).


2.3. Zróżnicowanie wypowiedzi BN-a w reakcji na tę samą odpowiedź postaci gracza przy użyciu warunków odpowiedzi, konkretny przykład:

IF ~NumTimesTalkedTo(0)~ THEN BEGIN rabunek_00 //postać jeszcze nie rozmawiała z nami

SAY ~Czego chcecie?~

IF ~CheckStatGT(LastTalkedToBy,18,STR)~ THEN REPLY ~Wyskakuj z sakiewki, bo ci facjatę przemodeluję!~ GOTO rabunek_01 // siła naszej postaci > 18

IF ~CheckStatLT(LastTalkedToBy,19,STR)~ THEN REPLY ~Wyskakuj z sakiewki, bo ci facjatę przemodeluję!~ GOTO rabunek_02 // siła naszej postaci < 19

END


IF ~~ THEN BEGIN rabunek_01

SAY ~Jesteś bardzo przekonujący, panie. Oto moja sakiewka.~

IF ~~ THEN EXIT // zakończenie dialogu

END


IF ~~ THEN BEGIN rabunek_02

SAY ~Ty mi możesz, ale nasikać w skarpetki. Spadaj, bo zawołam straże!~

IF ~~ THEN EXIT // zakończenie dialogu

END


Jak widać, wpisujemy dwa razy tą samą odpowiedź, ale z odmiennymi warunkami. W grze pojawi się zawsze tylko jedna wypowiedź, więc mechanizm jest przeźroczysty. Należy tylko zwrócić uwagę, aby warunki były wykluczające się i razem obejmowały pełny zakres cechy. Gdybyśmy dali warunki Siła>18 i Siła<18, to dla siły naszego rozmówcy równej 18 obie odpowiedzi nie miałyby spełnionego warunku i dialog zakończyłby się komunikatem błędu o braku odpowiedzi ("no valid response" czy coś w tym stylu). Gdyby warunki zachodziły na siebie (np. Siła>17 i Siła<19) to dla rozmówcy z siłą 18 pojawiły by się obie odpowiedzi, gracz nie widzi warunków, więc pozornie dwa razy to samo, ale reakcja BN-a zależałaby, którą odpowiedź klikniemy.


2.4. Podział tekstu nie mieszczącego się w okienku dialogowym na kilka części ("Multisay")

Co zrobić, jeśli tekst wypowiedzi BN-a jest za długi i nie mieści się w okienku dialogu? Przykładowo jakiś mądrala w bibliotece Candlekeep nie może oprzeć się podzieleniem z nami nową wiedzą:


IF ~NumTimesTalkedTo(0)~ THEN BEGIN medrek_00

SAY ~Wciornastki (Thysanoptera, Physapoda syn. przylżeńce, tripsy ang. thrips) to rząd owadów obejmujący ok. 5 tys. gatunków (w Polsce ok. 220) Są to zwierzęta kosmopolityczne. Większość wciornastków żywi się sokami i tkankami roślinnymi, jednak są wśród nich gatunki grzybożerne, drewnożerne, jak również drapieżne (żywiące się larwami i jajami innych drobnych bezkręgowców). Część wciornastków to potencjalne szkodniki upraw, obok strat związanych ze zjadaniem tkanek roślinnych są również przenosicielami wirusów, bakterii i grzybów chorobotwórczych. Rząd wciornastków to drobne owady 1-3 mm długości (wyjątkowo tropikalne gatunki 20 mm). Aparat gębowy kłująco-ssący, skrzydła wyposażone są w szczecinki zwiększające powierzchnię lotną, odnóża kroczne z tzw. przylgą. Owady te przechodzą rozwój z przeobrażeniem niezupełnym, ich larwa wyglądem przypomina postać dorosłą.~

IF ~~ THEN EXIT

END


Możemy sami podzielić go na kilka bloków wyświetlanych kolejno po każdym kliknięciu, ale to pracochłonne i zajmuje dużo miejsca:


IF ~NumTimesTalkedTo(0)~ THEN BEGIN medrek_00

SAY ~Wciornastki (Thysanoptera, Physapoda syn. przylżeńce, tripsy ang. thrips) to rząd owadów obejmujący ok. 5 tys. gatunków (w Polsce ok. 220) Są to zwierzęta kosmopolityczne. Większość wciornastków żywi się sokami i tkankami roślinnymi, jednak są wśród nich gatunki grzybożerne, drewnożerne, jak również drapieżne (żywiące się larwami i jajami innych drobnych bezkręgowców).~

IF ~~ THEN GOTO medrek_01

END


IF ~~ THEN BEGIN medrek_01

~Część wciornastków to potencjalne szkodniki upraw, obok strat związanych ze zjadaniem tkanek roślinnych są również przenosicielami wirusów, bakterii i grzybów chorobotwórczych. ~

IF ~~ THEN GOTO medrek_02

END


IF ~~ THEN BEGIN medrek_02

~Rząd wciornastków to drobne owady 1-3 mm długości (wyjątkowo tropikalne gatunki 20 mm). Aparat gębowy kłująco-ssący, skrzydła wyposażone są w szczecinki zwiększające powierzchnię lotną, odnóża kroczne z tzw. przylgą. Owady te przechodzą rozwój z przeobrażeniem niezupełnym, ich larwa wyglądem przypomina postać dorosłą.~

IF ~~ THEN EXIT

END


Stosując WeiDU możemy znacznie ułatwić sobie robotę używając opcji "Multisay". Dzielimy tekst na fragmenty rozdzielone znakiem "=". WeiDU przy kompilacji dialogu (jego instalacji w grze) automatycznie utworzy potrzebne dodatkowe bloki dialogu. Efekt wersji prostszej poniżej i tej powyższej będzie w grze dokładnie taki sam:


IF ~NumTimesTalkedTo(0)~ THEN BEGIN medrek_00

SAY ~Wciornastki (Thysanoptera, Physapoda syn. przylżeńce, tripsy ang. thrips) to rząd owadów obejmujący ok. 5 tys. gatunków (w Polsce ok. 220) Są to zwierzęta kosmopolityczne. Większość wciornastków żywi się sokami i tkankami roślinnymi, jednak są wśród nich gatunki grzybożerne, drewnożerne, jak również drapieżne (żywiące się larwami i jajami innych drobnych bezkręgowców).~

=

~Część wciornastków to potencjalne szkodniki upraw, obok strat związanych ze zjadaniem tkanek roślinnych są również przenosicielami wirusów, bakterii i grzybów chorobotwórczych. ~

=

~Rząd wciornastków to drobne owady 1-3 mm długości (wyjątkowo tropikalne gatunki 20 mm). Aparat gębowy kłująco-ssący, skrzydła wyposażone są w szczecinki zwiększające powierzchnię lotną, odnóża kroczne z tzw. przylgą. Owady te przechodzą rozwój z przeobrażeniem niezupełnym, ich larwa wyglądem przypomina postać dorosłą.~

IF ~~ THEN EXIT

END


3. Różnica między pojęciem dialogu/rozmowy a plikiem dialogu DLG, czyli umieszczenie więcej niż jednej rozmowy w pliku dialogu

Każda postać niezależna (BN) ma przypisany tylko jeden plik dialogu, który zazwyczaj zawiera wszystkie jej wypowiedzi i stanowi zbiór wszystkich możliwych jej rozmów (przypisany plik dialogu można zmienić skryptem, ale robi się to w wyjątkowych sytuacjach). Jak sprawić, aby w różnych sytuacjach, np. ze względu na postęp fabuły gry, stan wykonanych questów lub zdobyte przedmioty, przy kliknięciu na postać pojawiał się inny dialog, adekwatny do sytuacji?


Do tego celu służą właśnie warunki bloków. Tylko pierwszy blok z każdego ciągu bloków dialogu/rozmowy posiada listę aktywujących go warunków. Pozostałe nie mają warunku bloku. Kiedy klikamy na postać i aktywujemy jej plik dialogu, gra po kolei przegląda bloki od pierwszego do ostatniego. Te, które nie mają warunku, gra ignoruje. W blokach posiadających warunek jest on sprawdzany i jeżeli prawdziwy, dialog zacznie się od tego bloku, a zawarta w nim wypowiedź BN-a pojawi się na ekranie.


Jeżeli plik dialogu posiada dwa bloki, których warunek jest w danej chwili prawdziwy, to aktywowany zostanie ten pierwszy (znajdujący się w kolejności bliżej początku pliku). Dlatego wyjątkowo dla bloków inicjujących rozmowy kolejność ich w pliku ma zasadnicze znaczenie, która z rozmów pojawi się w grze. Dla bloków zawierających kontynuację rozmowy, które stanowią większość, położenie w pliku .D nie ma znaczenia, gdyż o kolejności ich wystąpienia w rozmowie decydują instrukcje skoku (GOTO).


Jeżeli po kliknięciu na postać żaden z bloków nie będzie miał spełnionego warunku, nie aktywuje się żaden dialog. W BG I na tym koniec. W BG II pojawi się komunikat "Postać nie ma nic do powiedzenia".


Spróbujmy rozwinąć dialog mądrali z powyższego rozdziału, aby reagował na różne sposoby:


IF ~StateCheck(Myself,STATE_CHARMED)~ THEN BEGIN medrek_zauroczony // pod wpływem zauroczenia

SAY ~Jestem niższym bakałarzem i osobistym służącym Tethtorila. Tethtoril jest ze mnie bardzo zadowolony. Twierdzi, że już za 5 lat awansuję na bakałarza i otrzymam przywilej jednego wolnego popołudnia w tygodniu. A na razie pozwala mi raz w miesiącu poczytać przez godzinę w bibliotece. Ta encyklopedia biologii jest fascynująca, przyjaciele!~

IF ~~ THEN EXIT

END


IF ~NumTimesTalkedTo(0)~ THEN BEGIN medrek_start // pierwsze zagadnięcie postaci

SAY ~Wciornastki (Thysanoptera, Physapoda syn. przylżeńce, tripsy ang. thrips) to rząd owadów obejmujący ok. 5 tys. gatunków (w Polsce ok. 220) Są to zwierzęta kosmopolityczne. Większość wciornastków żywi się sokami i tkankami roślinnymi, jednak są wśród nich gatunki grzybożerne, drewnożerne, jak również drapieżne (żywiące się larwami i jajami innych drobnych bezkręgowców).~

=

~Część wciornastków to potencjalne szkodniki upraw, obok strat związanych ze zjadaniem tkanek roślinnych są również przenosicielami wirusów, bakterii i grzybów chorobotwórczych. ~

=

~Rząd wciornastków to drobne owady 1-3 mm długości (wyjątkowo tropikalne gatunki 20 mm). Aparat gębowy kłująco-ssący, skrzydła wyposażone są w szczecinki zwiększające powierzchnię lotną, odnóża kroczne z tzw. przylgą. Owady te przechodzą rozwój z przeobrażeniem niezupełnym, ich larwa wyglądem przypomina postać dorosłą.~

IF ~~ THEN EXIT

END


IF ~NumTimesTalkedTo(1)~ THEN BEGIN medrek_pytanie_00 // drugie zagadnięcie postaci

SAY ~Macie jakiś konkretny problem?~

IF ~~ THEN REPLY ~Szukamy Tethtorila.~ GOTO medrek_pytanie_01

IF ~~ THEN REPLY ~Nie, nic takiego.~ EXIT

END


IF ~~ THEN BEGIN medrek_pytanie_01

SAY ~Drugi korytarz, siódme drzwi po prawej stronie. Koniecznie zapukajcie!~

IF ~~ THEN REPLY ~Dziękuję.~ EXIT

END


IF ~True()~ THEN BEGIN medrek_finisz // warunek zawsze jest prawdziwy

SAY ~Cssyt! W bibliotece obowiązuje cisza. Przeszkadzacie mi w czytaniu.~ // testowany, gdy żadna z powyższych sytuacji nie wystąpi

IF ~~ THEN EXIT // czyli w praktyce trzecie i każde następne zagadnięcie postaci, która

END // nie jest zauroczona


Priorytet ma reakcja na zauroczenie. Jeżeli postać nie jest zauroczona, testowane są warunki kolejnych bloków. "NumTimesTalked" to parametr przypisany każdej postaci (definiowanej przez plik CRE). Przy inicjacji postaci w grze jest równy 0, za każdą aktywacją dialogu tej postaci (z każdym jej zagadnięciem) zwiększa się o 1. Warunek ~NumTimesTalkedTo(x)~ testuje, czy parametr odpowiada liczbie x, w szczególności ~NumTimesTalkedTo(0)~ sprawdza, że postać jeszcze nigdy nie rozmawiała, a ~NumTimesTalkedTo(1)~, że rozmawiała z nami dokładnie raz. Warunek ~True()~ jest zawsze prawdziwy, więc ten blok aktywuje się zawsze, jeśli żaden z poprzednich się nie aktywował. W ten sposób nigdy nie zobaczymy komunikatu "Postać nie ma nic do powiedzenia", ale jednocześnie nigdy nie będą testowane warunki bloków leżące za blokiem z ~True()~. Dlatego ten warunek może być stosowany tylko jako ostatni w pliku dialogu.


4. Stworzenie dialogu dwóch lub więcej postaci niezależnych, którym postać gracza "przysłuchuje się". Zakończenie odpowiedzi w bloku instrukcją EXTERN, praktyczna instrukcja CHAIN

Składnia dialogu umożliwia tylko rozmowę między BN-em a postacią sterowaną przez gracza (członkiem drużyny). Jak więc stworzyć rozmowę między dwoma (i więcej) BN-ami? Rozwiązanie jest dosyć śmieszne, bo według składni, jaką wymusza konstrukcja dialogów w grach IE, BN-i naprzemiennie zwracają się do postaci gracza, która milczy. Każda postać posiada swoją część wypowiedzi w swoim pliku, odpowiedzi nie zawierają tekstu postaci gracza, a przeskok między plikami dialogów odbywa się instrukcją EXTERN. W grze wygląda to na zwykły dialog dwóch niezależnych osób. Na tej samej zasadzie działają także "bantery" między członkami drużyny (z wyjątkiem banterów z główną postacią gry).


EXTERN ~nazwa pliku dialogu~ identyfikator_bloku


Jest to trzeci, obok EXIT i GOTO, rodzaj zakończenia linii odpowiedzi. GOTO umożliwia przeskok do kolejnego bloku w tym samym pliku dialogu i cały czas mamy do czynienia z tym samym BN-em. EXTERN powoduje przeskok do kolejnego bloku w innym pliku dialogu należącym do kolejnej postaci niezależnej uczestniczącej w rozmowie. W ten sposób możemy zrealizować odezwanie się osoby trzeciej w trakcie dyskusji.


Klasyczna realizacja dialogu między dwoma (lub więcej) BN-ami przy użyciu bloków dialogowych wymaga rozdzielenia bloków między pliki dialogów poszczególnych postaci i jest w związku z tym mało czytelna, nieintuicyjna, a przy rozdzielaniu kwestii do plików bardzo łatwo się pomylić:


BEGIN ~Z#Grox~ //Grox - strachliwy szaman koboldów


IF ~NumTimesTalkedTo(0)~ THEN BEGIN G01

SAY ~Chyba znowu coś słyszałem... Może się schowamy?~

IF ~~ THEN DO ~SetGlobal("Z!GroxFarthTalk",”GLOBAL”,1)~ EXTERN ~Z#Farth~ F01

END


IF ~~ THEN BEGIN G02

SAY ~Zamknij się! Mówię ci, Farth, że widziałem smoka.~

IF ~~ THEN EXTERN ~Z#Farth~ F02

END


IF ~~ THEN BEGIN G03

SAY ~Widziałem!~

IF ~~ THEN EXTERN ~Z#Farth~ F03

END


IF ~~ THEN BEGIN G04

SAY ~Nic nie wiesz. Tu mogą być potwory, o jakich nawet nie słyszałeś.~

IF ~~ THEN EXTERN ~Z#Farth~ F04

END


IF ~~ THEN BEGIN G05

SAY ~Od kiedy to dzielny szaman ucieka przed kilkoma wilkami? Po prostu cię osłaniałem.~

IF ~~ THEN EXTERN ~Z#Farth~ F05

END


IF ~~ THEN BEGIN G06

SAY ~Ty parszywy psie! Powinienem... Ach, co to było!? Farth! Osłaniaj mnie!~

IF ~~ THEN EXTERN ~Z#Farth~ F06

END



BEGIN ~Z#Farth~ //Farth - ogr ochroniarz


IF ~~ THEN BEGIN F01

SAY ~Farth myśleć, że Grox musi zmienić ubranie. Zamoczone gacie strasznie drapać. Tak Farth słyszeć.~

IF ~~ THEN EXTERN ~Z#Grox~ G02

END


IF ~~ THEN BEGIN F02

SAY ~Tutaj nie być żadne smoki.~

IF ~~ THEN EXTERN ~Z#Grox~ G03

END


IF ~~ THEN BEGIN F03

SAY ~Ty na pewno coś był widzieć, ale to nie być smok. On nie zmieścić się w korytarze.~

IF ~~ THEN EXTERN ~Z#Grox~ G04

END


IF ~~ THEN BEGIN F04

SAY ~Tak. Farth wiedzieć swoje. Grox był uciec przed wilkami, ja zostać i walczyć.~

IF ~~ THEN EXTERN ~Z#Grox~ G05

END


IF ~~ THEN BEGIN F05

SAY ~Hej, dzielny szaman koboldów pachnieć sikami po walce.~

IF ~~ THEN EXTERN ~Z#Grox~ G06

END


IF ~~ THEN BEGIN F06

SAY ~To chyba jacyś ludzie. Czekać tu.~

IF ~~ THEN EXIT

END


Jeżeli dialog nie zawiera skomplikowanych rozgałęzień, ani dodatkowych warunków lub instrukcji skryptowych w kolejnych blokach, a w szczególności wypowiedzi postaci gracza, to zamiast powyższej karkołomnej listy bloków można użyć instrukcji WeiDU "CHAIN". Używa jej się podobnie jak podziału przydługiej kwestii opcją Multisay ("="), ale w tym wypadku kolejne bloki mogą znajdować się w różnych plikach dialogów, których nazwy wpisuje się po znaku "==". Grupa wypowiedzi zaczyna się słowem kluczowym CHAIN i kończy EXIT. Pełna składnia polecenia CHAIN jest dość skomplikowana i nie będę jej w dokładnie omawiał. Odsyłam do dokumentacji WeiDU. Podstawową zaletą CHAIN oprócz zmniejszenia ilości kodu do wpisania jest naturalna kolejność kwestii dialogowych, umożliwiające łatwe zapoznanie się z całością dialogu. Instrukcją CHAIN można dodać dialogi tylko do już istniejących plików DLG, inaczej wystąpi błąd przerywający instalację. Dlatego w przykładzie poniżej instrukcje BEGIN, tworzące nowy plik dialogu DLG, są niezbędne. Przy dodawaniu nowych rozmów do już istniejących plików DLG, np. plików banterów NPC-ów, nie jest to konieczne.


BEGIN ~Z#Grox~ //Grox - strachliwy szaman koboldów


BEGIN ~Z#Farth~ //Farth - ogr ochroniarz


CHAIN

IF ~NumTimesTalkedTo(0)~ THEN ~Z#Grox~ Dialog01

~Chyba znowu coś słyszałem... Może się schowamy?~

DO ~SetGlobal("Z!GroxFarthTalk",”GLOBAL”,1)~

== ~Z#Farth~ ~Farth myśleć, że Grox musi zmienić ubranie. Zamoczone gacie strasznie drapać. Tak Farth słyszeć.~

== ~Z#Grox~ ~Zamknij się! Mówię ci, Farth, że widziałem smoka.~

== ~Z#Farth~ ~Tutaj nie być żadne smoki.~

== ~Z#Grox~ ~Widziałem!~

== ~Z#Farth~ ~Ty na pewno coś był widzieć, ale to nie być smok. On nie zmieścić się w korytarze.~

== ~Z#Grox~ ~Nic nie wiesz. Tu mogą być potwory, o jakich nawet nie słyszałeś.~

== ~Z#Farth~ ~Tak. Farth wiedzieć swoje. Grox był uciec przed wilkami, ja zostać i walczyć.~

== ~Z#Grox~ ~Od kiedy to dzielny szaman ucieka przed kilkoma wilkami? Po prostu cię osłaniałem.~

== ~Z#Farth~ ~Hej, dzielny szaman koboldów pachnieć sikami po walce.~

== ~Z#Grox~ ~Ty parszywy psie! Powinienem... Ach, co to było!? Farth! Osłaniaj mnie!~

== ~Z#Farth~ ~To chyba jacyś ludzie. Czekać tu.~

EXIT


5. Struktura i funkcjonowanie bloku – informacje zaawansowane

5.1.Pełna składania ciągu tekstowego (tzw. "string"): wersje dla formy męskiej i żeńskiej, dźwięki skojarzone z tekstem, użycie tokenów.

Dotychczas zawsze używaliśmy zarówno dla wypowiedzi BN-a i odpowiedzi postaci gracza prostego pojedynczego tekstu zamkniętego w tyldach "~~". Nie zawsze jest to wystarczające. W wypowiedziach i odpowiedziach dialogu można użyć rozszerzonej składni:


5.1.1. Formy męskie i żeńskie tekstów w dialogach:

Jeżeli forma wypowiedzi różni się dla osoby męskiej i żeńskiej jako tekst wpisywane są obie wersje zamknięte w tyldach, pierwsza dla rodzaju męskiego, druga dla żeńskiego, np. w rozdziale 4. drugi blok przykładu powinien wyglądać następująco:


IF ~~ THEN BEGIN rabunek_01

SAY ~Jesteś bardzo przekonujący, panie. Oto moja sakiewka.~ ~Jesteś bardzo przekonująca, pani. Oto moja sakiewka.~

IF ~~ THEN EXIT

END


Wszystkie teksty używane w grze IE zebrane są w jednym dużym pliku TLK. Angielskie wersje gry posiadają tylko plik Dialog.TLK. Inne wersje językowe posiadają także alternatywną wersję tekstów w DialogF.TLK. Dialog.TLK używany jest, jeżeli postać gracza, która rozmawia z BN-em, jest mężczyzną, DialogF.TLK - jeśli postać gracza jest kobietą. I nie chodzi wyłącznie o płeć postaci głównej, jak jest napisane w wielu poradnikach! Łatwo to sprawdzić. Jeżeli np. gra się postacią męską w BG I i w jakimś dialogu prowadzonym przez nią wystąpią formy męskie "zrobiłem/zrobiłeś", to przy oddelegowaniu do tego samego dialogu postaci żeńskiej z drużyny (np. Imoen) wystąpią formy żeńskie ("zrobiłam/zrobiłaś").


Zróżnicowanie dotyczy wyłącznie kwestii w dialogach, opisy przedmiotów czy czarów zawsze brane są z Dialog.TLK


Angielskie wersje gier IE mają tylko Dialog.TLK, bo język ten nie posiada odrębnych form gramatycznych dla rodzaju żeńskiego i męskiego.


5.1.2. Tekst z podkładem dźwiękowym:

Niektóre teksty w grze są jednocześnie mówione. Można to uzyskać przez skojarzenie pliku WAV z tekstem. W tym celu za tekstem wpisuje się nazwę pliku WAV z zasobów gry w nawiasach kwadratowych (rzadko stosowane w modach, bo trzeba by nagrywać własne dźwięki):


SAY ~tekst dialogowy~ [WAVEFILE]


lub


SAY ~tekst dialogowy męski~ [WAVFILE1] ~tekst dialogowy żeński~ [WAVFILE2]


5.1.3. Użycie tzw. tokenów w dialogach:

Tokeny to coś w rodzaju zmiennych tekstowych, pod które gra w zależności od cech głównego bohatera, rozmówcy lub czasu gry podstawia konkretne teksty. Np. jeśli nazwiemy naszą główną postać "Bartolomeo" i w jakimś dialogu użyjemy tekstu:


~Bardzo mnie zaskoczyłeś swoim postępowaniem, <CHARNAME>~


to w grze pojawi się tekst:


~Bardzo mnie zaskoczyłeś swoim postępowaniem, Bartolomeo~


Za każdym razie, w grze pojawi się w tekście takie imię, jakim została nazwana główna postać.


Lista prawidłowo interpretowanych tokenów w BG I jest krótka, najczęściej wykorzystywane są <CHARNAME> i <GABBER>:


<CHARNAME> - zwraca imię głównego bohatera

<DAY> - zwraca dzień miesiąca w postaci cyfry

<DAYANDMONTH> - zwraca dzień i miesiąc (np. 9 Flamerule)

<DURATION> - liczba dni i godzin od początku gry (np. 68 dni i 7 godz.)

<DURATIONNOAND> - jw. ale bez "i" (68 dni 7 godz.)

<GABBER> - zwraca imię rozmówcy (postaci gracza prowadzącej dialog z BN-em, np. Minsc)

<MONTHNAME> - podaje nazwę miesiąca (np. Flamerule)

<HOUR> - podaje godzinę w grze

<YEAR> - podaje rok w grze (np. 1369)


Znacznie dłuższa lista tokenów występuje w BG II i tam są one bardzo często używane w dialogach.


5.2. Pełna struktura linii odpowiedzi: warunek, tekst odpowiedzi, akcje skryptowe, wpis do dziennika, zakończenie odpowiedzi.

IF ~ciąg instrukcji warunków~ THEN REPLY ~tekst dialogowy męski~ [WAVFILE1] ~tekst dialogowy żeński~ [WAVFILE2] DO ~ciąg instrukcji akcji skryptowych~ JOURNAL ~wpis do dziennika męski~ ~wpis do dziennika żeński~ GOTO/EXTERN/EXIT


Praktycznie nigdy nie stosuje się w linii odpowiedzi wszystkich możliwych elementów, jak to miało miejsce w powyższym schemacie. Większość z nich została już omówiona w poprzednich punktach tutoriala, do omówienia pozostały akcje skryptowe i wpis do dziennika.


Obowiązkowe są: warunek, choć może być pusty (IF ~~ THEN) i jedna z form zakończenia odpowiedzi (GOTO/EXTERN/EXIT)


Pozostałe trzy elementy są opcjonalne: tekst odpowiedzi (REPLY), akcje skryptowe (DO), wpis do dziennika (JOURNAL), mogą być użyte wszystkie trzy, żaden z nich, lub dowolne wybrane.


Akcje skryptowe po DO umieszcza się w tyldach i poszczególne instrukcje rozdziela zwykłą spacją. Są to dokładnie te same instrukcje, co używane w skryptach po słowie #RESPONSE. Zazwyczaj akcje skryptowe umieszcza się w linii odpowiedzi kończącej dialog. Ponieważ w trakcie dialogu czas gry jest zatrzymany, większość instrukcji realizowanych jest dopiero po zakończeniu dialogu. Niektóre instrukcje nie wymagające czasu jak przekazanie złota, przedmiotów czy przyznanie doświadczenia, realizowane są po następnym bloku. Więcej informacji na temat instrukcji warunków i akcji w podrozdziale 7.1..


Wpis do dziennika po JOURNAL także umieszcza się w tyldach. W BG I jest to jedyna instrukcja, bo wszystkie teksty umieszczane sa w jednym miejscu. W BG II występują trzy rodzaje instrukcji umieszczające tekst w trzech sekcjach dziennika: JOURNAL umieszcza tekst w sekcji "dziennik", UNSOLVED_JOURNAL umieszcza tekst w sekcji "zadania", SOLVED_JOURNAL umieszcza tekst w sekcji "wykonane zadania".


5.3. Zasady obsługi linii odpowiedzi w bloku przez engine gier IE.

Gra odmiennie traktuje linie odpowiedzi w bloku zależnie od tego, czy zawierają tekst odpowiedzi czy nie:


Jeżeli choć jedna linia odpowiedzi zawiera tekst odpowiedzi gracza (instrukcję REPLY), to linie odpowiedzi bez REPLY są ignorowane, a spośród tych zawierających odpowiedź gracz na ekranie wyświetlane są w kolejności występowania w bloku odpowiedzi, których warunek jest prawdziwy lub pusty. Wybranie przez kliknięcie którejś z wyświetlonych odpowiedzi powoduje wykonanie powiązanej z nią instrukcji przejścia do innego bloku lub zakończenia dialogu.


Jeżeli wszystkie linie odpowiedzi nie zawierają wypowiedzi gracza (instrukcji REPLY), to linie odpowiedzi sprawdzane są w odwrotnej kolejności od ostatniej do pierwszej. Pierwsza od końca, której warunek jest pusty lub prawdziwy zostanie natychmiast wykonana bez komunikatu na ekranie i następuje od razu przejście do następnego bloku lub zakończenie dialogu.


IF ~True()~ THEN BEGIN SRE02_00

SAY ~Ale jatki się tu rozegrały. Nawet nie wiadomo kto tu zginął, i czy to był bitwa, czy może rzeź mieszkańców jakiejś zapomnianej osady?~

IF ~~ THEN EXIT

IF ~InParty("KIVAN")~ THEN EXTERN KIVANJ SRE02_02

IF ~InParty("FALDORN")~ THEN EXTERN FALDOJ SRE02_01

IF ~InParty("JAHEIRA")~ THEN EXTERN JAHEIJ SRE02_01

END


W tym przykładzie po wypowiedzi postaci w pierwszej kolejności sprawdzany jest warunek, czy w drużynie jest Jaheira i następuje przeskok do jej komentarza. Jeśli nie ma Jaheiry w drużynie sprawdzany jest warunek obecności Faldorn i ona ma szansę na wygłoszenie komentarza. Jeśli nie ma Jaheiry i Faldorn przychodzi kolej na Kivana. Jeśli w drużynie nie ma żadnej z tych trzech osób dialog po prostu zakończy się.


5.4. Wagi bloków – zmiana kolejności sprawdzania warunków aktywacji rozmów.

Jeżeli tworzymy nowy plik dialogu, możemy ustawić sobie bloki z warunkami rozmów, aby były sprawdzane w takiej kolejności, jaka nam pasuje. Jeżeli jednak dodajemy nowe rozmowy do już istniejącego pliku dialogu w grze, nie możemy tego zrobić. Wstawienie nowego bloku na początku lub w środku istniejącego dialogu spowodowałby przesunięcie w numeracji bloków i instrukcje GOTO oraz EXTERN prowadziłyby do niewłaściwych bloków.


Twórcy Infinity Engine przewidzieli takie sytuacje wprowadzając mechanizm wag bloków (WEIGHTs), który umożliwia zmianę kolejności sprawdzania warunków bloków bez zmiany ich pozycji w pliku dialogowym. Wagę bloku wstawia się między IF a warunkiem bloku, ma ona sens tylko dla bloków posiadających niepusty warunek.


Tak więc proces aktywowania dialogu, przedstawiony w rozdziale 3. wymaga uzupełnienia: przy aktywacji dialogu (np. kliknięciu na BN-a) sprawdzane są najpierw warunki bloków posiadających ustawioną wagę od najniższej do najwyższej, a dopiero potem warunki pozostałych bloków nie posiadających wagi według kolejności ich wystąpienia. Bloki bez warunku są ignorowane, podobnie jak bloki, których warunek jest fałszywy (nie jest spełniony). Pierwszy sprawdzany blok z prawdziwym warunkiem rozpocznie aktualną rozmowę.


CIEKAWOSTKA: Pusty warunek linii odpowiedzi jest automatycznie traktowany jako prawdziwy i tekst odpowiedzi jest wyświetlany w grze. Odmiennie pusty warunek bloku jest automatycznie traktowany jako fałszywy i blok jest pomijany przy szukaniu aktualnego początku rozmowy.


Wartości wag bloków WEIGHT mogą przybierać wartości dodatnie od zera włącznie wzwyż. Nie może być dwóch bloków mających tą samą wartość parametru WEIGHT.


Mechanizm wag bloków był masowo wykorzystywany przy uzupełnianiu dialogów, także przez samych twórców gry. Między innymi w BG I kwestie wypowiadane przez BN-ów znajdujących się pod wpływem zauroczenia znajdują się zwykle na końcu pliku dialogu postaci, ale poprzez ustawienie "WEIGHT #0" sprawdzane są jako pierwsze, np.:


IF WEIGHT #0 ~StateCheck(Myself,STATE_CHARMED)~ THEN BEGIN 10

SAY ~Wiele ostatnio słyszałem. Ciężko zdobyć żelazo, a tu jeszcze ci bandyci i słaba ruda z Nashkel. Szykują się kłopoty między Wrotami Baldura a Amnem. Nie zdziwiłbym się, gdyby wybuchła tam wojna.~

IF ~~ THEN JOURNAL ~Zauroczyliśmy pewnego kupca, który zaczął mówić jakieś niejasne rzeczy na temat braku żelaza, bandytów i wojny.~ EXIT

END


6. Możliwości i znaczenie plików D

6.1. Reprezentacji tekstów w pliku D, traifikacja dialogów

Istnieją trzy warianty reprezentacji (zapisu) tekstów w plikach D: 1/ pełne ciągi tekstowe (jak w podrozdziale 5.1.), 2/ numery ciągów tekstowych w plikach TRA powiązanych z plikiem D (@numer), 3/ numery ciągów tekstowych w plikach Dialog.TLK i DialogF.TLK (#numer).


W dotychczasowych przykładach plików D zapis zawierał zarówno strukturę dialogu (warunki, akcje, skoki i inne) jak i same teksty dialogu. Taki zapis jest najbardziej przejrzysty i łatwy do rozbudowywania lub poprawiania, jednakże plik D w takiej formie może zawierać tylko jedną wersję językową. Np. fragment dialogu:


BEGIN ~Aval01~


IF ~NumTimesTalkedTo(0) Dead("tarnesh")~ THEN BEGIN Av01x00

SAY ~No, no no. Co my tu mamy? Nie wyglądało to na zwyczajną tawernianą "sprzeczkę". Mamy tutaj, że tak powiem, bardziej krwawy problem! Radzę ci, lepiej się schowaj przyjacielu.~ [aval01]

++ ~A to niby czemu?~ + Av01x01

++ ~Jesteś jednym z jego wspólników? Zaraz zginiesz!~ + Av01x02

++ ~Kim jesteś?~ + Av01x03

++ ~Nie mam na to czasu! Zjeżdżaj!~ + Av01x04

END


IF ~~ THEN BEGIN Av01x02

SAY ~Halo, halo! Stop! Czy ja ci właśnie nie poradziłem co masz zrobić? Jestem tylko obserwatorem.~

++ ~Kim jesteś?~ + Av01x03

++ ~Nie mam na to czasu! Zmiataj stąd!~ + Av01x04

++ ~Powiedziałeś, że mam się ukryć. Czemu tak sądzisz?~ + Av01x01

END


Zamiast pełnych tekstów dialogów plik D może zawierać numery poprzedzone znakiem "@" odnoszące się do ponumerowanej listy ciągów tekstowych w oddzielnym pliku TRA (@numer). Powyższy przykład zapisany w formie pary plików D i TRA będzie wyglądał następująco:


Zawartość straifikowanego pliku D:

BEGIN ~Aval01~


IF ~NumTimesTalkedTo(0) Dead("tarnesh")~ THEN BEGIN Av01x00

SAY @0 [aval01]

++ @1 + Av01x01

++ @2 + Av01x02

++ @3 + Av01x03

++ @4 + Av01x04

END


IF ~~ THEN BEGIN Av01x02

SAY @5

++ @3 + Av01x03

++ @6 + Av01x04

++ @7 + Av01x01

END


Zawartość pliku TRA:

@0 = ~No, no no. Co my tu mamy? Nie wyglądało to na zwyczajną tawernianą "sprzeczkę". Mamy tutaj, że tak powiem, bardziej krwawy problem! Radzę ci, lepiej się schowaj przyjacielu.~ [aval01]

@1 = ~A to niby czemu?~

@2 = ~Jesteś jednym z jego wspólników? Zaraz zginiesz!~

@3 = ~Kim jesteś?~

@4 = ~Nie mam na to czasu! Zjeżdżaj!~

@5 = ~Halo, halo! Stop! Czy ja ci właśnie nie poradziłem co masz zrobić? Jestem tylko obserwatorem.~

@6 = ~Nie mam na to czasu! Zmiataj stąd!~

@7 = ~Powiedziałeś, że mam się ukryć. Czemu tak sądzisz?~


W modach oferujących więcej niż jedną wersję językową, traifikacja pliku D to jedyna możliwość. Pliki o rozszerzeniu TRA musi mieć tą samą nazwę, co plik D. Jednemu plikowi D towarzyszy tyle plików TRA, ile jest wersji językowych.


Program WeiDU nie posiada interfejsu graficznego, więc polecenie traifikacji wprowadza się w linii komend (np. linia komend w Total Commanderze lub okienko DOS). WeiDU.EXE i plik D musza znajdować sie w tym samym katalogu gry, do której został stworzony dialog. Z linii komend wpisujemy:

WeiDU --traify Dialog.D

i wszystkie teksty zostają przeniesione z pliku D do TRA. Nowy plik Dialog.D nadpisze ten pierwotny. Teksty będą numerowane od @0. Jeżeli tekst się powtarza kilka razy, w TRA będzie umieszczony tylko raz. Nie ma to wpływu na strukturę (kolejność) dialogu. Jeśli w trakcie traifikacji WeiDU napotka błąd w strukturze pliku D, to plik TRA nie powstanie, a plik D zostanie zniszczony (nadpisany zawartością o zerowej długości). Wynikają z tego dwa wnioski:

1/ traifikacja stanowi pewna formę sprawdzenia poprawności pliku D

2/ lepiej przed traifikacją zrobić kopię bezpieczeństwa pliku D

Jeżeli nie chcemy nadpisywać starego pliku D, należy użyć opcji --out:

WeiDU --traify Dialog.D --out Dialog-new.D

Jeżeli chcemy zacząć numerację tekstów nie od zera, ale konkretnej liczby, tu np. 200, używamy polecenia --traify#, np.:

WeiDU --traify Dialog.D --traify# 200

Instrukcja --traify działa także dla tekstów zawartych w plikach BAF i TP2, ale to temat na inny tutorial.


Trzecią możliwością reprezentacji tekstów w pliku D są numery poprzedzone znakiem "#" odnoszące się do ciągów tekstowych w plikach Dialog.TLK i DialogF.TLK (#numer), określane też jako strrefy (z ang. string reference). Powyższy przykładowy dialog skompilowany do pliku DLG w grze, z wpisaniem tekstów do plików TLK i następnie ponownie zdekompilowany do pliku D, zostanie zapisany następująco:


BEGIN ~AVAL01~


IF ~NumberOfTimesTalkedTo(0) Dead("tarnesh")~ THEN BEGIN 0 // from:

SAY #26054 /* ~No, no no. Co my tu mamy? Nie wyglądało to na zwyczajną tawernianą "sprzeczkę". Mamy tutaj, że tak powiem, bardziej krwawy problem! Radzę ci, lepiej się schowaj przyjacielu.~ [aval01] */

IF ~~ THEN REPLY #26055 /* ~A to niby czemu?~ */ GOTO 3

IF ~~ THEN REPLY #26056 /* ~Jesteś jednym z jego wspólników? Zaraz zginiesz!~ */ GOTO 1

IF ~~ THEN REPLY #24157 /* ~Kim jesteś?~ */ GOTO 2

IF ~~ THEN REPLY #26057 /* ~Nie mam na to czasu! Zjeżdżaj!~ */ GOTO 9

END


IF ~~ THEN BEGIN 1 // from: 2.1 0.1

SAY #26058 /* ~Halo, halo! Stop! Czy ja ci właśnie nie poradziłem co masz zrobić? Jestem tylko obserwatorem.~ */

IF ~~ THEN REPLY #24157 /* ~Kim jesteś?~ */ GOTO 2

IF ~~ THEN REPLY #26059 /* ~Nie mam na to czasu! Zmiataj stąd!~ */ GOTO 9

IF ~~ THEN REPLY #26060 /* ~Powiedziałeś, że mam się ukryć. Czemu tak sądzisz?~ */ GOTO 3

END


Numery strrefów są zależne od wersji gry, zainstalowanych modów (wersji, kolejności instalacji, wybranych komponentów) i mogą być u każdego gracza inne. Oryginalne teksty w plikach TLK zostały dodane jako komentarz między znakami "/* */". W przeciwieństwie do komentarza po znakach "//" ograniczonego do jednego wiersza, komentarz między znakami "/* */" może mieć dowolną liczbę wierszy. Zamiast etykiet bloków, w zdekompilowanym pliku występują numery pozycji bloków w DLG.


6.2. Idea pliku D, wybrane instrukcje w plikach D: tworzenie nowego DLG, modyfikacja istniejących plików DLG

Do tej pory skupialiśmy się na formie zapisu i elementach samych bloków dialogowych, nie jest to jednak wystarczające do stworzenia funkcjonującego pliku D


Plik D nie jest po prostu zdekompilowaną, czytelną dla człowieka formą zapisu bloków pliku DLG, ale plikiem wykonywalnym zawierającym instrukcje jak stworzyć nowy lub zmodyfikować istniejący w grze jeden lub więcej plik DLG.


Nowy plik DLG tworzy się instrukcją BEGIN ~nazwa_pliku_DLG~, po którym następuje lista bloków, które ma zawierać tworzony plik DLG


 a.  Instrukcja BEGIN w przeciwieństwie do pozostałych instrukcji pliku D nie wymaga zamknięcia słowem END na końcu listy bloków

 b.  Gry IE nie obsługują długich nazw plików, więc nazwa tworzonego piku DLG nie może mieć więcej niż 8 znaków, podobnie jak wszystkie inne pliki z zasobów gry

 c.   Jeżeli wcześniej istniał w grze plik DLG o tej samej nazwie, tworzony instrukcją BEGIN plik DLG całkowicie (niszcząco) go zastępuje

 d.  Powszechnie w modach stosuje się pliki D, które zawierają instrukcję utworzenia jednego pliku DLG o takiej samej nazwie jak plik D, ale wcale nie jest to obowiązkowe:

   -  WeiDU obsługuje długie nazwy plików, więc nazwa pliku D może być dłuższa niż 8 znaków i nie musi być identyczna jak tworzonego pliku DLG

   -  jeden plik D może zawierać kilka instrukcji BEGIN, każda z następującą listą bloków, wtedy kompilacja takiego pliku D spowoduje utworzenie kilku plików DLG w zasobach gry

Kolejną często używaną instrukcją jest dodanie nowych bloków do istniejącego pliku DLG: APPEND ~nazwa_pliku_DLG~ lista_bloków END.

 a.  Poniższa przykładowa zawartość pliku D, który wprowadza dodatkowy komentarz dla specyficznej sytuacji do trzech plików: MCOOK2.DLG, NOBWBA.DLG i DELAINY1.DLG.

 b.  Bloki dodawane są na końcu istniejącego pliku DLG. Użycie etykiet a nie bezwzględnych numerów bloków jest bardzo wygodne, bo modyfikacja zainstaluje się poprawnie, niezależnie ile bloków liczą modyfikowane pliki DLG.

 c.   Aby dodane bloki mogły ukazać się w grze konieczna może być zmiana kolejności sprawdzania warunków bloków parametrem WEIGHT lub dodanie dodatkowych linii odpowiedzi w pierwotnym dialogu, prowadzących do nowych bloków (tak jak modyfikacja DELAINY1.DLG w poniższym przykładzie, instrukcje EXTEND_TOP lub EXTEND_BOTTOM omówione w dalszej części rozdziału).

   -  Wartość parametru WEIGHT bloków dialogu tworzonego instrukcją BEGIN lub dodawana do bloku instrukcją SET_WEIGHT jest wstawiana dokładnie taka jak w parametrze instrukcji. Natomiast wartości WEIGHT bloków dodawanych instrukcją APPEND ma charakter względny ("non-trivial weight" w opisie WeiDU) i jest modyfikowana w zależności pierwotnej zawartości modyfikowanego pliku DLG. Np. "WEIGHT #1" bloku w instrukcji APPEND oznacza "Wstaw taką wagę, aby nowy blok był sprawdzany PO bloku z niepustym warunkiem na pozycji #1 zawartości pliku DLG sprzed modyfikacji (według zasad sprawdzania bloków z uwzględnieniem istniejących wcześniej wag, inaczej po drugim bloku z warunkiem, bo bloki liczone są od zera)". Ostatecznie wprowadzany blok instrukcją APPEND otrzyma wagę WEIGHT #2, blok z warunkiem na pozycji #2 pierwotnego pliku, jeśli istnieje, otrzyma wagę WEIGHT #3, itd. Jeżeli jednak plik DLG miał tylko jeden blok z niepustym warunkiem (pozycja #0), to dodawany blok otrzyma wagę #1.

   -  W instrukcji APPEND można podać wartość ujemną wagi, np. "WEIGHT #-1", co oznacza "dodawany blok ma być sprawdzany w pierwszej kolejności przed wszystkimi z pierwotnego pliku DLG", a w praktyce dodawany blok otrzyma wagę WEIGHT #0.

   -  W instrukcji APPEND bloki mogą mieć tą samą wartość parametru WEIGHT. Jeżeli chcemy wprowadzić trzy bloki, których warunki mają być sprawdzane między blokiem #1 a blokiem #2 dotychczasowego pliku DLG, to wszystkie trzy muszą otrzymać WEIGHT #1, tak jak w modyfikacji NOBWBA.DLG poniższego przykładu. Wstawiane bloki otrzymają kolejno wagi: WEIGHT #2, WEIGHT #3 i WEIGHT #4. Istniejący blok #2 dotychczasowego pliku DLG otrzyma wagę WEIGHT #5.


//Kucharz w PPD/Gospodzie Feldposta/Elfiej Pieśni/1p. "Pod Trzema Baryłkami":

/////////////////////////////////////////////////////////

APPEND ~MCOOK2~


IF WEIGHT #1 ~InParty("Aval01") See("Z!AVDOG1") Global("Z!DogComment","LOCALS",0)~ THEN BEGIN DogComment10

SAY @0

IF ~~ THEN DO ~SetGlobal("Z!DogComment","LOCALS",1)~ EXIT

END


END


//Szlachcianki:

/////////////////////////////////////////////////////////

APPEND ~NOBWBA~


IF WEIGHT #1 ~InParty("Aval01") See("Z!AVDOG1") Range("Z!AVDOG1",10) Global("Z!DogComment","LOCALS",0) Global("Z!DogComment11","GLOBAL",0)~ THEN BEGIN DogComment11

SAY @1

IF ~~ THEN REPLY @2 DO ~SetGlobal("Z!DogComment","LOCALS",1) SetGlobal("Z!DogComment11","GLOBAL",1)~ EXIT

IF ~~ THEN REPLY @3 DO ~SetGlobal("Z!DogComment","LOCALS",1) SetGlobal("Z!DogComment11","GLOBAL",1)~ EXIT

END


IF WEIGHT #1 ~InParty("Aval01") See("Z!AVDOG1") Range("Z!AVDOG1",10) Global("Z!DogComment","LOCALS",0) Global("Z!DogComment12","GLOBAL",0)~ THEN BEGIN DogComment12

SAY @4

IF ~RandomNum(3,1)~ THEN REPLY @5 DO ~SetGlobal("Z!DogComment","LOCALS",1) SetGlobal("Z!DogComment12","GLOBAL",1)~ EXIT

IF ~RandomNum(3,2)~ THEN REPLY @6 DO ~SetGlobal("Z!DogComment","LOCALS",1) SetGlobal("Z!DogComment12","GLOBAL",1)~ EXIT

IF ~RandomNum(3,3)~ THEN REPLY @7 DO ~SetGlobal("Z!DogComment","LOCALS",1) SetGlobal("Z!DogComment12","GLOBAL",1)~ EXIT

END


IF WEIGHT #1 ~InParty("Aval01") See("Z!AVDOG1") Range("Z!AVDOG1",10) Global("Z!DogComment","LOCALS",0) Global("Z!DogComment13","GLOBAL",0)~ THEN BEGIN DogComment13

SAY @8

IF ~~ THEN REPLY @9 DO ~SetGlobal("Z!DogComment","LOCALS",1) SetGlobal("Z!DogComment13","GLOBAL",1)~ EXIT

END


END


// Delainy - z wioski wilkońaków

/////////////////////////////////////////////////////////

EXTEND_BOTTOM ~DELAINY1~ 2

IF ~InParty("Aval01") See("Z!AVDOG1") Global("Z!DogComment16","GLOBAL",0)~ THEN JOURNAL #23395 /* ~Powiedziano mi, öeby porozmawiaď z kobietî o imieniu Kaishas, a ona postara siđ, by mieszkaňcy wioski nie uwaöali nas za zagroöenie.~ */ GOTO DogComment16

END

APPEND ~DELAINY1~


IF ~~ THEN BEGIN DogComment16

SAY @18

=

@19

IF ~~ THEN DO ~SetGlobal("Z!DogComment16","GLOBAL",1)~ EXIT

END


END


Polecenie CHAIN omówione skrótowo w rozdziale 4. jest także pełnoprawną instrukcją pliku D, taj jak BEGIN czy APPEND, tyle że mocno wyspecjalizowaną w tworzeniu dłuższych konwersacji między BN-ami lub NPCami, w których postać gracza nie mówi nic.


Ponadto WeiDU daje do dyspozycji blisko 30 instrukcji modyfikujące w specyficzny sposób pliki DLG. Poniżej omawiam wybrane. W sprawie pozostałych polecam zajrzeć do opisu programu WeiDU (plik README-WeiDU.html).


Instrukcja EXTEND_TOP ~nazwa_pliku_DLG~ numery_bloków lista_linii_odowiedzi END dodaje nowe linie odpowiedzi na początku istniejących już linii odpowiedzi (czyli "od góry") we wskazanym bloku pliku DLG. Bliźniacza instrukcja EXTEND_BOTTOM o tej samej składni wstawia nowe linie odpowiedzi na końcu już istniejących (czyli "od dołu"). Kolejność linii odpowiedzi ma znaczenie przy kolejności wyświetlania ich tekstów w grze lub która linia odpowiedzi bez REPLY zostanie natychmiast wykonana (szczegóły w podrozdziale 5.3.).

EXTEND_TOP ~PRIILMU~ 3

IF ~~ THEN REPLY #17021 /* ~I jakież to mogą być działania, kapłanie?~ */ GOTO 11

END


   -  Jedna instrukcja EXTEND_TOP lub EXTEND_BOTTOM może wstawiać dowolnie dużą liczbę nowych linii odpowiedzi:

EXTEND_BOTTOM ~TAEROM~ 14

IF~Global("AQueenSREQuest","GLOBAL",0)~THEN REPLY @0 GOTO SREQuest1001

IF~Global("AQueenSREQuest","GLOBAL",1) PartyHasItem("MISC12")~THEN REPLY @1 GOTO SREQuest1002

IF~Global("AQueenSREQuest","GLOBAL",1) PartyHasItem("MISC52")~THEN REPLY @2 GOTO SREQuest1002

IF~Global("AQueenSREQuest","GLOBAL",1) PartyHasItem("SREAQI")~THEN REPLY @3 GOTO SREQuest1003

IF~GlobalTimerExpired("AnkhegQueenHelp","GLOBAL") Global("AQWeNeedHelp","GLOBAL",0)~THEN REPLY @4 GOTO SREQuest1010

IF~GlobalTimerExpired("AQItemWait","GLOBAL") GlobalGT("AQueenSREQuest","GLOBAL",2) GlobalLT("AQueenSREQuest","GLOBAL",7)~THEN REPLY @5 GOTO SREQuest1011

END


   -  Jedna instrukcja EXTEND_TOP lub EXTEND_BOTTOM może wstawiać ten sam zestaw linii odpowiedzi do kilku bloków na raz. Poniższa instrukcja dodaje linie odpowiedzi do bloków numer 2 i 3 pliku ELMIN1.DLG:

EXTEND_BOTTOM ~ELMIN1~ 2 3

IF ~PartyHasItem("Z!DAGG00")~ THEN + elmin1a_01

IF ~PartyHasItem("Z!DAGG01")~ THEN + elmin1a_01

IF ~PartyHasItem("Z!DAGG02")~ THEN + elmin1a_01

END


Instrukcja ADD_TRANS_TRIGGER ~nazwa_pliku_DLG~ numer_bloku ~warunki odpowiedzi~ DO numery_linii_odpowiedzi niszcząco ustawia nowy zestaw warunków we wskazanych liniach odpowiedzi. Jeżeli istniały wcześniej jakieś warunki odpowiedzi, zostaną one skasowane.

   -  WeiDU nie daje narzędzia umożliwiającego usuwanie linii odpowiedzi z bloków dialogu. Wstawienie warunku ~False~ czyni ją zawsze nieaktywną, co daje ten sam efekt, jakby została całkowicie usunięta.

ADD_TRANS_TRIGGER ~PRIILMU~ 3 ~False()~ DO 0

7. Informacje dodatkowe (warunki i akcje skryptowe, uproszczony zapis składni, specyficzne uwagi do funkcjonowania dialogów grze, użycie pliku D w instalce WeiDU)

7.1. Wybrane, najczęściej używane warunki i akcje skryptowe w dialogach

Każdą instrukcję warunku lub akcji używaną w skryptach gier IE można użyć w pliku dialogu i vice versa, ale niektóre instrukcje ze względu na swoje działanie używane są prawie wyłącznie w dialogach.

Kilka instrukcji w ramach jednego warunku połączonych jest domyślnie logicznym AND, czyli wszystkie muszą być spełnione, by cały warunek bloku lub linii odpowiedzi był prawdziwy.

Znak "!" można postawić niemal przed każdą instrukcją warunku i stanowi on odpowiednik logicznego NOT, czyli neguje warunek (warunek ze znakiem "!" jest prawdziwy, kiedy bez znaku "!" byłby fałszywy i odwrotnie).

Parametry w warunkach i akcjach określane są z punktu widzenia postaci, której dialog jest aktywowany, np. jeśli zagadniemy BN-a postacią z naszej drużyny, to "Myself" (ja, mnie) oznacza BN-a a "LastTalkedToBy" (ostatni rozmówca) postać z drużyny gracza.

UWAGA! Także w linii odpowiedzi, choć tekst po "REPLY" odnosi się do postaci gracza, parametry w akcjach skryptowych po "DO" muszą być ustalone z pozycji BN-a!

Po bardziej szczegółowe informacje odsyłam do poradników skryptowania i bazy IESDP


Przydatne instrukcje warunków:

NumTimesTalkedTo(0) – postać dotąd nie rozmawiała, inaczej: jest to pierwsze zagadnięcie tej postaci

NumTimesTalkedToGT(30) – postać została zagadnięta (aktywowała plik dialogu) już ponad 30 razy

Global("ImoenFlirt","GLOBAL",1) – zmienna "ImoenFlirt" ma dokładnie wartość 1

!Global("ImoenFlirt","GLOBAL",1) – zmienna "ImoenFlirt" ma wartość różną od 1

GlobalGT("ImoenFlirt","GLOBAL",1) – zmienna "ImoenFlirt" ma wartość większą niż 1

GlobalLT("ImoenFlirt","GLOBAL",1) – zmienna "ImoenFlirt" ma wartość mniejszą niż 1

GlobalGT("CHAPTER","GLOBAL",2) – w grze jest aktualnie rozdział wyższy niż drugi (czyli trzeci lub wyższy)

GlobalLT("Chapter","GLOBAL",5) – w grze jest aktualnie rozdział niższy niż piąty (czyli czwarty lub niższy)

GlobalTimerExpired("AvalQuestHurryUp","GLOBAL") – licznik czasu "AvalQuestHurryUp" zakończył odliczanie

GlobalTimerNotExpired("AvalQuestHurryUp","GLOBAL") – licznik czasu "AvalQuestHurryUp" biegnie

!GlobalTimerExpired("AvalQuestHurryUp","GLOBAL") – licznik czasu "AvalQuestHurryUp" biegnie lub w ogóle nie był ustawiony

!GlobalTimerNotExpired("AvalQuestHurryUp","GLOBAL") – licznik czasu "AvalQuestHurryUp" zakończył odliczanie lub w ogóle nie był ustawiony

RandomNum(4,1) – losowa liczba z zakresu 1-4 ma wartość 1 (czyli warunek losowo prawdziwy w 1/4 przypadków)

Dead("Imoen") – postać mająca nazwę skryptową (script name, zwana też death variable) "Imoen" jest martwa

InParty("Imoen") – postać mająca nazwę skryptową "Imoen" jest w drużynie

Exists("Ramazith") – postać mająca nazwę skryptową "Ramazith" istnieje na planszy, na której przebywa drużyna gracza

Gender(Player1,MALE) – główna postać gracza jest mężczyzną, (parametr FEMALE dla kobiety)

Class(LastTalkedToBy,FIGHTER) – nasz rozmówca, który zagadnął BN-a, jest jednoklasowym wojownikiem (nazwy klas w pliku CLASS.IDS zasobów gry)

Alignment(Player1,LAWFUL_NEUTRAL) – charakter głównej postaci jest praworządny dobry (w niezmodowanym BG I część nazw charakterów nie działa prawidłowo)

CheckStatGT(Player1,9,CHR) – charyzma głównej postaci jest wyższa niż 9 (czyli 10 lub więcej; inne nazwy statystyk: STR – siła, DEX – zręczność, CON – kondycja, INT – inteligencja, WIS – mądrość i wiele innych w pliku STATS.IDS zasobów gry)

CheckStatLT(Player1,15,STR) – siła głównej postaci jest niższa niż 15 (czyli 14 lub niższa)

StateCheck(Myself,STATE_CHARMED) – rozmówca jest pod wpływem zauroczenia (nazwy możliwych stanów postaci w pliku STATE.IDS)

ReactionLT(LastTalkedToBy,FRIENDLY_LOWER) – reakcja BN-a wobec rozmówcy z naszej strony jest niższa niż przyjazna (czyli neutralna lub wroga)

ReactionGT(LastTalkedToBy,NEUTRAL_UPPER) – reakcja BN-a wobec rozmówcy z naszej strony jest wyższa niz neutralna (czyli przyjazna)

PartyGoldGT(99) – drużyna ma więcej niż 99 sztuk złota (czyli 100 lub więcej)

PartyGoldLT(100) – drużyna ma mniej niż 100 sztuk złota (czyli 99 lub mniej)

PartyHasItem("AVLet01") - drużyna posiada przedmiot AVLet01.ITM

True() – zawsze prawdziwy, zwykle ustawiany jako ostatni warunek bloku, by aktywacja dialogu zawsze inicjowała standardową rozmowę/wypowiedź, jeśli inne warunki bardziej specyficznych rozmów nie są spełnione

False() – zawsze fałszywy, służy do "wyłączania" przeszkadzających bloków lub linii odpowiedzi w istniejących już dialogach, gdy je modyfikujemy


Przydatne instrukcje akcji:

SetGlobal("ImoenFlirt","GLOBAL",1) – ustawia zmienną "ImoenFlirt" na 1, niezależnie od jej dotychczasowej wartości

IncrementGlobal("ImoenFlirt","GLOBAL",1) – zwiększa dotychczasową wartość zmiennej "ImoenFlirt" o 1 (można też stosować wartości ujemne; zmienne nigdy wcześniej nie ustawione mają domyślnie wartość 0)

SetGlobalTimer("AvalQuestHurryUp","GLOBAL",ONE_DAY) – ustawia licznik czasu "AvalQuestHurryUp" na jeden dzień czasu gry (7200 sekund przy framerate=30)

JoinParty() – przyłącza rozmówcę do drużyny (tylko w linii odpowiedzi kończącej dialog, inaczej pojawi się Biff Niedouczony)

LeaveParty() – odłącza rozmówcę z drużyny (tylko w linii odpowiedzi kończącej dialog, inaczej j/w)

EscapeArea() – rozmówca ucieka i znika definitywnie z planszy (ma sens tylko w linii odpowiedzi kończącej dialog)

Enemy() – rozmówca staje się wrogi

RunAwayFrom(LastTalkedToBy,60) – rozmówca będzie uciekał od naszego adwersarza przez 60 cykli AI (4 sekundy przy framerate=30, ma sens tylko w linii odpowiedzi kończącej dialog)

AddexperienceParty(500) – dodaje drużynie 500 punktów doświadczenia

ReputationInc(-1) – obniża reputację drużyny o 1 (można stosować wartości dodatnie i ujemne)

GiveItem("PLAT05","Imoen") – daje postaci o script name "Imoen" przedmiot PLAT05.ITM, jeśli rozmówca ma go w inwentarzu, inaczej nic się nie dzieje

GiveItemCreate("AVLet01","Imoen",1,0,0) – tworzy i daje postaci o script name "Imoen" przedmiot AVLet01.ITM z jednym ładunkiem dla pierwszej własności. Nie ma znaczenia, czy rozmówca go posiada. Dla przedmiotów bez ładunków ulegających zużyciu wartości liczbowe są nieistotne, można wstawić "0,0,0".

TakePartyItem("AVLet01") – zabiera drużynie jeden egzemplarz (lub jeden stos, jeśli przedmiot można składać) AVLet01.ITM, przeszukując inwentarze członków drużyny w kolejności od Player1 do Player6

GivePartyGold(300) – rozmówca daje drużynie do 300 sztuk złota, jeśli je posiada

GiveGoldForce(300) – tworzy i daje drużynie 300 sztuk złota, niezależnie ile rozmówca go posiada

TakePartyGold(300) – zabiera drużynie do 300 sztuk złota (jeśli drużyna je posiada) i oddaje rozmówcy

Kill("AVAL01") – zabija natychmiast postać o nazwie skryptowej "AVAL01"

CreateCreature("FLAMEN",[-1.-1],0) – tworzy jak najbliżej rozmówcy istotę FLAMEN.CRE


7.2. Uproszczony zapis dialogów

Zamiast najczęściej powtarzającej się frazy "IF ~~ THEN REPLY" WeiDU umożliwia użycia skrótu "++". Analogicznie zamiast "GOTO" można użyć "+". W ten sposób blok z pierwszego przykładu tutoriala:

IF ~Global("TamokoMove","GLOBAL",0)~ THEN BEGIN 0

SAY ~Przepraszam za to, że skrywam pewne sprawy, ale czasy są niespokojne. Gdybyście znaleźli dla mnie chwilę czasu może usłyszelibyście coś, co... chcielibyście usłyszeć. Może moglibyśmy sobie pomóc nawzajem?~

IF ~~ THEN REPLY ~Skąd wiesz co chcemy wiedzieć? Nie chcemy informacji od szpiegów kryjących się w mroku.~ GOTO 1

IF ~~ THEN REPLY ~Jeśli twoja informacja jest coś warta, to owszem, chętnie posłucham.~ GOTO 3

IF ~~ THEN REPLY ~Chciałbym wiedzieć z kim mówię. "Nie biorę cukierków od obcych", jak to powiadają.~ GOTO 2

END


można zapisać też tak:

IF ~Global("TamokoMove","GLOBAL",0)~ THEN BEGIN 0

SAY ~Przepraszam za to, że skrywam pewne sprawy, ale czasy są niespokojne. Gdybyście znaleźli dla mnie chwilę czasu może usłyszelibyście coś, co... chcielibyście usłyszeć. Może moglibyśmy sobie pomóc nawzajem?~

++ ~Skąd wiesz co chcemy wiedzieć? Nie chcemy informacji od szpiegów kryjących się w mroku.~ + 1

++ ~Jeśli twoja informacja jest coś warta, to owszem, chętnie posłucham.~ + 3

++ ~Chciałbym wiedzieć z kim mówię. "Nie biorę cukierków od obcych", jak to powiadają.~ + 2

END


Jeżeli linia odpowiedzi posiada warunek "IF ~warunek~ THEN REPLY" to również można użyć skrótu "+ ~warunek~ +". Na przykład równorzędny zapis stanowią:

IF ~Dead("BASSILUS")~ THEN REPLY ~Obecnie nie potrzebujemy pomocy.~ EXIT

+ ~Dead("BASSILUS")~ + ~Obecnie nie potrzebujemy pomocy.~ EXIT


Istotne dla możliwości użycia skrótu jest obecność tekstu w linii odpowiedzi (REPLY). Odpowiedź bez tekstu jak poniżej nie może być skracana (konstrukcja typu "++ EXIT" jest błędna!):

IF ~~ THEN EXIT


7.3. Aktywacja dialogów w grze, specyficzne dialogi NPC-ów przyłączalnych do drużyny

Aby lepiej zrozumieć ideę dialogów i plików D konieczne jest poznanie, jak dialogi są przypisywane postaciom i aktywowane w grze. W grach IE istnieją zasadniczo trzy rodzaje dialogów;


7.3.1. Plik dialogu zawierający "zwykłe" rozmowy dostępny dla każdej postaci z gry (także zwierząt i potworów):

W pliku CRE definiującym postać gry jest jedno miejsce na wpisanie nazwy pliku DLG, zawierającego przypisany postaci zestaw dialogu/rozmów.

Aktywacja dialogu następuje, kiedy klikniemy na danego BN-a a postać jest neutralna, nie da się w ten sposób aktywować dialogu, gdy postać jest wroga, w BG I także nie, gdy jest członkiem naszej drużyny (w BG II i PST można w ten sposób zainicjować rozmowę z członkiem drużyny). Engine gry zablokuje możliwość rozmowy, jeśli któryś z rozmówców nie jest zdolny jej prowadzić.

Drugą możliwością jest wymuszenie aktywacji dialogu poprzez instrukcję skryptową akcji ze skryptu lub innego dialogu. W tym wypadku nie ma znaczenia, czy istota jest wroga, neutralna czy przyjazna. Mamy kilka opcji:

 a.  Dialog([PC]) – postać podejdzie do najbliższego członka drużyny i rozpocznie z nim dialog (zdefiniowany w CRE)

 b.  StartDialogueNoSet([PC]) – postać, z miejsca gdzie stoi, rozpocznie dialog (zdefiniowany w CRE) z najbliższym członkiem drużyny

 c.   StartDialog("inny_DLG",[PC]) – postać, z miejsca gdzie stoi, rozpocznie dialog zdefiniowany w instrukcji (tutaj inny_DLG.DLG) z najbliższym członkiem drużyny, niezależnie jaki dialog jest wpisany w CRE

 d.  jeżeli chcemy, aby postać rozmawiała tylko z głównym bohaterem a nie dowolnym członkiem drużyny, zamiast [PC] należy użyć Player1

 e.  w przypadku wymuszenia rozmowy skryptem należy samemu w warunkach bloku skryptu aktywujacego dialog zadbać, aby rozmówcy byli zdatni do rozmowy (nie martwi, uśpieni, uciszeni, za daleko, nie w trakcie walki itd.), szczegóły wkraczają już na teren poradnika skryptowania, nie będę wiec ich omawiał.

Sama aktywacja dialogu nie wystarcza, aby zaczęła się rozmowa. Plik dialogu musi jeszcze zawierać blok z aktualnie spełnionymi warunkami, inaczej widać animację postaci, jakby chciała się do kogoś odezwać, ale nic więcej się nie dzieje.

W przypadku istot wprowadzanych do gry przez wpis do własności lokacji gry (plik ARE), tzw. Actors, w pliku lokacji można zdefiniować specyficzny zestaw skryptów i dialog, które nadpisują ustawienia w pliku postaci CRE. W ten sposób postać użyta na różnych planszach może mieć za każdym razem inny dialog, odmienny od pierwotnie ustawionego w CRE. Przywoływanie postaci przez wpis do pliku lokacji wykorzystywany jest głównie przez oryginalna grę. Mody dodają nowe postacie na istniejących już planszach przez skrypt lokacji, nie trzeba wtedy modyfikować pliku ARE, co jest dużo trudniejsze niż rozszerzenie skryptu.

Dialog przypisany w pliku CRE można zmienić instrukcją skryptową SetDialog("nowy_DLG"). Od tego momentu postać będzie używała pliku nowy_DLG.DLG zamiast poprzedniego pliku DLG. Instrukcja używana rzadko, specyficznie u postaci przyłączalnych w BG I, o czym poniżej.

W przypadku NPC-ów (postaci przyłączalnych do drużyny) sprawa się mocno komplikuje. Nadal jednoczasowo postać używa tylko jednego pliku DLG wpisanego do CRE, ale ustawiony dialog zmienia się zależnie od statusu postaci. Dodatkowo zasady różnią się w poszczególnych grach:

 a.  W BG I niezależnie od początkowego dialogu ustawionego w CRE, plik pdialog.2da definiuje dwa dodatkowe pliki dialogów: JoinDialog używany po przyłączeniu postaci do drużyny oraz PostDialog używany po odłączeniu postaci z drużyny. Engine BG I przy przyłączeniu NPC-a do drużyny automatycznie podmienia w CRE starą nazwę dialogu na JoinDialog wpisaną w pdialog.2da, nie robi tego jednak przy odłączeniu NPC-a. Dlatego dialog odłączenia znajduje się w pliku JoinDialog, który kończy się instrukcją SetDialog("PostDialog") – nazwy JoinDialog i PostDialog oczywiście specyficzne dla danego NPC-a

 b.  W BG II jest podobnie, ale plik pdialog.2da zawiera wpis o dwóch zestawach plików JoinDialog i PostDialog – osobne dla SoA i ToB. Ponadto engine BG II automatycznie także podstawia PostDialog w pliku CRE przy odłączaniu NPC-a z drużyny. Dlatego oba dialogi na okoliczność przyłączenia i odłączenia znajdują się w PostDialog, nie ma potrzeby stosowania instrukcji SetDialog, a do rozróżniania obu sytuacji używa się specjalnej zmiennej

 c.   W PsT każdy NPC posiada tylko jeden ogromny plik dialogu zawierający rozmowy przed przyjęciem do drużyny, w drużynie i po odłączeniu. Być może plik pdialog.2da jest funkcjonalny, ale nie jest używany, nie ma żonglerki Dialog > JoinDialog > PostDialog.


7.3.2. Plik dialogu zawierający "bantery", czyli pogaduchy między członkami drużyny:

Nie jest nigdzie przypisany w pliku CRE. Odpowiednie pliki banterów dla poszczególnych NPC-ów zdefiniowane są przez wpis w interdia.2da.

Banter odbywa się na tych samych zasadach, co dialog miedzy dwoma BN-ami, czyli każdy z NPC-ów posiada swoje wypowiedzi we własnym pliku DLG banterów i przekazują sobie na zmianę inicjatywę instrukcją EXTERN. Napisanie banteru miedzy dwoma NPC-ami z użyciem klasycznych bloków dialogowych jest żmudne, dlatego najlepiej użyć konstrukcji CHAIN.

Engine gry inicjuje co jakiś czas losowo banter miedzy dwoma członkami drużyny. Jeżeli jednak postacie nie maja ułożonego miedzy sobą dialogu w pliku banterów, to nic się nie stanie. Gra BG I inicjuje bantery równie często, co BG II. To, że rozmowy-bantery występują w BG I znacznie rzadziej niż w BG II, wynika jedynie z faktu, ze jest ich znacznie mniej i zostały ułożone tylko dla niewielkiego odsetka kombinacji par NPC-ów.

Drugą możliwością jest aktywacja banteru za pomocą instrukcji akcji skryptowej. Na tej zasadzie działają mody przyspieszające bantery i zawierające romanse. Np. jeżeli chcemy wymusić aktywację napisanego przez nas banteru miedzy Edwinem a Imoen, w skrypcie Edwina należy dodać blok z instrukcją akcji Interact("Imoen"). Ponieważ plik DLG z banterami Edwina może zawierać wiele rozmów-banterów z różnymi NPC-ami, blok zaczynający banter z Imoen powinien zawierać warunek InteractingWith("Imoen"). W przypadku wymuszenia banteru skryptem należy samemu w warunkach bloku skryptu aktywującego banter zadbać, aby rozmówcy byli zdatni do rozmowy.


7.3.3. Plik dialogu zawierający "rumors", czyli pogłoski wyświetlane przy piciu alkoholi w karczmach i ofiarach na tacę w światyniach:

Nazwa pliku DLG wpisana jest w odpowiednim miejscu pliku STO, definiującego usługi sklepów, tawern, gospod i świątyń.

Pliki DLG zawierające "rumors" działają odmiennie niż dialogi istot: spośród wszystkich bloków zawierających spełniony warunek wybierany jest jeden losowo (a nie bloki sprawdzane są kolejno) i wypowiedź (SAY) bloku wyświetlana jest w okienku pogłosek ekranu gospody/światyni. Dlatego bloki, które nie mają szczególnych warunków wystąpienia, opatrywane są instrukcją warunku ~True()~. Może być więcej niż jeden blok z warunkiem ~True()~ i mogą być one stosowane na początku pliku DLG. Każdy blok pliku pogłosek zawiera odpowiedni wpis do dziennika i kończy się instrukcją EXIT, nie zawiera natomiast tekstu odpowiedzi (REPLY).

Oto przykładowy fragment pliku pogłosek z Wrót Baldura:

BEGIN ~RBALDU~


IF ~True()~ THEN BEGIN 0

SAY #14701 /* ~Dużo słyszałem o wzmacnianiu armii. Angelo, nowy dowódca Pięści wysłał wielu żołnierzy na południe do Beregostu. Obawiam się, że nadchodzi wojna.~ */

IF ~~ THEN DO ~~ JOURNAL #14924 /* ~Angelo, nowy przywódca Pięści, wysłał mnóstwo żołnierzy do Beregostu. Ludzie boją się, że może wybuchnąć wojna.~ */ EXIT

END


IF ~GlobalGT("Chapter","GLOBAL",6)~ THEN BEGIN 1

SAY #14702 /* ~W czasie następnej elekcji zapewne zwycięży Sarevok. Ma wielkie poparcie szlachty, a oni mają najwięcej do gadania.~ */

IF ~~ THEN DO ~~ JOURNAL #14927 /* ~Sarevok jest bardzo popularny wśród szlachty, co może mu dopomóc w zdobyciu tytułu książęcego we Wrotach Baldura.~ */ EXIT

END


IF ~GlobalGT("Chapter","GLOBAL",6)~ THEN BEGIN 2

SAY #14703 /* ~Słyszeliście o morderstwach w Candlekeep? Wygląda na to, że przywódcy Żelaznego Tronu zostali tam zabici. Dokonali tego ci sami ludzie, którzy pomogli w kopalniach Nashkel. Mówią, że to agenci z Amnu. Tak czy inaczej syn jednego z zabitych został nowym przywódcą. Mówią, że ma na imię Sarevok.~ */

IF ~~ THEN DO ~~ JOURNAL #16316 /* ~Sarevok przejął dowodzenie nad Żelaznym Tronem. Ciągle oskarżają nas o zabicie poprzednich przywódców, a nawet myślą, że jesteśmy agentami Amnu. Komuś zależy, aby wybuchła wojna.~ */ EXIT

END


IF ~True()~ THEN BEGIN 3

SAY #14704 /* ~Dziwne rzeczy się dzieją, od kiedy Żelazny Tron zajął tę posiadłość na południowym-zachodzie. Nie wiem co takiego jest w tym budynku i chyba nie chciałbym wiedzieć.~ */

IF ~~ THEN DO ~~ JOURNAL #16317 /* ~Coś dziwnego dzieje się w nowej siedzibie Żelaznego Tronu na południowy-zachód od Wrót Baldura.~ */ EXIT

END

IF ~GlobalGT("Chapter","GLOBAL",6)~ THEN BEGIN 4

SAY #14705 /* ~Niedobrze się ostatnio dzieje. Najpierw sprawa Entara Srebrnej Tarczy, potem zachorował Eltan, teraz śmierć Scara. Coś się szykuje i to nic dobrego.~ */

IF ~~ THEN DO ~~ JOURNAL #16318 /* ~Jest całkiem możliwe, że wydarzenia, w których udział brali Entar Srebrna Tarcza, Eltan i Scar są w jakiś sposób połączone. Przynajmniej tak mówi się w mieście.~ */ EXIT

END


7.4. Wstawianie dialogów (plików D) do gry

Samo stworzenie pliku D, choć zajmuje najwięcej czasu, to jeszcze nie koniec pracy. Zaplanowane dialogi trzeba jeszcze jakoś umieścić w grze.


7.4.1. Kompilacja pliku D z linii komend Windows

WeiDU.EXE i plik D musza znajdować sie w tym samym katalogu gry, do której został stworzony dialog. Z linii komend wpisujemy:

WeiDU Dialog.D --tlkout DIALOG.TLK --ftlkout DIALOGF.TLK


W efekcie zostanie utworzony plik Dialog.DLG oraz nowe teksty (stringi) zostaną dodane do Dialog.TLK i DialogF.TLK. Wystarczy teraz przenieść utworzony plik DLG do folderu OVERRIDE gry i już nowy dialog znajduje się w zasobach gry.

W przypadku pominięcia –tlkout też zostanie utworzony plik Dialog.DLG, ale będzie bezużyteczny, bo będzie zawierał strrefy do nieistniejących tekstów (nie dodanych do plików TLK). Mimo to instrukcja jest użyteczna do testowania poprawności plików D. Jeżeli w wyniku instrukcji powstanie niezerowej długości plik DLG, to znaczy, że plik D nie zawiera błędów krytycznych, uniemożliwiających kompilację. Kompilacja pliku D wykrywa więcej błędów niż traifikacja. Jednocześnie mamy pewność, że pliki Dialog.TLK i DialogF.TLK w trakcie testów pozostaną niezmienione.

WeiDU Dialog.D

Aby skompilować plik D, który został straifikowany, należy podać także plik TRA w komendzie:

WeiDU Dialog.D –transin Dialog.TRA --tlkout DIALOG.TLK --ftlkout DIALOGF.TLK

Lub prościej:

WeiDU Dialog.D Dialog.TRA --tlkout DIALOG.TLK --ftlkout DIALOGF.TLK

Jeżeli pliki D i TRA znajdują się w innym folderze niż WeiDU.exe (np. w podfolderze moda) należy podać także ścieżkę dostępu:

WeiDU MyMod\Dialogi\Dialog.D MyMod\Language\Polski\Dialog.TRA --tlkout DIALOG.TLK --ftlkout DIALOGF.TLK


7.4.2. Komendy pliku TP2 w instalce WeiDU

Aby skompilować plik D stanowiący część moda WeiDU, należy dodać do pliku instalacyjnego TP2 następujący wiersz poleceń:

COMPILE MyMod\Dialogi\Dialog.D

Dodanie nowych tekstów do plików TLK odbywa się domyślnie i nie trzeba go wymuszać specjalnym parametrem bądź instrukcją. Taka forma obowiązuje także w przypadku modyfikacji zawierających wersje językowe i straifikowane pliki dialogów. Tylko w przypadku braku flagi AUTO_TRA w nagłówku pliku TP2 konieczne jest wskazanie pliku TRA z folderu wybranego języka:

COMPILE MyMod\Dialogi\Dialog.D  USING Dialog.TRA

Samo wstawienie dialogu do zasobów gry nie oznacza jeszcze, że go w grze zobaczymy. Musimy jeszcze zapewnić możliwość jego aktywacji w grze, przez przypisanie dialogu do postaci (pliku CRE), dodanie wpisu do pdialog.2da lub interdia.2da, dodanie skryptu aktywującego dialog itp.

Poniższe instrukcje w TP2 na bazie oryginalnego pliku FFHUNT.CRE tworzą nowy plik Z!FFHNT1.CRE na potrzeby moda, modyfikując imię, Script Varible i odnośniki do skojarzonego pliku skryptu i dialogu:

COPY_EXISTING ~FFHUNT.CRE~ ~override/Z!FFHNT1.CRE~ //Dowódca patrolu

SAY NAME1 @1661

SAY NAME2 @1661

WRITE_ASCII DEATHVAR ~Z!FFHNT1~ (9)

WRITE_ASCII SCRIPT_CLASS ~Z!FFHNT1~

WRITE_ASCII DIALOG ~Z!FFHUNT~


Wiersz poleceń w TP2 dodający do pdialog.2da wpis przyporządkowujący NPC-owi o Script Name "amorth" JoinDialog "amorthJ.DLG" i PostDialog "amorthP.DLG":

APPEND ~pdialog.2da~ ~amorth amorthP amorthJ~ UNLESS ~amorth~


Wiersz poleceń w TP2 dodający do interdia.2da wpis przyporządkowujący NPC-owi o Script Name "amorth" dialog banterów "Bamorth.DLG":

APPEND ~interdia.2da~ ~amorth Bamorth~ UNLESS ~amorth~


7.4.3. Minimalistyczna ale kompletna, działająca instalka moda WeiDU

Plik D oraz kod w TP2, kompilujący dialog i wprowadzający go do gry, to jeszcze nie cały działający mod. Zakładamy, że tworzymy mod o nazwie "MyMod" w głównym folderze BG I:

Umieszczamy plik WeiDU.exe (najlepiej najnowszą wersję) i przemianujemy na "MyMod-Setup.exe"

Zakładamy folder "MyMod", umieszczamy w nim plik "Dialog.D" oraz tworzymy podfolder "BACKUP"

Tworzymy edytorem tekstu plik "MyMod-Setup.TP2" w tym samym folderze co "MyMod-Setup.exe" lub możemy go schować do folderu "MyMod". Wpisujemy w nim następującą treść:

BACKUP ~MyMod/BACKUP~

AUTHOR ~(wizytówka autora, zwykle nick i e-mail)~

BEGIN ~(oficjalna nazwa modyfikacji)~


/* od tego miejsca kod kompilujący i wprowadzający dialog do gry jak w punkcie 7.4.2 */

Jeżeli nie ma żadnego błędu, uruchomienie "MyMod-Setup.exe" spowoduje zainstalowanie naszego dialogu w grze. Gratulacje!