Skip to content

stationsTut

extrazi edited this page Jun 30, 2023 · 132 revisions
original    original
EN     PL
Tutorial m4nfo
Pokaż pełny zestaw ramek, aby ułatwić nawigację (en.)

Tutorial kodowania stacji

Introduction

Stacje są istotną funkcją TTD, a od TTDPatch 2.0.1 a6 (10 listopada 2003) możliwe jest kodowanie stacji niestandardowych. Chociaż kodowanie stacji jest nieco trudniejsze niż kodowanie pojazdów, w m4nfo jest o wiele prostsze niż robienie tego w zwykłym nfo.

Najważniejszą różnicą w porównaniu z kodowaniem pojazdów jest to, że stacje mają nie tylko grafikę i dostęp do szeregu zmiennych gry TTD w taki sam sposób, jak pojazdy, ale także wykorzystują sprite i układ kafli, wprowadzając w ten sposób informacje 3D. Daje to szereg możliwości 'recyklingu' układów sprite'ów lub kafli w kodzie, co może sprawić, że stacje kodujące będą całkiem wydajne.

Przykłady
Pamiętaj, że poniższe przykłady nie zawsze prowadzą do 100% kompletnych newGRFów, ale zamiast tego koncentrują się na najważniejszych cechach kodu, zamiast implementować go we wszystkich szczegółach.
Przykład 1: Prosta stacja                              
Pierwszy przykład zademonstruje bardzo podstawowy typ stacji, składający się tylko z jednego konkretnego kafla, ale z możliwością powielenia go w celu budowy większych sekcji stacji. Ten typ stacji bazuje na jednej ze stacji zajezdni w zestawie NewStations (patrz zdjęcie po lewej), a ponadto zademonstruje sposoby samoregulacji poprzez włączenie PBS i rozpoznawanie trasy. Ponadto pokaże, jak radzić sobie ze świadomością śniegu (a raczej terenu).

Definiowanie stacji

Pierwszą rzeczą do zrobienia byłoby w ogóle zdefiniowanie stacji. W m4nfo odbywa się to za pomocą funkcji definestation() i oto ona:



 definestation(ENGINESHED,"Engine shed",
 	class(DEPOT)
 	callbacks(CB_LAYOUT)                        
 	exclude_widths(8)
 	exclude_lengths(8)
 	pylons(TTD_ALLTILES)
 	flags(FOUNDATIONS)
 )

Jak widać na liście parametrów funkcji, ID tej stacji to "ENGINESHED" (pewna liczba zdefiniowana w innym miejscu), a jej (domyślna) nazwa to "Engine sheds".

Teraz w treści funkcji można znaleźć więcej funkcji właściwości, definiujących jeszcze więcej atrybutów tej stacji:
  • class(DEPOT) ustawia ID-klasy stacji na "DEPOT" , aby poprawnie umieścić go w odpowiedniej klasie wewnątrz menu budowania stacji,
  • callbacks (CB_LAYOUT) aktywuje callback CB_LAYOUT dla tej stacji, aby obsłużyć jej układ kafli,
  • exclude_widths(8) i exclude_lengths(8) określają liczbę i długość możliwych platform stacji do zbudowania za jednym zamachem, tj. pozwala na liczbę platform od 1 do 7, a długość tych platform również od 1 do 7
  • pylony (TTD_ALLTILES) pozwalają na rysowanie pylonów trakcyjnych na wszystkich 'typach kafli' TTD ,
  • flagi ('FUNDAMENTY') nakazują grze, aby automatycznie dodawała niestandardowe (gotowe) fundamenty zamiast standardowych fundamentów gry dla tej stacji, gdy jest budowana na zboczach
