@edit: 17:00 17.08.2010 - poprawka ds. terminate_monitor i trigger'ów

Witam,

Postanowiłem napisać tutorial o tworzeniu skryptów w Rome: Total War. Będę zajmował się, jak sama nazwa tematu wskazuje, jedynie podstawowymi zagadnieniami.

Całkiem niedawno miałem problem z napisaniem prostego skryptu spawn'ującego armię. Na właściwy tor rozumowania naprowadził mnie Tiberius Caesar Augustus. Postanowiłem więc pomóc też innym, tym bardziej, że w naszym zbiorze poradników nie ma jeszcze żadnego traktującego o skryptach.

Teraz informacja ku pokrzepieniu serc ;] Nauka języka skryptowego Romy, o ile kiedykolwiek wcześniej miałeś styczność z jakimkolwiek językiem programowania (c++, C#, Delphi itd.), zajmie Ci mniej niż godzinę. Pozostałym nie mającym przyjemności programować, około 1 – 3 godzin.

Źródła:
http://www.twcenter.net/forums/showthread.php?t=259487
http://www.twcenter.net/forums/showthread.php?t=169689

Spis treści:

1. Czym są skrypty w Romie?
2. Organizacja pracy
3. Nasz pierwszy skrypt
4. Co zrobić by nasz skrypt zadziałał?
5. Linki

1.

Skrypty w Romie są niczym innym jak językiem skryptowym stworzonym na potrzeby gry. W przeciwieństwie do języków programowania (np.: C++) ten kod interpretowany jest bezpośrednio przez silnik Romy, a nie procesor. Tak więc nie trzeba chyba wyjaśniać, że będzie działał tylko pod grami z serii Total War (i to w każdej gierce z serii może mieć ciut inną formę).

2.


Organizacja pracy, czyli jak nie zgubić się we własnych śmieciach ;]

Przedstawię parę zasad, które ułatwią Wam tworzenie skryptów:

1. Właściwe ułożenie folderów. Skrypty powinno się wrzucać do folderu SCRIPTS znajdującego się w Rome Total War\DATA lub w Rome Total War \ moj_mod \ DATA. Ponadto, warto tworzyć podfoldery w których będziemy mogli z głową ułożyć nasze przyszłe skrypty. Na sam początek zalecam stworzenie folderu naszej modyfikacji, np.: DATA \ SCRIPTS \ moj_mod. Teraz zastanawiamy się czy nasz skrypt będzie „globalny” (tzn. dotyczył albo wszystkich frakcji, albo kilku, albo tylko jednej) czy nie. Jeżeli tak, to w folderze moj_mod tworzymy kolejne w zależności od zapotrzebowania (np.: moj_mod \ grecja , moj_mod \ rzym itd.). Oczywiście można użyć innych kryteriów, ponieważ by wykonać jedną akcję, możemy użyć kilku skryptów, więc jak nie trudno się domyślić, dla wygody, lepiej je umieścić w jednym folderze. W naszym przykładzie będzie to:
DATA \ SCRIPTS \ moj_mod \ KASA
2. Odpowiedni edytor. Powiedzmy sobie szczerze, że możliwości poczciwego notatnika są bardzo małe. Dla wygody i lepszego wglądu w kod, a także zaawansowanych możliwości edycyjnych (szczególnie rozbudowana opcja „zamień” :]) polecam notepad++ (link na dole posta)

3.

Nasz pierwszy skrypt, czyli walkę Dawida z Goliatem czas zacząć ;]

Idąc za dobrym przykładem poradników C++, najpierw przedstawię kod, a później, wyczerpująco go omówię.


Kod:
Script ;--------------------------------------------------początek skryptu

monitor_event FactionTurnStart FactionIsLocal
          and I_TurnNumber = 3

	console_command add_money 10000

    terminate_monitor

end_monitor

while I_TurnNumber < 9999
    suspend_unscripted_advice true
end_while

end_script

Pierwsza linijka:
script – informuje grę, że to nie jest jakiś tam plik tekstowy, tylko skrypt; oznacza rozpoczęcie naszego skryptu.
; - służy do wstawiania komentarzy, wszystko co napiszemy w LINIJCE za tym znaczkiem będzie ominięte przez skrypt

