Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
177 changes: 177 additions & 0 deletions examples/bridge/example_bridge.nml
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
/*
* This file is aimed to provide an example on how to code a custom bridge in NML.
* It creates a cable bridge with support for multiple transport types (rail, road,
* monorail, maglev). The same set of cable sprites is reused across all transport
* types; the game overlays the correct track or road surface at runtime.
*
* To keep the code readable, not every property is documented in detail.
* Refer to the bridge-specific reference in the documentation for the full list
* of available properties and callbacks.
*
* Apart from this file, you will also need the following:
* - Graphics, in gfx/cable.png
* - Language files, to be placed in the 'lang' folder.
* Currently english.lng is supplied.
*
* Graphics are from JP+ Bridges by Emperor Jake.
*/

/*
* First, define a grf block. This defines some basic properties of the grf,
* which are required for the grf to be valid and loadable.
*/
grf {
/* This grf is part of NML, therefore "NML" is chosen as the first three
* characters of the GRFID. It is the eighth example grf defined as part of
* NML, therefore the last character is set to 7 as numbering is zero-based.
*/
grfid: "NML\07";
/* GRF name and description strings are defined in the lang files */
name: string(STR_GRF_NAME);
desc: string(STR_GRF_DESC);
/* This is the first version, start numbering at 1. */
version: 1;
min_compatible_version: 1;
}

/* Next: a series of templates for the bridge graphics.
* Templates allow you to avoid repeating sprite coordinates for each spriteset.
* Parameter t selects the column within the sprite sheet (0 = leftmost column,
* 1 = next, and so on), so different bridge part slots can share the same layout.
*
* Each template produces two sprites: one for each track orientation (X-axis and Y-axis).
*
* A bridge span is divided into four visual layers:
* head - the ramp pieces where vehicles enter and leave the bridge
* back - the structural section drawn behind vehicles
* front - the structural section drawn in front of vehicles
* pillar - the support columns underneath the span
*/
template template_head(t) {
[1 + t*130, 1, 64, 64, -31, -24]
[66 + t*130, 1, 64, 64, -31, -24]
}

template template_back(t) {
/* ANIM marks these sprites as safe for animated palettes */
[1 + t*130, 66, 64, 128, -31, -84, ANIM]
[66 + t*130, 66, 64, 128, -31, -84, ANIM]
}

template template_front(t) {
[1 + t*130, 195, 64, 128, -55, -96, ANIM]
[66 + t*130, 195, 64, 128, -7, -96, ANIM]
}

template template_pillar(t) {
[1 + t*130, 324, 64, 128, -55, -96]
[66 + t*130, 324, 64, 128, -7, -96]
}

/* The back, front and pillar spritesets each contain six part slots (0-5), which
* the game assembles in different combinations depending on bridge length:
*
* _0_ Bridge of length 3
* _0(23)1_ Bridge of even length
* _0(23)4(23)1_ Bridge of lengths 5, 9, 13, 17 etc.
* _0(23)253(23)1_ Bridge of lengths 7, 11, 15, 19 etc.
*
* Here _ represents the head/ramp (a separate spriteset), and the numbers 0-5 are
* the span slots drawn in sequence. Slots 2 and 3 repeat as needed to fill the span.
* Slots 4 and 5 are inserted once in the middle to handle longer odd-length bridges.
*/

/* The head spriteset has eight sprites in total: a flat approach piece and a ramp piece
* for each bridge end (north and south), each in both track orientations (X-axis and Y-axis).
* Each template call produces the X-axis and Y-axis sprites for one piece.
*/
spriteset(cable_head, "gfx/cable.png") {
template_head(0) // north flat (X and Y)
template_head(1) // south flat (X and Y)
template_head(2) // north ramp (X and Y)
template_head(3) // south ramp (X and Y)
}

/* The back and front spritesets have one pair of sprites per slot.
* Slots 1 and 4 reuse the same column as slot 0 since they share the same appearance.
*/
spriteset(cable_back, "gfx/cable.png") {
template_back(0) // slot 0
template_back(0) // slot 1: same appearance as slot 0
template_back(2) // slot 2
template_back(3) // slot 3
template_back(0) // slot 4: same appearance as slot 0
template_back(5) // slot 5
}

