Moin,
da sich hier auch gelegentlich Leute tummeln, die das eine oder andere NewGRF etwas modden oder schreiben, ein kurzer Hinweis auf ein neues Werkzeug, welches - zumindest meiner Meinung nach - das Schreiben von NewGRFs um einiges vereinfacht: NML
Worum geht's?
NML ist eine NewGRF Hochsprache mit Compiler, welcher nml-Dateien in newgrf - Dateien (und, so gewünscht, nfo) übersetzt.
Der Hauptautor ist Yexo, weitere wichtige Autoren sind Hirundo und Alberth. Inzwischen ist NML so weit gediehen, dass es offiziell bekannt geben werden kann, da es nicht mehr wahrscheinlich ist, dass der Syntax sich ändert.
Wie funktioniert's?
Ein NewGRF in NML zu schreiben funktioniert ähnlich dem Schreiben eines Programms in einer Programmiersprache - mit der Ausnahme, dass keine Sub-Routinen verfügbar sind; man kann die üblichen Zuweisungen und bedingten Anweisungen nutzen. Weiter unten habe ich eine kleine Gegenüberstellung von NFO und NML am Beispiel eines kleine Railtypes NewGRF angehängt. Einer der m.M.n. angenehmen Änderungen ist, dass NML erlaubt beliebige Bilddateien zu lesen (vorausgesetzt sie sind in 8bpp und der korrekten Palette und von der Python Image Library unterstützt). PNG bevorzuge ich persönlich, da Browser sie auch anzeigen können.
Das grf wird mit einem einfachen Kommandozeilen-Aufruf erzeugt:
Code:
nml2nfo --nfo swedishrails.nfo --grf swedishrails.grf swedishrails.nml
Die Erzeugung des NFO kann natürlich ausgelassen werden, da das grf direkt geschrieben wird; der NFO - Output dient eigentlich nur dem Vergleich für die Entwickler
Was braucht man? Wo kriegt man's her?
Man braucht Python 2.5 ... < 3.0 und eine Reihe Module, insbes. die Python Image Library und Python lz77 sowie natürlich NML selbst.
Die neueste Version von
NML gibt's hier und die
Dokumentation findet sich hier welche leider aber noch sehr unvollständig ist.
Viele (die meisten?) wichtigen NewGRF - Features sind bereits implementiert in NML, aber hier und dort gibt's noch ein paar fehlende Punkte. Diese werden jedoch laufend weniger. Und wenn etwas bestimmtes fehlt, so kann man die Entwickler ansprechen, die dann diesen Wunsch ggf. relativ zügig umsetzen. Feedback, Kommentare, Feature-Requests und Fehlerberichte sind immer willkommen. Siehe auch unseren
Bug tracker.
Schließen möchte ich mit einem kleinen Vergleich zwischen traditioneller NFO - Programmierung mit der NML - Programmierung vorstellen. In den letzten Tagen habe ich am SwedishRails Set gearbeitet, welches ich in NML schreibe. Zuerst zeige ich den NFO - Code, im Anschluß das gleiche in NML. Teile, die nur bereits gesagtes wiederholen werde ich im gezeigten Sourcecode auslassen. Zwei vollständig in NML programmierte NewGRFs sind
OpenGFX+ und
SwedishRails, insbes. in OpenGFX+ finden sich auch Beispiele für Fahrzeuge.
NFO
Zuerst definieren wir das NewGRF:
Code:
// Automatically generated by GRFCODEC. Do not modify!
// (Info version 7)
// Escapes: 2+ = 71 = D= = DR 2- = 70 = D+ = DF 2< = 7= = D- = DC 2> = 7! = Du* = DM 2u< = 7< = D* = DnF 2u> = 7> = Du<< = DnC
2/ = 7G = D<< = DO 2% = 7g = D& 2u/ = 7gG = D| 2u% = 7GG = Du/ 2* = 7gg = D/ 2& = 7c = Du% 2| = 7C = D% 2^ 2sto = 2s 2rst = 2r 2+
2ror = 2rot
// Format: spritenum pcxfile xpos ypos compression ysize xsize xrel yrel
0 * 4 \d416
1 * 92 08 07 "SER0" "ÞSwedish Rails nightly-r52M" 00 "ÞSwedish rails are a replacement for the default rails" 00
Dann definieren wir die standard Schienen um und definieren sie kompatibel zu elektrifizierten Schienen. Wir weisen diesen Schienen neue Grafiken für Schienen, deren Unterbau, Tunnel und Depots zu. Da nur OpenTTD Railtypes unterstützt, tun wir dies nur, wenn dieses NewGRF unter OpenTTD läuft:
Code:
412 * 9 07 9D 04 \7! \dx00000001 04
413 * 18 00 10 \b2 01 FF \wx000A
08 "RAIL"
0E \b1 "ELRL"
414 * 22 03 10 01 0A \b5
01 \wx00FB
02 \wx00FC
07 \wx00FD
03 \wx00F8
08 \wx00FA
\wx00FE
Wir nutzen var40 um zu unterscheiden, ob wir schneebedeckte Grafiken verwenden oder normale:
Code:
383 * 6 01 10 \b1 FF \wx0004
384 src/gfx/tunnel_track_normal.png 75 0 01 31 64 -31 0
385 src/gfx/tunnel_track_normal.png 0 0 01 31 64 -31 0
386 src/gfx/tunnel_track_normal.png 75 50 01 31 64 -31 0
387 src/gfx/tunnel_track_normal.png 0 50 01 31 64 -31 0
388 * 7 02 10 FA \b1 \b0
\w0
389 * 6 01 10 \b1 FF \wx0004
390 src/gfx/tunnel_track_snow.png 75 0 01 31 64 -31 0
391 src/gfx/tunnel_track_snow.png 0 0 01 31 64 -31 0
392 src/gfx/tunnel_track_snow.png 75 50 01 31 64 -31 0
393 src/gfx/tunnel_track_snow.png 0 50 01 31 64 -31 0
394 * 7 02 10 F9 \b1 \b0
\w0
395 * 23 02 10 F8 89
40 00 \dx000000FF \b1
\wx00F9 \dx00000004 \dx00000004
\wx00FA
Wir liefern neue Grafiken für Tunnelportale im gemäßigten Klima - aber nur wenn TTDPatch läuft oder per NewGRF-Parameter nach gefragt wird:
Code:
2 * 9 0D 40 \D- 9D FF \dx00000000
3 * 5 0D 40 \D/ 40 40
4 * 9 0D 40 \D- FF 40 \dx00000001
5 * 5 0D 0A \D| 40 00
6 * 9 0D 41 \D- 83 FF \dx00000000
7 * 5 0D 41 \D/ 41 41
8 * 9 0D 41 \D- FF 41 \dx00000001
9 * 9 0D 42 \D- 0A FF \dx00000001
10 * 5 0D 42 \D/ 42 42
11 * 9 0D 42 \D- FF 42 \dx00000001
12 * 5 0D 40 \D& 41 42
13 * 9 07 40 04 \7= \dx00000000 11
48 * 5 0A \b1 \b8 \wx093D
49 src/gfx/temperate_rails_TTD.png 4 6 01 33 33 -31 -2
50 src/gfx/temperate_rails_TTD.png 52 6 01 38 40 -7 -38
51 src/gfx/temperate_rails_TTD.png 95 6 01 19 27 -31 2
52 src/gfx/temperate_rails_TTD.png 129 6 01 23 42 -9 -30
53 src/gfx/temperate_rails_TTD.png 177 6 01 18 25 8 3
54 src/gfx/temperate_rails_TTD.png 209 6 01 23 43 -31 -30
55 src/gfx/temperate_rails_TTD.png 258 6 01 33 33 0 -2
56 src/gfx/temperate_rails_TTD.png 295 6 01 38 40 -31 -38
109 * 2 10 11
Und nun das ganze in NML:
Code:
grf {
grfid : "SER0";
name : string(STR_GRF_NAME);
desc : string(STR_GRF_DESCRIPTION);
}
und in der Sprach-Datei, welche der OpenTTD-Konvention für Sprachdateien folgt:
Code:
lang: 7F
STR_GRF_NAME :Swedish Rails {VERSION}
STR_GRF_DESCRIPTION :Swedish rails are a replacement for the default rails
Railtypes nur für OpenTTD definieren:
Code:
if (ttd_platform == PLATFORM_OPENTTD) {
item(FEAT_RAILTYPES, rail, 0x0A) {
property {
label: "RAIL";
compatible_railtype_list: ["ELRL"];
// We don't want to change rail properties. Just provide new graphics
}
graphics {
TRACKOVERLAY: ground_switch_overlay;
UNDERLAY: ground_switch_underlay;
LEVEL_CROSSINGS: level_crossing_group;
TUNNELS: tunnel_switch;
DEPOTS: depot_normal_rail_group;
track_overlay_group; // irrelevant, we just need one set here
}
}
} // of OpenTTD only
Normale und verschneite Gleise für Tunnel definieren. Wir nutzen die gleichen Alignment-Informationen für beide Grafiken, da wir sie in identischen Dateien gespeichert haben:
Code:
template tmpl_tunnel_tracks() {
[ 75, 0, 64,31, -31, 0]
[ 0, 0, 64,31, -31, 0]
[ 75, 50, 64,31, -31, 0]
[ 0, 50, 64,31, -31, 0]
}
spriteblock(FEAT_RAILTYPES) {
spriteset(tunnel_overlay, "src/gfx/tunnel_track_normal.png") {
tmpl_tunnel_tracks()
}
spritegroup tunnel_group {
default: tunnel_overlay;
}
}
spriteblock(FEAT_RAILTYPES) {
spriteset(tunnel_overlay_snow, "src/gfx/tunnel_track_snow.png") {
tmpl_tunnel_tracks()
}
spritegroup tunnel_snow_group {
default: tunnel_overlay_snow;
}
}
switch(FEAT_RAILTYPES, SELF, tunnel_switch, terrain_type) {
TILETYPE_SNOW: tunnel_snow_group;
tunnel_group;
}
Ersetzen der Tunnelportale im gemäßigten Klima für TTDPatch oder wenn per Parameter erwünscht. Wiederum nutzen wir die Alignment-Informationen für die Tunnelsprites, um sie auch für andere Klimata zu nutzen - nur verschoben um einen konstanten Offset in vertikaler Richtung:
Code:
template tmpl_tunnel(y) {
[ 4, 6, 33,33, -31, -2]
[ 52, 6, 40,38, -7,-38]
[ 95, 6, 27,19, -31, 2]
[129, 6, 42,23, -9,-30]
[177, 6, 25,18, 8, 3]
[209, 6, 43,23, -31,-30]
[258, 6, 33,33, -0, -2]
[295, 6, 40,38, -31,-38]
}
param[10] = (ttd_platform == PLATFORM_TTDPATCH) | (param[0]); // Use TTD ground sprites
if (((climate == CLIMATE_ARCTIC) | (climate == CLIMATE_TEMPERATE)) & (param[10] == 1)) {
// Standard temperate rail sprites
replace (1037, "src/gfx/snow_rails_TTD.png") {
tmpl_ttd_ground(358)
}
// Replacement of standard rail tunnels in temperate
replace (2397, "src/gfx/snow_rails_TTD.png") {
tmpl_tunnel(6)
}
} // snow for arctic + temperate TTD mode