06-11-2011, 22:44
Kontynuując wątek tworzenia drezyny WM-15A, przedstawiam opis kolejnego etapu prac.
Projekt drezyny - wizualizacja towarów
Kolejnym etapem rozwoju projektu drezyny WM-15A jest dodanie wizualizacji przewożonych ładunków. Dotychczas bowiem drezyna była widoczna w grze cały czas jako pusta, nawet wtedy, kiedy była załadowana jakimś towarem.
Wprowadzenie takiej wizualizacji wymaga wcześniejszego opracowania dodatkowych kompletów grafik, przedstawiających pojazd w stanie załadowanym. W przypadku prezentowanej drezyny zestaw takich dodatkowych grafik jest bardzo obszerny, gdyż drezynę można przebudowywać do przewozu przeróżnych towarów:
![[Obrazek: 308wm15a.png]](https://openttd-polska.pl/uploads/other/308wm15a.png)
Powyższy zestaw grafik ukazuje drezynę wypełnioną towarami wynikającymi z obsługiwanej kombinacji (CC_PASSANGERS, CC_MAIL, CC_EXPRESS, CC_GOODS). Mamy więc: drewno, stal, żywiec i papier. Pozostałe ładunki wynikające z tej kombinacji (poczta i towary) oraz towary wprowadzone przez inne dodatki (FIRS, ECS, itp.) będą przedstawiane umownie w postaci uniwersalnych skrzyń. Pasażerowie (z oczywistych powodów) nie będą wizualizowani.
Każdy komplet grafik przedstawia dodatkowo wagon załadowany w różnym stopniu: w około 50% oraz w 100%. Grafiki te będą wykorzystane m.in. do animacji procesu ładowania wagonu.
Uaktywnienie powyższych grafik wymaga zdefiniowania w kodzie NML wszystkich nowych spriteset-ów, przedstawiających ładunki, oraz połączenia ich w tzw. grupy sprite-ów (spritegroup), odpowiedzialnych za animacje. Grupa sprite-ów to obiekt, zawierający dwie listy spriteset-ów. Lista loading przedstawia sekwencję stanów pojazdu w trakcie jego załadunku/wyładunku. Natomiast lista loaded przedstawia stany pojazdu już pustego/załadowanego (będącego w trasie). Każda z obu list może zawierać dowolną liczbę spriteset-ów, przy czym pierwszy na liście odpowiada załadunkowi 0%, ostatni: 100%, a pośrednie: kolejnym stanom pośrednim załadunku. Definicja spritegroup-y dla jednego z przykładowych ładunków (towary) będzie wyglądała następująco:
Tak zdefiniowana przykładowa grupa sprite-ów będzie odpowiedzialna za animację, w której ilość widocznego ładunku będzie zależna od stopnia załadowania wagonu:
0 - 33%: wagon pusty,
33 - 66%: wagon z 1 skrzynią,
66 - 100%: wagon z 2 skrzyniami.
Takie przedziały animacji nie są jednak optymalne. Dla wagonu załadowanego np. w 67% bardziej bowiem pasowałby widok wagonu załadowanego w 50% niż w 100%.
Na szczęście "charakterystykę załadunku" można kształtować w dowolny sposób. I tak na przykład, zapis:
... spowoduje, że zależność od stopnia załadowania wagonu będzie teraz wyglądała następująco:
0 - 25%: wagon pusty,
25 - 75%: wagon z 1 skrzynią,
75 - 100%: wagon z 2 skrzyniami.
Zdefiniowane spritegroup-y należy teraz powiązać z konkretnymi rodzajami ładunku. W tym celu konieczna jest rozbudowa naszej tabeli towarów o nowe wizualizowane towary:
Wyświetlanie odpowiednich grafik można zrealizować za pośrednictwem switch-y. Jednak najprostszą metodą jest powiązanie towarów z grafikami bezpośrednio w sekcji graphics pojazdu:
W przypadku pasażerów i turystów wyświetlany jest zawsze tylko jeden i ten sam spriteset, ukazujący drezynę z pustą paką. Dla innych wyszczególnionych ładunków wyświetlane są odpowiednie spritegroup-y. Natomiast w przypadku pozostałych ładunków (również tych nie wymienionych w GRF-ie) będzie wyświetlany uniwersalny spritegroup przedstawiający ładunek skrzyń.
Kompletny kod dodatku NewGRF
Poniżej zamieszczam pełną treść pliku nml, zawierającego wyżej opisane zmiany. Pozostałe pliki, prezentowane w poprzednim poście, nie uległy zmianie.
wm15a.nml
Projekt drezyny - wizualizacja towarów
Kolejnym etapem rozwoju projektu drezyny WM-15A jest dodanie wizualizacji przewożonych ładunków. Dotychczas bowiem drezyna była widoczna w grze cały czas jako pusta, nawet wtedy, kiedy była załadowana jakimś towarem.
Wprowadzenie takiej wizualizacji wymaga wcześniejszego opracowania dodatkowych kompletów grafik, przedstawiających pojazd w stanie załadowanym. W przypadku prezentowanej drezyny zestaw takich dodatkowych grafik jest bardzo obszerny, gdyż drezynę można przebudowywać do przewozu przeróżnych towarów:
![[Obrazek: 308wm15a.png]](https://openttd-polska.pl/uploads/other/308wm15a.png)
Powyższy zestaw grafik ukazuje drezynę wypełnioną towarami wynikającymi z obsługiwanej kombinacji (CC_PASSANGERS, CC_MAIL, CC_EXPRESS, CC_GOODS). Mamy więc: drewno, stal, żywiec i papier. Pozostałe ładunki wynikające z tej kombinacji (poczta i towary) oraz towary wprowadzone przez inne dodatki (FIRS, ECS, itp.) będą przedstawiane umownie w postaci uniwersalnych skrzyń. Pasażerowie (z oczywistych powodów) nie będą wizualizowani.

Każdy komplet grafik przedstawia dodatkowo wagon załadowany w różnym stopniu: w około 50% oraz w 100%. Grafiki te będą wykorzystane m.in. do animacji procesu ładowania wagonu.
Uaktywnienie powyższych grafik wymaga zdefiniowania w kodzie NML wszystkich nowych spriteset-ów, przedstawiających ładunki, oraz połączenia ich w tzw. grupy sprite-ów (spritegroup), odpowiedzialnych za animacje. Grupa sprite-ów to obiekt, zawierający dwie listy spriteset-ów. Lista loading przedstawia sekwencję stanów pojazdu w trakcie jego załadunku/wyładunku. Natomiast lista loaded przedstawia stany pojazdu już pustego/załadowanego (będącego w trasie). Każda z obu list może zawierać dowolną liczbę spriteset-ów, przy czym pierwszy na liście odpowiada załadunkowi 0%, ostatni: 100%, a pośrednie: kolejnym stanom pośrednim załadunku. Definicja spritegroup-y dla jednego z przykładowych ładunków (towary) będzie wyglądała następująco:
Kod:
/* empty sprites */
spriteset(spriteset_wm15a_empty, "gfx/wm15a.png") {
template_sprite_train(4, 20)
}
/* sprites with cargos */
spriteset(spriteset_wm15a_goods_half, "gfx/wm15a.png") {
template_sprite_train(4, 80)
}
spriteset(spriteset_wm15a_goods_full, "gfx/wm15a.png") {
template_sprite_train(4, 120)
}
spritegroup spritegroup_wm15a_goods {
loaded: [spriteset_wm15a_empty, spriteset_wm15a_goods_half, spriteset_wm15a_goods_full];
loading: [spriteset_wm15a_empty, spriteset_wm15a_goods_half, spriteset_wm15a_goods_full];
}Tak zdefiniowana przykładowa grupa sprite-ów będzie odpowiedzialna za animację, w której ilość widocznego ładunku będzie zależna od stopnia załadowania wagonu:
0 - 33%: wagon pusty,
33 - 66%: wagon z 1 skrzynią,
66 - 100%: wagon z 2 skrzyniami.
Takie przedziały animacji nie są jednak optymalne. Dla wagonu załadowanego np. w 67% bardziej bowiem pasowałby widok wagonu załadowanego w 50% niż w 100%.
Na szczęście "charakterystykę załadunku" można kształtować w dowolny sposób. I tak na przykład, zapis:
Kod:
spritegroup spritegroup_wm15a_goods {
loaded: [spriteset_wm15a_empty, spriteset_wm15a_goods_half, spriteset_wm15a_goods_half, spriteset_wm15a_goods_full];
loading: [spriteset_wm15a_empty, spriteset_wm15a_goods_half, spriteset_wm15a_goods_half, spriteset_wm15a_goods_full];
}0 - 25%: wagon pusty,
25 - 75%: wagon z 1 skrzynią,
75 - 100%: wagon z 2 skrzyniami.
Zdefiniowane spritegroup-y należy teraz powiązać z konkretnymi rodzajami ładunku. W tym celu konieczna jest rozbudowa naszej tabeli towarów o nowe wizualizowane towary:
Kod:
/* cargos */
cargotable {
PASS, TOUR, MAIL,
GOOD, WOOD, LVST, STEL, PAPR // <- dodane
}Wyświetlanie odpowiednich grafik można zrealizować za pośrednictwem switch-y. Jednak najprostszą metodą jest powiązanie towarów z grafikami bezpośrednio w sekcji graphics pojazdu:
Kod:
graphics {
PASS: spriteset_wm15a_empty;
TOUR: spriteset_wm15a_empty;
WOOD: spritegroup_wm15a_wood;
LVST: spritegroup_wm15a_lvstck;
STEL: spritegroup_wm15a_steel;
PAPR: spritegroup_wm15a_paper;
default: spritegroup_wm15a_goods;
purchase: spriteset_wm15a_purchase;
can_attach_wagon: switch_wm15a_can_attach;
start_stop: switch_wm15a_start_stop;
cargo_capacity: switch_wm15a_capacity;
}Kompletny kod dodatku NewGRF
Poniżej zamieszczam pełną treść pliku nml, zawierającego wyżej opisane zmiany. Pozostałe pliki, prezentowane w poprzednim poście, nie uległy zmianie.
wm15a.nml
Kod:
grf {
grfid: "TD\01\01";
name: string(STR_GRF_NAME);
desc: string(STR_GRF_DESCRIPTION);
version: 0;
min_compatible_version: 0;
}
/* graphics definition */
template template_sprite_train(x, y) {
// [left_x, upper_y, width, height, offset_x, offset_y]
[x, y, 10, 28, -5, -14]
[x+ 16, y, 26, 28, -15, -17]
[x+ 46, y, 36, 20, -18, -13]
[x+ 86, y, 26, 28, -7, -17]
[x+120, y, 10, 28, -5, -14]
[x+136, y, 26, 28, -15, -17]
[x+166, y, 36, 20, -18, -13]
[x+206, y, 26, 28, -7, -17]
}
template template_sprite_purchase(x, y) {
// [left_x, upper_y, width, height, offset_x, offset_y]
[x, y, 50, 12, -25, -6]
}
/* empty sprites */
spriteset(spriteset_wm15a_purchase, "gfx/wm15a.png") {
template_sprite_purchase(4, 60)
}
spriteset(spriteset_wm15a_empty, "gfx/wm15a.png") {
template_sprite_train(4, 20)
}
spriteset(spriteset_pwm15_purchase, "gfx/wm15a.png") {
template_sprite_purchase(244, 60)
}
spriteset(spriteset_pwm15_empty, "gfx/wm15a.png") {
template_sprite_train(244, 20)
}
/* sprites with cargos */
spriteset(spriteset_wm15a_goods_half, "gfx/wm15a.png") {
template_sprite_train(4, 80)
}
spriteset(spriteset_wm15a_goods_full, "gfx/wm15a.png") {
template_sprite_train(4, 120)
}
spriteset(spriteset_pwm15_goods_half, "gfx/wm15a.png") {
template_sprite_train(244, 80)
}
spriteset(spriteset_pwm15_goods_full, "gfx/wm15a.png") {
template_sprite_train(244, 120)
}
spritegroup spritegroup_wm15a_goods {
loaded: [spriteset_wm15a_empty, spriteset_wm15a_goods_half, spriteset_wm15a_goods_half, spriteset_wm15a_goods_full];
loading: [spriteset_wm15a_empty, spriteset_wm15a_goods_half, spriteset_wm15a_goods_half, spriteset_wm15a_goods_full];
}
spritegroup spritegroup_pwm15_goods {
loaded: [spriteset_pwm15_empty, spriteset_pwm15_goods_half, spriteset_pwm15_goods_half, spriteset_pwm15_goods_full];
loading: [spriteset_pwm15_empty, spriteset_pwm15_goods_half, spriteset_pwm15_goods_half, spriteset_pwm15_goods_full];
}
spriteset(spriteset_wm15a_wood_half, "gfx/wm15a.png") {
template_sprite_train(4, 160)
}
spriteset(spriteset_wm15a_wood_full, "gfx/wm15a.png") {
template_sprite_train(4, 200)
}
spriteset(spriteset_pwm15_wood_half, "gfx/wm15a.png") {
template_sprite_train(244, 160)
}
spriteset(spriteset_pwm15_wood_full, "gfx/wm15a.png") {
template_sprite_train(244, 200)
}
spritegroup spritegroup_wm15a_wood {
loaded: [spriteset_wm15a_empty, spriteset_wm15a_wood_half, spriteset_wm15a_wood_half, spriteset_wm15a_wood_full];
loading: [spriteset_wm15a_empty, spriteset_wm15a_wood_half, spriteset_wm15a_wood_half, spriteset_wm15a_wood_full];
}
spritegroup spritegroup_pwm15_wood {
loaded: [spriteset_pwm15_empty, spriteset_pwm15_wood_half, spriteset_pwm15_wood_half, spriteset_pwm15_wood_full];
loading: [spriteset_pwm15_empty, spriteset_pwm15_wood_half, spriteset_pwm15_wood_half, spriteset_pwm15_wood_full];
}
spriteset(spriteset_wm15a_steel_half, "gfx/wm15a.png") {
template_sprite_train(4, 240)
}
spriteset(spriteset_wm15a_steel_full, "gfx/wm15a.png") {
template_sprite_train(4, 280)
}
spriteset(spriteset_pwm15_steel_half, "gfx/wm15a.png") {
template_sprite_train(244, 240)
}
spriteset(spriteset_pwm15_steel_full, "gfx/wm15a.png") {
template_sprite_train(244, 280)
}
spritegroup spritegroup_wm15a_steel {
loaded: [spriteset_wm15a_empty, spriteset_wm15a_steel_half, spriteset_wm15a_steel_half, spriteset_wm15a_steel_full];
loading: [spriteset_wm15a_empty, spriteset_wm15a_steel_half, spriteset_wm15a_steel_half, spriteset_wm15a_steel_full];
}
spritegroup spritegroup_pwm15_steel {
loaded: [spriteset_pwm15_empty, spriteset_pwm15_steel_half, spriteset_pwm15_steel_half, spriteset_pwm15_steel_full];
loading: [spriteset_pwm15_empty, spriteset_pwm15_steel_half, spriteset_pwm15_steel_half, spriteset_pwm15_steel_full];
}
spriteset(spriteset_wm15a_lvstck_half, "gfx/wm15a.png") {
template_sprite_train(4, 320)
}
spriteset(spriteset_wm15a_lvstck_full, "gfx/wm15a.png") {
template_sprite_train(4, 360)
}
spriteset(spriteset_pwm15_lvstck_half, "gfx/wm15a.png") {
template_sprite_train(244, 320)
}
spriteset(spriteset_pwm15_lvstck_full, "gfx/wm15a.png") {
template_sprite_train(244, 360)
}
spritegroup spritegroup_wm15a_lvstck {
loaded: [spriteset_wm15a_empty, spriteset_wm15a_lvstck_half, spriteset_wm15a_lvstck_half, spriteset_wm15a_lvstck_full];
loading: [spriteset_wm15a_empty, spriteset_wm15a_lvstck_half, spriteset_wm15a_lvstck_half, spriteset_wm15a_lvstck_full];
}
spritegroup spritegroup_pwm15_lvstck {
loaded: [spriteset_pwm15_empty, spriteset_pwm15_lvstck_half, spriteset_pwm15_lvstck_half, spriteset_pwm15_lvstck_full];
loading: [spriteset_pwm15_empty, spriteset_pwm15_lvstck_half, spriteset_pwm15_lvstck_half, spriteset_pwm15_lvstck_full];
}
spriteset(spriteset_wm15a_paper_half, "gfx/wm15a.png") {
template_sprite_train(4, 400)
}
spriteset(spriteset_wm15a_paper_full, "gfx/wm15a.png") {
template_sprite_train(4, 440)
}
spriteset(spriteset_pwm15_paper_half, "gfx/wm15a.png") {
template_sprite_train(244, 400)
}
spriteset(spriteset_pwm15_paper_full, "gfx/wm15a.png") {
template_sprite_train(244, 440)
}
spritegroup spritegroup_wm15a_paper {
loaded: [spriteset_wm15a_empty, spriteset_wm15a_paper_half, spriteset_wm15a_paper_half, spriteset_wm15a_paper_full];
loading: [spriteset_wm15a_empty, spriteset_wm15a_paper_half, spriteset_wm15a_paper_half, spriteset_wm15a_paper_full];
}
spritegroup spritegroup_pwm15_paper {
loaded: [spriteset_pwm15_empty, spriteset_pwm15_paper_half, spriteset_pwm15_paper_half, spriteset_pwm15_paper_full];
loading: [spriteset_pwm15_empty, spriteset_pwm15_paper_half, spriteset_pwm15_paper_half, spriteset_pwm15_paper_full];
}
/* cargos */
cargotable {
PASS, TOUR, MAIL,
GOOD, WOOD, LVST, STEL, PAPR
}
/* switches */
switch(FEAT_TRAINS, SELF, switch_wm15a_can_attach, vehicle_type_id) {
item_pwm15: return CB_RESULT_ATTACH_ALLOW;
return string(STR_WM15A_ATTACH_DISALLOW);
}
switch (FEAT_TRAINS, SELF, switch_wm15a_start_stop, num_vehs_in_consist) {
1..2: return 0xFF;
return string(STR_WM15A_CANNOT_START);
}
switch (FEAT_TRAINS, SELF, switch_wm15a_capacity, cargo_type_in_veh) {
PASS..TOUR: return 7;
MAIL: return 30;
return 15;
}
switch (FEAT_TRAINS, SELF, switch_pwm15_capacity, cargo_type_in_veh) {
MAIL: return 30;
return 15;
}
/* Define the actual train */
item(FEAT_TRAINS, item_wm15a) {
property {
/* common properties */
name: string(STR_WM15A_NAME);
climates_available: bitmask(CLIMATE_TEMPERATE, CLIMATE_ARCTIC);
introduction_date: date(1900, 1, 1); // !!!
model_life: VEHICLE_NEVER_EXPIRES;
vehicle_life: 30;
reliability_decay: 20;
refittable_cargo_classes: bitmask(CC_PASSENGERS, CC_MAIL, CC_EXPRESS, CC_PIECE_GOODS);
non_refittable_cargo_classes: bitmask(CC_REFRIGERATED);
refittable_cargo_types: bitmask();
loading_speed: 5;
cost_factor: 18; // 7%
running_cost_factor: 25; // 10%
/* train properties */
sprite_id: SPRITE_ID_NEW_TRAIN;
speed: 80 km/h;
misc_flags: bitmask();
refit_cost: 0;
track_type: RAIL;
ai_special_flag: AI_FLAG_CARGO;
power: 147 kW;
running_cost_base: RUNNING_COST_DIESEL;
dual_headed: 0;
cargo_capacity: 15;
weight: 20 ton;
ai_engine_rank: 0; // not intended to be used by the ai
engine_class: ENGINE_CLASS_DIESEL;
extra_power_per_wagon: 0 kW;
tractive_effort_coefficient: 0.3;
air_drag_coefficient: 0.1;
shorten_vehicle: SHORTEN_TO_8_8;
extra_weight_per_wagon: 0;
visual_effect_and_powered: visual_effect_and_powered(VISUAL_EFFECT_DIESEL, -2, DISABLE_WAGON_POWER);
extra_weight_per_wagon: 0 ton;
bitmask_vehicle_info: 0;
}
graphics {
PASS: spriteset_wm15a_empty;
TOUR: spriteset_wm15a_empty;
WOOD: spritegroup_wm15a_wood;
LVST: spritegroup_wm15a_lvstck;
STEL: spritegroup_wm15a_steel;
PAPR: spritegroup_wm15a_paper;
default: spritegroup_wm15a_goods;
purchase: spriteset_wm15a_purchase;
can_attach_wagon: switch_wm15a_can_attach;
start_stop: switch_wm15a_start_stop;
cargo_capacity: switch_wm15a_capacity;
}
}
item(FEAT_TRAINS, item_pwm15) {
property {
/* common properties */
name: string(STR_PWM15_NAME);
climates_available: bitmask(CLIMATE_TEMPERATE, CLIMATE_ARCTIC);
introduction_date: date(1900, 1, 1); // !!!
model_life: VEHICLE_NEVER_EXPIRES;
vehicle_life: 30;
reliability_decay: 20;
refittable_cargo_classes: bitmask(CC_MAIL, CC_EXPRESS, CC_PIECE_GOODS);
non_refittable_cargo_classes: bitmask(CC_REFRIGERATED);
refittable_cargo_types: bitmask();
loading_speed: 5;
cost_factor: 25; // 10%
running_cost_factor: 0;
/* train properties */
sprite_id: SPRITE_ID_NEW_TRAIN;
speed: 80 km/h;
misc_flags: bitmask();
refit_cost: 0;
track_type: RAIL;
ai_special_flag: AI_FLAG_CARGO;
power: 0;
running_cost_base: RUNNING_COST_NONE;
dual_headed: 0;
cargo_capacity: 15;
weight: 12 ton;
ai_engine_rank: 0; // not intended to be used by the ai
engine_class: ENGINE_CLASS_DIESEL;
extra_power_per_wagon: 0 kW;
tractive_effort_coefficient: 0.3;
air_drag_coefficient: 0.1;
shorten_vehicle: SHORTEN_TO_6_8;
extra_weight_per_wagon: 0;
visual_effect_and_powered: visual_effect_and_powered(VISUAL_EFFECT_DISABLE, 0, DISABLE_WAGON_POWER);
extra_weight_per_wagon: 0 ton;
bitmask_vehicle_info: 0;
}
graphics {
WOOD: spritegroup_pwm15_wood;
LVST: spritegroup_pwm15_lvstck;
STEL: spritegroup_pwm15_steel;
PAPR: spritegroup_pwm15_paper;
default: spritegroup_pwm15_goods;
purchase: spriteset_pwm15_purchase;
cargo_capacity: switch_pwm15_capacity;
}
}![OpenTTD #Polska - Polskie forum gry OpenTTD [ARCHIWUM] OpenTTD #Polska - Polskie forum gry OpenTTD [ARCHIWUM]](https://forum.openttd.pl/images/logo.png)