Następną rzeczą do zrobienia byłoby podanie nazw klas i stacji. Nazwę klasy podaje się tylko raz dla każdej klasy, ale nazwę stacji należy podać dla każdej pojedynczej stacji (na wypadek, gdyby trzeba było ją przetłumaczyć, zamiast uwzględniać tylko nazwę domyślną). Nazwa stacji podana na liście parametrów definestation() będzie domyślną nazwą stacji (najprawdopodobniej w języku Angielskim), ale możliwe jest dołączenie dodatkowych nazw stacji również dla innych języków. Należy pamiętać, że do kodowania UTF-8 wymagany jest atrybut UTF8 przed jakimkolwiek ciągiem tekstowym do zakodowania.



 classnames(DEPOT,  
        {US, "Locomotive depot"},
        {D, "Bahnbetriebswerk"},
        {F, UTF8 "Dépôt de locomotives"},
        {E, UTF8 "Depósitos de locomotoras"},
        {NL, "Locomotief depot"},
        {I, "Rimesse locomotive"},
        {PL, "Parowozownia"},
        {HR, "Depo za lokomotive"},
        {S, "Lokverkstad"},
        {H, UTF8 "Mozdonyszín"},
        {RUS, UTF8 "Локомотивное депо"}
 stationnames(ENGINESHED,  
        {US, "Engine sheds"},
        {D, "Lokschuppen"},
        {F, UTF8 "Remises à locomotives"},
        {E, "Naves de locomotoras"},
        {NL, "Locomotiefloods"},
        {I, "Rimesse per locomotive"}
        {PL, "Lokomotywownie"},
        {HR, "Remiza za lokomotive"},
        {S, "Lokstaller"},
        {H, UTF8 "Mozdonyszín"},
        {RUS, UTF8 "Локомотивное депо"}
 )


Zapewnienie grafiki

Teraz trzeba zdefiniować niektóre prawdziwe (graficzne) 'sprites', robi się to w m4nfo w następujący sposób:



 spriteblock(
    set(
// [0 .. 5] locomotive shed
    sprite(shed5.pcx 10 10 09 16 21 -31 4)
    sprite(shed5.pcx 35 10 09 27 43 -32 -7)
    sprite(shed5.pcx 80 10 09 41 66 -31 1)
    sprite(shed5.pcx 150 10 09 41 66 -33 1)
    sprite(shed5.pcx 220 10 09 27 43 -9 -7)
 	sprite(shed5.pcx 270 10 09 16 21 12 4)
 ...
// [10 .. 13] walls & doors
 	sprite(shed5.pcx 10 110 09 18 15 0 0)
 	sprite(shed5.pcx 28 110 09 18 15 0 0)
 	sprite(shed5.pcx 46 110 09 18 15 0 0)
 	sprite(shed5.pcx 64 110 09 18 15 0 0)
// [14 .. 15] locomotive shed (snowy roof)
    sprite(shed5.pcx 10 130 09 41 66 -31 1)
    sprite(shed5.pcx 80 130 09 41 66 -33 1)
 ...
  // [18 .. 19] icon locomotive shed
    sprite(shed5.pcx 10 223 09 46 66 -31 -15)
    sprite(shed5.pcx 80 223 09 46 66 -31 -15)
 ...
   )
 )
 
 def(1) spriteset(little(0),lots(0))

Sprite'y 0 ... 5 stanowią budynek parowozowni. Zgodnie z przykładami układu kafli , istnieją sprite'y dla kierunku x i y. W dalszej części będziemy mówić tylko o kierunku x od teraz.

Sprite'y #0 i #1 to grafika platformy, #0 to tylna platforma, a #1 to przednia platforma. Sprite #2 to dach szopy. Teraz TTD narysuje te sprite'y we właściwej kolejności, aby uniknąć błędów graficznych w grze. Osiąga się to poprzez uwzględnienie informacji 3D, które zostaną podane później w funkcji layout(). W każdym razie sprite #0 zostanie narysowany jako pierwszy, następnie zostaną narysowane pojazdy pociągów, potem sprite #1, z 'dachem' (#2) na końcu.

Zwróć uwagę, że "nadmiernie niebieski" dla 'dachowych' 'sprites' #2 i #3. Jest to potrzebne, aby móc ustawić drzwiczki podrzędne drzwi i ściany w taki sposób, aby całkowicie pasowały do ​​granic ich 'nadrzędnego sprite' (tego 'sprite' 'dachowego').

Funkcja spriteset() w def(1) zbiera wszystkie podane 'sprites' i udostępnia je w łańcuchu kontroli, który ma zostać ustawiony. Ponieważ nie ma stanów ładowania ładunku dla tego typu stacji, oba jej parametry są zerowane przez funkcje pomocnicze little() i lots().

Ustawianie układu kafli

Teraz, gdy potrzebne sprite'y zostały zdefiniowane w funkcji "spriteblock", potrzebujemy środków, aby [stationFunctions#examples]] z ich poszczególnych 'sprites'. Odbywa się to za pomocą funkcji layout(). Jego pierwszym parametrem jest ID-stacji powiązany z tym konkretnym układem, po którym następuje blok definicji kafli:


layout(ENGINESHED,

// [0 .. 1] open
    tile(_open,
  ground(1012)
  regular(0, xyz(0,0,0),dxdydz(16,5,7))
  regular(1, xyz(0,11,0),dxdydz(16,5,7))
  regular(2, xyz(0,0,16),dxdydz(16,16,26)) // roof
    )
    tile(
  ground(1011)
  regular(5, xyz(0,0,0),dxdydz(5,16,7))
  regular(4, xyz(11,0,0),dxdydz(5,16,7))
  regular(3, xyz(0,0,16),dxdydz(16,16,26)) // roof


	// [2 .. 3] closed

    tile(_closed,
  ground(1012)
  regular(0, xyz(0,0,0),dxdydz(16,5,7))
  regular(1, xyz(0,11,0),dxdydz(16,5,7))
  regular(2, xyz(0,0,16),dxdydz(16,16,26)) // roof
  regular(10, xyoff(8,23)) // wall
    )
    tile(
  ground(1011)
  regular(5, xyz(0,0,0),dxdydz(5,16,7))
  regular(4, xyz(11,0,0),dxdydz(5,16,7))
  regular(3, xyz(0,0,16),dxdydz(16,16,26)) // roof
 regular(11, xyoff(43,23)) // wall


	// [4 .. 5] door closed

    tile(_door_closed,
  ground(1012)
  regular(0, xyz(0,0,0),dxdydz(16,5,7))
  regular(1, xyz(0,11,0),dxdydz(16,5,7))
  regular(2, xyz(0,0,16),dxdydz(16,16,26)) // roof
  regular(12, xyoff(8,23)) // door
    )
    tile(
  ground(1011)
  regular(5, xyz(0,0,0),dxdydz(5,16,7))
  regular(4, xyz(11,0,0),dxdydz(5,16,7))
  regular(3, xyz(0,0,16),dxdydz(16,16,26)) // roof
  regular(13, xyoff(43,23)) // door


// snow



	// [6 .. 7] open

    tile(_open_snow,
  ground(1012)
  regular(0, xyz(0,0,0),dxdydz(16,5,7))
  regular(1, xyz(0,11,0),dxdydz(16,5,7))
  regular(14, xyz(0,0,16),dxdydz(16,16,26)) // roof
    )
    tile(
  ground(1011)
  regular(5, xyz(0,0,0),dxdydz(5,16,7))
  regular(4, xyz(11,0,0),dxdydz(5,16,7))
  regular(15, xyz(0,0,16),dxdydz(16,16,26)) // roof


	// [8 .. 9] closed

    tile(_closed_snow,
  ground(1012)
  regular(0, xyz(0,0,0),dxdydz(16,5,7))
  regular(1, xyz(0,11,0),dxdydz(16,5,7))
  regular(14, xyz(0,0,16),dxdydz(16,16,26)) // roof
  regular(10, xyoff(8,23)) // wall
    )
    tile(
  ground(1011)
  regular(5, xyz(0,0,0),dxdydz(5,16,7))
  regular(4, xyz(11,0,0),dxdydz(5,16,7))
  regular(15, xyz(0,0,16),dxdydz(16,16,26)) // roof
  regular(11, xyoff(43,23)) // wall


	// [10 .. 11] door closed

    tile(_door_closed_snow,
  ground(1012)
  regular(0, xyz(0,0,0),dxdydz(16,5,7))
  regular(1, xyz(0,11,0),dxdydz(16,5,7))
  regular(14, xyz(0,0,16),dxdydz(16,16,26)) // roof
  regular(12, xyoff(8,23)) // door
    )
    tile(
  ground(1011)
  regular(5, xyz(0,0,0),dxdydz(5,16,7))
  regular(4, xyz(11,0,0),dxdydz(5,16,7))
  regular(15, xyz(0,0,16),dxdydz(16,16,26)) // roof
  regular(13, xyoff(43,23)) // door


	// [12 .. 13] icons

    tile(_icon,
  ground(1012)
  regular(18, xyz(0,0,0),dxdydz(16,16,1))
    )
    tile(
  ground(1011)
  regular(19, xyz(0,0,0),dxdydz(16,16,1))
    )
 )

Jak widać, są powiązane pary kafli, jedna w kierunku x, a druga w kierunku y. Każdy kafel składający się z 'sprite gruntu', reprezentującego ścieżkę (sprite'y TTD 1011 i 1012) oraz jednego lub więcej 'sprites budowlanych', reprezentujących tylną lub przednią ścianę oraz dach. Należy również pamiętać, że funkcja kafla może otrzymać opcjonalną etykietę ułatwiającą odniesienie do tego konkretnego kafla.

Każdy z tych 'sprites' jest opisany przez kolejny numer, powiązany z jego rangą w graficznym bloku sprite'ów, trójwymiarową współrzędną, wskazującą jego położenie w przestrzeni 3D oraz jego rozmiar w x, y i wysokość. Niektóre z tych 'sprites' to tak zwane 'sprites' 'potomne', które nie mają ani współrzędnych, ani rozmiarów, ale zamiast tego definiują dwuwymiarowe 'przesunięcie' względem lewego górnego rogu ich nadrzędnych 'sprites', dzieląc trójwymiarową ramkę 'nadrzędną'. Oceniając te liczby, sortownik sprite'ów TTD narysuje sprite'y we właściwej kolejności, odcinając (które mają być) ukryte części

Teraz kafle #0 i #1 (etykieta "_open") reprezentują otwartą szopę (zarówno w kierunku x, jak i y), kafle #2 i #3 ("_closed") wyświetlają szopę bez wpisu, stosując tzw. nazywane 'podrzędnym sprite' dla 'dachowego' 'sprite', kafla #4 i #5 ("_door_closed") robią to samo, ale z grafiką zamkniętych drzwi zamiast ściany, kaflami #6/#7, #8/#9 i #10/#11 są takie same tylko ze śnieżnym 'dachem', a kafle #12/#13 ("_icon") stanowią ikony potrzebne w menu budynku.

Kładąc wszystko razem

Po przygotowaniu potrzebnych grafik i ich układu będziemy musieli zbudować 'łańcuch kontroli' , łącząc grafikę i układ z ID-stacji, a także zająć się funkcjonalnością szopy.

Pierwszą rzeczą, którą chcemy osiągnąć, jest dostosowanie wyglądu szopy w przypadku podłączenia do niej toru. Jeśli przed szopą nie ma toru, powinien przedstawiać prostą ścianę bez drzwi. Drugą cechą, którą chcemy zakodować, jest to, że w przypadku, gdy do szopy prowadzi tor, drzwi szopy powinny otwierać się tylko wtedy, gdy pociąg wjeżdża do lub z szopy.

Pierwsza funkcja będzie obsługiwana przez funkcję m4nfo tinfo_trackconnect(), a druga jest obsługiwana przez funkcję pbsinfo() .

Ostatnią rzeczą, którą należy zrobić, jest narysowanie niestandardowych fundamentów podczas budowy na zboczu i pokazanie śnieżnej grafiki podczas budowania na zaśnieżonym terenie. Pierwsza jest obsługiwana przez funkcję spritetype(), druga za pomocą funkcji tinfo_terrain() .

Oto kod:


 def(0) spritetype(  
 	ref(CF_NORMAL) if(2) // custom foundation                       
 	ref(1) else	     // normal ground or building sprite  
 )  
 def(6) pbsinfo(  
 	reftile(_open) if(PBSRESERVED) // open  
 	reftile(_door_closed) else     // closed  
 )  
 def(7) tinfo_trackconnect(shiftmask(0,1),  
 	ref(6) if(1)	      // track from below  
 	reftile(_closed) else // no track, show wall  
 )  
 // snow  
 def(6) pbsinfo(  
 	reftile(_open_snow) if(PBSRESERVED) // open  
 	reftile(_door_closed_snow) else	    // closed  
 )  
 def(8) tinfo_trackconnect(shiftmask(0,1),  
 	ref(6) if(1)		   // track from below  
 	reftile(_closed_snow) else // no track, show wall  
 )  
 // check for snow  
 def(9) tinfo_terrain(  
 	ref(8) if(SNOW) // snow  
 	ref(7) else     // no snow  
 )  
 def(10) callback(  
 	ref(9) if(CB_LAYOUT) // tile layout  
 	ref(0) else	     // graphics chain  
 )  
 // menu  
 def(11) callback(  
 	reftile(_icon) if(CB_LAYOUT) // tile layout  
 	ref(0) else		     // graphics chain  
 )  
 makestation(ENGINESHED,  
 	link(ref(11), MENU)  
 	default(ref(10))  
 )  


A teraz podsumujmy, co zrobiliśmy. Jak zwykle zacznijmy od końca, od funkcji makestation().

Ta funkcja łączy się z dwoma łańcuchami, jednym łańcuchem do wpisu w menu budynku, a łańcuch główny stanowi opis stacji w grze. Teraz łańcuch dla pozycji menu jest prosty: w przypadku CB_LAYOUT, pokaże kafel (y) #12/#13 z układu (tj. Ikona), a w przeciwnym razie będzie odnosił się do def(0), który reprezentuje zarówno główny zestaw grafik, jak i niestandardowy podstawowy zestaw grafik. Ponieważ w menu nie ma potrzeby stosowania fundamentu, w menu zostaną wyświetlone grafiki kafli #12/13.

Teraz spójrzmy teraz na główny ('domyślny') łańcuch. Najpierw (w def(10)) sprawdzamy również callback CB_LAYOUT. W przypadku braku wywołania zwrotnego, ponownie łączymy się z def(0) i pokazujemy odpowiednią grafikę, albo niestandardowe sprite'y fundacji (w przypadku, gdy spritetype() zwraca "2") lub po prostu budujemy sprite'y.

W przypadku CB_LAYOUT sprawdzamy śnieżny teren (w def(9) ). W przypadku śniegu, przykuwamy do def(8), a jeśli nie ma śniegu, łańcuch przechodzi z def(7). Ponieważ oba łańcuchy zasadniczo robią to samo, dokładniej opisano tylko łańcuch bez śniegu.

Po pierwsze, w def(7) sprawdzamy połączenie torów z drzwiami wejściowymi szopy. Odbywa się to poprzez wywołanie funkcji tinfo_trackconnect(). Aby sprawdzić tylko ścieżkę prowadzącą do (widocznych) drzwi wejściowych, będziemy musieli sprawdzić bit0 zwracany przez tę funkcję. Odbywa się to poprzez sprawdzenie wyniku funkcji tylko dla tego bitu, co można zrobić za pomocą funkcji pomocniczej shiftmask() . W przypadku, gdy ścieżka istnieje, przechodzimy do def(6) sprawdzania stanu PBS, a w przypadku braku ścieżki, po prostu pokazujemy zwykłą ścianę, zwracając kafel #2 do wywołania zwrotnego.

Funkcja pbsinfo() w def(6) kwestionuje stan PBS, a w przypadku PBSRESERVED (tj. Ścieżka została zarezerwowana przez pathfinder), lubimy wyświetlać kafel #0 (drzwi otwarte), w przeciwnym razie kafel #4 (drzwi zamknięte) jest pokazane.

I to wszystko w tym przykładzie. W następnym rozwiniemy ten, aby pokazać, jak wiele podobnych typów stacji może być obsługiwanych przy użyciu tej samej struktury, tylko przez wprowadzenie pewnych rozszerzeń w układzie kafli stacji.


Przykład 2: Współdzielone układy i struktury układów

W przypadku stacji układ v jest oddzielony od prawdziwych sprite'ów zdefiniowanych w spriteblock() i dostępny za pomocą funkcji spriteset(). Otwiera to interesujące możliwości udostępniania układów v lub spriteów między stacjami. W tym drugim przykładzie chcemy rozszerzyć stację z przykładu 1 o inną podobną stację ("stary magazyn", patrz zdjęcie po lewej), w taki sposób, aby układy kafli dla obu różniły się tylko adresami sprite'ów. W ten sposób druga stacja nie potrzebuje własnego łańcucha sterowania, ale może używać tego z pierwszej stacji.

Definiowanie stacji

Pierwszą rzeczą do dodania byłaby definicja drugiej stacji, jak powyżej.


definestation(ENGINESHED,{"Engine sheds", "Old warehouses"},
 	class(DEPOT, PIECEGOODS)
 	callbacks(CB_LAYOUT, CB_LAYOUT)                        
 	exclude_widths(8, 8)
 	exclude_lengths(8, 8)
 	pylons(TTD_ALLTILES, TTD_ALLTILES)
 	flags(FOUNDATIONS, FOUNDATIONS)
 )

Jak widać, funkcja definestation() może obsługiwać więcej niż jedną definicję stacji. W tym przypadku ID drugiej stacji zostałby przydzielony automatycznie, tzn. Będzie to ENGINESHED + 1. Ponieważ druga stacja powinna mieć inną klasę, musimy dodać nową klasę i nazwy stacji dla tej stacji.


 classnames(PIECEGOODS,
          {US, "Freight stations (piece goods)"},
          {D, UTF8 "Güterbahnhöfe (Stückgut)"},
          {F, "Gares de marchandises (r.o.)"},
          {E, UTF8 "Estaciónes de mercancías (bulto)"},
          {NL, "Goederenloodsen (stukgoederen)"},
          {I, "Scali merci (collettame)"},
          {PL, "Stacje towarowe (drobnica)"},
          {HR, "Teretne stanice (komadni teret)"},
          {S, "Godsstationer (styckegods)"},
          {H, UTF8 "Teherpályaudvar (darabáru)"},
          {RUS, UTF8 "Грузовые станции (товары)"}
 )
 stationnames(ENGINESHED+1,
          {US, "Old warehouses"},
          {D, UTF8 "alte Lagerhäuser"},
          {F, UTF8 "Vieux entrepôts"},
          {E, "Almacenes viejos"},
          {NL, "Oude magazijnen"},
          {I, "Vecchi magazzini"}
          {PL, "Stare magazyny"},
          {HR, UTF8 "Stara skladišta"},
          {S, "Packhus"},
          {H, UTF8 "Régi raktár"},
          {RUS, UTF8 "Старые склады"}
 )

Zapewnienie grafiki

Ponownie, sprite'y graficzne muszą zostać zdefiniowane, tym razem dla obu stacji:


 spriteblock(
  set(
// [0 .. 5] locomotive shed
                sprite(shed5.pcx 10 10 09 16 21 -31 4)
                sprite(shed5.pcx 35 10 09 27 43 -32 -7)
                sprite(shed5.pcx 80 10 09 41 66 -31 1)
                sprite(shed5.pcx 150 10 09 41 66 -33 1)
                sprite(shed5.pcx 220 10 09 27 43 -9 -7)
                sprite(shed5.pcx 270 10 09 16 21 12 4)
// [6 .. 9] old warehouse
                sprite(shed5.pcx 35 60 09 27 43 -32 -7)
                sprite(shed5.pcx 80 60 09 44 64 -31 -2)
                sprite(shed5.pcx 150 60 09 44 64 -31 -2)
                sprite(shed5.pcx 220 60 09 27 43 -9 -7)
// [10 .. 13] walls & doors
 	sprite(shed5.pcx 10 110 09 18 15 0 0)  
 	sprite(shed5.pcx 28 110 09 18 15 0 0)  
 	sprite(shed5.pcx 46 110 09 18 15 0 0)  
 	sprite(shed5.pcx 64 110 09 18 15 0 0)  
// [14 .. 15] snowy roof locomotive shed
                sprite(shed5.pcx 10 130 09 41 66 -31 1)
                sprite(shed5.pcx 80 130 09 41 66 -33 1)
// [16 .. 17] snowy roof warehouse
                sprite(shed5.pcx 10 175 09 44 64 -31 -2)
                sprite(shed5.pcx 80 175 09 44 64 -31 -2)


	// [18 .. 19] icon locomotive shed  
                sprite(shed5.pcx 10 223 09 46 66 -31 -15)
                sprite(shed5.pcx 80 223 09 46 66 -31 -15)
// [20 .. 21] icon warehouse
                sprite(shed5.pcx 10 273 09 49 64 -31 -18)
                sprite(shed5.pcx 80 273 09 49 64 -31 -18)
  )
 )
 def(1) spriteset(little(0),lots(0))

   Zestaw 'sprites' jest taki sam, jak w przykładzie 1, z dodawaniem tylko starych 'sprites' magazynu.

Ustawianie układu kafli

Ponownie, potrzebne sprite'y dla drugiej stacji zostały zdefiniowane w funkcji "spriteblock", należy zdefiniować nowy układ dla drugiej stacji:


 layout(ENGINESHED +1,
// [0 .. 1] open
    tile(_open,
      ground(1012)
      regular(0, xyz(0,0,0),dxdydz(16,5,7))
      regular(6, xyz(0,11,0),dxdydz(16,5,7))
      regular(7, xyz(0,0,16),dxdydz(16,16,26)) // roof
    )
    tile(
      ground(1011)
      regular(5, xyz(0,0,0),dxdydz(5,16,7))
      regular(9, xyz(11,0,0),dxdydz(5,16,7))
      regular(8, xyz(0,0,16),dxdydz(16,16,26)) // roof
    )
// [2 .. 3] closed
    tile(_closed,
      ground(1012)
      regular(0, xyz(0,0,0),dxdydz(16,5,7))
      regular(6, xyz(0,11,0),dxdydz(16,5,7))
      regular(7, xyz(0,0,16),dxdydz(16,16,26)) // roof
      regular(10, xyoff(8,26)) // wall
    )
    tile(
      ground(1011)
      regular(5, xyz(0,0,0),dxdydz(5,16,7))
      regular(9, xyz(11,0,0),dxdydz(5,16,7))
      regular(8, xyz(0,0,16),dxdydz(16,16,26)) // roof
      regular(11, xyoff(43,26)) // wall
    )
// [4 .. 5] door closed
    tile(_door_closed,
      ground(1012)
      regular(0, xyz(0,0,0),dxdydz(16,5,7))
      regular(6, xyz(0,11,0),dxdydz(16,5,7))
      regular(7, xyz(0,0,16),dxdydz(16,16,26)) // roof
      regular(12, xyoff(8,26)) // door
    )
    tile(
      ground(1011)
      regular(5, xyz(0,0,0),dxdydz(5,16,7))
      regular(9, xyz(11,0,0),dxdydz(5,16,7))
      regular(8, xyz(0,0,16),dxdydz(16,16,26)) // roof
      regular(13, xyoff(43,26)) // door
    )
 // snow
// [6 .. 7] open
    tile(_open_snow,
      ground(1012)
      regular(0, xyz(0,0,0),dxdydz(16,5,7))
      regular(6, xyz(0,11,0),dxdydz(16,5,7))
      regular(16, xyz(0,0,16),dxdydz(16,16,26)) // roof
    )
    tile(
      ground(1011)
      regular(5, xyz(0,0,0),dxdydz(5,16,7))
      regular(9, xyz(11,0,0),dxdydz(5,16,7))
      regular(17, xyz(0,0,16),dxdydz(16,16,26)) // roof
    )
// [8 .. 9] closed
    tile(_closed_snow,
      ground(1012)
      regular(0, xyz(0,0,0),dxdydz(16,5,7))
      regular(6, xyz(0,11,0),dxdydz(16,5,7))
      regular(16, xyz(0,0,16),dxdydz(16,16,26)) // roof
      regular(10, xyoff(8,26)) // wall
    )
    tile(
      ground(1011)
      regular(5, xyz(0,0,0),dxdydz(5,16,7))
      regular(9, xyz(11,0,0),dxdydz(5,16,7))
      regular(17, xyz(0,0,16),dxdydz(16,16,26)) // roof
      regular(11, xyoff(43,26)) // wall
    )
// [10 .. 11] door closed
    tile(_door_closed_snow,
      ground(1012)
      regular(0, xyz(0,0,0),dxdydz(16,5,7))
      regular(6, xyz(0,11,0),dxdydz(16,5,7))
      regular(16, xyz(0,0,16),dxdydz(16,16,26)) // roof
      regular(12, xyoff(8,26)) // door
    )
    tile(
      ground(1011)
      regular(5, xyz(0,0,0),dxdydz(5,16,7))
      regular(9, xyz(11,0,0),dxdydz(5,16,7))
      regular(17, xyz(0,0,16),dxdydz(16,16,26)) // roof
      regular(13, xyoff(43,26)) // door
    )
// [12 .. 13] icons
    tile(_icon,
      ground(1012)
      regular(20, xyz(0,0,0),dxdydz(16,16,1))
    )
    tile(
      ground(1011)
      regular(21, xyz(0,0,0),dxdydz(16,16,1))
    )
 )

Jak widać, struktura układu jest identyczna jak w ENGINESHED, tylko numery 'sprites' są różne.

Kładąc wszystko razem

Teraz, gdy druga stacja używa tego samego układu kafli, co pierwsza stacja i oczywiście wymaga tego samego zachowania, co pierwsza, jeśli chodzi o połączenie torów, otwieranie drzwi i rozpoznawanie terenu, dodatkowy kod staje się dość mały:


 def(0) spritetype(
 	ref(CF_NORMAL) if(2) // niestandardowy fundament                       
 	ref(1) else	     // normalny grunt lub sprite budynku
 )
 def(6) pbsinfo(
 	reftile(_open) if(PBSRESERVED) // open
 	reftile(_door_closed) else     // closed
 )
 def(7) tinfo_trackconnect(shiftmask(0,1),
 	ref(6) if(1)	      // track od dołu
 	reftile(_closed) else // bez toru, pokaż ścianę
 )
 // snow
 def(6) pbsinfo(
 	reftile(_open_snow) if(PBSRESERVED) // open
 	reftile(_door_closed_snow) else	    // closed
 )
 def(8) tinfo_trackconnect(shiftmask(0,1),
 	ref(6) if(1)		   // track od dołu
 	reftile(_closed_snow) else // bez toru, pokaż ścianę
 )
 // check for snow
 def(9) tinfo_terrain(
 	ref(8) if(SNOW) // snow
 	ref(7) else     // no snow
 )
 def(10) callback(
 	ref(9) if(CB_LAYOUT) // tile layout
 	ref(0) else	     // łańcuch graficzny
 )
 // menu
 def(11) callback(
 	reftile(_icon) if(CB_LAYOUT) // tile layout
 	ref(0) else		     // graphics chain
 )
 makestation(ENGINESHED,
 	link(ref(11), MENU)
 	default(ref(10))
 )
// dodatkowy kod dla stacji "stary magazyn"
makestation(ENGINESHED+1,
  link(ref(11), MENU)
  default(ref(10))
)

To wszystko! Ponieważ nowa stacja używa dokładnie tego samego kodu i tych samych wskaźników do struktury sprite i layoutu. Jedyną różnicą są rzeczywiste liczby 'sprites'v w układzie kafli.

Jest więcej możliwości. Jednym z nich jest użycie tych samych układów zawierających nawet te same rzeczywiste liczby 'sprites', używając dwóch różnych bloków sprite'ów z prawdziwymi 'sprites'. W tym przypadku potrzebny jest tylko jeden układ kafli, który zostanie skopiowany przez funkcję copylayout() dla innych ID-stacji. Zostanie to pokazane w następnym przykładzie.

Przykład 3: Animacja

Definiowanie stacji

Po raz kolejny pierwszą rzeczą do zrobienia byłoby zdefiniowanie stacji, tym razem zawierającej specjalne definicje animacji:

 definestation(ANIMTEST,{"animation test"},
 	class(DEPOT)
 	callbacks(CB_LAYOUT)                        
 	include_widths(1)
 	include_lengths(1)
 	setcargotriggers({PASS, MAIL})
 	anim_info({8,LOOP})
 	anim_triggers({ARRIVE, LEAVE})
 	anim_speed(2)
 )

Większość definicji powinna być już znana, z wyjątkiem tych specyficznych dla animacji.

  • anim_info({8,LOOP}) określa liczbę klatek animacji do wykorzystania, a także charakter animacji: zapętlony lub niepętlowy,
  • anim_triggers({ARRIVE, LEAVE}) definiuje wyzwalacze do użycia w tej animacji. Tutaj definiujemy dwa wyzwalacze: jeden dla przyjeżdżających pociągów, a drugi dla wyjeżdżających pociągów,
  • anim_speed(2) ustawia prędkość animacji, tj. umiarkowanie dużą.
Jest jeszcze jedna specyficzna funkcja, a mianowicie setcargotriggers({PASS, MAIL}) , która określa typy ładunków, dla których powinna nastąpić ponowna randomizacja.

Zapewnienie grafiki

Graficzne 'sprites' są definiowane w zwykły sposób:

spriteblock(
   set(
                sprite(animtest.pcx 10 10 09 31 64 -31 0) // 0
                sprite(animtest.pcx 78 10 09 31 64 -31 0) // 1
                sprite(animtest.pcx 146 10 09 31 64 -31 0) // 2
                sprite(animtest.pcx 214 10 09 31 64 -31 0) // 3
                sprite(animtest.pcx 10 48 09 31 64 -31 0) // 4
                sprite(animtest.pcx 78 48 09 31 64 -31 0) // 5
                sprite(animtest.pcx 146 48 09 31 64 -31 0) // 6
                sprite(animtest.pcx 214 48 09 31 64 -31 0) // 7
    )
 )
 def(1) spriteset(little(0),lots(0))

Ustawianie układu kafli

W następnym kroku należy ustawić układ kafli przy użyciu 'sprites' zdefiniowanych powyżej:

 layout(ANIMTEST,
 // [0/1]
    tile(
      ground(1012))
      regular(0, xyz(0,0,0),dxdydz(16,16,1))
    )
    tile(
      ground(1011)
      regular(0, xyz(0,0,0),dxdydz(16,16,1))
    )
 // [2/3]
    tile(
      ground(1012)
      regular(1, xyz(0,0,0),dxdydz(16,16,1))
    )
    tile(
      ground(1011)
      regular(1, xyz(0,0,0),dxdydz(16,16,1))
    )
 // [4/5]
    tile(
      ground(1012)
      regular(2, xyz(0,0,0),dxdydz(16,16,1))
    )
    tile(
      ground(1011)
      regular(2, xyz(0,0,0),dxdydz(16,16,1))
    )
 // [6/7]
    tile(
      ground(1012)
      regular(3, xyz(0,0,0),dxdydz(16,16,1))
    )
    tile(
      ground(1011)
      regular(3, xyz(0,0,0),dxdydz(16,16,1))
    )
 // [8/9]
    tile(
      ground(1012)
      regular(4, xyz(0,0,0),dxdydz(16,16,1))
    )
    tile(
      ground(1011)
      regular(4, xyz(0,0,0),dxdydz(16,16,1))
    )
 // [10/11]
    tile(
      ground(1012)
      regular(5, xyz(0,0,0),dxdydz(16,16,1))
    )
    tile(
      ground(1011)
      regular(5, xyz(0,0,0),dxdydz(16,16,1))
    )
 // [12/13]
    tile(
      ground(1012)
      regular(6, xyz(0,0,0),dxdydz(16,16,1))
    )
    tile(
      ground(1011)
      regular(6, xyz(0,0,0),dxdydz(16,16,1))
    )
 // [14/15]
    tile(
      ground(1012)
      regular(7, xyz(0,0,0),dxdydz(16,16,1))
    )
    tile(
      ground(1011)
      regular(7, xyz(0,0,0),dxdydz(16,16,1))
    )
 )

Kładąc wszystko razem

Teraz, po skonfigurowaniu potrzebnej grafiki i ich układu kafli, będziemy musieli zbudować 'łańcuch kontroli', łącząc grafikę i układ z ID-stacji, a także obsługiwać animację kafli.


 def(2) anim_frame(
 	cbr(0) if(0)
 	cbr(2) if(1)
 	cbr(4) if(2)
 	cbr(6) if(3)
 	cbr(8) if(4)
 	cbr(10) if(5)
 	cbr(12) if(6)
 	cbr(14) else
 )
 def(3) anim_trigger(
 	animcontrol(A_START) if(ARRIVE) // przybyć (arrive)
 	animcontrol(A_STOP) if(LEAVE)	// odejście (leave)
 	animcontrol(A_NOP) else		// do nothing
 )
 def(4) callback(
 	ref(2) if(CB_LAYOUT)
 	ref(3) if(CB_ACONTROL)
 	ref(1) else
 )
 makestation(ANIMTEST,
 	default(ref(4))
 )