Druga linijka:

monitor_event – uruchamia monitor. Jak sama nazwa wskazuje monitor „monitoruje” ;], w przypadku Romy sprawdza w jakim miejscu rozgrywki właśnie jesteśmy (w jakiej turze, czy to jest nowa tura, czy dana frakcja istnieje, lub jaką frakcją my właśnie gramy itd.)

FactionTurnStart
– jest to „event” (zdarzenie) rozpoznawane przez monitor_event. Dzięki jego zastosowaniu monitor sprawdza czy zaczęła się nowa tura, jeśli tak, to skrypt przechodzi do następnych linijek kodu aż do end_monitor.
Mała degresja dla koderów w C++, delphi itd.: monitor_event jest pewną formą pętli if, jeśli FactionTurnStart jest TRUE (czyli zaczęła się nowa tura) to wtedy robię to.

FactionIsLocal – jest to „event” rozpoznawany przez monitor_event. Jest ściśle związany z FactionTurnStart i sprawdza, jaką frakcją grasz. Jest to bardzo przydatna funkcja. Wyjaśnię to na bardzo prostym przykładzie:
Stworzyliśmy sobie skrypt spawn'ujący armię Hanniballa. Oczywiste jest, że nie chcemy aby to zaszło kiedy będziemy grać Kartaginą (przecież to my będziemy miłościwie panującymi władcami Kartaginy i to od naszej świętej woli powinno zależeć czy wysłać do Italli Hanniballa czy nie ;]). Tutaj na ratunek przychodzi nam w.w. funkcja w formie: FactionIsLocal (frakcja jest lokalna). Dzięki odpowiedniemu zapisowi w kodzie skryptu (po więcej informacji zapraszam do anglojęzycznego tutka, podanego w źródłach lub jeżeli ktoś ma ochotę, niech napiszę polski poradnik zaawansowanego kodowania) gdy monitor_event wykryje, że gramy Kartaginą skrypt się nie wykona :].
Kolejna dygresja do koderów w C++ itp.: mamy kolejny dowód, że jest to pętla if, więc jak by kod naszego skryptu spawn'ujacego Hanniballa wyglądał w ogólnym zapisie?

monitor_event (jeśli zaczęła się nowa tura [i] NIE jest to frakcja lokalna)

wykonuję kod

end_monitor

przykład praktyczny:

Kod:
monitor_event FactionTurnStart FactionIsLocal
  and not I_LocalFaction carthage ; [i] Lokalna frakcja nie jest Kartaginą

spawn_army kod
   character kod
   units kod
   end
end_monitor

Trzecia linijka:

and I_TurnNumber = 3 – dodatkowy event do monitor_event (numer tury).

Interpretacja całości monitor_event:
gdy zaczyna się nowa tura [i] frakcja jest lokalna [i] NUMER TURY = 3, to robię kod

Czwarta linijka:
console_command – użycie tej funkcji przed komendą konsoli, pozwala zasymulować jej wpisanie.
Tak jakby Rom'a otwiera sobie wirtualnie konsolkę i wpisuje komendę

add_money 10000 – dodaje 10000 do konta naszej frakcji :] lista wszystkich komend i eventów w dokumentacji do BI – link na dole postu

Piąta linijka:
terminate_monitor – monitor zostaje wyłączony i skrypt nie zadziała w następnej turze

Szósta linijka:
end_monitor – kończy pętlę monitora

Następnie mamy pętlę while zakończoną end_while. Jest to opcja nieobowiązkowa, aczkolwiek powinniście ten blok kodu umieszczać w swoich skryptach.

No i wszystko kończymy zamknięciem skryptu poprzez end_script.

Podsumowując otrzymaliśmy skrypt dodający w 3 rundzie 10000 do konta frakcji, którą aktualnie gramy :]

Tak przygotowany skrypt w naszym przypadku, zapisujemy w DATA \ SCRIPTS \ moj_mod \ KASA, jako typ wybieramy dokument tekstowy txt, a nazwę, np.: kasa :]. A więc pełna ścieżka powinna wyglądać tak:
DATA \ SCRIPTS \ moj_mod \ KASA \ kasa.txt

