by Marcin
Wersja 2.0



I. Co to jest cutscenka?



Cutscenka jest to zaplanowane zdarzenie warunkowane właściwym skryptem.

W grze znajduje się bardzo wiele Cutscenek. Cutscenkami są sny z Irenicusem, czy choćby torturowanie na początku gry przez Irenicusa.



II. Jak zrobić cutscenkę?



1. Cutscenkę tworzymy jak zwykły skrypt. Możemy napisać ją w notatniku, lub w DLTCEP, ale ja polecam Infinity Script editor. Przeznaczony jest właśnie do pisania skryptów i gwarantuje łatwy dostęp do plików IDS.

2. Cutscenka jak każdy skrypt składa się z bloku:



IF
Tutaj będą warunki, jakie muszą być spełnione
THEN
RESPONSE #100
Tutaj piszemy akcje, jakie mają wykonywać różne postacie
END


III. Jak rozpocząć tworzenie cutscenki.



Najczęściej cutscenki są aktywowane poprzez odpowiednią komendę w dialogu lub innym skrypcie.


W skrypcie:



IF
Global("cutscenka","GLOBAL",0) // Warunki konieczne do rozpoczęcia cutscenki
THEN
RESPONSE #100
StartCutSceneMode() // blokuje mysz, klawiaturę i powoduje zniknięcie wszystkich pasków interfejsu
ClearAllActions() //Wstrzymuje wszystkie akcje obecnie wykonywane (np.: atak, rzucanie czaru, chód)
StartCutScene("cutscenka1") //odwołanie do określonego pliku w którym mamy cutscenkę.
SetGlobal("cutscenka","GLOBAL",1) // zmiana wartości zmiennej.
END

Tutaj warunkiem jest zmienna która tylko gdy jest ustawiona na 0 powoduje start cutscenki


W dialogu:



IF ~~ THEN BEGIN cutscenka1
SAY ~tekst.~
IF ~~ THEN REPLY ~tekst.~ DO ~ StartCutSceneMode()
ClearAllActions()
StartCutScene("cutscenka1") ~ EXIT
END

Tutaj piszemy odwołanie do konkretnego pliku wcześniej blokując mysz i klawiaturę oraz przerywając wszystkie akcje.


Najważniejsze komendy, które towarzyszą zawsze cutscenkom to:



StartCutSceneMode() // blokuje mysz, klawiaturę i powoduje zniknięcie wszystkich pasków interfejsu

ClearAllActions() //Wstrzymuje wszystkie akcje obecnie wykonywane (np.: atak, rzucanie czaru, chód)

StartCutScene("cutscenka1") //odwołanie do określonego pliku w którym mamy cutscenkę.



IV. Następnie tworzymy skrypt w którym będą wszystkie akcje budujące nasza cutscenkę.



Zamieszczam tu przykładową cutscenkę pochodzącą z nowej wersji Larshy. Pokazuje ona porwanie Larshy.