spriteset(cable_front, "gfx/cable.png") {
template_front(0) // slot 0
template_front(0) // slot 1: same appearance as slot 0
template_front(2) // slot 2
template_front(3) // slot 3
template_front(0) // slot 4: same appearance as slot 0
template_front(5) // slot 5
}

/* Empty sprite pairs [] [] mean no pillar is drawn for that slot.
* Pillars are only needed at slots 3 and 5, where longer spans need support.
*/
spriteset(cable_pillar, "gfx/cable.png") {
[] [] // slot 0: no pillar at the ramp
[] [] // slot 1: short spans need no pillar
[] [] // slot 2: no pillar here
template_pillar(3) // slot 3: pillar for longer spans
[] [] // slot 4: no pillar here
template_pillar(3) // slot 5: reuse the same pillar graphics
}

/*
* Now define the bridge item itself.
* Unlike vehicles, bridges are identified by a fixed slot index rather than a
* named label. Valid IDs are 0x00 to 0x0D (0–13), each replacing the corresponding
* default OpenTTD bridge. It is not possible to add bridges beyond that range.
*/
item(FEAT_BRIDGES, wood_rail_bridge, 0x00) {
property {
min_length: 0; // allow spans of any length
max_length: 255; // effectively unlimited
cost_factor: 224;
speed_limit: 300 km/h;
flags: bitmask(FAR_PILLARS_DISABLE); // omit distant pillars for this style
avail_year: 1970;
name: string(STR_BRIDGE_NAME);
description_road: string(STR_BRIDGE_DESC_ROAD);
description_rail: string(STR_BRIDGE_DESC_RAIL);

/* pillar_info declares where the bridge's pillars and walls are located for each
* span slot. There are six slots, each with one byte for the X orientation and one
* for Y, giving 12 values in total. Each byte is a bitmask of corners and edges,
* using BRIDGE_PILLAR_CORNER_{N,S,W,E} and BRIDGE_PILLAR_EDGE_{NE,SE,SW,NW}.
* The game checks this against whatever sits on the tiles underneath: if a tile
* conflicts with a declared pillar position, the game skips rendering that pillar
* for that tile. A value of 0 means no pillars for that slot and orientation.
*/
pillar_info: [
0, 0, // slot 0: no pillars
0, 0, // slot 1: no pillars
0, 0, // slot 2: no pillars
bitmask(BRIDGE_PILLAR_CORNER_N, BRIDGE_PILLAR_CORNER_E), // slot 3, X-axis
bitmask(BRIDGE_PILLAR_CORNER_S, BRIDGE_PILLAR_CORNER_W), // slot 3, Y-axis
0, 0, // slot 4: no pillars
bitmask(BRIDGE_PILLAR_CORNER_N, BRIDGE_PILLAR_CORNER_E), // slot 5, X-axis
bitmask(BRIDGE_PILLAR_CORNER_S, BRIDGE_PILLAR_CORNER_W), // slot 5, Y-axis
];
}

/* Assign spritesets to each visual layer.
* Each list holds four entries, one per transport type: [rail, road, monorail, maglev].
* We supply the same cable sprites for all transport types; the game draws
* the appropriate track or road surface on top automatically.
*/
graphics {
bridge_head: [cable_head, cable_head, cable_head, cable_head];
bridge_back: [cable_back, cable_back, cable_back, cable_back];
bridge_front: [cable_front, cable_front, cable_front, cable_front];
bridge_pillars: [cable_pillar, cable_pillar, cable_pillar, cable_pillar];
}
}
Binary file added examples/bridge/gfx/cable.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions examples/bridge/lang/english.lng
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
##grflangid 0x01
STR_GRF_NAME :NML Example NewGRF: Bridge
STR_GRF_DESC :{ORANGE}NML Example NewGRF: Bridge{}{BLACK}This NewGRF is intended to provide a coding example for the high-level NewGRF-coding language NML.{}It demonstrates how to define a custom bridge using FEAT_BRIDGES with sprite templates, 6-table duplication for bridge_parts, and 4x zoom alternatives.
STR_BRIDGE_NAME :Cable-Stayed Bridge
STR_BRIDGE_DESC_RAIL :Cable-Stayed Rail Bridge
STR_BRIDGE_DESC_ROAD :Cable-Stayed Road Bridge
Loading