Bridges in TTD, TTDPatch and OpenTTD are quite simplistic, they lack most of the attributes other features in TTD offer. In fact, there are only means to define a handful of properties for a bridge, and a layout definition, determining what sprites should be displayed and where they are to be displayed for the various bridge segments and bridge types (rail, road, monorail, maglev).
This tutorial will deal primarily with the two different ways of setting up a bridge layout, either by using and/or overwriting TTD's original sprites, or by allocating and providing new sprites.
In case of new graphic sprites, the bridge's graphics are supplied by a spriteblock() function, either overriding original TTD sprites (most probably those of the toyland climate), or by allocating new sprites for the needed graphics.
The definition sets the feature properties of a bridge: its ID, its length, price, max speed, etc. Please note that a bridge definition may only occur after a prior grfinit() function call for the newGRF file.
Following definition will be used for both of the examples:
// define the bridge purchase text deftxt(_mybridge, ALL,"Test bridge", D, UTF8 "Brückentest", I, "Ponte di prova" ) // rebuilt TTD's original suspension steel (golden) bridge definebridge(5, intro(1950) minlength(3) maxlength(9) price(50) maxspeed(80 km/h) flags(NOFARPILLARS) purchasetext(_mybridge) // bridgerailtext(_railtext) // bridgeroadtext(_roadtext) )
This establishes a copy of TTD´s original suspension steel bridge (in its gold-coloured variant). The ID is "5", introduction year is 1950, the bridge can be built for all lengths between 3 tiles and 9 tiles, purchase price factor is "50", the bridge allows for a max speed of 80 km/h, there are no "far" pillars being drawn for "higher" bridges, and there´s a purchase text defined by function deftxt().
Be aware that following examples don't result into 100% complete newGRFs, but instead concentrate on the most important code features. E.g., in the given bridge property functions you'd have to add even more properties to get a well-working newGRF.
The first example is the more straightforward application. It will simply overwrite a bridge's original graphics and replace them by newly-defined graphic sprites.
setfeature(_BRIDGE) // EXIT if Toyland skipif(EXIT,CLIMATE,==,TOYLAND) grf_init(GRF_BRIDGETUT1, ...) // bridge definition as above layout(5,0, segment( railbridge(front(0x99F+_BYELLOW, 0x997+_BYELLOW), back(0x9A9+_BYELLOW, 0x9A5+_BYELLOW), pillar(0x9B1, 0x9AD)) roadbridge(front(0x99F+_BYELLOW, 0x997+_BYELLOW), back(0x99D+_BYELLOW, 0x995+_BYELLOW), pillar(0x9B1, 0x9AD)) monobridge(front(0x99F+_BYELLOW, 0x997+_BYELLOW), back(0x10F2+_BYELLOW, 0x10EE+_BYELLOW), pillar(0x9B1, 0x9AD)) mlevbridge(front(0x99F+_BYELLOW, 0x997+_BYELLOW), back(0x111A+_BYELLOW, 0x1116+_BYELLOW), pillar(0x9B1, 0x9AD)) ) segment( railbridge(front(0x9A0+_BYELLOW, 0x998+_BYELLOW), back(0x9AA+_BYELLOW, 0x9A6+_BYELLOW), pillar(0x9B2, 0x9AE)) roadbridge(front(0x9A0+_BYELLOW, 0x998+_BYELLOW), back(0x99E+_BYELLOW, 0x996+_BYELLOW), pillar(0x9B2, 0x9AE)) monobridge(front(0x9A0+_BYELLOW, 0x998+_BYELLOW), back(0x10F3+_BYELLOW, 0x10EF+_BYELLOW), pillar(0x9B2, 0x9AE)) mlevbridge(front(0x9A0+_BYELLOW, 0x998+_BYELLOW), back(0x111B+_BYELLOW, 0x1117+_BYELLOW), pillar(0x9B2, 0x9AE)) ) segment( railbridge(front(0x9A4+_BYELLOW, 0x99C+_BYELLOW), back(0x9AC+_BYELLOW, 0x9A8+_BYELLOW), pillar(0x9B4, 0x9B0)) roadbridge(front(0x9A4+_BYELLOW, 0x99C+_BYELLOW), back(0x9A2+_BYELLOW, 0x99A+_BYELLOW), pillar(0x9B4, 0x9B0)) monobridge(front(0x9A4+_BYELLOW, 0x99C+_BYELLOW), back(0x10F5+_BYELLOW, 0x10F1+_BYELLOW), pillar(0x9B4, 0x9B0)) mlevbridge(front(0x9A4+_BYELLOW, 0x99C+_BYELLOW), back(0x111D+_BYELLOW, 0x1119+_BYELLOW), pillar(0x9B4, 0x9B0)) ) segment( railbridge(front(0x9A3+_BYELLOW, 0x99B+_BYELLOW), back(0x9AB+_BYELLOW, 0x9A7+_BYELLOW), pillar(0x9B3, 0x9AF)) roadbridge(front(0x9A3+_BYELLOW, 0x99B+_BYELLOW), back(0x9A1+_BYELLOW, 0x999+_BYELLOW), pillar(0x9B3, 0x9AF)) monobridge(front(0x9A3+_BYELLOW, 0x99B+_BYELLOW), back(0x10F4+_BYELLOW, 0x10F0+_BYELLOW), pillar(0x9B3, 0x9AF)) mlevbridge(front(0x9A3+_BYELLOW, 0x99B+_BYELLOW), back(0x111C+_BYELLOW, 0x1118+_BYELLOW), pillar(0x9B3, 0x9AF)) ) segment( railbridge(front(0x9BA+_BYELLOW, 0x9B9+_BYELLOW), back(0x9B6+_BYELLOW, 0x9B5+_BYELLOW), pillar(0x9BC, 0x9BB)) roadbridge(front(0x9BA+_BYELLOW, 0x9B9+_BYELLOW), back(0x9B8+_BYELLOW, 0x9B7+_BYELLOW), pillar(0x9BC, 0x9BB)) monobridge(front(0x9BA+_BYELLOW, 0x9B9+_BYELLOW), back(0x10F7+_BYELLOW, 0x10F6+_BYELLOW), pillar(0x9BC, 0x9BB)) mlevbridge(front(0x9BA+_BYELLOW, 0x9B9+_BYELLOW), back(0x111F+_BYELLOW, 0x111E+_BYELLOW), pillar(0x9BC, 0x9BB)) ) segment( railbridge(front(0x9C1+_BYELLOW, 0x9C2+_BYELLOW), back(0x9BD+_BYELLOW, 0x9BE+_BYELLOW), pillar(0, 0)) roadbridge(front(0x9C1+_BYELLOW, 0x9C2+_BYELLOW), back(0x9BF+_BYELLOW, 0x9C0+_BYELLOW+_BYELLOW), pillar(0, 0)) monobridge(front(0x9C1+_BYELLOW, 0x9C2+_BYELLOW), back(0x10F8+_BYELLOW, 0x10F9+_BYELLOW), pillar(0, 0)) mlevbridge(front(0x9C1+_BYELLOW, 0x9C2+_BYELLOW), back(0x1120+_BYELLOW, 0x1121+_BYELLOW), pillar(0, 0)) ) segment( railbridge(flat(front(0x985+_BYELLOW, 0x987+_BYELLOW), back(0x986+_BYELLOW, 0x988+_BYELLOW)), ramp(front(0x989+_BYELLOW, 0x98B+_BYELLOW),back(0x98A+_BYELLOW,0x98C+_BYELLOW))) roadbridge(flat(front(0x98D+_BYELLOW, 0x98F+_BYELLOW), back(0x98E+_BYELLOW, 0x990+_BYELLOW)), ramp(front(0x991+_BYELLOW, 0x993+_BYELLOW),back(0x992+_BYELLOW,0x994+_BYELLOW))) monobridge(flat(front(0x10E6+_BYELLOW, 0x10E8+_BYELLOW), back(0x10E7+_BYELLOW, 0x10E9+_BYELLOW)), ramp(front(0x10EA+_BYELLOW, 0x10EC+_BYELLOW),back(0x10EB+_BYELLOW,0x10ED+_BYELLOW))) mlevbridge(flat(front(0x110E+_BYELLOW, 0x1110+_BYELLOW), back(0x110F+_BYELLOW, 0x1111+_BYELLOW)), ramp(front(0x1112+_BYELLOW, 0x1114+_BYELLOW),back(0x1113+_BYELLOW,0x1115+_BYELLOW))) ) )
The second example deals with the more sophisticated method of using new sprites for the bridge's graphics. It is the more flexible method, because it doesn't need to compete for original TTD sprites against other sets.
If recolouring is needed, the required recolour maps are to be defined as well.
setfeature(_BRIDGE)
setpath(..\sprites)
// EXIT if Toyland
skipif(EXIT,CLIMATE,==,TOYLAND)
grf_init(GRF_BRIDGETUT1, ...)
// bridge definition as above ...
// allocating bridge sprites
spriteblock(ALLOCATE,
set(
// [0] b_front
sprite(test_bridge.pcx 2 40 01 47 47 -44 -35)
sprite(test_bridge.pcx 66 40 01 42 52 -49 -30)
sprite(test_bridge.pcx 130 40 01 47 52 -49 -35)
sprite(test_bridge.pcx 194 40 01 42 47 -44 -29)
sprite(test_bridge.pcx 258 40 01 47 47 1 -34)
sprite(test_bridge.pcx 322 40 01 42 52 1 -29)
sprite(test_bridge.pcx 386 40 01 47 52 1 -34)
sprite(test_bridge.pcx 450 40 01 42 47 0 -29)
// [8] b_rail
sprite(test_bridge.pcx 514 40 01 47 50 -23 -24)
sprite(test_bridge.pcx 578 40 01 41 50 -23 -18)
sprite(test_bridge.pcx 642 40 01 47 50 -23 -24)
sprite(test_bridge.pcx 706 40 01 41 50 -24 -17)
sprite(test_bridge.pcx 2 104 01 47 50 -23 -23)
sprite(test_bridge.pcx 66 104 01 41 50 -23 -17)
sprite(test_bridge.pcx 130 104 01 47 50 -23 -23)
sprite(test_bridge.pcx 194 104 01 41 50 -23 -17)
// [16] b_road
sprite(test_bridge.pcx 258 104 01 47 50 -23 -24)
sprite(test_bridge.pcx 322 104 01 41 50 -23 -18)
sprite(test_bridge.pcx 386 104 01 47 50 -23 -24)
sprite(test_bridge.pcx 450 104 01 41 50 -23 -17)
sprite(test_bridge.pcx 514 104 01 47 50 -23 -23)
sprite(test_bridge.pcx 578 104 01 41 50 -23 -17)
sprite(test_bridge.pcx 642 104 01 47 50 -23 -23)
sprite(test_bridge.pcx 706 104 01 41 50 -23 -17)
// [24] b_mono
sprite(test_bridge.pcx 2 168 01 47 50 -23 -24)
sprite(test_bridge.pcx 66 168 01 41 50 -23 -18)
sprite(test_bridge.pcx 130 168 01 47 50 -23 -24)
sprite(test_bridge.pcx 194 168 01 41 50 -23 -17)
sprite(test_bridge.pcx 258 168 01 47 50 -23 -23)
sprite(test_bridge.pcx 322 168 01 41 50 -23 -17)
sprite(test_bridge.pcx 386 168 01 47 50 -23 -23)
sprite(test_bridge.pcx 450 168 01 41 50 -23 -17)
// [32] b_maglev
sprite(test_bridge.pcx 514 168 01 47 50 -23 -24)
sprite(test_bridge.pcx 578 168 01 41 50 -23 -18)
sprite(test_bridge.pcx 642 168 01 47 50 -23 -24)
sprite(test_bridge.pcx 706 168 01 41 50 -23 -17)
sprite(test_bridge.pcx 2 232 01 47 50 -23 -23)
sprite(test_bridge.pcx 66 232 01 41 50 -23 -17)
sprite(test_bridge.pcx 130 232 01 47 50 -23 -23)
sprite(test_bridge.pcx 194 232 01 41 50 -23 -17)
// [40] b_pillar
sprite(test_bridge.pcx 258 232 01 13 12 -5 -7)
sprite(test_bridge.pcx 290 232 01 13 12 -5 -7)
// [42] b_pillar_s (shifted pillars)
sprite(test_bridge.pcx 258 232 01 13 12 -37 9)
sprite(test_bridge.pcx 290 232 01 13 12 27 9)
// b_pal (recolour tables)
// [44] blue grey, rail
colourtable(DOSMAP,
47 .. 48, D7 .. D8,
49 .. 4F, 10 .. 16
)
// [45] ocean blue, road
colourtable(DOSMAP,
47 .. 48, D7 .. D8,
49 .. 4F, 9A .. A0
)
// [46] golden, mono + maglev
colourtable(DOSMAP,
47 .. 4F, 3C .. 44,
)
)
)
spriteset(0)
// entry points for sprites
define(b_base,0)
define(b_front,{eval(b_base+$1)})
define(b_rail,{eval(b_front(8)+$1)})
define(b_road,{eval(b_rail(8)+$1)})
define(b_mono,{eval(b_road(8)+$1)})
define(b_mlev,{eval(b_mono(8)+$1)})
define(b_pillar,{eval(b_mlev(8)+$1)})
define(b_pillar_s,{eval(b_pillar(2)+$1)})
define(b_pal,{eval((b_pillar_s(2)+$1) << 16)})
// general bridge segment
define(b_seg,{
segment(
railbridge(front(b_front($1)+b_pal(0), b_front($2)+b_pal(0)), back(b_rail($1)+b_pal(0), b_rail($2)+b_pal(0)), pillar($3, $4))
roadbridge(front(b_front($1)+b_pal(1), b_front($2)+b_pal(1)), back(b_road($1)+b_pal(1), b_road($2)+b_pal(1)), pillar($3, $4))
monobridge(front(b_front($1)+b_pal(2), b_front($2)+b_pal(2)), back(b_mono($1)+b_pal(2), b_mono($2)+b_pal(2)), pillar($3, $4))
mlevbridge(front(b_front($1)+b_pal(2), b_front($2)+b_pal(2)), back(b_mlev($1)+b_pal(2), b_mlev($2)+b_pal(2)), pillar($3, $4))
)}
)
// special bridge segments:
//
// segment entry
// bridge entry gate
// segment exit
// segment body
// segment connector
define(b_segentr,{b_seg(0,4, NONE, NONE)}) // this is '/', without pillar
define(b_segent0,{b_seg(0,4, b_pillar_s(0), b_pillar_s(1))}) // this is '/', with front pillar
define(b_segexit,{b_seg(1,5,b_pillar(0), b_pillar(1))}) // this is '\', with pillar
define(b_segbody,{b_seg(2,6, NONE, NONE)}) // this is '=', without pillar
define(b_segconn,{b_seg(3,7, b_pillar(0), b_pillar(1))}) // this is '^', with pillar
// make all 6 bridge elements
layout(5,0,
b_segexit()
b_segent0()
b_segentr()
b_segexit()
b_segbody()
b_segconn()
)
// bridge ramps - use original sprites but recolor them
layout(5,6,
segment(
railbridge(flat(front(0x985, 0x987), back(0x986, 0x988)),
ramp(front(0x989, 0x98B), back(0x98A, 0x98C)))
roadbridge(flat(front(0x98D+b_pal(1), 0x98F+b_pal(1)), back(0x98E+b_pal(1), 0x990+b_pal(1))),
ramp(front(0x991+b_pal(1), 0x993+b_pal(1)), back(0x992+b_pal(1),0x994+b_pal(1))))
monobridge(flat(front(0x10E6+_BYELLOW, 0x10E8+_BYELLOW), back(0x10E7+_BYELLOW, 0x10E9+_BYELLOW)),
ramp(front(0x10EA+_BYELLOW, 0x10EC+_BYELLOW), back(0x10EB+_BYELLOW,0x10ED+_BYELLOW)))
mlevbridge(flat(front(4366+_BWHITE, 4368+_BWHITE), back(4367+_BWHITE, 4369+_BWHITE)),
ramp(front(4370+_BWHITE, 4372+_BWHITE), back(4371+_BWHITE,4373+_BWHITE)))
)
)
Due to the way sprites are allocated and used in TTDPatch/OpenTTD, a fixed range of parameters has to be used by m4nfo internally in bridge sprite allocation. To directly access bridge sprites (0 .. n) for a single bridge ID, m4nfo uses parameters starting with 0x20 (decimal 32). In addition, for each bridge ID (0 .. 12) an extra parameter is needed which holds the starting address of the bridge's sprite block. This means that parameter range 0 .. 31 - 13 (max number of bridges) is freely available for parameters to be defined by the coder of a bridge set, to set bridge properties like cost or bridge speed.
The way m4nfo handles sprite allocation allows re-use of bridge sprites, e.g. bridge ramps, which had already been allocated for a different bridge ID. This is done by copying the needed sprites from the 'source' bridge's spriteblock into the 0x20 parameter block used for direct sprite access:
// rail ramps
forloop(X,{pcalc(parameter(0x20 + X - 44) := parameter(3) + X)
},44 .. 51)
// road ramps
forloop(X,{pcalc(parameter(0x20 + X - 44) := parameter(3) + X)
},52 .. 59)
layout(GIRDER_1,6,
segment(
railbridge(flat(front(2+_BBLUE,3+_BBLUE), back(0+_BBLUE,1+_BBLUE)),
ramp(front(6+_BBLUE,7+_BBLUE), back(4+_BBLUE,5+_BBLUE)))
roadbridge(flat(front(10+_BBLUE,11+_BBLUE), back(8+_BBLUE,9+_BBLUE)),
ramp(front(14+_BBLUE,15+_BBLUE), back(12+_BBLUE,13+_BBLUE)))
...
)
)
Here, the flats and ramps from the 'concrete bridge' (ID 01, parameter = 3), namely sprites 44 .. 51 (for rail) and 52 .. 59 (for road), are copied using function pcalc() into the 0x20 parameter block to be addressed like this:
| bridge | sprite new | sprite old |
| rail | 0 | 44 |
| 1 | 45 | |
| ... | ... | |
| 7 | 51 | |
| road | 8 | 52 |
| 9 | 53 | |
| ... | ... | |
| 15 | 59 |