IF
True() // wszystkie warunki są spełnione
THEN
RESPONSE #100
CutSceneId("kasnimf1") // cutscenkę kontroluje kasnimf1, do niej odnoszą się wszystkie akcje. Jest to komenda zaczynająca każda cutscenke. Istota ją kontrolująca powinna się znajdować na obszarze, gdzie ma się odbywać cutscenka.
FaceObject("Larsha") // kasnimf1 obraca się w stronę Larshy
MoveViewObject("kasnimf1",INSTANT) // Kamera natychmiast przeskakuje w miejsce gdzie znajduje się kasnimf1
SmallWait(10) // To oznacza, że jest chwilowa przerwa, 1/10 sekundy, jest to zauważalne przez gracza.
DisplayStringHead(Myself,~Nie obronisz się przede mną driado! Już po Tobie.~) // tekst wyświetla się nad postacią
CreateVisualEffectObject("SPCMWOUI",Myself) //specjalny efekt animacji
ForceSpell("Larsha",CUTSCENE_DAMAGE_1B) //kasnimf1 rzuca na Larshę zaklęcie
SmallWait(1)
SetGlobal("porwanie","GLOBAL",4) // zmiana wartości zmiennej
SetGlobal("czas","GLOBAL",1)
ActionOverride("Larsha",SetInterrupt(FALSE)) // zablokowanie możliwości interakcji z Larshą, jest to konieczne dla tej cutscenki.
SmallWait(1)
ActionOverride("Larsha",LeaveParty()) //Larsha odłącza się od drużyny, a dzięki zablokowaniu możliwości interakcji nie podchodzi do nas z gadką na odejście z drużyny.
ActionOverride("Larsha",DropInventory()) //Larsha opuszcza na ziemie cały ekwipunek
SmallWait(1)
ActionOverride("Larsha",PlayDead(85)) // Teraz Larsha udaje martwą
Wait(3) // 3 sekundy przerwy
DisplayStringHead("Larsha",~ ratuj mnie!~) // tekst wyświetla się nad Larshą
ForceSpell("Larsha",DRYAD_TELEPORT) // kasnimf1 rzuca czar na larshę, który powoduje znikniecie jej z mapy
SmallWait(20)
MoveToObject("larsha") //kasnimf1 podchodzi do larshy
Wait(1)
DestroySelf() // kasnimf1 ulega zniszczeniu bez pozostawiania ciała ani ekwipunku
EndCutSceneMode() //zakończenie cutscenki odblokowanie klawiatury i myszki, ukazanie z powrotem pasków interfejsu.
END

Z tej cutscenki także można wybrać akcje, które zawsze występują w cutscenkach np.:


CutSceneId("kasnimf1") - w nawiasie podajemy death variable postaci, która ma kontrolować cutscenkę i do której odnoszą się wszystkie akcje bezpośrednio. Rozpoczyna każdą cutscenkę


EndCutSceneMode() - zakończenie cutscenki, konieczne, bo inaczej wciąż klawiatura i myszka pozostaną zablokowane, a my nie będziemy mogli grać dalej. Zakańcza każda cutscenkę.


W tej cutscence pojawiła się niezwykle ważna komenda


ActionOverride(O:Actor*,A:Action*) - jest ona bardzo przydatna w tworzeniu cutscenek, sprawia, że, pomimo że cutscenkę kontroluje jedna postać to może ona kazać innym wykonywać jakieś akcje.


W Actor wpisujemy death variable postaci, która ma wykonać jakąś akcję znajdującą się w action. Np.:


ActionOverride("Larsha",LeaveParty()) pomimo że Larsha nie kontroluje bezpośrednio cutscenki to dzięki ActionOverride ma nakazane, że ma się odłączyć od drużyny.



V. Dlaczego ActionOverride jest bardzo ważne i pomocne



Dzięki ActionOverride możemy umieścić całą cutscenkę w jednym bloku, co jest korzystniejsze niż umieszczać ją w kilku np.:



IF
True() // wszystkie warunki są spełnione
THEN
RESPONSE #100
CutSceneId("kasnimf1") // cutscenkę kontroluje kasnimf1, do niej odnoszą się wszystkie akcje..
FaceObject("Larsha")
MoveViewObject("kasnimf1",INSTANT)
SmallWait(10)
DisplayStringHead(Myself,~Nie obronisz się przede mną driado! Już po Tobie.~)
CreateVisualEffectObject("SPCMWOUI")
ForceSpell("Larsha",CUTSCENE_DAMAGE_1B)
SmallWait(1)
SetGlobal("porwanie","GLOBAL",4)
SetGlobal("czas","GLOBAL",1).
SmallWait(1)
SmallWait(1)
Wait(3)
ForceSpell("Larsha",DRYAD_TELEPORT)
SmallWait(20)
MoveToObject("larsha")
Wait(1)
DestroySelf()
EndCutSceneMode()
END


