Just my 2cc.
For a good project start, we should address three domains:
1. generate a grammatic specification of current ".nfo", preferably in BNF notation,
2. specify a "target language", generate a grammatic specification for it and build a parser,
3. build the "code generation" part for the compiler.
I´d like to address item 2 from a user´s POV and present some ideas for the target language. My approach being simply to "re-implement" existing (train) .nfo code into a "mock-C" language.
Some thoughts before on the subject.
I don´t think the main benefit of a "compiler" and a new "language" would be the move to more "human-readable" vars and operations in the first place. Instead, the real benefit would be the hiding of those many interdependencies in current .nfo and releasing the programmer from the burden of today´s superfluous work. (Therefore, we need to work on item 1.)
A neat side effect would be that we don´t have to implement each and every .nfo feature or var in a 1:1 fashion, but instead we could encapsulate the underlying mechanism into the "language" itself (either in special data types or in special functions). E.g., see "var 7F" versus the data type "nfo_colormap" which should define a new colourmap and have GRM provide the necessary new sprite for it, including all the needed overhead.
Now, let´s see how the first (very simple) engine, the BR92 of DBXL would look like.
1.
The current "action0 " for a certain engine could be simply replaced by a var of type "nfo_trainveh", a structure using "tags", simply filled with the needed constants:
Code: Select all
nfo_trainveh br92 := {
id = 0; // engine ID
intro = 1920; // introduction year
rel_decr = 9; // rate of reliability decreas
v_life = 28; // vehicle life in years
m_life = 28; // model life in years
track_type = RAIL; // track type
climate = TEMP | ARCTIC; // climates this engine will work in
AI_loco = FALSE; // AI uses this one for passenger transport exclusively
speed = 50; // speed in km/h
power = 500; // power in hp, PS, whatever. We should move to kW
Rcost_mul = 96; // running cost multiplier
Rcost_base = _STEAM; // running cost base
sprite_ID = NEW; // this needs new graphics
dual_head = FALSE; // single head engine
cargo_capacity = NIL; // w/o any cargo capacity
cargo_type = NIL; // sic!
weight = 57; // weight in tons
price_facto r= 2; // purchase price multiplier
AI_rank = 1; // AI rank of this engine
traction_type = STEAM; // for effects: steam plumes, diesel soot or elctric sparks
sort_purchaselist = 0; // sort in purchase list ???
callbacks = cb_COLOR | cb_SOUND; // callbacks used
trac_effort = 80; // tractive effort in kN
shortage = 1; // short engine, level 1
retire = 3 // early retirement, 3 years
}
2.
Vars should be strongly typed. However, we would gain some more flexibility if there´d be distinct classes for vars which could be accessed in a "hierarchical" way. E.g., a var of type "nfo_spriteblock" could be referenced by one of type "nfo_cid" (which would be the standard var type, BTW) as well. Other types could include "nfo_text" for C000/D000 texts, "nfo_sound" for sounds, "nfo_colormap" for colour translation tables, etc. pp.
Code: Select all
nfo_cid f1, f2, f3, f4; // some vars
nfo_spriteblock br92_display {
<sprite info>
} // special graphics for purchasing list
nfo_spriteblock br92_frame1 {
<sprite info>
} // animation frame 1
nfo_spriteblock br92_frame2 {
<sprite info>
} // animation frame 2
nfo_spriteblock br92_frame3 {
<sprite info>
} // animation frame 3
nfo_spriteblock br92_frame4 {
<sprite info>
} // animation frame 4
f1 := br92_frame1; // just to demonstrate var assignment. In fact, we wouldn´t need vars here
f2 := br92_frame2;
f3 := br92_frame3;
f4 := br92_frame4;
// assemble animation frames
nfo_cid br92_animated := switch (nfo_animcounter >>8 &03) {
case 0: f1; // respectively, "case 0: br92_frame1;" etc ... s.a.
case 1: f2;
case 2: f3;
default: f4;
}
I´d propose "action2s" to rely heavily on the "switch statement" when checking that large number of .nfo vars available. In addition, we could easily allow for the complete C set of bit operators.
3.
Text should be handled transparently, regardless if it´s a class C000 or D000 text:
Code: Select all
nfo_text br92text { txt_ALL; "BR92 (ex wü T5)";} // text to use in action4 for all languages
nfo_text br92addtext {
txt_ENG; "axle scheme: E \n builder: Union \n tank engine for basic passenger and freight services.\n";
txt_GER; "Achsfolge: E \n Hersteller: Union \n Universelle Tenderlok für einfachen Personen- und Güterverkehr";
} // additional text for callback 23
nfo_sound br92sound {
<filename>
} // define engine sounds
nfo_colormap KPEV { ..... } // define new colourmap and have GRM provide a new sprite for it
nfo_colormap ttd_GREEN; // use original TTD colormap #30E
nfo_cid n1, n2, br92, br92display; // more vars
n1 := switch (nfo_date_year) {
1920 ... 1927: KPEV; // to have ranges too would be very convenient!
default: ttd_GREEN;
} // check global nfo var "date_year" and branch accordingly
n2 := switch (nfo_soundevent) {
snd_START ... snd_TUNNEL: br92_sound;
default: void;
} // check global nfo var "soundevent", use sound when engine starts and/or in tunnel
br92 := switch (nfo_callback) {
case cb_COLOR: n1; // recolouring
case cb_SOUND: n2; // sound
default: br92_animated; // graphics
} // check global nfo var "callback", branch according to callback type
br92display := switch (nfo_callback) {
case cb_COLOR: n1; // recolouring
case cb_TEXT: br92addtext; // additional text in purchase menu
default: br92_display; // graphics for purchase menu
}
// make_engine (nfo_trainveh, nfo_cid, nfo_cid, nfo_text);
make_engine (br92, br92, br92display, br92text);
regards
Michael