4.

Co zrobić, żeby nasz skrypt zadziałał, czyli tworzymy swój własny świat ;]

Skrypty dają nam prawie nieograniczone możliwości wpływania na świat w grze Rome: Total War. Możemy coś dodawać, zabierać, zmieniać – jednym słowem mieć dynamiczny wpływ na rozgrywkę.

Ale żeby nasz skrypt na kasiorkę zechciał zadziałać musimy jeszcze wyedytować pewne pliki.
DATA \ export_descr_advice.txt
DATA \ text \ export_advice.txt
DATA \ world \ maps \ campaign \ moja_kampania \ descr_strat.txt
DATA \ world \ maps \ campaign \ moja_kampania \ skrypt_kampanii.txt - ten plik należy utworzyć


A teraz do dzieła :]

export_descr_advice.txt

Na samej górze, pod „ADVICE THREAD DATA STARTS HERE” należy wkleić poniższy kod:

Kod:
;-------------------------------------------------------------------Mój_skrypt
AdviceThread kasiorka ; kasiorka – nazwa naszego „rozkazu”
    GameArea Campaign ; deklarujemy gdzie ma działać nasz skrypt, w kampanii czy podczas bitwy

    Item kasiorkaItem1 ; Nazwa itemu
        Uninhibitable
        Verbosity  0
        Threshold  1
        MaxRepeats  0
        RepeatInterval  1
        Attitude Normal
        Presentation Default
        Title kasiorkaTitle1 ; odwołanie do tytułu naszego skryptu
        On_display scripts\moj_mod\KASA\kasa.txt ; ścieżka do pliku skryptowego
        Text kasiorkaText1 ; odwołanie do opisu naszego skryptu
Teraz powinniśmy dodać na końcu pliku export_descr_advice, triggery, czyli wyzwalacze naszego skryptu. „Powinniśmy”, ponieważ nasz skrypt zadziała i bez nich (podaliśmy przecież jasne warunki FactionTurnStart FactionIsLocal and I_TurnNumber, a także za chwilę uruchomimy skrypt za pomocą skrypt_kampanii.txt - w ten sposób skrypt uruchomi się tylko podczas startu kampanii!!). Triggery przydają się np.: przy skryptach typu force_diplomacy (wymuś reakcję dyplomatyczną) i gdy chcemy uruchamiać nasze skrypty po wczytaniu stanu gry itp.. Wtedy taki skrypt jest uruchamiany (o ile nie ma wymogu co do numeru tury w monitor_events) np.: gdy naciśniemy przycisk akceptacji dyplomacji.

Przykład triggera:

Kod:
;------------------------------------------
Trigger background_script_trigger_1
    WhenToTest ButtonPressed ; sprawdza czy przycisk jest naciśnięty

    Condition ButtonPressed construction_button ; gdy naciśniemy przycisk konstrukcji...

    AdviceThread kasiorka  1 ; … dostaniemy kasiorkę :]
export_advice.txt

Kod:
{kasiorkaTitle1} Kasiorka
{kasiorkaText1} W 3 rundzie dostaniesz premie ;]
export_advice zawiera opisy skryptów. Opis pojawi się na początku kampanii w lewym górnym rogu (dla pewności przy starcie kampanii zaznacz: częste porady).

skrypt_kampanii.txt

Kod:
script

wait 1
advance_advice_thread kasiorka ; odwołanie (zaczep) do naszego rozkazu, reszta zostaje bez zmian
wait 1
select_ui_element advisor_portrait_button
simulate_mouse_click lclick_up

end_script
descr_strat.txt

Na końcu pliku descr strat, dodajemy:

Kod:
script
skrypt_kampanii.txt ; nazwa skryptu kampanii
Na koniec na wszelki wypadek usuwamy map.rwm z folderu WORLD\MAPS\BASE

5.


Linki, czyli nasze zaplecze sztabowe :]

Notepad ++
http://download.tuxfamily.org/notepa....Installer.exe

Dokumentacja (zbiór wszystkich komend konsoli i event'ów):
http://www.bigarchive.cba.pl/RTW/BI_docs.zip