IF
True() // wszystkie warunki są spełnione
THEN
RESPONSE #100
CutSceneId("Larsha") // cutscenkę kontroluje Larsha, do niej odnoszą się wszystkie akcje..
SmallWait(10)
SetInterrupt(FALSE)
SmallWait(1)
LeaveParty()
DropInventory()
SmallWait(1)
PlayDead(85)
Wait(3)
DisplayStringHead("Larsha",~ ratuj mnie~)
SmallWait(20)
Wait(1)
END

Ta cutscenka odpowiada dokładnie tamtej, tyle, że tworzymy 2 osobne bloki, a w każdym są akcje dotyczące danej postaci.


Ten typ jest zawsze bardzo niepewny, co do swojej poprawności. W tym przypadku jest poprawnie, ale bardzo łatwo się pomylić, zwłaszcza, jeśli chodzi o czas pomiędzy akcjami. W rozdzielonych blokach może się okazać, że postacie za sobą nie nadążają, bo źle rozmieściliśmy odstępy pomiędzy akcjami.


Dzięki możliwości korzystania z ActionOverride możemy połączyć wszystko w jeden blok i mamy pewność, że wszyscy zakończą swoje akcje równocześnie.


Co do akcji, jakie mogą wystąpić w cutscenkach nie będę się rozpisywał, bo do tego trzeba poznać działanie większości plików IDS. W wyżej przedstawionej cutscence zamieściłem tylko te najczęściej używane.



VI. Różne dziwne rzeczy w cutscenkach.



W cutscenkach można stosować także rozpoczęcia dialogów.

Można wtedy tworzyć ciekawe połączenia pomiędzy Cutscenkami a dialogami.


Np.:



IF
Global("cutscenka","GLOBAL",0)
THEN
RESPONSE #100
StartCutSceneMode()
ClearAllActions()
SetGlobal("cutscenka","GLOBAL",1)
SmallWait(10)
MoveToObject(player1)
StartDialogueNoSet(Player1) // rozpoczęcie dialogu z player1, czyli dzieckiem Bhaala
END

Ten skrypt musi być przypisany postaci, która wykona te akcje. Oczywiście można zrobić odwołanie do konkretnego pliku cutscenki, jednak przy bardzo krótkich cutscenkach można zrobić jak wyżej zamieszczając ją w skrypcie określonej postaci do której akcje się odnoszą np: chcemy żeby Larsha wykonała wszystkie akcje jakie są zamieszczone w tym skrypcie, to ten cały blok, musi się znaleźć w skrypcie, który jest jej przypisany.


Na końcu jak widać nie ma EndCutSceneMode() jest natomiast StartDialogueNoSet(Player1) który sprawia, że zaczynamy dialog z player1.

Jeśli używamy StartDialogueNoSet(Player1) to dany dialog musi być z góry przypisany postaci, która dialog rozpoczyna. Jeśli na natomiast nie jest on przypisany to należy użyć


StartDialog("dialog",Player1) - wtedy dana postać której ta akcja dotyczy rozmawia z dzieckiem Bhaala wg Dialogu „dialog” i nie musi go mieć przypisanego.


Wraz z zakończeniem dialogu cutscenka się skończy, a jeśli chcemy, żeby się kontynuowała to należy zrobić konkretne odwołanie na końcu dialogu.



IF ~~ THEN BEGIN cutscenka1
SAY ~tekst.~
IF ~~ THEN REPLY ~tekst.~ DO ~ StartCutSceneMode()
ClearAllActions()
StartCutScene("cutscenka2") ~ EXIT
END

W ten sposób cutscenka jest kontynuowana, jednak wprowadzenie dialogu sprawia, że musimy stworzyć nowy plik. Tzn. akcje które były przed dialogiem znajdują się w skrypcie postaci (przykładowo) a następne akcje po dialogu musza się znajdować w osobnym skrypcie (w tym przypadku "cutscenka2")



VII. Teraz będzie najważniejsze, czyli tworzenie snów.



Sen składa się z 2 podstawowych elementów, a mianowicie cutscenki i nadpisania skryptu Player1d


1. Tworzymy sobie folder gdzie będą pliki naszego moda

2. Tworzymy plik tp2 o nazwie Setup-nazwamoda.tp2 (np.: za pomocą notatnika lub po prostu zmieniając nazwę już istniejącego pliku tp2 z innego moda, wtedy kasujemy także zawartość tego pliku.

3. Bierzemy z innego moda lub ściągamy Weidu i zmieniamy jego nazwę na setup-nazwamoda.exe

4. W folderze moda za pomocą Infinity Script Editor lub notatnika tworzymy plik Player1d.baf

5. Piszemy w nim:



IF
Global("sen","GLOBAL",0)
THEN
RESPONSE #100
SetGlobal("sen","GLOBAL",1)
StartCutSceneMode()
ClearAllActions()
StartCutScene("cutsen")
END

i zapisujemy

6. dzięki temu możliwe będzie odwołanie do pliku gdzie będzie cutscenka

7. teraz tworzymy następny plik o nazwie (w tym przypadku) cutsen.baf

8. zamieszamy w nim przykładową cutscenke, która będzie snem


Zamieszczam tutaj fragment pierwszego snu z Irenicusem i Imoen



IF
True()
THEN
RESPONSE #100
CutSceneId(Player1) //Player1 kontroluje cutscenkę
StorePartyLocations() //zapamiętanie obecnego położenia drużyny
FadeToColor([30.0],0) // zaciemnienie ekranu
Wait(1) //1sekunda zaciemnienia
Rest() // odpoczynek dla Player1, dzięki któremu odzyskuje wszystkie czary, leczy rany itd.
ActionOverride(Player2,Rest())
ActionOverride(Player3,Rest())
ActionOverride(Player4,Rest())
ActionOverride(Player5,Rest())
ActionOverride(Player6,Rest()) // odpoczynek reszty drużyny
Wait(1) // 2 sekunda zaciemnienia
LeaveAreaLUAPanic("AR0011","",[799.1579],0)
LeaveAreaLUA("AR0011","",[799.1579],8)
ActionOverride(Player2,LeaveAreaLUA("AR0011","",[715.1619],2))
ActionOverride(Player3,LeaveAreaLUA("AR0011","",[733.1691],14))
ActionOverride(Player4,LeaveAreaLUA("AR0011","",[801.1736],0))
ActionOverride(Player5,LeaveAreaLUA("AR0011","",[700.1730],4))
ActionOverride(Player6,LeaveAreaLUA("AR0011","",[651.1636],2)) // cała drużyna przenosi się na lokację gdzie ma się odbyć sen ( biblioteka w której się wychowaliśmy)
MultiPlayerSync()
MoveViewPoint([649.2036],INSTANT) //Natychmiastowe przesunięcie kamery do danego punkt na mapie
ApplySpell(Player2,SURE_SLEEP)
ApplySpell(Player3,SURE_SLEEP)
ApplySpell(Player4,SURE_SLEEP)
ApplySpell(Player5,SURE_SLEEP)
ApplySpell(Player6,SURE_SLEEP) // wszyscy oprócz player1 leżą na ziemi jakby spali.
Explore() // odsłonięcie całej mapy
SmallWait(5)
FadeFromColor([30.0],0) //koniec zaciemnienia, teraz widzimy gdzie jesteśmy
MoveViewPoint([799.1579],6) //Player1 przemieszcza się do konkretnego punktu
Wait(3)
MAŁA ZMIANA W SKRYPCIE
ActionOverride("Dream1",MoveToObject(Player1)) //Imoen idzie w naszą stronę
........... ciąg dalszy cutscenki............
EndCutSceneMode() //zakończenie cutscenki
RestParty() //dzięki temu mamy ten filmik jak śpimy pod namiotem lub gdzieś indziej.
END

9. Jak widać sen składa się z kilku podstawowych elementów


- sen jest cutscenką i ma wszystkie komendy, które cutscenkę charakteryzują

- w snach często pojawia się


FadeToColor([30.0],0) i FadeFromColor([30.0],0) czyli zaciemnienie i odsłonięcie ekranu, bo zazwyczaj w czasie snu gdzieś się przenosimy

- komenda odpoczynku, dzięki której nasza drużyna zapamiętuje czary i leczy rany:



Rest()
ActionOverride(Player2,Rest())
ActionOverride(Player3,Rest())
ActionOverride(Player4,Rest())
ActionOverride(Player5,Rest())
ActionOverride(Player6,Rest())

oraz z RestParty() dzięki któremu nasza drużyna odpoczywa i pojawia się filmik towarzyszący odpoczynkowi.


10. Mamy już cutscenkę i zapisujemy. Tworzymy także w folderze moda pod folder Backup (musi być on pusty)

11. Teraz tworzymy instalkę, czyli edytujemy plik .tp2 notatnikiem


I wpisujemy w niej



BACKUP ~nazwa folderu moda\Backup~
AUTHOR ~wpisujemy siebie~
BEGIN ~nazwa moda~
EXTEND_TOP ~player1d.bcs~ ~nazwa folderu moda/player1d.baf~

Dzięki temu nasz plik Player1d. baf zostanie skompilowany i dopisany do pliku player1d


COMPILE ~nazwa folderu moda /cutsen.baf~

Teraz nasza cutscenka zostanie skompilowana.


Włączamy setup-nazwamoda.exe i instalujemy.


Właśnie stworzyliśmy sen.



VIII. Co jeśli nie chcemy aktywowac cutscenki tylko zastosować coś innego np: ustawić timer.



- często jest tak, że chcemy aby dopiero po jakimś czasie sen się pojawił, wtedy trzeba ustawić w pliku player1d takze blok go warunkujący np:



IF
Global("czas","GLOBAL",0)
THEN
RESPONSE #100
SetGlobalTimer("sen1","GLOBAL",TWO_DAYS) //Ustawiamy timer na czas dwóch dni wewnątrz gry.
SetGlobal("czas","GLOBAL",1)
Rest()
ActionOverride(Player2,Rest())
ActionOverride(Player3,Rest())
ActionOverride(Player4,Rest())
ActionOverride(Player5,Rest())
ActionOverride(Player6,Rest())
RestParty() // dzięki temu odbędzie się także normalny odpoczynek, z przywróceniem zapamiętanych czarów, przyczym przy okazji zostanie ustawiony timer.
END

Teraz piszemy pod spodem



IF
Global("czas","GLOBAL",1)
GlobalTimerExpired("sen1","GLOBAL") // czas ustawiony w timerze minął.
THEN
RESPONSE #100
SetGlobal("czas","GLOBAL",2)
StartCutSceneMode()
ClearAllActions()
StartCutScene("cutsen")
END


IX. Dlaczego należy nadpisywać właśnie Player1d



Ponieważ tylko dzięki nadpisaniu tego skryptu będzie możliwe pokazanie cutscenki w postaci snu, tzn. normalnie gdybyśmy włączyli grę, by sprawdzić czy nasza cutscenka działa to byśmy zobaczyli, ze nie działa, bo niby wg warunków powinna się włączyć od razu po tym jak zaczęliśmy grę.


Dzięki temu, ze napisaliśmy plik player1d to cutscenka tworząca sen uaktywni się dopiero wtedy gdy klikniemy ikonkę odpoczynku.


Myślę, że jeszcze poprawię ten tutorial, mam nadzieję, że choć trochę przybliżył wam jak tworzy się cutscenki lub sny.


Dziękuję każdemu, kto przeczytał ten tutorial.




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