diff options
-rw-r--r-- | BUGS | 7 | ||||
-rw-r--r-- | Makefile | 1 | ||||
-rw-r--r-- | ai/default/default.c | 45 | ||||
-rw-r--r-- | bridge.h | 2 | ||||
-rw-r--r-- | bridge_map.h | 1 | ||||
-rw-r--r-- | data/elrailsw.grf | bin | 0 -> 7699 bytes | |||
-rw-r--r-- | docs/elrail.svg | 1091 | ||||
-rw-r--r-- | docs/elrail_tile.png | bin | 0 -> 64028 bytes | |||
-rw-r--r-- | docs/elrail_track.png | bin | 0 -> 62629 bytes | |||
-rw-r--r-- | docs/landscape.html | 2 | ||||
-rw-r--r-- | elrail.c | 352 | ||||
-rw-r--r-- | engine_gui.c | 7 | ||||
-rw-r--r-- | gfxinit.c | 3 | ||||
-rw-r--r-- | lang/english.txt | 4 | ||||
-rw-r--r-- | misc.c | 3 | ||||
-rw-r--r-- | newgrf.c | 11 | ||||
-rw-r--r-- | npf.c | 24 | ||||
-rw-r--r-- | npf.h | 12 | ||||
-rw-r--r-- | openttd.c | 69 | ||||
-rw-r--r-- | openttd.dsp | 4 | ||||
-rw-r--r-- | openttd.vcproj | 3 | ||||
-rw-r--r-- | pathfind.c | 13 | ||||
-rw-r--r-- | pathfind.h | 2 | ||||
-rw-r--r-- | player.h | 1 | ||||
-rw-r--r-- | rail.h | 22 | ||||
-rw-r--r-- | rail_cmd.c | 15 | ||||
-rw-r--r-- | rail_map.h | 9 | ||||
-rw-r--r-- | railtypes.h | 65 | ||||
-rw-r--r-- | road_cmd.c | 1 | ||||
-rw-r--r-- | saveload.c | 2 | ||||
-rw-r--r-- | station_cmd.c | 2 | ||||
-rw-r--r-- | table/elrail_data.h | 362 | ||||
-rw-r--r-- | table/engines.h | 15 | ||||
-rw-r--r-- | table/landscape_const.h | 29 | ||||
-rw-r--r-- | table/sprites.h | 64 | ||||
-rw-r--r-- | train_cmd.c | 87 | ||||
-rw-r--r-- | train_gui.c | 4 | ||||
-rw-r--r-- | tunnelbridge_cmd.c | 13 | ||||
-rw-r--r-- | variables.h | 1 | ||||
-rw-r--r-- | vehicle.c | 4 | ||||
-rw-r--r-- | vehicle.h | 2 | ||||
-rw-r--r-- | vehicle_gui.c | 26 |
42 files changed, 2258 insertions, 122 deletions
@@ -0,0 +1,7 @@ +/* $Id */ + +KNOWN BUGS / PROBLEMS: + +Normal and elrail depots look the same. Use 'X' (transparent buildings) + to distinguish between them +Missing curors / icons for construction (currently using the conventional ones) @@ -630,6 +630,7 @@ SRCS += dock_gui.c SRCS += driver.c SRCS += dummy_land.c SRCS += economy.c +SRCS += elrail.c SRCS += engine.c SRCS += engine_gui.c SRCS += fileio.c diff --git a/ai/default/default.c b/ai/default/default.c index 2a638e3e9..8c4d2569e 100644 --- a/ai/default/default.c +++ b/ai/default/default.c @@ -126,7 +126,7 @@ static void AiStateVehLoop(Player *p) p->ai.state_counter = 0; } -static EngineID AiChooseTrainToBuild(byte railtype, int32 money, byte flag, TileIndex tile) +static EngineID AiChooseTrainToBuild(RailType railtype, int32 money, byte flag, TileIndex tile) { EngineID best_veh_index = INVALID_ENGINE; byte best_veh_score = 0; @@ -137,7 +137,7 @@ static EngineID AiChooseTrainToBuild(byte railtype, int32 money, byte flag, Tile const RailVehicleInfo *rvi = RailVehInfo(i); const Engine* e = GetEngine(i); - if (e->railtype != railtype || + if (!IsCompatibleRail(e->railtype, railtype) || rvi->flags & RVI_WAGON || (rvi->flags & RVI_MULTIHEAD && flag & 1) || !HASBIT(e->player_avail, _current_player) || @@ -2321,6 +2321,41 @@ static StationID AiGetStationIdByDef(TileIndex tile, int id) return GetStationIndex(TILE_ADD(tile, ToTileIndexDiff(p->tileoffs))); } +static EngineID AiFindBestWagon(CargoID cargo, RailType railtype) +{ + EngineID best_veh_index = INVALID_ENGINE; + EngineID i; + uint16 best_capacity = 0; + uint16 best_speed = 0; + uint speed; + + for (i = 0; i < NUM_TRAIN_ENGINES; i++) { + const RailVehicleInfo *rvi = RailVehInfo(i); + const Engine* e = GetEngine(i); + + if (!IsCompatibleRail(e->railtype, railtype) || + !(rvi->flags & RVI_WAGON) || + !HASBIT(e->player_avail, _current_player)) { + continue; + } + + if (rvi->cargo_type != cargo) { + continue; + } + + /* max_speed of 0 indicates no speed limit */ + speed = rvi->max_speed == 0 ? 0xFFFF : rvi->max_speed; + + if (rvi->capacity >= best_capacity && speed >= best_speed) { + best_capacity = rvi->capacity; + best_speed = best_speed; + best_veh_index = i; + } + } + + return best_veh_index; +} + static void AiStateBuildRailVeh(Player *p) { const AiDefaultBlockData *ptr; @@ -2337,10 +2372,14 @@ static void AiStateBuildRailVeh(Player *p) tile = TILE_ADD(p->ai.src.use_tile, ToTileIndexDiff(ptr->tileoffs)); + cargo = p->ai.cargo_type; for (i = 0;;) { if (p->ai.wagon_list[i] == INVALID_VEHICLE) { - veh = _cargoc.ai_railwagon[p->ai.railtype_to_use][cargo]; + veh = AiFindBestWagon(cargo, p->ai.railtype_to_use); + /* veh will return INVALID_ENGINE if no suitable wagon is available. + * We shall treat this in the same way as having no money */ + if (veh == INVALID_ENGINE) goto handle_nocash; cost = DoCommandByTile(tile, veh, 0, DC_EXEC, CMD_BUILD_RAIL_VEHICLE); if (CmdFailed(cost)) goto handle_nocash; p->ai.wagon_list[i] = _new_wagon_id; @@ -22,4 +22,6 @@ typedef struct Bridge { extern const Bridge orig_bridge[MAX_BRIDGES]; extern Bridge _bridge[MAX_BRIDGES]; +uint GetBridgeFoundation(uint tileh, Axis axis); + #endif /* BRIDGE_H */ diff --git a/bridge_map.h b/bridge_map.h index f799077b3..014a3671c 100644 --- a/bridge_map.h +++ b/bridge_map.h @@ -127,6 +127,7 @@ TileIndex GetSouthernBridgeEnd(TileIndex t); */ TileIndex GetOtherBridgeEnd(TileIndex); +uint GetBridgeHeight(TileIndex t); static inline void SetClearUnderBridge(TileIndex t) { diff --git a/data/elrailsw.grf b/data/elrailsw.grf Binary files differnew file mode 100644 index 000000000..f722d1e3a --- /dev/null +++ b/data/elrailsw.grf diff --git a/docs/elrail.svg b/docs/elrail.svg new file mode 100644 index 000000000..39098a388 --- /dev/null +++ b/docs/elrail.svg @@ -0,0 +1,1091 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://web.resource.org/cc/" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://inkscape.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="744.09448819" + height="1052.3622047" + id="svg2" + sodipodi:version="0.32" + inkscape:version="0.42.2" + sodipodi:docbase="/home/vici/openttd/branch/elrail/docs" + sodipodi:docname="elrail.svg"> + <defs + id="defs4" /> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="1.0000000" + inkscape:pageshadow="2" + inkscape:zoom="3.6009318" + inkscape:cx="144.00958" + inkscape:cy="578.90292" + inkscape:document-units="px" + inkscape:current-layer="layer1" + showguides="true" + inkscape:guide-bbox="true" + showgrid="true" + inkscape:window-width="1131" + inkscape:window-height="791" + inkscape:window-x="1295" + inkscape:window-y="46" + inkscape:grid-points="false" + inkscape:guide-points="false" /> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1"> + <rect + style="opacity:1.0000000;fill:none;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.0067960;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + id="rect1306" + width="159.49321" + height="159.49657" + x="227.17947" + y="-24.569920" + transform="matrix(0.707107,0.707107,-0.707107,0.707107,0.000000,0.000000)" + inkscape:export-filename="/home/vici/openttd/branch/elrail/docs/elrail_tile.png" + inkscape:export-xdpi="299.91397" + inkscape:export-ydpi="299.91397" /> + <rect + y="272.36218" + x="134.28571" + height="100.00000" + width="100.00000" + id="rect2059" + style="opacity:1.0000000;fill:none;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-opacity:1.0000000;stroke-width:0.0000000;stroke-miterlimit:4.0000000;stroke-dasharray:none" + inkscape:export-filename="/home/vici/openttd/branch/elrail/docs/elrail_tile.png" + inkscape:export-xdpi="299.91397" + inkscape:export-ydpi="299.91397" /> + <text + xml:space="preserve" + style="font-size:12.000000px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1.0000000;stroke:none;stroke-width:1.0000000px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;font-family:Bitstream Vera Sans" + x="177.84283" + y="159.25369" + id="text6598" + sodipodi:linespacing="125%" + inkscape:export-filename="/home/vici/openttd/branch/elrail/docs/elrail_tile.png" + inkscape:export-xdpi="299.91397" + inkscape:export-ydpi="299.91397"><tspan + sodipodi:role="line" + id="tspan6600" + x="177.84283" + y="159.25369" + style="font-family:Bitstream Vera Sans;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal;font-size:12.000000;text-anchor:middle;text-align:center;writing-mode:lr;line-height:125%">N</tspan></text> + <path + transform="translate(-192.0645,-106.5709)" + d="M 213.86573 240.06183 A 4.8679605 4.8679605 0 1 1 204.12981,240.06183 A 4.8679605 4.8679605 0 1 1 213.86573 240.06183 z" + sodipodi:ry="4.8679605" + sodipodi:rx="4.8679605" + sodipodi:cy="240.06183" + sodipodi:cx="208.99777" + id="use6604" + style="opacity:1.0000000;fill:#9fb276;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.29899999;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + sodipodi:type="arc" + inkscape:export-filename="/home/vici/openttd/branch/elrail/docs/elrail_tile.png" + inkscape:export-xdpi="299.91397" + inkscape:export-ydpi="299.91397" /> + <text + xml:space="preserve" + style="font-size:12.000000px;font-style:normal;font-weight:normal;line-height:125.00000%;fill:#000000;fill-opacity:1.0000000;stroke:none;stroke-width:1.0000000px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;font-family:Bitstream Vera Sans" + x="27.214844" + y="135.69812" + id="text6606" + sodipodi:linespacing="125.00000%" + inkscape:export-filename="/home/vici/openttd/branch/elrail/docs/elrail_tile.png" + inkscape:export-xdpi="299.91397" + inkscape:export-ydpi="299.91397"><tspan + sodipodi:role="line" + id="tspan6608" + x="27.214844" + y="135.69812" + style="font-size:8.0000000px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125.00000%;writing-mode:lr-tb;text-anchor:start;font-family:Bitstream Vera Sans">Pylon Control Point (PCP)</tspan></text> + <g + id="g6780" + inkscape:export-filename="/home/vici/openttd/branch/elrail/docs/elrail_tile.png" + inkscape:export-xdpi="299.91397" + inkscape:export-ydpi="299.91397"> + <path + transform="translate(-16.64392,10.04300)" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + sodipodi:ry="2.2717149" + sodipodi:rx="2.2717149" + sodipodi:cy="189.43504" + sodipodi:cx="237.55647" + id="path6610" + style="opacity:1.0000000;fill:#00ffff;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + sodipodi:type="arc" /> + <path + transform="translate(-16.64392,-3.625430)" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + sodipodi:ry="2.2717149" + sodipodi:rx="2.2717149" + sodipodi:cy="189.43504" + sodipodi:cx="237.55647" + id="path6612" + style="opacity:1.0000000;fill:#0000ff;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + sodipodi:type="arc" /> + <path + transform="translate(-2.975490,-3.625430)" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + sodipodi:ry="2.2717149" + sodipodi:rx="2.2717149" + sodipodi:cy="189.43504" + sodipodi:cx="237.55647" + id="path6614" + style="opacity:1.0000000;fill:#0000ff;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + sodipodi:type="arc" /> + <path + transform="translate(10.69294,-3.625430)" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + sodipodi:ry="2.2717149" + sodipodi:rx="2.2717149" + sodipodi:cy="189.43504" + sodipodi:cx="237.55647" + id="path6616" + style="opacity:1.0000000;fill:#ff0000;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + sodipodi:type="arc" /> + <path + transform="translate(10.69294,10.04300)" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + sodipodi:ry="2.2717149" + sodipodi:rx="2.2717149" + sodipodi:cy="189.43504" + sodipodi:cx="237.55647" + id="path6620" + style="opacity:1.0000000;fill:#0000ff;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + sodipodi:type="arc" /> + <path + transform="translate(10.69294,23.71143)" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + sodipodi:ry="2.2717149" + sodipodi:rx="2.2717149" + sodipodi:cy="189.43504" + sodipodi:cx="237.55647" + id="path6622" + style="opacity:1.0000000;fill:#00ffff;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + sodipodi:type="arc" /> + <path + transform="translate(-2.975490,23.71143)" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + sodipodi:ry="2.2717149" + sodipodi:rx="2.2717149" + sodipodi:cy="189.43504" + sodipodi:cx="237.55647" + id="path6624" + style="opacity:1.0000000;fill:#00ffff;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + sodipodi:type="arc" /> + <path + transform="translate(-16.64392,23.71143)" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + sodipodi:ry="2.2717149" + sodipodi:rx="2.2717149" + sodipodi:cy="189.43504" + sodipodi:cx="237.55647" + id="path6626" + style="opacity:1.0000000;fill:#ffff00;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + sodipodi:type="arc" /> + <path + sodipodi:type="arc" + style="opacity:1.0000000;fill:#9fb276;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.29899999;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + id="path6732" + sodipodi:cx="208.99777" + sodipodi:cy="240.06183" + sodipodi:rx="4.8679605" + sodipodi:ry="4.8679605" + d="M 213.86573 240.06183 A 4.8679605 4.8679605 0 1 1 204.12981,240.06183 A 4.8679605 4.8679605 0 1 1 213.86573 240.06183 z" + transform="matrix(0.707107,-0.707107,0.707107,0.707107,-82.95220,177.5124)" /> + </g> + <use + x="0.0000000" + y="0.0000000" + xlink:href="#path6616" + id="use6742" + transform="translate(-222.0702,-28.15283)" + width="744.09448" + height="1052.3622" + inkscape:export-filename="/home/vici/openttd/branch/elrail/docs/elrail_tile.png" + inkscape:export-xdpi="299.91397" + inkscape:export-ydpi="299.91397" /> + <path + transform="translate(-211.3773,-22.11155)" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + sodipodi:ry="2.2717149" + sodipodi:rx="2.2717149" + sodipodi:cy="189.43504" + sodipodi:cx="237.55647" + id="path6744" + style="opacity:1.0000000;fill:#0000ff;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + sodipodi:type="arc" + inkscape:export-filename="/home/vici/openttd/branch/elrail/docs/elrail_tile.png" + inkscape:export-xdpi="299.91397" + inkscape:export-ydpi="299.91397" /> + <path + transform="translate(-211.3773,-12.44495)" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + sodipodi:ry="2.2717149" + sodipodi:rx="2.2717149" + sodipodi:cy="189.43504" + sodipodi:cx="237.55647" + id="path6746" + style="opacity:1.0000000;fill:#00ffff;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + sodipodi:type="arc" + inkscape:export-filename="/home/vici/openttd/branch/elrail/docs/elrail_tile.png" + inkscape:export-xdpi="299.91397" + inkscape:export-ydpi="299.91397" /> + <path + transform="translate(-211.3773,-2.778260)" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + sodipodi:ry="2.2717149" + sodipodi:rx="2.2717149" + sodipodi:cy="189.43504" + sodipodi:cx="237.55647" + id="path6748" + style="opacity:1.0000000;fill:#ffff00;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + sodipodi:type="arc" + inkscape:export-filename="/home/vici/openttd/branch/elrail/docs/elrail_tile.png" + inkscape:export-xdpi="299.91397" + inkscape:export-ydpi="299.91397" /> + <text + xml:space="preserve" + style="font-size:12.000000px;font-style:normal;font-weight:normal;line-height:125.00000%;fill:#000000;fill-opacity:1.0000000;stroke:none;stroke-width:1.0000000px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;font-family:Bitstream Vera Sans" + x="32.279297" + y="159.58745" + id="text6750" + sodipodi:linespacing="125.00000%" + inkscape:export-filename="/home/vici/openttd/branch/elrail/docs/elrail_tile.png" + inkscape:export-xdpi="299.91397" + inkscape:export-ydpi="299.91397"><tspan + sodipodi:role="line" + id="tspan6752" + x="32.279297" + y="159.58745" + style="font-size:6.0000000px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125.00000%;writing-mode:lr-tb;text-anchor:start;font-family:Bitstream Vera Sans">not allowed, not owned</tspan></text> + <text + xml:space="preserve" + style="font-size:12.000000px;font-style:normal;font-weight:normal;line-height:125.00000%;fill:#000000;fill-opacity:1.0000000;stroke:none;stroke-width:1.0000000px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;font-family:Bitstream Vera Sans" + x="32.279297" + y="188.58745" + id="text6754" + sodipodi:linespacing="125.00000%" + inkscape:export-filename="/home/vici/openttd/branch/elrail/docs/elrail_tile.png" + inkscape:export-xdpi="299.91397" + inkscape:export-ydpi="299.91397"><tspan + sodipodi:role="line" + id="tspan6756" + x="32.279297" + y="188.58745" + style="font-size:6.0000000px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125.00000%;writing-mode:lr-tb;text-anchor:start;font-family:Bitstream Vera Sans">not allowed, owned</tspan></text> + <text + xml:space="preserve" + style="font-size:12.000000px;font-style:normal;font-weight:normal;line-height:125.00000%;fill:#000000;fill-opacity:1.0000000;stroke:none;stroke-width:1.0000000px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;font-family:Bitstream Vera Sans" + x="32.279297" + y="178.92078" + id="text6758" + sodipodi:linespacing="125.00000%" + inkscape:export-filename="/home/vici/openttd/branch/elrail/docs/elrail_tile.png" + inkscape:export-xdpi="299.91397" + inkscape:export-ydpi="299.91397"><tspan + sodipodi:role="line" + id="tspan6760" + x="32.279297" + y="178.92078" + style="font-size:6.0000000px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125.00000%;writing-mode:lr-tb;text-anchor:start;font-family:Bitstream Vera Sans">allowed, owned</tspan></text> + <text + xml:space="preserve" + style="font-size:12.000000px;font-style:normal;font-weight:normal;line-height:125.00000%;fill:#000000;fill-opacity:1.0000000;stroke:none;stroke-width:1.0000000px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;font-family:Bitstream Vera Sans" + x="32.279297" + y="169.25417" + id="text6762" + sodipodi:linespacing="125.00000%" + inkscape:export-filename="/home/vici/openttd/branch/elrail/docs/elrail_tile.png" + inkscape:export-xdpi="299.91397" + inkscape:export-ydpi="299.91397"><tspan + sodipodi:role="line" + id="tspan6764" + x="32.279297" + y="169.25417" + style="font-size:6.0000000px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125.00000%;writing-mode:lr-tb;text-anchor:start;font-family:Bitstream Vera Sans">allowed, not owned</tspan></text> + <text + xml:space="preserve" + style="font-size:12.000000px;font-style:normal;font-weight:normal;line-height:125.00000%;fill:#000000;fill-opacity:1.0000000;stroke:none;stroke-width:1.0000000px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;font-family:Bitstream Vera Sans" + x="27.214844" + y="149.62065" + id="text6766" + sodipodi:linespacing="125.00000%" + inkscape:export-filename="/home/vici/openttd/branch/elrail/docs/elrail_tile.png" + inkscape:export-xdpi="299.91397" + inkscape:export-ydpi="299.91397"><tspan + sodipodi:role="line" + id="tspan6768" + x="27.214844" + y="149.62065" + style="font-size:8.0000000px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125.00000%;writing-mode:lr-tb;text-anchor:start;font-family:Bitstream Vera Sans">Pylon Position Point (PPP)</tspan></text> + <use + x="0.0000000" + y="0.0000000" + xlink:href="#g6780" + id="use6791" + transform="matrix(-1.720846e-15,1.000000,-1.000000,-1.720846e-15,433.4807,78.77855)" + width="744.09448" + height="1052.3622" + inkscape:export-filename="/home/vici/openttd/branch/elrail/docs/elrail_tile.png" + inkscape:export-xdpi="299.91397" + inkscape:export-ydpi="299.91397" /> + <g + id="g6793" + transform="matrix(1.776357e-15,-1.000000,1.000000,1.776357e-15,-78.04526,434.0590)" + inkscape:export-filename="/home/vici/openttd/branch/elrail/docs/elrail_tile.png" + inkscape:export-xdpi="299.91397" + inkscape:export-ydpi="299.91397"> + <path + sodipodi:type="arc" + style="opacity:1.0000000;fill:#00ffff;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + id="path6795" + sodipodi:cx="237.55647" + sodipodi:cy="189.43504" + sodipodi:rx="2.2717149" + sodipodi:ry="2.2717149" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + transform="translate(-16.64392,10.04300)" /> + <path + sodipodi:type="arc" + style="opacity:1.0000000;fill:#0000ff;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + id="path6797" + sodipodi:cx="237.55647" + sodipodi:cy="189.43504" + sodipodi:rx="2.2717149" + sodipodi:ry="2.2717149" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + transform="translate(-16.64392,-3.625430)" /> + <path + sodipodi:type="arc" + style="opacity:1.0000000;fill:#0000ff;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + id="path6799" + sodipodi:cx="237.55647" + sodipodi:cy="189.43504" + sodipodi:rx="2.2717149" + sodipodi:ry="2.2717149" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + transform="translate(-2.975490,-3.625430)" /> + <path + sodipodi:type="arc" + style="opacity:1.0000000;fill:#ff0000;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + id="path6801" + sodipodi:cx="237.55647" + sodipodi:cy="189.43504" + sodipodi:rx="2.2717149" + sodipodi:ry="2.2717149" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + transform="translate(10.69294,-3.625430)" /> + <path + sodipodi:type="arc" + style="opacity:1.0000000;fill:#0000ff;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + id="path6803" + sodipodi:cx="237.55647" + sodipodi:cy="189.43504" + sodipodi:rx="2.2717149" + sodipodi:ry="2.2717149" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + transform="translate(10.69294,10.04300)" /> + <path + sodipodi:type="arc" + style="opacity:1.0000000;fill:#00ffff;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + id="path6805" + sodipodi:cx="237.55647" + sodipodi:cy="189.43504" + sodipodi:rx="2.2717149" + sodipodi:ry="2.2717149" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + transform="translate(10.69294,23.71143)" /> + <path + sodipodi:type="arc" + style="opacity:1.0000000;fill:#00ffff;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + id="path6807" + sodipodi:cx="237.55647" + sodipodi:cy="189.43504" + sodipodi:rx="2.2717149" + sodipodi:ry="2.2717149" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + transform="translate(-2.975490,23.71143)" /> + <path + sodipodi:type="arc" + style="opacity:1.0000000;fill:#ffff00;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + id="path6809" + sodipodi:cx="237.55647" + sodipodi:cy="189.43504" + sodipodi:rx="2.2717149" + sodipodi:ry="2.2717149" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + transform="translate(-16.64392,23.71143)" /> + <path + transform="matrix(0.707107,-0.707107,0.707107,0.707107,-82.95220,177.5124)" + d="M 213.86573 240.06183 A 4.8679605 4.8679605 0 1 1 204.12981,240.06183 A 4.8679605 4.8679605 0 1 1 213.86573 240.06183 z" + sodipodi:ry="4.8679605" + sodipodi:rx="4.8679605" + sodipodi:cy="240.06183" + sodipodi:cx="208.99777" + id="path6811" + style="opacity:1.0000000;fill:#9fb276;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.29899999;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + sodipodi:type="arc" /> + </g> + <g + id="g6813" + transform="matrix(-1.000000,0.000000,0.000000,-1.000000,356.0752,512.3398)" + inkscape:export-filename="/home/vici/openttd/branch/elrail/docs/elrail_tile.png" + inkscape:export-xdpi="299.91397" + inkscape:export-ydpi="299.91397"> + <path + sodipodi:type="arc" + style="opacity:1.0000000;fill:#00ffff;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + id="path6815" + sodipodi:cx="237.55647" + sodipodi:cy="189.43504" + sodipodi:rx="2.2717149" + sodipodi:ry="2.2717149" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + transform="translate(-16.64392,10.04300)" /> + <path + sodipodi:type="arc" + style="opacity:1.0000000;fill:#0000ff;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + id="path6817" + sodipodi:cx="237.55647" + sodipodi:cy="189.43504" + sodipodi:rx="2.2717149" + sodipodi:ry="2.2717149" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + transform="translate(-16.64392,-3.625430)" /> + <path + sodipodi:type="arc" + style="opacity:1.0000000;fill:#0000ff;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + id="path6819" + sodipodi:cx="237.55647" + sodipodi:cy="189.43504" + sodipodi:rx="2.2717149" + sodipodi:ry="2.2717149" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + transform="translate(-2.975490,-3.625430)" /> + <path + sodipodi:type="arc" + style="opacity:1.0000000;fill:#ff0000;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + id="path6821" + sodipodi:cx="237.55647" + sodipodi:cy="189.43504" + sodipodi:rx="2.2717149" + sodipodi:ry="2.2717149" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + transform="translate(10.69294,-3.625430)" /> + <path + sodipodi:type="arc" + style="opacity:1.0000000;fill:#0000ff;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + id="path6823" + sodipodi:cx="237.55647" + sodipodi:cy="189.43504" + sodipodi:rx="2.2717149" + sodipodi:ry="2.2717149" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + transform="translate(10.69294,10.04300)" /> + <path + sodipodi:type="arc" + style="opacity:1.0000000;fill:#00ffff;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + id="path6825" + sodipodi:cx="237.55647" + sodipodi:cy="189.43504" + sodipodi:rx="2.2717149" + sodipodi:ry="2.2717149" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + transform="translate(10.69294,23.71143)" /> + <path + sodipodi:type="arc" + style="opacity:1.0000000;fill:#00ffff;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + id="path6827" + sodipodi:cx="237.55647" + sodipodi:cy="189.43504" + sodipodi:rx="2.2717149" + sodipodi:ry="2.2717149" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + transform="translate(-2.975490,23.71143)" /> + <path + sodipodi:type="arc" + style="opacity:1.0000000;fill:#ffff00;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + id="path6829" + sodipodi:cx="237.55647" + sodipodi:cy="189.43504" + sodipodi:rx="2.2717149" + sodipodi:ry="2.2717149" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + transform="translate(-16.64392,23.71143)" /> + <path + transform="matrix(0.707107,-0.707107,0.707107,0.707107,-82.95220,177.5124)" + d="M 213.86573 240.06183 A 4.8679605 4.8679605 0 1 1 204.12981,240.06183 A 4.8679605 4.8679605 0 1 1 213.86573 240.06183 z" + sodipodi:ry="4.8679605" + sodipodi:rx="4.8679605" + sodipodi:cy="240.06183" + sodipodi:cx="208.99777" + id="path6831" + style="opacity:1.0000000;fill:#9fb276;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.29899999;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + sodipodi:type="arc" /> + </g> + <use + x="0.0000000" + y="0.0000000" + xlink:href="#path6616" + id="use6841" + transform="translate(-225.5836,232.1805)" + width="744.09448" + height="1052.3622" + inkscape:export-filename="/home/vici/openttd/branch/elrail/docs/elrail_track.png" + inkscape:export-xdpi="299.91397" + inkscape:export-ydpi="299.91397" /> + <path + transform="translate(-214.8907,238.2218)" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + sodipodi:ry="2.2717149" + sodipodi:rx="2.2717149" + sodipodi:cy="189.43504" + sodipodi:cx="237.55647" + id="path6843" + style="opacity:1.0000000;fill:#0000ff;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + sodipodi:type="arc" + inkscape:export-filename="/home/vici/openttd/branch/elrail/docs/elrail_track.png" + inkscape:export-xdpi="299.91397" + inkscape:export-ydpi="299.91397" /> + <path + transform="translate(-214.8907,247.8884)" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + sodipodi:ry="2.2717149" + sodipodi:rx="2.2717149" + sodipodi:cy="189.43504" + sodipodi:cx="237.55647" + id="path6845" + style="opacity:1.0000000;fill:#00ffff;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + sodipodi:type="arc" + inkscape:export-filename="/home/vici/openttd/branch/elrail/docs/elrail_track.png" + inkscape:export-xdpi="299.91397" + inkscape:export-ydpi="299.91397" /> + <path + transform="translate(-214.8907,257.5551)" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + sodipodi:ry="2.2717149" + sodipodi:rx="2.2717149" + sodipodi:cy="189.43504" + sodipodi:cx="237.55647" + id="path6847" + style="opacity:1.0000000;fill:#ffff00;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + sodipodi:type="arc" + inkscape:export-filename="/home/vici/openttd/branch/elrail/docs/elrail_track.png" + inkscape:export-xdpi="299.91397" + inkscape:export-ydpi="299.91397" /> + <text + xml:space="preserve" + style="font-size:12.000000px;font-style:normal;font-weight:normal;line-height:125.00000%;fill:#000000;fill-opacity:1.0000000;stroke:none;stroke-width:1.0000000px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;font-family:Bitstream Vera Sans" + x="28.765947" + y="419.92078" + id="text6849" + sodipodi:linespacing="125.00000%" + inkscape:export-filename="/home/vici/openttd/branch/elrail/docs/elrail_track.png" + inkscape:export-xdpi="299.91397" + inkscape:export-ydpi="299.91397"><tspan + sodipodi:role="line" + id="tspan6851" + x="28.765947" + y="419.92078" + style="font-size:6.0000000px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125.00000%;writing-mode:lr-tb;text-anchor:start;font-family:Bitstream Vera Sans">not allowed, but preferred (end of line marker)</tspan></text> + <text + xml:space="preserve" + style="font-size:12.000000px;font-style:normal;font-weight:normal;line-height:125.00000%;fill:#000000;fill-opacity:1.0000000;stroke:none;stroke-width:1.0000000px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;font-family:Bitstream Vera Sans" + x="28.765947" + y="448.92078" + id="text6853" + sodipodi:linespacing="125.00000%" + inkscape:export-filename="/home/vici/openttd/branch/elrail/docs/elrail_track.png" + inkscape:export-xdpi="299.91397" + inkscape:export-ydpi="299.91397"><tspan + sodipodi:role="line" + id="tspan6855" + x="28.765947" + y="448.92078" + style="font-size:6.0000000px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125.00000%;writing-mode:lr-tb;text-anchor:start;font-family:Bitstream Vera Sans">not allowed</tspan></text> + <text + xml:space="preserve" + style="font-size:12.000000px;font-style:normal;font-weight:normal;line-height:125.00000%;fill:#000000;fill-opacity:1.0000000;stroke:none;stroke-width:1.0000000px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;font-family:Bitstream Vera Sans" + x="28.765947" + y="439.25409" + id="text6857" + sodipodi:linespacing="125.00000%" + inkscape:export-filename="/home/vici/openttd/branch/elrail/docs/elrail_track.png" + inkscape:export-xdpi="299.91397" + inkscape:export-ydpi="299.91397"><tspan + sodipodi:role="line" + id="tspan6859" + x="28.765947" + y="439.25409" + style="font-size:6.0000000px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125.00000%;writing-mode:lr-tb;text-anchor:start;font-family:Bitstream Vera Sans">preferred, allowed</tspan></text> + <text + xml:space="preserve" + style="font-size:12.000000px;font-style:normal;font-weight:normal;line-height:125.00000%;fill:#000000;fill-opacity:1.0000000;stroke:none;stroke-width:1.0000000px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;font-family:Bitstream Vera Sans" + x="28.765947" + y="429.58746" + id="text6861" + sodipodi:linespacing="125.00000%" + inkscape:export-filename="/home/vici/openttd/branch/elrail/docs/elrail_track.png" + inkscape:export-xdpi="299.91397" + inkscape:export-ydpi="299.91397"><tspan + sodipodi:role="line" + x="28.765947" + y="429.58746" + style="font-size:6.0000000px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125.00000%;writing-mode:lr-tb;text-anchor:start;font-family:Bitstream Vera Sans" + id="tspan6933">allowed</tspan></text> + <text + xml:space="preserve" + style="font-size:12.000000px;font-style:normal;font-weight:normal;line-height:125.00000%;fill:#000000;fill-opacity:1.0000000;stroke:none;stroke-width:1.0000000px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;font-family:Bitstream Vera Sans" + x="23.701494" + y="409.95398" + id="text6865" + sodipodi:linespacing="125.00000%" + inkscape:export-filename="/home/vici/openttd/branch/elrail/docs/elrail_track.png" + inkscape:export-xdpi="299.91397" + inkscape:export-ydpi="299.91397"><tspan + sodipodi:role="line" + id="tspan6867" + x="23.701494" + y="409.95398" + style="font-size:8.0000000px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125.00000%;writing-mode:lr-tb;text-anchor:start;font-family:Bitstream Vera Sans">Pylon Position Point (PPP)</tspan></text> + <g + id="g6989" + inkscape:export-filename="/home/vici/openttd/branch/elrail/docs/elrail_track.png" + inkscape:export-xdpi="299.91397" + inkscape:export-ydpi="299.91397"> + <path + transform="translate(-103.1355,254.9297)" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + sodipodi:ry="2.2717149" + sodipodi:rx="2.2717149" + sodipodi:cy="189.43504" + sodipodi:cx="237.55647" + id="path6871" + style="opacity:1.0000000;fill:#0000ff;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + sodipodi:type="arc" /> + <path + transform="translate(-103.1355,241.2613)" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + sodipodi:ry="2.2717149" + sodipodi:rx="2.2717149" + sodipodi:cy="189.43504" + sodipodi:cx="237.55647" + id="path6873" + style="opacity:1.0000000;fill:#00ffff;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + sodipodi:type="arc" /> + <path + transform="translate(-89.46705,241.2613)" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + sodipodi:ry="2.2717149" + sodipodi:rx="2.2717149" + sodipodi:cy="189.43504" + sodipodi:cx="237.55647" + id="path6875" + style="opacity:1.0000000;fill:#0000ff;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + sodipodi:type="arc" /> + <path + transform="translate(-75.79862,241.2613)" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + sodipodi:ry="2.2717149" + sodipodi:rx="2.2717149" + sodipodi:cy="189.43504" + sodipodi:cx="237.55647" + id="path6877" + style="opacity:1.0000000;fill:#ff0000;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + sodipodi:type="arc" /> + <path + transform="translate(-75.79862,254.9297)" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + sodipodi:ry="2.2717149" + sodipodi:rx="2.2717149" + sodipodi:cy="189.43504" + sodipodi:cx="237.55647" + id="path6879" + style="opacity:1.0000000;fill:#0000ff;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + sodipodi:type="arc" /> + <path + transform="translate(-75.79862,268.5982)" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + sodipodi:ry="2.2717149" + sodipodi:rx="2.2717149" + sodipodi:cy="189.43504" + sodipodi:cx="237.55647" + id="path6881" + style="opacity:1.0000000;fill:#00ffff;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + sodipodi:type="arc" /> + <path + transform="translate(-89.46705,268.5982)" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + sodipodi:ry="2.2717149" + sodipodi:rx="2.2717149" + sodipodi:cy="189.43504" + sodipodi:cx="237.55647" + id="path6883" + style="opacity:1.0000000;fill:#0000ff;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + sodipodi:type="arc" /> + <path + transform="translate(-103.1355,268.5982)" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + sodipodi:ry="2.2717149" + sodipodi:rx="2.2717149" + sodipodi:cy="189.43504" + sodipodi:cx="237.55647" + id="path6885" + style="opacity:1.0000000;fill:#ffff00;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + sodipodi:type="arc" /> + <path + sodipodi:type="arc" + style="opacity:1.0000000;fill:#0000ff;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + id="path6891" + sodipodi:cx="237.55647" + sodipodi:cy="189.43504" + sodipodi:rx="2.2717149" + sodipodi:ry="2.2717149" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + transform="matrix(-1.000000,0.000000,0.000000,-1.000000,286.2275,747.1836)" /> + <path + sodipodi:type="arc" + style="opacity:1.0000000;fill:#00ffff;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + id="path6893" + sodipodi:cx="237.55647" + sodipodi:cy="189.43504" + sodipodi:rx="2.2717149" + sodipodi:ry="2.2717149" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + transform="matrix(-1.000000,0.000000,0.000000,-1.000000,286.2275,760.8520)" /> + <path + sodipodi:type="arc" + style="opacity:1.0000000;fill:#0000ff;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + id="path6895" + sodipodi:cx="237.55647" + sodipodi:cy="189.43504" + sodipodi:rx="2.2717149" + sodipodi:ry="2.2717149" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + transform="matrix(-1.000000,0.000000,0.000000,-1.000000,272.5591,760.8520)" /> + <path + sodipodi:type="arc" + style="opacity:1.0000000;fill:#ff0000;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + id="path6897" + sodipodi:cx="237.55647" + sodipodi:cy="189.43504" + sodipodi:rx="2.2717149" + sodipodi:ry="2.2717149" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + transform="matrix(-1.000000,0.000000,0.000000,-1.000000,258.8907,760.8520)" /> + <path + sodipodi:type="arc" + style="opacity:1.0000000;fill:#0000ff;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + id="path6899" + sodipodi:cx="237.55647" + sodipodi:cy="189.43504" + sodipodi:rx="2.2717149" + sodipodi:ry="2.2717149" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + transform="matrix(-1.000000,0.000000,0.000000,-1.000000,258.8907,747.1836)" /> + <path + sodipodi:type="arc" + style="opacity:1.0000000;fill:#00ffff;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + id="path6901" + sodipodi:cx="237.55647" + sodipodi:cy="189.43504" + sodipodi:rx="2.2717149" + sodipodi:ry="2.2717149" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + transform="matrix(-1.000000,0.000000,0.000000,-1.000000,258.8907,733.5153)" /> + <path + sodipodi:type="arc" + style="opacity:1.0000000;fill:#0000ff;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + id="path6903" + sodipodi:cx="237.55647" + sodipodi:cy="189.43504" + sodipodi:rx="2.2717149" + sodipodi:ry="2.2717149" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + transform="matrix(-1.000000,0.000000,0.000000,-1.000000,272.5591,733.5153)" /> + <path + sodipodi:type="arc" + style="opacity:1.0000000;fill:#ffff00;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + id="path6905" + sodipodi:cx="237.55647" + sodipodi:cy="189.43504" + sodipodi:rx="2.2717149" + sodipodi:ry="2.2717149" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + transform="matrix(-1.000000,0.000000,0.000000,-1.000000,286.2275,733.5153)" /> + <path + id="path6931" + d="M 35.005290,557.75123 L 148.46835,444.19631" + style="fill:none;fill-opacity:0.75000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.0000000px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000" /> + </g> + <g + id="g7091" + inkscape:export-filename="/home/vici/openttd/branch/elrail/docs/elrail_track.png" + inkscape:export-xdpi="299.91397" + inkscape:export-ydpi="299.91397"> + <path + sodipodi:nodetypes="ccc" + id="path6935" + d="M 143.36688,574.86732 L 185.00000,533.20050 L 241.50000,533.20050" + style="fill:none;fill-opacity:0.75000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.0000011px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000" /> + <g + id="g7081"> + <path + transform="matrix(-1.000000,0.000000,0.000000,-1.000000,436.0591,722.7997)" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + sodipodi:ry="2.2717149" + sodipodi:rx="2.2717149" + sodipodi:cy="189.43504" + sodipodi:cx="237.55647" + id="path6973" + style="opacity:1.0000000;fill:#ffff00;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + sodipodi:type="arc" /> + <path + transform="matrix(-1.000000,0.000000,0.000000,-1.000000,436.0591,736.4681)" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + sodipodi:ry="2.2717149" + sodipodi:rx="2.2717149" + sodipodi:cy="189.43504" + sodipodi:cx="237.55647" + id="path6975" + style="opacity:1.0000000;fill:#0000ff;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + sodipodi:type="arc" /> + <path + transform="matrix(-1.000000,0.000000,0.000000,-1.000000,422.3907,736.4681)" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + sodipodi:ry="2.2717149" + sodipodi:rx="2.2717149" + sodipodi:cy="189.43504" + sodipodi:cx="237.55647" + id="path6977" + style="opacity:1.0000000;fill:#0000ff;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + sodipodi:type="arc" /> + <path + transform="matrix(-1.000000,0.000000,0.000000,-1.000000,408.7223,736.4681)" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + sodipodi:ry="2.2717149" + sodipodi:rx="2.2717149" + sodipodi:cy="189.43504" + sodipodi:cx="237.55647" + id="path6979" + style="opacity:1.0000000;fill:#ffff00;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + sodipodi:type="arc" /> + <path + transform="matrix(-1.000000,0.000000,0.000000,-1.000000,408.7223,722.7997)" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + sodipodi:ry="2.2717149" + sodipodi:rx="2.2717149" + sodipodi:cy="189.43504" + sodipodi:cx="237.55647" + id="path6981" + style="opacity:1.0000000;fill:#ffff00;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + sodipodi:type="arc" /> + <path + transform="matrix(-1.000000,0.000000,0.000000,-1.000000,408.7223,709.1314)" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + sodipodi:ry="2.2717149" + sodipodi:rx="2.2717149" + sodipodi:cy="189.43504" + sodipodi:cx="237.55647" + id="path6983" + style="opacity:1.0000000;fill:#0000ff;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + sodipodi:type="arc" /> + <path + transform="matrix(-1.000000,0.000000,0.000000,-1.000000,422.3907,709.1314)" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + sodipodi:ry="2.2717149" + sodipodi:rx="2.2717149" + sodipodi:cy="189.43504" + sodipodi:cx="237.55647" + id="path6985" + style="opacity:1.0000000;fill:#0000ff;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + sodipodi:type="arc" /> + <path + transform="matrix(-1.000000,0.000000,0.000000,-1.000000,436.0591,709.1314)" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + sodipodi:ry="2.2717149" + sodipodi:rx="2.2717149" + sodipodi:cy="189.43504" + sodipodi:cx="237.55647" + id="path6987" + style="opacity:1.0000000;fill:#ffff00;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + sodipodi:type="arc" /> + </g> + </g> + <path + transform="matrix(-0.707107,0.707107,-0.707107,-0.707107,608.1528,439.3340)" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + sodipodi:ry="2.2717149" + sodipodi:rx="2.2717149" + sodipodi:cy="189.43504" + sodipodi:cx="237.55647" + id="path7037" + style="opacity:1.0000000;fill:#0000ff;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + sodipodi:type="arc" + inkscape:export-filename="/home/vici/openttd/branch/elrail/docs/elrail_track.png" + inkscape:export-xdpi="299.91397" + inkscape:export-ydpi="299.91397" /> + <path + transform="matrix(-0.707107,0.707107,-0.707107,-0.707107,617.8178,448.9990)" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + sodipodi:ry="2.2717149" + sodipodi:rx="2.2717149" + sodipodi:cy="189.43504" + sodipodi:cx="237.55647" + id="path7039" + style="opacity:1.0000000;fill:#ff00ff;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + sodipodi:type="arc" + inkscape:export-filename="/home/vici/openttd/branch/elrail/docs/elrail_track.png" + inkscape:export-xdpi="299.91397" + inkscape:export-ydpi="299.91397" /> + <path + transform="matrix(-0.707107,0.707107,-0.707107,-0.707107,608.1528,458.6640)" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + sodipodi:ry="2.2717149" + sodipodi:rx="2.2717149" + sodipodi:cy="189.43504" + sodipodi:cx="237.55647" + id="path7041" + style="opacity:1.0000000;fill:#0000ff;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + sodipodi:type="arc" + inkscape:export-filename="/home/vici/openttd/branch/elrail/docs/elrail_track.png" + inkscape:export-xdpi="299.91397" + inkscape:export-ydpi="299.91397" /> + <path + transform="matrix(-0.707107,0.707107,-0.707107,-0.707107,598.4878,468.3290)" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + sodipodi:ry="2.2717149" + sodipodi:rx="2.2717149" + sodipodi:cy="189.43504" + sodipodi:cx="237.55647" + id="path7043" + style="opacity:1.0000000;fill:#ffff00;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + sodipodi:type="arc" + inkscape:export-filename="/home/vici/openttd/branch/elrail/docs/elrail_track.png" + inkscape:export-xdpi="299.91397" + inkscape:export-ydpi="299.91397" /> + <path + transform="matrix(-0.707107,0.707107,-0.707107,-0.707107,588.8228,458.6640)" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + sodipodi:ry="2.2717149" + sodipodi:rx="2.2717149" + sodipodi:cy="189.43504" + sodipodi:cx="237.55647" + id="path7045" + style="opacity:1.0000000;fill:#0000ff;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + sodipodi:type="arc" + inkscape:export-filename="/home/vici/openttd/branch/elrail/docs/elrail_track.png" + inkscape:export-xdpi="299.91397" + inkscape:export-ydpi="299.91397" /> + <path + transform="matrix(-0.707107,0.707107,-0.707107,-0.707107,579.1578,448.9991)" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + sodipodi:ry="2.2717149" + sodipodi:rx="2.2717149" + sodipodi:cy="189.43504" + sodipodi:cx="237.55647" + id="path7047" + style="opacity:1.0000000;fill:#ff00ff;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + sodipodi:type="arc" + inkscape:export-filename="/home/vici/openttd/branch/elrail/docs/elrail_track.png" + inkscape:export-xdpi="299.91397" + inkscape:export-ydpi="299.91397" /> + <path + transform="matrix(-0.707107,0.707107,-0.707107,-0.707107,588.8229,439.3341)" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + sodipodi:ry="2.2717149" + sodipodi:rx="2.2717149" + sodipodi:cy="189.43504" + sodipodi:cx="237.55647" + id="path7049" + style="opacity:1.0000000;fill:#0000ff;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + sodipodi:type="arc" + inkscape:export-filename="/home/vici/openttd/branch/elrail/docs/elrail_track.png" + inkscape:export-xdpi="299.91397" + inkscape:export-ydpi="299.91397" /> + <path + transform="matrix(-0.707107,0.707107,-0.707107,-0.707107,598.4879,429.6690)" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + sodipodi:ry="2.2717149" + sodipodi:rx="2.2717149" + sodipodi:cy="189.43504" + sodipodi:cx="237.55647" + id="path7051" + style="opacity:1.0000000;fill:#ffff00;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + sodipodi:type="arc" + inkscape:export-filename="/home/vici/openttd/branch/elrail/docs/elrail_track.png" + inkscape:export-xdpi="299.91397" + inkscape:export-ydpi="299.91397" /> + <path + style="fill:none;fill-opacity:0.75000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.0000004px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000" + d="M 296.56288,570.12631 L 296.49793,409.60031" + id="path7053" + inkscape:export-filename="/home/vici/openttd/branch/elrail/docs/elrail_track.png" + inkscape:export-xdpi="299.91397" + inkscape:export-ydpi="299.91397" /> + <path + transform="translate(-214.8907,267.2614)" + d="M 239.82819 189.43504 A 2.2717149 2.2717149 0 1 1 235.28476,189.43504 A 2.2717149 2.2717149 0 1 1 239.82819 189.43504 z" + sodipodi:ry="2.2717149" + sodipodi:rx="2.2717149" + sodipodi:cy="189.43504" + sodipodi:cx="237.55647" + id="path7111" + style="opacity:1.0000000;fill:#ff00ff;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.12500000;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" + sodipodi:type="arc" + inkscape:export-filename="/home/vici/openttd/branch/elrail/docs/elrail_track.png" + inkscape:export-xdpi="299.91397" + inkscape:export-ydpi="299.91397" /> + <text + xml:space="preserve" + style="font-size:12.000000px;font-style:normal;font-weight:normal;line-height:125.00000%;fill:#000000;fill-opacity:1.0000000;stroke:none;stroke-width:1.0000000px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;font-family:Bitstream Vera Sans" + x="28.765947" + y="458.93326" + id="text7113" + sodipodi:linespacing="125.00000%" + inkscape:export-filename="/home/vici/openttd/branch/elrail/docs/elrail_track.png" + inkscape:export-xdpi="299.91397" + inkscape:export-ydpi="299.91397"><tspan + sodipodi:role="line" + id="tspan7115" + x="28.765947" + y="458.93326" + style="font-size:6.0000000px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125.00000%;writing-mode:lr-tb;text-anchor:start;font-family:Bitstream Vera Sans">preferred, allowed and</tspan><tspan + sodipodi:role="line" + x="28.765947" + y="466.43326" + style="font-size:6.0000000px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125.00000%;writing-mode:lr-tb;text-anchor:start;font-family:Bitstream Vera Sans" + id="tspan7117">can be ignored to create</tspan><tspan + sodipodi:role="line" + x="28.765947" + y="473.93326" + style="font-size:6.0000000px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125.00000%;writing-mode:lr-tb;text-anchor:start;font-family:Bitstream Vera Sans" + id="tspan7119">long wires (which span</tspan><tspan + sodipodi:role="line" + x="28.765947" + y="481.43326" + style="font-size:6.0000000px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125.00000%;writing-mode:lr-tb;text-anchor:start;font-family:Bitstream Vera Sans" + id="tspan7121">2 tiles)</tspan></text> + </g> +</svg> diff --git a/docs/elrail_tile.png b/docs/elrail_tile.png Binary files differnew file mode 100644 index 000000000..82407de48 --- /dev/null +++ b/docs/elrail_tile.png diff --git a/docs/elrail_track.png b/docs/elrail_track.png Binary files differnew file mode 100644 index 000000000..af88bd331 --- /dev/null +++ b/docs/elrail_track.png diff --git a/docs/landscape.html b/docs/landscape.html index 92fed219e..b28a8932f 100644 --- a/docs/landscape.html +++ b/docs/landscape.html @@ -129,7 +129,7 @@ m5 bit 7 clear: railway track <tr><td nowrap valign=top><tt>B</tt> </td><td align=left>fence on the N side (track in the S corner)</td></tr> <tr><td nowrap valign=top><tt>C</tt> </td><td align=left>on snow or desert</td></tr> </table></li> -<li>m3 bits 0..3 = <a name="TrackType">track type</a>: <tt>0</tt> - conventional railway, <tt>1</tt> - monorail, <tt>2</tt> - maglev +<li>m3 bits 0..3 = <a name="TrackType">track type</a>: <tt>0</tt> - conventional railway, <tt>1</tt> - electrified railway, <tt>2</tt> - monorail, <tt>3</tt> - maglev </ul> m5 bits 7 and 6 set: railway depot / checkpoints <ul> diff --git a/elrail.c b/elrail.c new file mode 100644 index 000000000..cfe69b2ab --- /dev/null +++ b/elrail.c @@ -0,0 +1,352 @@ +/* $Id$ */ +/** @file elrail.c + * This file deals with displaying wires and pylons for electric railway systems. +<h2>Basics</h2> + +<h3>Tile Types</h3> + +We have two different types of tiles in the drawing code: +Normal Railway Tiles (NRTs) which can have more than one track on it, and +Special Railways tiles (SRTs) which have only one track (like crossings, depots +stations, etc). + +<h3>Location Categories</h3> + +All tiles are categorized into three location groups (TLG): +Group 0: Tiles with both an even X coordinate and an even Y coordinate +Group 1: Tiles with an even X and an odd Y coordinate +Group 2: Tiles with an odd X and an even Y coordinate +Group 3: Tiles with both an odd X and Y coordnate. + +<h3>Pylon Points</h3> +<h4>Control Points</h4> +A Pylon Control Point (PCP) is a position where a wire (or rather two) +is mounted onto a pylon. +Each NRT does contain 4 PCPs which are mapped to a byte +variable and are represented by the DiagDirection enum: + +A wire that ends on the PCP has a dark ending, otherwise the end is bright.<p> + +Now on each edge there are two PCPs: One from each adjacent tile. Both PCPs are merged +using an OR matrix (i. e. if one tile needs a PCP at the postion in question, both +tiles get it). + +<h4>Position Points</h4> +A Pylon Position Point (PPP) is a position where a pylon is located on the ground. +Each PCP owns 8 in (45 degree steps) PPPs that are located around it. PPPs are numbered +0 to 7 with 0 starting north and numbering in clockwise direction. Each track bit has PPPs +that are impossible (because the pylon would be situated on the track), preferred (because +the pylon would be rectangular to the track). PPPs are represented by the Direction enum. + +<img src="../../elrail_tile.png"> +<img src="../../elrail_track.png"> + + */ + +#include "stdafx.h" +#include "openttd.h" +#include "tile.h" +#include "viewport.h" +#include "functions.h" /* We should REALLY get rid of this goddamn file, as it is butt-ugly */ +#include "variables.h" /* ... same here */ +#include "rail.h" +#include "debug.h" +#include "tunnel_map.h" +#include "road_map.h" +#include "bridge_map.h" +#include "bridge.h" +#include "rail_map.h" +#include "table/sprites.h" +#include "table/elrail_data.h" + +static inline TLG GetTLG(TileIndex t) +{ + return (HASBIT(TileX(t), 0) << 1) + HASBIT(TileY(t), 0); +} + +/** Finds which Rail Bits are present on a given tile. For bridge tiles, + * returns track bits under the bridge + */ +static TrackBits GetRailTrackBitsUniversal(TileIndex t, byte *override) +{ + switch (GetTileType(t)) { + case MP_RAILWAY: + if (GetRailType(t) != RAILTYPE_ELECTRIC) return 0; + switch (GetRailTileType(t)) { + case RAIL_TYPE_NORMAL: case RAIL_TYPE_SIGNALS: + return GetTrackBits(t); + default: + return 0; + } + break; + case MP_TUNNELBRIDGE: + if (IsTunnel(t)) { + if (GetRailType(t) != RAILTYPE_ELECTRIC) return 0; + if (override != NULL) *override = 1 << GetTunnelDirection(t); + return (_m[t].m5 & 1) ? TRACK_BIT_Y : TRACK_BIT_X; + } else { + if (GetRailType(t) != RAILTYPE_ELECTRIC) return 0; + if ( + IsBridgeMiddle(t) && + IsTransportUnderBridge(t) && + GetTransportTypeUnderBridge(t) == TRANSPORT_RAIL) { + return GetRailBitsUnderBridge(t); + } else { + if (override != NULL && DistanceMax(t, GetOtherBridgeEnd(t)) > 1) *override = 1 << GetBridgeRampDirection(t); + + return GetBridgeAxis(t) == AXIS_X ? TRACK_BIT_X : TRACK_BIT_Y; + } + } + case MP_STREET: + if ((_m[t].m4 & 0xF) != RAILTYPE_ELECTRIC) return 0; + return GetCrossingRailBits(t); + case MP_STATION: + if (GetRailType(t) != RAILTYPE_ELECTRIC) return 0; + return _m[t].m5 & 1 ? TRACK_BIT_Y : TRACK_BIT_X; + default: + return 0; + } +} + +/** Draws wires and, if required, pylons on a given tile + * @param ti The Tileinfo to draw the tile for + * @todo Currently, each pylon is drawn twice (once for each neighbouring tiles use OwnedPPPonPCP for this) + */ +static void DrawCatenaryRailway(const TileInfo *ti) +{ + /* Pylons are placed on a tile edge, so we need to take into account + the track configuration of 2 adjacent tiles. trackconfig[0] stores the + current tile (home tile) while [1] holds the neighbour */ + TrackBits trackconfig[TS_END]; + bool isflat[TS_END]; + /* Note that ti->tileh has already been adjusted for Foundations */ + uint tileh[TS_END] = {ti->tileh, 0}; + + TLG tlg = GetTLG(ti->tile); + byte PCPstatus = 0; + byte OverridePCP = 0; + byte PPPpreferred[DIAGDIR_END] = {0xFF, 0xFF, 0xFF, 0xFF}; + byte PPPallowed[DIAGDIR_END] = {AllowedPPPonPCP[0], AllowedPPPonPCP[1], AllowedPPPonPCP[2], AllowedPPPonPCP[3]}; + byte PPPbuffer[DIAGDIR_END]; + DiagDirection i; + Track t; + + /* Find which rail bits are present, and select the override points. + We don't draw a pylon: + 1) INSIDE a tunnel (we wouldn't see it anyway) + 2) on the "far" end of a bridge head (the one that connects to bridge middle), + because that one is drawn on the bridge. Exception is for length 0 bridges + which have no middle tiles */ + trackconfig[TS_HOME] = GetRailTrackBitsUniversal(ti->tile, &OverridePCP); + /* If a track bit is present that is not in the main direction, the track is level */ + isflat[TS_HOME] = trackconfig[TS_HOME] & (TRACK_BIT_UPPER | TRACK_BIT_LOWER | TRACK_BIT_LEFT | TRACK_BIT_RIGHT); + + if (IsTunnelTile(ti->tile)) tileh[TS_HOME] = 0; + if (IsBridgeTile(ti->tile) && IsBridgeRamp(ti->tile)) { + if (tileh[TS_HOME] != 0) { + tileh[TS_HOME] = 0; + } else { + switch (GetBridgeRampDirection(ti->tile)) { + case DIAGDIR_NE: tileh[TS_HOME] = 12; break; + case DIAGDIR_SE: tileh[TS_HOME] = 6; break; + case DIAGDIR_SW: tileh[TS_HOME] = 3; break; + case DIAGDIR_NW: tileh[TS_HOME] = 9; break; + default: break; + } + } + } + + for (i = DIAGDIR_NE; i < DIAGDIR_END; i++) { + extern const TileIndexDiffC _tileoffs_by_dir[]; + TileIndex neighbour = ti->tile + TileOffsByDir(i); + uint foundation = 0; + int k; + + /* Here's one of the main headaches. GetTileSlope does not correct for possibly + existing foundataions, so we do have to do that manually later on.*/ + tileh[TS_NEIGHBOUR] = GetTileSlope(neighbour, NULL); + trackconfig[TS_NEIGHBOUR] = GetRailTrackBitsUniversal(neighbour, NULL); + isflat[TS_NEIGHBOUR] = trackconfig[TS_NEIGHBOUR] & (TRACK_BIT_UPPER | TRACK_BIT_LOWER | TRACK_BIT_LEFT | TRACK_BIT_RIGHT); + + /* We cycle through all the existing tracks at a PCP and see what + PPPs we want to have, or may not have at all */ + for (k = 0; k < TRACKS_AT_PCP; k++) { + /* Next to us, we have a bridge head, don't worry about that one, if it shows away from us */ + if ( + trackorigin[i][k] == TS_NEIGHBOUR && + IsBridgeTile(neighbour) && IsBridgeRamp(neighbour) && + GetBridgeRampDirection(neighbour) == ReverseDiagDir(i) + ) continue; + + if (HASBIT(trackconfig[trackorigin[i][k]], PPPtracks[i][k])) { + DiagDirection PCPpos = (trackorigin[i][k] == 0) ? i : ReverseDiagDir(i); + PCPstatus |= 1 << i; /* This PCP is in use */ + PPPpreferred[i] &= PreferredPPPofTrackBitAtPCP[PPPtracks[i][k]][PCPpos]; + PPPallowed[i] &= ~DisallowedPPPofTrackBitAtPCP[PPPtracks[i][k]][PCPpos]; + } + } + + /* Deactivate all PPPs if PCP is not used */ + PPPpreferred[i] *= HASBIT(PCPstatus, i); + PPPallowed[i] *= HASBIT(PCPstatus, i); + + /* Station on a non-flat tile means foundation. add one height level and adjust tileh */ + if (IsTileType(neighbour, MP_STATION) && tileh[TS_NEIGHBOUR] != 0) tileh[TS_NEIGHBOUR] = 0; + + /* Read the foundataions if they are present, and adjust the tileh */ + if (IsTileType(neighbour, MP_RAILWAY)) foundation = GetRailFoundation(tileh[TS_NEIGHBOUR], trackconfig[TS_NEIGHBOUR]); + if (IsBridgeTile(neighbour) && IsBridgeRamp(neighbour)) foundation = GetBridgeFoundation(tileh[TS_NEIGHBOUR], GetBridgeAxis(neighbour)); + if (foundation != 0) { + if (foundation < 15) { + tileh[TS_NEIGHBOUR] = 0; + } else { + tileh[TS_NEIGHBOUR] = _inclined_tileh[foundation - 15]; + } + } + + /* Convert the real tileh into a pseudo-tileh for the track */ + if (IsTunnelTile(neighbour)) tileh[TS_NEIGHBOUR] = 0; + if (IsBridgeTile(neighbour) && IsBridgeRamp(neighbour)) { + if (tileh[TS_NEIGHBOUR] != 0) { + tileh[TS_NEIGHBOUR] = 0; + } else { + switch (GetBridgeRampDirection(neighbour)) { + case DIAGDIR_NE: tileh[TS_NEIGHBOUR] = 12; break; + case DIAGDIR_SE: tileh[TS_NEIGHBOUR] = 6; break; + case DIAGDIR_SW: tileh[TS_NEIGHBOUR] = 3; break; + case DIAGDIR_NW: tileh[TS_NEIGHBOUR] = 9; break; + default: break; + } + } + } + + /* If we have a straight (and level) track, we want a pylon only every 2 tiles + Delete the PCP if this is the case. */ + /* Level means that the slope is the same, or the track is flat */ + if (tileh[TS_HOME] == tileh[TS_NEIGHBOUR] || (isflat[TS_HOME] && isflat[TS_NEIGHBOUR])) { + for (k = 0; k < NUM_IGNORE_GROUPS; k++) + if (PPPpreferred[i] == IgnoredPCP[k][tlg][i]) PCPstatus &= ~(1 << i); + } + + /* Now decide where we draw our tiles. First try the preferred PPPs, but they may not exist. + In that case, we try the any of the allowed ones. if they don't exist either, don't draw + anything */ + if (PPPpreferred[i] != 0) { + /* Some of the preferred PPPs (the ones in direct extension of the track bit) + have been used as an "end of line" marker. As these are not ALLOWED, this operation + cancles them out */ + PPPbuffer[i] = PPPpreferred[i] & PPPallowed[i]; + /* We haven't any buffer yet, so try something else. Fixes 90° curves */ + if (PPPbuffer[i] == 0) PPPbuffer[i] = PPPallowed[i]; + } else { + PPPbuffer[i] = PPPallowed[i]; + } + + if (PPPbuffer[i] != 0 && HASBIT(PCPstatus, i) && !HASBIT(OverridePCP, i)) { + for (k = 0; k < DIR_END; k++) { + byte temp = PPPorder[i][GetTLG(ti->tile)][k]; + if (HASBIT(PPPbuffer[i], temp)) { + uint x = ti->x + x_pcp_offsets[i] + x_ppp_offsets[temp]; + uint y = ti->y + y_pcp_offsets[i] + y_ppp_offsets[temp]; + + /* Don't build the pylon if it would be outside the tile */ + if (!HASBIT(OwnedPPPonPCP[i], temp)) { + /* We have a neighour that will draw it, bail out */ + if (trackconfig[TS_NEIGHBOUR] != 0) break; + continue; /* No neighbour, go looking for a better position */ + } + + AddSortableSpriteToDraw(pylons_normal[temp], x, y, 1, 1, 10, + GetSlopeZ(ti->x + x_pcp_offsets[i], ti->y + y_pcp_offsets[i])); + break; /* We already have drawn a pylon, bail out */ + } + } + } + } + + /* Drawing of pylons is finished, now draw the wires */ + for (t = 0; t < TRACK_END; t++) { + if (HASBIT(trackconfig[TS_HOME], t)) { + + byte PCPconfig = HASBIT(PCPstatus, PCPpositions[t][0]) + + (HASBIT(PCPstatus, PCPpositions[t][1]) << 1); + + const SortableSpriteStruct *sss; + int tileh_selector = !(tileh[TS_HOME] % 3) * tileh[TS_HOME] / 3; /* tileh for the slopes, 0 otherwise */ + + if ( /* We are not drawing a wire under a low bridge */ + IsBridgeTile(ti->tile) && + IsBridgeMiddle(ti->tile) && + !(_display_opt & DO_TRANS_BUILDINGS) && + GetBridgeHeight(t) <= TilePixelHeight(t) + ) return; + + assert(PCPconfig != 0); /* We have a pylon on neither end of the wire, that doesn't work (since we have no sprites for that) */ + assert(!IsSteepTileh(tileh[TS_HOME])); + sss = &CatenarySpriteData[Wires[tileh_selector][t][PCPconfig]]; + + AddSortableSpriteToDraw( sss->image, ti->x + sss->x_offset, ti->y + sss->y_offset, + sss->x_size, sss->y_size, sss->z_size, GetSlopeZ(ti->x + min(sss->x_offset, 15), ti->y + min(sss->y_offset, 15)) + sss->z_offset); + } + } +} + +static void DrawCatenaryOnBridge(const TileInfo *ti) +{ + TileIndex start = GetOtherBridgeEnd(GetSouthernBridgeEnd(ti->tile)); + uint length = GetBridgeLength(GetSouthernBridgeEnd(ti->tile), GetOtherBridgeEnd(GetSouthernBridgeEnd(ti->tile))); + uint num = DistanceMax(ti->tile, start); + const SortableSpriteStruct *sss; + Axis axis = GetBridgeAxis(ti->tile); + TLG tlg = GetTLG(ti->tile); + + CatenarySprite offset = axis == AXIS_X ? 0 : WIRE_Y_FLAT_BOTH - WIRE_X_FLAT_BOTH; + + if ((length % 2) && num == length) { + sss = &CatenarySpriteData[WIRE_X_FLAT_BOTH + offset]; + } else { + sss = &CatenarySpriteData[WIRE_X_FLAT_SW + (num % 2) + offset]; + } + + if (num % 2) { + if (axis == AXIS_X) { + AddSortableSpriteToDraw( pylons_bridge[0 + HASBIT(tlg, 0)], ti->x, ti->y + 4 + 8 * HASBIT(tlg, 0), 1, 1, 10, GetBridgeHeight(ti->tile) + 8); + } else { + AddSortableSpriteToDraw( pylons_bridge[2 + HASBIT(tlg, 1)], ti->x + 4 + 8 * HASBIT(tlg, 1), ti->y, 1, 1, 10, GetBridgeHeight(ti->tile) + 8); + } + } + + if (DistanceMax(ti->tile, start) == length) { /* need a pylon here (the southern end) */ + if (axis == AXIS_X) { + AddSortableSpriteToDraw( pylons_bridge[0 + HASBIT(tlg, 0)], ti->x + 16, ti->y + 4 + 8 * HASBIT(tlg, 0), 1, 1, 10, GetBridgeHeight(ti->tile) + 8); + } else { + AddSortableSpriteToDraw( pylons_bridge[2 + HASBIT(tlg, 1)], ti->x + 4 + 8 * HASBIT(tlg, 1), ti->y + 16, 1, 1, 10, GetBridgeHeight(ti->tile) + 8); + } + } + + AddSortableSpriteToDraw( sss->image, ti->x + sss->x_offset, ti->y + sss->y_offset, + sss->x_size, sss->y_size, sss->z_size, GetBridgeHeight(ti->tile) + sss->z_offset + 8); +} + +void DrawCatenary(const TileInfo *ti) +{ + switch (GetTileType(ti->tile)) { + case MP_RAILWAY: + if (GetRailTileType(ti->tile) == RAIL_TYPE_DEPOT_WAYPOINT && GetRailTileSubtype(ti->tile) == RAIL_SUBTYPE_DEPOT) { + const SortableSpriteStruct *sss = &CatenarySpriteData[WIRE_DEPOT_SW + ReverseDiagDir(GetRailDepotDirection(ti->tile))]; + AddSortableSpriteToDraw( sss->image, ti->x + sss->x_offset, ti->y + sss->y_offset, + sss->x_size, sss->y_size, sss->z_size, GetSlopeZ(ti->x, ti->y) + sss->z_offset); + return; + } + /* Fall through */ + case MP_TUNNELBRIDGE: + if (IsBridgeTile(ti->tile) && IsBridgeMiddle(ti->tile) && GetRailTypeOnBridge(ti->tile) == RAILTYPE_ELECTRIC) DrawCatenaryOnBridge(ti); + /* Fall further */ + case MP_STREET: case MP_STATION: + DrawCatenaryRailway(ti); + break; + default: + break; + } +} + diff --git a/engine_gui.c b/engine_gui.c index 61147d644..69df50f42 100644 --- a/engine_gui.c +++ b/engine_gui.c @@ -20,9 +20,10 @@ static StringID GetEngineCategoryName(EngineID engine) { if (engine < NUM_TRAIN_ENGINES) { switch (GetEngine(engine)->railtype) { - case RAILTYPE_RAIL: return STR_8102_RAILROAD_LOCOMOTIVE; - case RAILTYPE_MONO: return STR_8106_MONORAIL_LOCOMOTIVE; - case RAILTYPE_MAGLEV: return STR_8107_MAGLEV_LOCOMOTIVE; + case RAILTYPE_RAIL: return STR_8102_RAILROAD_LOCOMOTIVE; + case RAILTYPE_ELECTRIC: return STR_8102_RAILROAD_LOCOMOTIVE; + case RAILTYPE_MONO: return STR_8106_MONORAIL_LOCOMOTIVE; + case RAILTYPE_MAGLEV: return STR_8107_MAGLEV_LOCOMOTIVE; } } @@ -358,6 +358,9 @@ static void LoadSpriteTables(void) load_index = SPR_AUTORAIL_BASE; load_index += LoadGrfFile("autorail.grf", load_index, i++); + assert(load_index == SPR_ELRAIL_BASE); + load_index += LoadGrfFile("elrailsw.grf", load_index, i++); + assert(load_index == SPR_2CCMAP_BASE); load_index += LoadGrfFile("2ccmap.grf", load_index, i++); diff --git a/lang/english.txt b/lang/english.txt index 7b7c1bf4a..e2dee014b 100644 --- a/lang/english.txt +++ b/lang/english.txt @@ -1443,6 +1443,7 @@ STR_1005_NO_SUITABLE_RAILROAD_TRACK :{WHITE}No suita STR_1007_ALREADY_BUILT :{WHITE}...already built STR_1008_MUST_REMOVE_RAILROAD_TRACK :{WHITE}Must remove railway track first STR_100A_RAILROAD_CONSTRUCTION :{WHITE}Railway Construction +STR_TITLE_ELRAIL_CONSTRUCTION :{WHITE}Electrified Railway Construction STR_100B_MONORAIL_CONSTRUCTION :{WHITE}Monorail Construction STR_100C_MAGLEV_CONSTRUCTION :{WHITE}MagLev Construction STR_100D_SELECT_RAIL_BRIDGE :{WHITE}Select Rail Bridge @@ -1454,6 +1455,7 @@ STR_1012_CAN_T_REMOVE_RAILROAD_TRACK :{WHITE}Can't re STR_1013_CAN_T_REMOVE_SIGNALS_FROM :{WHITE}Can't remove signals from here... STR_1014_TRAIN_DEPOT_ORIENTATION :{WHITE}Train Depot Orientation STR_1015_RAILROAD_CONSTRUCTION :Railway construction +STR_TOOLB_ELRAIL_CONSTRUCTION :Electrified Railway construction STR_1016_MONORAIL_CONSTRUCTION :Monorail construction STR_1017_MAGLEV_CONSTRUCTION :MagLev construction STR_1018_BUILD_RAILROAD_TRACK :{BLACK}Build railway track @@ -2410,6 +2412,7 @@ STR_8819_TRAIN_TOO_LONG :{WHITE}Train to STR_881A_TRAINS_CAN_ONLY_BE_ALTERED :{WHITE}Trains can only be altered when stopped inside a depot STR_881B_TRAINS :{WHITE}{COMPANY} - {COMMA} Train{P "" s} STR_881C_NEW_RAIL_VEHICLES :{WHITE}New Rail Vehicles +STR_NEW_ELRAIL_VEHICLES :{WHITE}New Electric Rail Vehicles STR_881D_NEW_MONORAIL_VEHICLES :{WHITE}New Monorail Vehicles STR_881E_NEW_MAGLEV_VEHICLES :{WHITE}New Maglev Vehicles STR_881F_BUILD_VEHICLE :{BLACK}Build Vehicle @@ -2810,6 +2813,7 @@ STR_SIGN_LIST_CAPTION :{WHITE}Sign Lis ############ Lists rail types STR_RAIL_VEHICLES :Rail Vehicles +STR_ELRAIL_VEHICLES :Electrified Rail Vehicles STR_MONORAIL_VEHICLES :Monorail Vehicles STR_MAGLEV_VEHICLES :Maglev Vehicles @@ -401,8 +401,6 @@ typedef struct LandscapePredefVar { byte transit_days_table_1[NUM_CARGO]; byte transit_days_table_2[NUM_CARGO]; - byte railwagon_by_cargo[3][NUM_CARGO]; - byte road_veh_by_cargo_start[NUM_CARGO]; byte road_veh_by_cargo_count[NUM_CARGO]; } LandscapePredefVar; @@ -419,7 +417,6 @@ void InitializeLandscapeVariables(bool only_constants) lpd = &_landscape_predef_var[_opt.landscape]; - memcpy(_cargoc.ai_railwagon, lpd->railwagon_by_cargo, sizeof(lpd->railwagon_by_cargo)); memcpy(_cargoc.ai_roadveh_start, lpd->road_veh_by_cargo_start,sizeof(lpd->road_veh_by_cargo_start)); memcpy(_cargoc.ai_roadveh_count, lpd->road_veh_by_cargo_count,sizeof(lpd->road_veh_by_cargo_count)); @@ -355,6 +355,9 @@ static bool RailVehicleChangeInfo(uint engine, int numinfo, int prop, byte **buf engclass = 0; } else if (traction <= 0x27) { engclass = 1; + } else if (traction <= 0x31) { + engclass = 2; + ei[i].railtype = RAILTYPE_ELECTRIC; } else if (traction <= 0x41) { engclass = 2; } else { @@ -2309,12 +2312,8 @@ static void InitializeGRFSpecial(void) | (1 << 0x18) /* newrvs */ | (1 << 0x19) /* newships */ | (1 << 0x1A) /* newplanes */ - | (_patches.signal_side ? (1 << 0x1B) : 0); /* signalsontrafficside */ - /* Uncomment following if you want to fool the GRF file. - * Some GRF files will refuse to load without this - * but you can still squeeze something from them even - * without the support - i.e. USSet. --pasky */ - //| (1 << 0x1C); /* electrifiedrailway */ + | (_patches.signal_side ? (1 << 0x1B) : 0) /* signalsontrafficside */ + | (1 << 0x1C); /* electrifiedrailway */ _ttdpatch_flags[2] = (_patches.build_on_slopes ? (1 << 0x0D) : 0) /* buildonslopes */ | (_patches.build_on_slopes ? (1 << 0x15) : 0) /* buildoncoasts */ @@ -587,7 +587,7 @@ static void NPFFollowTrack(AyStar* aystar, OpenListNode* current) /* check correct rail type (mono, maglev, etc) */ if (type == TRANSPORT_RAIL) { RailType dst_type = GetTileRailType(dst_tile, src_trackdir); - if (!IsCompatibleRail(aystar->user_data[NPF_RAILTYPE], dst_type)) + if (!HASBIT(aystar->user_data[NPF_RAILTYPES], dst_type)) return; } @@ -661,7 +661,7 @@ static void NPFFollowTrack(AyStar* aystar, OpenListNode* current) * multiple targets that are spread around, we should perform a breadth first * search by specifiying CalcZero as our heuristic. */ -static NPFFoundTargetData NPFRouteInternal(AyStarNode* start1, AyStarNode* start2, NPFFindStationOrTileData* target, AyStar_EndNodeCheck target_proc, AyStar_CalculateH heuristic_proc, TransportType type, Owner owner, RailType railtype, uint reverse_penalty) +static NPFFoundTargetData NPFRouteInternal(AyStarNode* start1, AyStarNode* start2, NPFFindStationOrTileData* target, AyStar_EndNodeCheck target_proc, AyStar_CalculateH heuristic_proc, TransportType type, Owner owner, RailTypeMask railtypes, uint reverse_penalty) { int r; NPFFoundTargetData result; @@ -703,7 +703,7 @@ static NPFFoundTargetData NPFRouteInternal(AyStarNode* start1, AyStarNode* start /* Initialize user_data */ _npf_aystar.user_data[NPF_TYPE] = type; _npf_aystar.user_data[NPF_OWNER] = owner; - _npf_aystar.user_data[NPF_RAILTYPE] = railtype; + _npf_aystar.user_data[NPF_RAILTYPES] = railtypes; /* GO! */ r = AyStarMain_Main(&_npf_aystar); @@ -721,7 +721,7 @@ static NPFFoundTargetData NPFRouteInternal(AyStarNode* start1, AyStarNode* start return result; } -NPFFoundTargetData NPFRouteToStationOrTileTwoWay(TileIndex tile1, Trackdir trackdir1, TileIndex tile2, Trackdir trackdir2, NPFFindStationOrTileData* target, TransportType type, Owner owner, RailType railtype) +NPFFoundTargetData NPFRouteToStationOrTileTwoWay(TileIndex tile1, Trackdir trackdir1, TileIndex tile2, Trackdir trackdir2, NPFFindStationOrTileData* target, TransportType type, Owner owner, RailTypeMask railtypes) { AyStarNode start1; AyStarNode start2; @@ -735,15 +735,15 @@ NPFFoundTargetData NPFRouteToStationOrTileTwoWay(TileIndex tile1, Trackdir track start2.direction = trackdir2; start2.user_data[NPF_TRACKDIR_CHOICE] = INVALID_TRACKDIR; - return NPFRouteInternal(&start1, (IsValidTile(tile2) ? &start2 : NULL), target, NPFFindStationOrTile, NPFCalcStationOrTileHeuristic, type, owner, railtype, 0); + return NPFRouteInternal(&start1, (IsValidTile(tile2) ? &start2 : NULL), target, NPFFindStationOrTile, NPFCalcStationOrTileHeuristic, type, owner, railtypes, 0); } -NPFFoundTargetData NPFRouteToStationOrTile(TileIndex tile, Trackdir trackdir, NPFFindStationOrTileData* target, TransportType type, Owner owner, RailType railtype) +NPFFoundTargetData NPFRouteToStationOrTile(TileIndex tile, Trackdir trackdir, NPFFindStationOrTileData* target, TransportType type, Owner owner, RailTypeMask railtypes) { - return NPFRouteToStationOrTileTwoWay(tile, trackdir, INVALID_TILE, 0, target, type, owner, railtype); + return NPFRouteToStationOrTileTwoWay(tile, trackdir, INVALID_TILE, 0, target, type, owner, railtypes); } -NPFFoundTargetData NPFRouteToDepotBreadthFirstTwoWay(TileIndex tile1, Trackdir trackdir1, TileIndex tile2, Trackdir trackdir2, TransportType type, Owner owner, RailType railtype, uint reverse_penalty) +NPFFoundTargetData NPFRouteToDepotBreadthFirstTwoWay(TileIndex tile1, Trackdir trackdir1, TileIndex tile2, Trackdir trackdir2, TransportType type, Owner owner, RailTypeMask railtypes, uint reverse_penalty) { AyStarNode start1; AyStarNode start2; @@ -759,15 +759,15 @@ NPFFoundTargetData NPFRouteToDepotBreadthFirstTwoWay(TileIndex tile1, Trackdir t /* perform a breadth first search. Target is NULL, * since we are just looking for any depot...*/ - return NPFRouteInternal(&start1, (IsValidTile(tile2) ? &start2 : NULL), NULL, NPFFindDepot, NPFCalcZero, type, owner, railtype, reverse_penalty); + return NPFRouteInternal(&start1, (IsValidTile(tile2) ? &start2 : NULL), NULL, NPFFindDepot, NPFCalcZero, type, owner, railtypes, reverse_penalty); } -NPFFoundTargetData NPFRouteToDepotBreadthFirst(TileIndex tile, Trackdir trackdir, TransportType type, Owner owner, RailType railtype) +NPFFoundTargetData NPFRouteToDepotBreadthFirst(TileIndex tile, Trackdir trackdir, TransportType type, Owner owner, RailTypeMask railtypes) { - return NPFRouteToDepotBreadthFirstTwoWay(tile, trackdir, INVALID_TILE, 0, type, owner, railtype, 0); + return NPFRouteToDepotBreadthFirstTwoWay(tile, trackdir, INVALID_TILE, 0, type, owner, railtypes, 0); } -NPFFoundTargetData NPFRouteToDepotTrialError(TileIndex tile, Trackdir trackdir, TransportType type, Owner owner, RailType railtype) +NPFFoundTargetData NPFRouteToDepotTrialError(TileIndex tile, Trackdir trackdir, TransportType type, Owner owner, RailTypeMask railtypes) { /* Okay, what we're gonna do. First, we look at all depots, calculate * the manhatten distance to get to each depot. We then sort them by @@ -38,7 +38,7 @@ typedef struct NPFFindStationOrTileData { /* Meant to be stored in AyStar.target enum { /* Indices into AyStar.userdata[] */ NPF_TYPE = 0, /* Contains a TransportTypes value */ NPF_OWNER, /* Contains an Owner value */ - NPF_RAILTYPE, /* Contains the RailType value of the engine when NPF_TYPE == TRANSPORT_RAIL. Unused otherwise. */ + NPF_RAILTYPES, /* Contains a bitmask the compatible RailTypes of the engine when NPF_TYPE == TRANSPORT_RAIL. Unused otherwise. */ }; enum { /* Indices into AyStarNode.userdata[] */ @@ -64,28 +64,28 @@ typedef struct NPFFoundTargetData { /* Meant to be stored in AyStar.userpath */ /* Will search from the given tile and direction, for a route to the given * station for the given transport type. See the declaration of * NPFFoundTargetData above for the meaning of the result. */ -NPFFoundTargetData NPFRouteToStationOrTile(TileIndex tile, Trackdir trackdir, NPFFindStationOrTileData* target, TransportType type, Owner owner, RailType railtype); +NPFFoundTargetData NPFRouteToStationOrTile(TileIndex tile, Trackdir trackdir, NPFFindStationOrTileData* target, TransportType type, Owner owner, RailTypeMask railtypes); /* Will search as above, but with two start nodes, the second being the * reverse. Look at the NPF_FLAG_REVERSE flag in the result node to see which * direction was taken (NPFGetBit(result.node, NPF_FLAG_REVERSE)) */ -NPFFoundTargetData NPFRouteToStationOrTileTwoWay(TileIndex tile1, Trackdir trackdir1, TileIndex tile2, Trackdir trackdir2, NPFFindStationOrTileData* target, TransportType type, Owner owner, RailType railtype); +NPFFoundTargetData NPFRouteToStationOrTileTwoWay(TileIndex tile1, Trackdir trackdir1, TileIndex tile2, Trackdir trackdir2, NPFFindStationOrTileData* target, TransportType type, Owner owner, RailTypeMask railtypes); /* Will search a route to the closest depot. */ /* Search using breadth first. Good for little track choice and inaccurate * heuristic, such as railway/road.*/ -NPFFoundTargetData NPFRouteToDepotBreadthFirst(TileIndex tile, Trackdir trackdir, TransportType type, Owner owner, RailType railtype); +NPFFoundTargetData NPFRouteToDepotBreadthFirst(TileIndex tile, Trackdir trackdir, TransportType type, Owner owner, RailTypeMask railtypes); /* Same as above but with two start nodes, the second being the reverse. Call * NPFGetBit(result.node, NPF_FLAG_REVERSE) to see from which node the path * orginated. All pathfs from the second node will have the given * reverse_penalty applied (NPF_TILE_LENGTH is the equivalent of one full * tile). */ -NPFFoundTargetData NPFRouteToDepotBreadthFirstTwoWay(TileIndex tile1, Trackdir trackdir1, TileIndex tile2, Trackdir trackdir2, TransportType type, Owner owner, RailType railtype, uint reverse_penalty); +NPFFoundTargetData NPFRouteToDepotBreadthFirstTwoWay(TileIndex tile1, Trackdir trackdir1, TileIndex tile2, Trackdir trackdir2, TransportType type, Owner owner, RailTypeMask railtypes, uint reverse_penalty); /* Search by trying each depot in order of Manhattan Distance. Good for lots * of choices and accurate heuristics, such as water. */ -NPFFoundTargetData NPFRouteToDepotTrialError(TileIndex tile, Trackdir trackdir, TransportType type, Owner owner, RailType railtype); +NPFFoundTargetData NPFRouteToDepotTrialError(TileIndex tile, Trackdir trackdir, TransportType type, Owner owner, RailTypeMask railtypes); void NPFFillWithOrderData(NPFFindStationOrTileData* fstd, Vehicle* v); @@ -1238,6 +1238,75 @@ bool AfterLoadGame(void) } } + /* Elrails got added in rev 24 */ + if (CheckSavegameVersion(24)) { + Vehicle* v; + uint i; + TileIndex t; + bool make_elrail = false; + + for (i = 0; i < lengthof(_engines); i++) { + Engine* e = GetEngine(i); + if (e->type == VEH_Train && + (e->railtype != RAILTYPE_RAIL || RailVehInfo(i)->engclass == 2)) { + e->railtype++; + } + } + + FOR_ALL_VEHICLES(v) { + if (v->type == VEH_Train) { + RailType rt = GetEngine(v->engine_type)->railtype; + + v->u.rail.railtype = rt; + if (rt == RAILTYPE_ELECTRIC) make_elrail = true; + } + } + + /* .. so we convert the entire map from normal to elrail (so maintain "fairness") */ + for (t = 0; t < MapSize(); t++) { + switch (GetTileType(t)) { + case MP_RAILWAY: + if (GetRailType(t) > RAILTYPE_RAIL || make_elrail) AB(_m[t].m3, 0, 4, 1); + break; + + case MP_STREET: + if (IsLevelCrossing(t) && (GetRailTypeCrossing(t) > RAILTYPE_RAIL || make_elrail)) AB(_m[t].m4, 0, 4, 1); + break; + + case MP_STATION: + if (_m[t].m5 < 8 && (GB(_m[t].m3, 0, 4) > RAILTYPE_RAIL || make_elrail)) AB(_m[t].m3, 0, 4, 1); + break; + + case MP_TUNNELBRIDGE: + if (GB(_m[t].m5, 4, 4) == 0) { // tunnel? + if (GB(_m[t].m5, 2, 2) == 0) { // railway tunnel? + if (GB(_m[t].m3, 0, 4) > RAILTYPE_RAIL || make_elrail) AB(_m[t].m3, 0, 4, 1); + } + } else { + if (GB(_m[t].m5, 1, 2) == 0) { // railway bridge? + if (GB(_m[t].m5, 6, 1) == 0) { // bridge ending? + if (GB(_m[t].m3, 0, 4) > RAILTYPE_RAIL || make_elrail) AB(_m[t].m3, 0, 4, 1); + } else { + if (GB(_m[t].m3, 4, 4) > RAILTYPE_RAIL || make_elrail) AB(_m[t].m3, 4, 4, 1); + } + } + if ((_m[t].m5 & 0xF8) == 0xE0) { // bridge middle part with rails below? + if (GB(_m[t].m3, 0, 4) > RAILTYPE_RAIL || make_elrail) AB(_m[t].m3, 0, 4, 1); + } + } + break; + + default: + break; + } + } + + FOR_ALL_VEHICLES(v) { + if (v->type == VEH_Train && (IsFrontEngine(v) || IsFreeWagon(v))) TrainConsistChanged(v); + } + + } + /* In version 16.1 of the savegame a player can decide if trains, which get * replaced, shall keep their old length. In all prior versions, just default * to false */ diff --git a/openttd.dsp b/openttd.dsp index b8e28ed7f..f4e2cea22 100644 --- a/openttd.dsp +++ b/openttd.dsp @@ -209,6 +209,10 @@ SOURCE=.\economy.c # End Source File # Begin Source File +SOURCE=.\elrail.c +# End Source File +# Begin Source File + SOURCE=.\engine.c # End Source File # Begin Source File diff --git a/openttd.vcproj b/openttd.vcproj index 1fa63899d..3afcd36d9 100644 --- a/openttd.vcproj +++ b/openttd.vcproj @@ -215,6 +215,9 @@ RelativePath=".\economy.c"> </File> <File + RelativePath=".\elrail.c"> + </File> + <File RelativePath=".\engine.c"> </File> <File diff --git a/pathfind.c b/pathfind.c index b70e51887..b6c268938 100644 --- a/pathfind.c +++ b/pathfind.c @@ -454,7 +454,8 @@ typedef struct { void *userdata; TileIndex dest; - byte tracktype; + TransportType tracktype; + RailTypeMask railtypes; uint maxlength; HashLink *new_link; @@ -791,6 +792,11 @@ start_at: * bits, not just reachable ones, to prevent infinite loops. */ if (bits == 0 || TracksOverlap(allbits)) break; + if (!HASBIT(tpf->railtypes, GetRailType(tile))) { + bits = 0; + break; + } + /* If we reach here, the tile has exactly one track, and this track is reachable => Rail segment continues */ @@ -926,14 +932,15 @@ start_at: // new pathfinder for trains. better and faster. -void NewTrainPathfind(TileIndex tile, TileIndex dest, DiagDirection direction, NTPEnumProc* enum_proc, void* data) +void NewTrainPathfind(TileIndex tile, TileIndex dest, RailTypeMask railtypes, DiagDirection direction, NTPEnumProc* enum_proc, void* data) { NewTrackPathFinder tpf; tpf.dest = dest; tpf.userdata = data; tpf.enum_proc = enum_proc; - tpf.tracktype = 0; + tpf.tracktype = TRANSPORT_RAIL; + tpf.railtypes = railtypes; tpf.maxlength = min(_patches.pf_maxlength * 3, 10000); tpf.nstack = 0; tpf.new_link = tpf.links; diff --git a/pathfind.h b/pathfind.h index 748ec5ab9..8796774dd 100644 --- a/pathfind.h +++ b/pathfind.h @@ -68,6 +68,6 @@ typedef struct { } FindLengthOfTunnelResult; FindLengthOfTunnelResult FindLengthOfTunnel(TileIndex tile, DiagDirection direction); -void NewTrainPathfind(TileIndex tile, TileIndex dest, DiagDirection direction, NTPEnumProc* enum_proc, void* data); +void NewTrainPathfind(TileIndex tile, TileIndex dest, RailTypeMask railtypes, DiagDirection direction, NTPEnumProc* enum_proc, void* data); #endif /* PATHFIND_H */ @@ -248,6 +248,7 @@ static inline RailType GetBestRailtype(const Player* p) { if (HasRailtypeAvail(p, RAILTYPE_MAGLEV)) return RAILTYPE_MAGLEV; if (HasRailtypeAvail(p, RAILTYPE_MONO)) return RAILTYPE_MONO; + if (HasRailtypeAvail(p, RAILTYPE_ELECTRIC)) return RAILTYPE_ELECTRIC; return RAILTYPE_RAIL; } @@ -9,7 +9,6 @@ #include "rail_map.h" #include "tile.h" - /** These are a combination of tracks and directions. Values are 0-5 in one direction (corresponding to the Track enum) and 8-13 in the other direction. */ typedef enum Trackdirs { @@ -110,8 +109,11 @@ typedef struct RailtypeInfo { /** sprite number difference between a piece of track on a snowy ground and the corresponding one on normal ground */ SpriteID snow_offset; - /** bitmask to the OTHER railtypes that can be used by an engine of THIS railtype */ - byte compatible_railtypes; + /** bitmask to the OTHER railtypes on which an engine of THIS railtype generates power */ + RailTypeMask powered_railtypes; + + /** bitmask to the OTHER railtypes on which an engine of THIS railtype can physically travel */ + RailTypeMask compatible_railtypes; /** * Offset between the current railtype and normal rail. This means that:<p> @@ -478,6 +480,11 @@ static inline bool IsCompatibleRail(RailType enginetype, RailType tiletype) return HASBIT(GetRailTypeInfo(enginetype)->compatible_railtypes, tiletype); } +static inline bool HasPowerOnRail(RailType enginetype, RailType tiletype) +{ + return HASBIT(GetRailTypeInfo(enginetype)->powered_railtypes, tiletype); +} + /** * Checks if the given tracks overlap, ie form a crossing. Basically this * means when there is more than one track on the tile, exept when there are @@ -497,4 +504,13 @@ static inline bool TracksOverlap(TrackBits bits) void DrawTrainDepotSprite(int x, int y, int image, RailType railtype); void DrawDefaultWaypointSprite(int x, int y, RailType railtype); + +/** + * Draws overhead wires and pylons for electric railways. + * @param ti The TileInfo struct of the tile being drawn + * @see DrawCatenaryRailway + */ +void DrawCatenary(const TileInfo *ti); + +uint GetRailFoundation(uint tileh, uint bits); #endif /* RAIL_H */ diff --git a/rail_cmd.c b/rail_cmd.c index 039acf9bd..75e0263f1 100644 --- a/rail_cmd.c +++ b/rail_cmd.c @@ -896,11 +896,13 @@ int32 CmdRemoveSignalTrack(int x, int y, uint32 flags, uint32 p1, uint32 p2) return CmdSignalTrackHelper(x, y, flags, p1, SETBIT(p2, 0)); } -typedef int32 DoConvertRailProc(TileIndex tile, uint totype, bool exec); +typedef int32 DoConvertRailProc(TileIndex tile, RailType totype, bool exec); -static int32 DoConvertRail(TileIndex tile, uint totype, bool exec) +static int32 DoConvertRail(TileIndex tile, RailType totype, bool exec) { - if (!CheckTileOwnership(tile) || !EnsureNoVehicle(tile)) return CMD_ERROR; + if (!CheckTileOwnership(tile)) return CMD_ERROR; + + if (!EnsureNoVehicle(tile) && (!IsCompatibleRail(GetRailType(tile), totype) || IsPlainRailTile(tile))) return CMD_ERROR; // tile is already of requested type? if (GetRailType(tile) == totype) return CMD_ERROR; @@ -1297,6 +1299,9 @@ static void DrawTrackBits(TileInfo* ti, TrackBits track, bool earth, bool snow, if (track & TRACK_BIT_LEFT) DrawGroundSprite(rti->base_sprites.single_w); if (track & TRACK_BIT_RIGHT) DrawGroundSprite(rti->base_sprites.single_e); } + + if (GB(_m[ti->tile].m3, 0, 4) == RAILTYPE_ELECTRIC) DrawCatenary(ti); + } static void DrawTile_Track(TileInfo *ti) @@ -1388,6 +1393,8 @@ static void DrawTile_Track(TileInfo *ti) DrawGroundSprite(image); + if (GetRailType(ti->tile) == RAILTYPE_ELECTRIC) DrawCatenary(ti); + foreach_draw_tile_seq(seq, cust->seq) { DrawSpecialBuilding( seq->image + relocation, 0, ti, @@ -1420,6 +1427,8 @@ static void DrawTile_Track(TileInfo *ti) DrawGroundSprite(image); + if (GetRailType(ti->tile) == RAILTYPE_ELECTRIC) DrawCatenary(ti); + for (; drss->image != 0; drss++) { DrawSpecialBuilding( drss->image, type < 4 ? rti->total_offset : 0, ti, diff --git a/rail_map.h b/rail_map.h index 5a3828282..a74982cf1 100644 --- a/rail_map.h +++ b/rail_map.h @@ -32,13 +32,16 @@ typedef enum RailTileSubtypes { typedef enum RailTypes { - RAILTYPE_RAIL = 0, - RAILTYPE_MONO = 1, - RAILTYPE_MAGLEV = 2, + RAILTYPE_RAIL = 0, + RAILTYPE_ELECTRIC = 1, + RAILTYPE_MONO = 2, + RAILTYPE_MAGLEV = 3, RAILTYPE_END, INVALID_RAILTYPE = 0xFF } RailType; +typedef byte RailTypeMask; + static inline RailType GetRailType(TileIndex t) { return (RailType)GB(_m[t].m3, 0, 4); diff --git a/railtypes.h b/railtypes.h index 12180c352..1b9b665c4 100644 --- a/railtypes.h +++ b/railtypes.h @@ -40,8 +40,11 @@ const RailtypeInfo _railtypes[] = { /* Offset of snow tiles */ SPR_RAIL_SNOW_OFFSET, + /* Powered railtypes */ + 1 << RAILTYPE_RAIL | 1 << RAILTYPE_ELECTRIC, + /* Compatible railtypes */ - (1 << RAILTYPE_RAIL), + 1 << RAILTYPE_RAIL | 1 << RAILTYPE_ELECTRIC, /* main offset */ 0, @@ -50,6 +53,56 @@ const RailtypeInfo _railtypes[] = { 0, }, + /** Electrified railway */ + { /* Main Sprites */ + { SPR_RAIL_TRACK_Y, SPR_RAIL_TRACK_N_S, SPR_RAIL_TRACK_BASE, SPR_RAIL_SINGLE_Y, SPR_RAIL_SINGLE_X, + SPR_RAIL_SINGLE_NORTH, SPR_RAIL_SINGLE_SOUTH, SPR_RAIL_SINGLE_EAST, SPR_RAIL_SINGLE_WEST, + SPR_CROSSING_OFF_X_RAIL, + SPR_TUNNEL_ENTRY_REAR_RAIL + }, + + /* GUI sprites */ + { + SPR_BUILD_NS_ELRAIL, + SPR_BUILD_X_ELRAIL, + SPR_BUILD_EW_ELRAIL, + SPR_BUILD_Y_ELRAIL, + SPR_OPENTTD_BASE + 0, + 0x50E, + SPR_BUILD_TUNNEL_ELRAIL, + SPR_IMG_CONVERT_RAIL + }, + + { + SPR_CURSOR_NS_ELRAIL, + SPR_CURSOR_SWNE_ELRAIL, + SPR_CURSOR_EW_ELRAIL, + SPR_CURSOR_NWSE_ELRAIL, + SPR_CURSOR_AUTORAIL, + SPR_CURSOR_RAIL_DEPOT, + SPR_CURSOR_TUNNEL_ELRAIL, + SPR_CURSOR_CONVERT_RAIL + }, + + /* strings */ + { STR_TITLE_ELRAIL_CONSTRUCTION }, + + /* Offset of snow tiles */ + SPR_RAIL_SNOW_OFFSET, + + /* Powered railtypes */ + 1 << RAILTYPE_ELECTRIC, + + /* Compatible railtypes */ + 1 << RAILTYPE_ELECTRIC | 1 << RAILTYPE_RAIL, + + /* main offset */ + 0, + + /* bridge offset */ + 0 + }, + /** Monorail */ { /* Main Sprites */ { SPR_MONO_TRACK_Y, SPR_MONO_TRACK_N_S, SPR_MONO_TRACK_BASE, SPR_MONO_SINGLE_Y, SPR_MONO_SINGLE_X, @@ -83,8 +136,11 @@ const RailtypeInfo _railtypes[] = { /* Offset of snow tiles */ SPR_MONO_SNOW_OFFSET, + /* Powered railtypes */ + 1 << RAILTYPE_MONO, + /* Compatible Railtypes */ - (1 << RAILTYPE_MONO), + 1 << RAILTYPE_MONO, /* main offset */ 82, @@ -126,8 +182,11 @@ const RailtypeInfo _railtypes[] = { /* Offset of snow tiles */ SPR_MGLV_SNOW_OFFSET, + /* Powered railtypes */ + 1 << RAILTYPE_MAGLEV, + /* Compatible Railtypes */ - (1 << RAILTYPE_MAGLEV), + 1 << RAILTYPE_MAGLEV, /* main offset */ 164, diff --git a/road_cmd.c b/road_cmd.c index 854c59cff..20505c959 100644 --- a/road_cmd.c +++ b/road_cmd.c @@ -787,6 +787,7 @@ static void DrawTile_Road(TileInfo *ti) } DrawGroundSprite(image); + if (GB(_m[ti->tile].m4, 0, 4) == RAILTYPE_ELECTRIC) DrawCatenary(ti); break; } diff --git a/saveload.c b/saveload.c index 3b304f445..75f75c6d0 100644 --- a/saveload.c +++ b/saveload.c @@ -30,7 +30,7 @@ #include "variables.h" #include <setjmp.h> -const uint16 SAVEGAME_VERSION = 23; +const uint16 SAVEGAME_VERSION = 24; uint16 _sl_version; /// the major savegame version identifier byte _sl_minor_version; /// the minor savegame version, DO NOT USE! diff --git a/station_cmd.c b/station_cmd.c index 986e73241..1cbad1c0d 100644 --- a/station_cmd.c +++ b/station_cmd.c @@ -1959,6 +1959,8 @@ static void DrawTile_Station(TileInfo *ti) // but this is something else. If AI builds station with 114 it looks all weird DrawGroundSprite(image); + if (GB(_m[ti->tile].m3, 0, 4) == RAILTYPE_ELECTRIC) DrawCatenary(ti); + foreach_draw_tile_seq(dtss, t->seq) { image = dtss->image + relocation; image += offset; diff --git a/table/elrail_data.h b/table/elrail_data.h new file mode 100644 index 000000000..32a283483 --- /dev/null +++ b/table/elrail_data.h @@ -0,0 +1,362 @@ +/* $Id */ +/** @file elrail_data.h Stores all the data for overhead wire and pylon drawing. @see elrail.c */ + +#ifndef ELRAIL_DATA_H +#define ELRAIL_DATA_H + +/** Tile Location group. This defines whether the X and or Y coordinate of a tile is even */ +typedef enum TLG { + XEVEN_YEVEN = 0, + XEVEN_YODD = 1, + XODD_YEVEN = 2, + XODD_YODD = 3, + TLG_END +} TLG; + +/** When determining the pylon configuration on the edge, two tiles are taken into account: + * the tile being drawn itself (the home tile, the one in ti->tile), and the neighbouring tile + */ +typedef enum { + TS_HOME = 0, + TS_NEIGHBOUR = 1, + + TS_END +} TileSource; + +enum { + TRACKS_AT_PCP = 6 +}; + +/** Which PPPs are possible at all on a given PCP */ +static byte AllowedPPPonPCP[DIAGDIR_END] = { + 1 << DIR_N | 1 << DIR_E | 1 << DIR_SE | 1 << DIR_S | 1 << DIR_W | 1 << DIR_NW, + 1 << DIR_N | 1 << DIR_NE | 1 << DIR_E | 1 << DIR_S | 1 << DIR_SW | 1 << DIR_W, + 1 << DIR_N | 1 << DIR_E | 1 << DIR_SE | 1 << DIR_S | 1 << DIR_W | 1 << DIR_NW, + 1 << DIR_N | 1 << DIR_NE | 1 << DIR_E | 1 << DIR_S | 1 << DIR_SW | 1 << DIR_W, +}; + +/** Which of the PPPs are inside the tile. For the two PPPs on the tile border the following system is used: + if you rotate the PCP so that it is in the north, the eastern PPP belongs to the tile. */ +static byte OwnedPPPonPCP[DIAGDIR_END] = { + 1 << DIR_SE | 1 << DIR_S | 1 << DIR_SW | 1 << DIR_W, + 1 << DIR_N | 1 << DIR_SW | 1 << DIR_W | 1 << DIR_NW, + 1 << DIR_N | 1 << DIR_NE | 1 << DIR_E | 1 << DIR_NW, + 1 << DIR_NE | 1 << DIR_E | 1 << DIR_SE | 1 << DIR_S +}; + +/** Preferred points of each trackbit. Those are the ones perpendicular to the track, plus the point in + extension of the track (to mark end-of-track).*/ +static byte PreferredPPPofTrackBitAtPCP[TRACK_END][DIAGDIR_END] = { + {1 << DIR_NE | 1 << DIR_SE | 1 << DIR_NW, 0xFF, 1 << DIR_SE | 1 << DIR_SW | 1 << DIR_NW, 0xFF }, /* X */ + {0xFF, 1 << DIR_NE | 1 << DIR_SE | 1 << DIR_SW, 0xFF, 1 << DIR_SW | 1 << DIR_NW | 1 << DIR_NE }, /* Y */ + {1 << DIR_E | 1 << DIR_N | 1 << DIR_S, 0xFF, 0xFF, 1 << DIR_W | 1 << DIR_N | 1 << DIR_S}, /* UPPER */ + {0xFF, 1 << DIR_E | 1 << DIR_N | 1 << DIR_S, 1 << DIR_W | 1 << DIR_N | 1 << DIR_S, 0xFF}, /* LOWER */ + {0xFF, 0xFF, 1 << DIR_S | 1 << DIR_E | 1 << DIR_W, 1 << DIR_N | 1 << DIR_E | 1 << DIR_W}, /* LEFT */ + {1 << DIR_N | 1 << DIR_E | 1 << DIR_W, 1 << DIR_S | 1 << DIR_E | 1 << DIR_W, 0xFF, 0xFF}, /* RIGHT */ +}; + +#define NUM_IGNORE_GROUPS 3 +/** In case we have a staight line, we place pylon only every two tiles, so there are certain tiles + which we ignore. A straight line is found if we have exactly two preferred points.*/ +static byte IgnoredPCP[NUM_IGNORE_GROUPS][TLG_END][DIAGDIR_END] = { + { + {1 << DIR_N | 1 << DIR_S , 1 << DIR_NE | 1 << DIR_SW, 1 << DIR_NW | 1 << DIR_SE, 1 << DIR_W | 1 << DIR_E}, + {0xFF , 1 << DIR_E | 1 << DIR_W, 1 << DIR_NW | 1 << DIR_SE, 1 << DIR_NE | 1 << DIR_SW}, + {1 << DIR_NW | 1 << DIR_SE, 1 << DIR_NE | 1 << DIR_SW, 1 << DIR_N | 1 << DIR_S , 0xFF}, + {1 << DIR_NW | 1 << DIR_SE, 0xFF , 0xFF, 1 << DIR_NE | 1 << DIR_SW} + }, + { + {1 << DIR_E | 1 << DIR_W, 1 << DIR_N | 1 << DIR_S, 0xFF, 1 << DIR_E | 1 << DIR_W}, + {0xFF, 0xFF, 1 << DIR_N | 1 << DIR_S, 1 << DIR_N | 1 << DIR_S}, + {0xFF, 1 << DIR_E | 1 << DIR_W, 1 << DIR_E | 1 << DIR_W, 1 << DIR_N | 1 << DIR_S}, + {1 << DIR_N | 1 << DIR_S, 1 << DIR_N | 1 << DIR_S, 0xFF, 1 << DIR_E | 1 << DIR_W} + }, + { + {0xFF, 0xFF, 0xFF, 0xFF}, + {0xFF, 0xFF, 1 << DIR_E | 1 << DIR_W, 0xFF}, + {0xFF, 0xFF, 0xFF, 0xFF}, + {1 << DIR_E | 1 << DIR_W, 0xFF, 0xFF, 0xFF} + } +}; + +/** Which pylons can definately NOT be built */ +static byte DisallowedPPPofTrackBitAtPCP[TRACK_END][DIAGDIR_END] = { + {1 << DIR_SW | 1 << DIR_NE, 0, 1 << DIR_SW | 1 << DIR_NE, 0 }, /* X */ + {0, 1 << DIR_NW | 1 << DIR_SE, 0, 1 << DIR_NW | 1 << DIR_SE}, /* Y */ + {1 << DIR_W | 1 << DIR_E, 0, 0, 1 << DIR_W | 1 << DIR_E }, /* UPPER */ + {0, 1 << DIR_W | 1 << DIR_E, 1 << DIR_W | 1 << DIR_E, 0 }, /* LOWER */ + {0, 0, 1 << DIR_S | 1 << DIR_N, 1 << DIR_N | 1 << DIR_S }, /* LEFT */ + {1 << DIR_S | 1 << DIR_N, 1 << DIR_S | 1 << DIR_N, 0, 0, }, /* RIGHT */ +}; + +typedef struct { + SpriteID image; + int8 x_offset; + int8 y_offset; + int8 x_size; + int8 y_size; + int8 z_size; + int8 z_offset; +} SortableSpriteStruct; + +enum { + /** Distance between wire and rail */ + ELRAIL_ELEVATION = 8, + /** Corrects an off-by-one error in some places (tileh 12 and 9) (TODO -- find source of error) */ + ELRAIL_ELEV_CORR = ELRAIL_ELEVATION + 1, + /** Wires that a draw one level higher than the north corner. */ + ELRAIL_ELEVRAISE = ELRAIL_ELEVATION + TILE_HEIGHT +}; + +static const SortableSpriteStruct CatenarySpriteData[] = { +/* X direction */ + /* Flat tiles: */ + /* Wires */ + { SPR_WIRE_X_SW, 0, 8, 16, 1, 1, ELRAIL_ELEVATION }, //! 0: Wire in X direction, pylon on the SW end only + { SPR_WIRE_X_NE, 0, 8, 16, 1, 1, ELRAIL_ELEVATION }, //! 1: Wire in X direction, pylon on the NE end + { SPR_WIRE_X_SHORT, 0, 8, 16, 1, 1, ELRAIL_ELEVATION }, //! 2: Wire in X direction, pylon on both ends + + /* "up" tiles */ + /* Wires */ + { SPR_WIRE_X_SW_UP, 0, 8, 16, 8, 1, ELRAIL_ELEVRAISE }, //! 3: Wire in X pitch up, pylon on the SW end only + { SPR_WIRE_X_NE_UP, 0, 8, 16, 8, 1, ELRAIL_ELEVRAISE }, //! 4: Wire in X pitch up, pylon on the NE end + { SPR_WIRE_X_SHORT_UP, 0, 8, 16, 8, 1, ELRAIL_ELEVRAISE }, //! 5: Wire in X pitch up, pylon on both ends + + /* "down" tiles */ + /* Wires */ + { SPR_WIRE_X_SW_DOWN, 0, 8, 16, 8, 1, ELRAIL_ELEV_CORR }, //! 6: Wire in X pitch down, pylon on the SW end + { SPR_WIRE_X_NE_DOWN, 0, 8, 16, 8, 1, ELRAIL_ELEV_CORR }, //! 7: Wire in X pitch down, pylon on the NE end + { SPR_WIRE_X_SHORT_DOWN, 0, 8, 16, 8, 1, ELRAIL_ELEV_CORR }, //! 8: Wire in X pitch down, pylon on both ends + + +/* Y direction */ + /* Flat tiles: */ + /* Wires */ + { SPR_WIRE_Y_SE, 8, 0, 1, 16, 1, ELRAIL_ELEVATION }, //! 9: Wire in Y direction, pylon on the SE end only + { SPR_WIRE_Y_NW, 8, 0, 1, 16, 1, ELRAIL_ELEVATION }, //!10: Wire in Y direction, pylon on the NW end + { SPR_WIRE_Y_SHORT, 8, 0, 1, 16, 1, ELRAIL_ELEVATION }, //!11: Wire in Y direction, pylon on both ends + + /* "up" tiles */ + /* Wires */ + { SPR_WIRE_Y_SE_UP, 8, 0, 8, 16, 1, ELRAIL_ELEVRAISE }, //!12: Wire in Y pitch up, pylon on the SE end only + { SPR_WIRE_Y_NW_UP, 8, 0, 8, 16, 1, ELRAIL_ELEVRAISE }, //!13: Wire in Y pitch up, pylon on the NW end + { SPR_WIRE_Y_SHORT_UP, 8, 0, 8, 16, 1, ELRAIL_ELEVRAISE }, //!14: Wire in Y pitch up, pylon on both ends + + /* "down" tiles */ + /* Wires */ + { SPR_WIRE_Y_SE_DOWN, 8, 0, 8, 16, 1, ELRAIL_ELEV_CORR }, //!15: Wire in Y pitch down, pylon on the SE end + { SPR_WIRE_Y_NW_DOWN, 8, 0, 8, 16, 1, ELRAIL_ELEV_CORR }, //!16: Wire in Y pitch down, pylon on the NW end + { SPR_WIRE_Y_SHORT_DOWN, 8, 0, 8, 16, 1, ELRAIL_ELEV_CORR }, //!17: Wire in Y pitch down, pylon on both ends + +/* NS Direction */ + { SPR_WIRE_NS_SHORT, 8, 0, 8, 8, 1, ELRAIL_ELEVATION }, //!18: LEFT trackbit wire, pylon on both ends + { SPR_WIRE_NS_SHORT, 0, 8, 8, 8, 1, ELRAIL_ELEVATION }, //!19: RIGHT trackbit wire, pylon on both ends + + { SPR_WIRE_NS_N, 8, 0, 8, 8, 1, ELRAIL_ELEVATION }, //!20: LEFT trackbit wire, pylon on N end + { SPR_WIRE_NS_N, 0, 8, 8, 8, 1, ELRAIL_ELEVATION }, //!21: RIGHT trackbit wire, pylon on N end + + { SPR_WIRE_NS_S, 8, 0, 8, 8, 1, ELRAIL_ELEVATION }, //!22: LEFT trackbit wire, pylon on S end + { SPR_WIRE_NS_S, 0, 8, 8, 8, 1, ELRAIL_ELEVATION }, //!23: RIGHT trackbit wire, pylon on S end + +/* EW Direction */ + { SPR_WIRE_EW_SHORT, 8, 0, 8, 8, 1, ELRAIL_ELEVATION }, //!24: UPPER trackbit wire, pylon on both ends + { SPR_WIRE_EW_SHORT, 16, 8, 8, 8, 1, ELRAIL_ELEVATION }, //!25: LOWER trackbit wire, pylon on both ends + + { SPR_WIRE_EW_W, 8, 0, 8, 8, 1, ELRAIL_ELEVATION }, //!28: UPPER trackbit wire, pylon on both ends + { SPR_WIRE_EW_W, 16, 8, 8, 8, 1, ELRAIL_ELEVATION }, //!29: LOWER trackbit wire, pylon on both ends + + { SPR_WIRE_EW_E, 8, 0, 8, 8, 1, ELRAIL_ELEVATION }, //!32: UPPER trackbit wire, pylon on both ends + { SPR_WIRE_EW_E, 16, 8, 8, 8, 1, ELRAIL_ELEVATION }, //!33: LOWER trackbit wire, pylon on both ends + +/* Depots */ + { SPR_WIRE_DEPOT_SW, 0, 8, 8, 1, 1, ELRAIL_ELEVATION }, //!36: Wire for SW depot exit + { SPR_WIRE_DEPOT_NW, 8, 0, 1, 8, 1, ELRAIL_ELEVATION }, //!37: Wire for NW depot exit + { SPR_WIRE_DEPOT_NE, 0, 8, 8, 1, 1, ELRAIL_ELEVATION }, //!38: Wire for NE depot exit + { SPR_WIRE_DEPOT_SE, 8, 0, 1, 8, 1, ELRAIL_ELEVATION }, //!39: Wire for SE depot exit +}; + +/** Refers to a certain element of the catenary. + * Identifiers for Wires: + * <ol><li>Direction of the wire</li> + * <li>Slope of the tile for diagonals, placement inside the track for horiz/vertical pieces</li> + * <li>Place where a pylon shoule be</li></ol> + * Identifiers for Pylons: + * <ol><li>Direction of the wire</li> + * <li>Slope of the tile</li> + * <li>Position of the Pylon relative to the track</li> + * <li>Position of the Pylon inside the tile</li></ol> + */ +typedef enum { + WIRE_X_FLAT_SW, + WIRE_X_FLAT_NE, + WIRE_X_FLAT_BOTH, + + WIRE_X_UP_SW, + WIRE_X_UP_NE, + WIRE_X_UP_BOTH, + + WIRE_X_DOWN_SW, + WIRE_X_DOWN_NE, + WIRE_X_DOWN_BOTH, + + WIRE_Y_FLAT_SE, + WIRE_Y_FLAT_NW, + WIRE_Y_FLAT_BOTH, + + WIRE_Y_UP_SE, + WIRE_Y_UP_NW, + WIRE_Y_UP_BOTH, + + WIRE_Y_DOWN_SE, + WIRE_Y_DOWN_NW, + WIRE_Y_DOWN_BOTH, + + WIRE_NS_W_BOTH, + WIRE_NS_E_BOTH, + + WIRE_NS_W_N, + WIRE_NS_E_N, + + WIRE_NS_W_S, + WIRE_NS_E_S, + + WIRE_EW_N_BOTH, + WIRE_EW_S_BOTH, + + WIRE_EW_N_W, + WIRE_EW_S_W, + + WIRE_EW_N_E, + WIRE_EW_S_E, + + WIRE_DEPOT_SW, + WIRE_DEPOT_NW, + WIRE_DEPOT_NE, + WIRE_DEPOT_SE, + + INVALID_CATENARY = 0xFF +} CatenarySprite; + +/* This array stores which track bits can meet at a tile edge */ +static const Track PPPtracks[DIAGDIR_END][TRACKS_AT_PCP] = { + {TRACK_X, TRACK_X, TRACK_UPPER, TRACK_LOWER, TRACK_LEFT, TRACK_RIGHT}, + {TRACK_Y, TRACK_Y, TRACK_UPPER, TRACK_LOWER, TRACK_LEFT, TRACK_RIGHT}, + {TRACK_X, TRACK_X, TRACK_UPPER, TRACK_LOWER, TRACK_LEFT, TRACK_RIGHT}, + {TRACK_Y, TRACK_Y, TRACK_UPPER, TRACK_LOWER, TRACK_LEFT, TRACK_RIGHT}, +}; + +/* takes each of the 8 track bits from the array above and + assigns it to the home tile or neighbour tile */ +static const TileSource trackorigin[DIAGDIR_END][TRACKS_AT_PCP] = { + {TS_HOME, TS_NEIGHBOUR, TS_HOME , TS_NEIGHBOUR, TS_NEIGHBOUR, TS_HOME }, + {TS_HOME, TS_NEIGHBOUR, TS_NEIGHBOUR, TS_HOME , TS_NEIGHBOUR, TS_HOME }, + {TS_HOME, TS_NEIGHBOUR, TS_NEIGHBOUR, TS_HOME , TS_HOME , TS_NEIGHBOUR}, + {TS_HOME, TS_NEIGHBOUR, TS_HOME , TS_NEIGHBOUR, TS_HOME , TS_NEIGHBOUR}, +}; + +/* Several PPPs maybe exist, here they are sorted in order of preference. */ +static const Direction PPPorder[DIAGDIR_END][TLG_END][DIR_END] = { /* X - Y */ + { /* PCP 0 */ + {DIR_NE, DIR_NW, DIR_SE, DIR_SW, DIR_N, DIR_E, DIR_S, DIR_W}, /* evn - evn */ + {DIR_NE, DIR_SE, DIR_SW, DIR_NW, DIR_S, DIR_W, DIR_N, DIR_E}, /* evn - odd */ + {DIR_SW, DIR_NW, DIR_NE, DIR_SE, DIR_S, DIR_W, DIR_N, DIR_E}, /* odd - evn */ + {DIR_SW, DIR_SE, DIR_NE, DIR_NW, DIR_N, DIR_E, DIR_S, DIR_W}, /* odd - odd */ + }, {/* PCP 1 */ + {DIR_NE, DIR_NW, DIR_SE, DIR_SW, DIR_S, DIR_E, DIR_N, DIR_W}, /* evn - evn */ + {DIR_NE, DIR_SE, DIR_SW, DIR_NW, DIR_N, DIR_W, DIR_S, DIR_E}, /* evn - odd */ + {DIR_SW, DIR_NW, DIR_NE, DIR_SE, DIR_N, DIR_W, DIR_S, DIR_E}, /* odd - evn */ + {DIR_SW, DIR_SE, DIR_NE, DIR_NW, DIR_S, DIR_E, DIR_N, DIR_W}, /* odd - odd */ + }, {/* PCP 2 */ + {DIR_NE, DIR_NW, DIR_SE, DIR_SW, DIR_S, DIR_W, DIR_N, DIR_E}, /* evn - evn */ + {DIR_NE, DIR_SE, DIR_SW, DIR_NW, DIR_N, DIR_E, DIR_S, DIR_W}, /* evn - odd */ + {DIR_SW, DIR_NW, DIR_NE, DIR_SE, DIR_N, DIR_E, DIR_S, DIR_W}, /* odd - evn */ + {DIR_SW, DIR_SE, DIR_NE, DIR_NW, DIR_S, DIR_W, DIR_N, DIR_E}, /* odd - odd */ + }, {/* PCP 3 */ + {DIR_NE, DIR_NW, DIR_SE, DIR_SW, DIR_N, DIR_W, DIR_S, DIR_E}, /* evn - evn */ + {DIR_NE, DIR_SE, DIR_SW, DIR_NW, DIR_S, DIR_E, DIR_N, DIR_W}, /* evn - odd */ + {DIR_SW, DIR_NW, DIR_NE, DIR_SE, DIR_S, DIR_E, DIR_N, DIR_W}, /* odd - evn */ + {DIR_SW, DIR_SE, DIR_NE, DIR_NW, DIR_N, DIR_W, DIR_S, DIR_E}, /* odd - odd */ + } +}; +/* Geometric placement of the PCP relative to the tile origin */ +static const char x_pcp_offsets[DIAGDIR_END] = {0, 8, 15, 8}; +static const char y_pcp_offsets[DIAGDIR_END] = {8, 15, 8, 0}; +/* Geometric placement of the PPP relative to the PCP*/ +static const char x_ppp_offsets[DIR_END] = {-3, -4, -3, 0, +3, +4, +3, 0}; +static const char y_ppp_offsets[DIR_END] = {-3, 0, +3, +4, +3, 0, -3, -4}; +/* The type of pylon to draw at each PPP */ +static const SpriteID pylons_normal[] = { + SPR_PYLON_EW_N, + SPR_PYLON_Y_NE, + SPR_PYLON_NS_E, + SPR_PYLON_X_SE, + SPR_PYLON_EW_S, + SPR_PYLON_Y_SW, + SPR_PYLON_NS_W, + SPR_PYLON_X_NW +}; + +static const SpriteID pylons_bridge[] = { + SPR_PYLON_X_NW, + SPR_PYLON_X_SE, + SPR_PYLON_Y_NE, + SPR_PYLON_Y_SW +}; + +/* Maps a track bit onto two PCP positions */ +static const byte PCPpositions[TRACK_END][2] = { + {0, 2}, /* X */ + {1, 3}, /* Y */ + {3, 0}, /* UPPER */ + {1, 2}, /* LOWER */ + {2, 3}, /* LEFT */ + {0, 1}, /* RIGHT */ +}; + +/* Selects a Wire (with white and grey ends) depending on whether: + a) none (should never happen) + b) the first + c) the second + d) both + PCP exists.*/ +static const CatenarySprite Wires[5][TRACK_END][4] = { + { /* Tileh == 0 */ + {INVALID_CATENARY, WIRE_X_FLAT_NE, WIRE_X_FLAT_SW, WIRE_X_FLAT_BOTH}, + {INVALID_CATENARY, WIRE_Y_FLAT_SE, WIRE_Y_FLAT_NW, WIRE_Y_FLAT_BOTH}, + {INVALID_CATENARY, WIRE_EW_N_W, WIRE_EW_N_E, WIRE_EW_N_BOTH}, + {INVALID_CATENARY, WIRE_EW_S_E, WIRE_EW_S_W, WIRE_EW_S_BOTH}, + {INVALID_CATENARY, WIRE_NS_W_S, WIRE_NS_W_N, WIRE_NS_W_BOTH}, + {INVALID_CATENARY, WIRE_NS_E_N, WIRE_NS_E_S, WIRE_NS_E_BOTH}, + }, { /* Tileh == 3 */ + {INVALID_CATENARY, WIRE_X_UP_NE, WIRE_X_UP_SW, WIRE_X_UP_BOTH}, + {INVALID_CATENARY, INVALID_CATENARY, INVALID_CATENARY, INVALID_CATENARY}, + {INVALID_CATENARY, INVALID_CATENARY, INVALID_CATENARY, INVALID_CATENARY}, + {INVALID_CATENARY, INVALID_CATENARY, INVALID_CATENARY, INVALID_CATENARY}, + {INVALID_CATENARY, INVALID_CATENARY, INVALID_CATENARY, INVALID_CATENARY}, + {INVALID_CATENARY, INVALID_CATENARY, INVALID_CATENARY, INVALID_CATENARY}, + }, { /* Tileh == 6 */ + {INVALID_CATENARY, INVALID_CATENARY, INVALID_CATENARY, INVALID_CATENARY}, + {INVALID_CATENARY, WIRE_Y_UP_SE, WIRE_Y_UP_NW, WIRE_Y_UP_BOTH}, + {INVALID_CATENARY, INVALID_CATENARY, INVALID_CATENARY, INVALID_CATENARY}, + {INVALID_CATENARY, INVALID_CATENARY, INVALID_CATENARY, INVALID_CATENARY}, + {INVALID_CATENARY, INVALID_CATENARY, INVALID_CATENARY, INVALID_CATENARY}, + {INVALID_CATENARY, INVALID_CATENARY, INVALID_CATENARY, INVALID_CATENARY}, + }, { /* Tileh == 9 */ + {INVALID_CATENARY, INVALID_CATENARY, INVALID_CATENARY, INVALID_CATENARY}, + {INVALID_CATENARY, WIRE_Y_DOWN_SE, WIRE_Y_DOWN_NW, WIRE_Y_DOWN_BOTH}, + {INVALID_CATENARY, INVALID_CATENARY, INVALID_CATENARY, INVALID_CATENARY}, + {INVALID_CATENARY, INVALID_CATENARY, INVALID_CATENARY, INVALID_CATENARY}, + {INVALID_CATENARY, INVALID_CATENARY, INVALID_CATENARY, INVALID_CATENARY}, + {INVALID_CATENARY, INVALID_CATENARY, INVALID_CATENARY, INVALID_CATENARY}, + }, { /* Tileh == 12 */ + {INVALID_CATENARY, WIRE_X_DOWN_NE, WIRE_X_DOWN_SW, WIRE_X_DOWN_BOTH}, + {INVALID_CATENARY, INVALID_CATENARY, INVALID_CATENARY, INVALID_CATENARY}, + {INVALID_CATENARY, INVALID_CATENARY, INVALID_CATENARY, INVALID_CATENARY}, + {INVALID_CATENARY, INVALID_CATENARY, INVALID_CATENARY, INVALID_CATENARY}, + {INVALID_CATENARY, INVALID_CATENARY, INVALID_CATENARY, INVALID_CATENARY}, + {INVALID_CATENARY, INVALID_CATENARY, INVALID_CATENARY, INVALID_CATENARY}, + } +}; + +#endif /* ELRAIL_DATA_H */ + diff --git a/table/engines.h b/table/engines.h index b8fe40190..dc33ebafa 100644 --- a/table/engines.h +++ b/table/engines.h @@ -27,11 +27,13 @@ // Rail types // R = Conventional railway +// E = Electrified railway // M = Monorail // L = MagLev #define R 0 -#define M 1 -#define L 2 +#define E 1 +#define M 2 +#define L 3 // Climates // T = Temperate // A = Arctic @@ -65,10 +67,10 @@ const EngineInfo orig_engine_info[] = { MK( 20454, 20, 22, 30, R, A|S ), /* 20 Turner Turbo (Diesel) */ MK( 16071, 20, 22, 30, R, A|S ), /* 21 MJS 1000 (Diesel) */ MK( 20820, 20, 20, 25, R, T ), /* 22 SH '125' (Diesel) */ - MK( 16437, 20, 23, 30, R, T ), /* 23 SH '30' (Electric) */ - MK( 19359, 20, 23, 80, R, T ), /* 24 SH '40' (Electric) */ - MK( 23376, 20, 25, 30, R, T ), /* 25 'T.I.M.' (Electric) */ - MK( 26298, 20, 25, 50, R, T ), /* 26 'AsiaStar' (Electric) */ + MK( 16437, 20, 23, 30, E, T ), /* 23 SH '30' (Electric) */ + MK( 19359, 20, 23, 80, E, T ), /* 24 SH '40' (Electric) */ + MK( 23376, 20, 25, 30, E, T ), /* 25 'T.I.M.' (Electric) */ + MK( 26298, 20, 25, 50, E, T ), /* 26 'AsiaStar' (Electric) */ MW( 1827, 20, 20, 50, R, T|A|S|Y), /* 27 Passenger Carriage */ MW( 1827, 20, 20, 50, R, T|A|S|Y), /* 28 Mail Van */ MW( 1827, 20, 20, 50, R, T|A ), /* 29 Coal Truck */ @@ -306,6 +308,7 @@ const EngineInfo orig_engine_info[] = { #undef L #undef M #undef R +#undef E /** Writes the properties of a rail vehicle into the RailVehicleInfo struct. * @see RailVehicleInfo diff --git a/table/landscape_const.h b/table/landscape_const.h index d7c3179e7..8cf1083c0 100644 --- a/table/landscape_const.h +++ b/table/landscape_const.h @@ -42,13 +42,6 @@ static const LandscapePredefVar _landscape_predef_var[4] = { 24, 255, 90, 255, 18, 28, 40, 255, 255, 255, 32, 30, }, - /* normal railveh by cargo */ - { - {27, 29, 28, 30, 31, 32, 33, 34, 35, 36, 37, 38}, - {57, 59, 58, 60, 61, 62, 63, 64, 65, 66, 67, 68}, - {89, 91, 90, 92, 93, 94, 95, 96, 97, 98, 99, 100} - }, - /* normal road veh by cargo start & count */ {116, 123, 126, 132, 135, 138, 141, 144, 147, 150, 153, 156}, {7, 3, 6, 3, 3, 3, 3, 3, 3, 3, 3, 3} @@ -95,14 +88,6 @@ static const LandscapePredefVar _landscape_predef_var[4] = { 24, 255, 90, 255, 18, 28, 40, 255, 255, 60, 40, 30 }, - /* hilly railveh by cargo */ - { - {27, 29, 28, 30, 31, 32, 33, 34, 35, 39, 37, 38}, - {57, 59, 58, 60, 61, 62, 63, 64, 65, 69, 67, 68}, - {89, 91, 90, 92, 93, 94, 95, 96, 97, 101, 99, 100} - }, - - /* hilly road veh by cargo start & count */ {116, 123, 126, 132, 135, 138, 141, 144, 147, 159, 153, 156}, {7, 3, 6, 3, 3, 3, 3, 3, 3, 3, 3, 3}, @@ -150,13 +135,6 @@ static const LandscapePredefVar _landscape_predef_var[4] = { 24, 20, 90, 255, 15, 28, 40, 255, 255, 80, 255, 30 }, - /* desert railveh by cargo */ - { - {27, 43, 28, 30, 42, 32, 33, 34, 40, 41, 37, 38}, - {57, 73, 58, 60, 72, 62, 63, 64, 70, 71, 67, 68}, - {89, 105, 90, 92, 104, 94, 95, 96, 102, 103, 99, 100} - }, - /* desert road veh by cargo start & count */ {116, 171, 126, 132, 168, 138, 141, 144, 162, 165, 153, 156}, {7, 3, 6, 3, 3, 3, 3, 3, 3, 3, 3, 3} @@ -203,13 +181,6 @@ static const LandscapePredefVar _landscape_predef_var[4] = { 24, 255, 90, 255, 30, 40, 60, 75, 25, 80, 255, 50 }, - /* candy railveh by cargo */ - { - {27, 44, 28, 50, 51, 49, 46, 48, 45, 47, 53, 52}, - {57, 74, 58, 80, 81, 79, 76, 78, 75, 77, 83, 82}, - {89, 106, 90, 112, 113, 111, 108, 110, 107, 109, 115, 114} - }, - /* candy road veh by cargo start & count */ {116, 174, 126, 186, 192, 189, 183, 177, 180, 201, 198, 195}, {7, 3, 6, 3, 3, 3, 3, 3, 3, 3, 3, 3} diff --git a/table/sprites.h b/table/sprites.h index 34fda3f64..f7e0817c2 100644 --- a/table/sprites.h +++ b/table/sprites.h @@ -51,7 +51,8 @@ enum Sprites { SPR_CANALS_BASE = 5382, SPR_SLOPES_BASE = SPR_CANALS_BASE + 70, SPR_AUTORAIL_BASE = SPR_SLOPES_BASE + 78, - SPR_2CCMAP_BASE = SPR_AUTORAIL_BASE + 55, + SPR_ELRAIL_BASE = SPR_AUTORAIL_BASE + 55, + SPR_2CCMAP_BASE = SPR_ELRAIL_BASE + 53, SPR_OPENTTD_BASE = SPR_2CCMAP_BASE + 256, SPR_BLOT = SPR_OPENTTD_BASE + 29, // colored circle (mainly used as vehicle profit marker and for sever compatibility) @@ -206,6 +207,54 @@ enum Sprites { OFFSET_TILEH_13 = 19, OFFSET_TILEH_14 = 16, + /* Elrail stuff */ + /* Wires. First identifier is the direction of the track, second is the required placement of the pylon. + "short" denotes a wire that requires a pylon on each end. Third identifier is the direction of the slope + (in positive coordinate direction) */ + SPR_WIRE_X_SHORT = SPR_ELRAIL_BASE + 3, + SPR_WIRE_Y_SHORT = SPR_ELRAIL_BASE + 4, + SPR_WIRE_EW_SHORT = SPR_ELRAIL_BASE + 5, + SPR_WIRE_NS_SHORT = SPR_ELRAIL_BASE + 6, + SPR_WIRE_X_SHORT_DOWN = SPR_ELRAIL_BASE + 7, + SPR_WIRE_Y_SHORT_UP = SPR_ELRAIL_BASE + 8, + SPR_WIRE_X_SHORT_UP = SPR_ELRAIL_BASE + 9, + SPR_WIRE_Y_SHORT_DOWN = SPR_ELRAIL_BASE + 10, + + SPR_WIRE_X_SW = SPR_ELRAIL_BASE + 11, + SPR_WIRE_Y_SE = SPR_ELRAIL_BASE + 12, + SPR_WIRE_EW_E = SPR_ELRAIL_BASE + 13, + SPR_WIRE_NS_S = SPR_ELRAIL_BASE + 14, + SPR_WIRE_X_SW_DOWN = SPR_ELRAIL_BASE + 15, + SPR_WIRE_Y_SE_UP = SPR_ELRAIL_BASE + 16, + SPR_WIRE_X_SW_UP = SPR_ELRAIL_BASE + 17, + SPR_WIRE_Y_SE_DOWN = SPR_ELRAIL_BASE + 18, + + SPR_WIRE_X_NE = SPR_ELRAIL_BASE + 19, + SPR_WIRE_Y_NW = SPR_ELRAIL_BASE + 20, + SPR_WIRE_EW_W = SPR_ELRAIL_BASE + 21, + SPR_WIRE_NS_N = SPR_ELRAIL_BASE + 22, + SPR_WIRE_X_NE_DOWN = SPR_ELRAIL_BASE + 23, + SPR_WIRE_Y_NW_UP = SPR_ELRAIL_BASE + 24, + SPR_WIRE_X_NE_UP = SPR_ELRAIL_BASE + 25, + SPR_WIRE_Y_NW_DOWN = SPR_ELRAIL_BASE + 26, + + /* Tunnel entries */ + SPR_WIRE_DEPOT_SW = SPR_ELRAIL_BASE + 27, + SPR_WIRE_DEPOT_NW = SPR_ELRAIL_BASE + 28, + SPR_WIRE_DEPOT_NE = SPR_ELRAIL_BASE + 29, + SPR_WIRE_DEPOT_SE = SPR_ELRAIL_BASE + 30, + + + /* Pylons, first identifier is the direction of the track, second the placement relative to the track */ + SPR_PYLON_Y_NE = SPR_ELRAIL_BASE + 31, + SPR_PYLON_Y_SW = SPR_ELRAIL_BASE + 32, + SPR_PYLON_X_NW = SPR_ELRAIL_BASE + 33, + SPR_PYLON_X_SE = SPR_ELRAIL_BASE + 34, + SPR_PYLON_EW_N = SPR_ELRAIL_BASE + 35, + SPR_PYLON_EW_S = SPR_ELRAIL_BASE + 36, + SPR_PYLON_NS_W = SPR_ELRAIL_BASE + 37, + SPR_PYLON_NS_E = SPR_ELRAIL_BASE + 38, + /* sprites for airports and airfields*/ /* Small airports are AIRFIELD, everything else is AIRPORT */ SPR_HELIPORT = 2633, @@ -955,6 +1004,13 @@ enum Sprites { SPR_BUBBLE_ABSORB_3 = 4761, SPR_BUBBLE_ABSORB_4 = 4762, + /* Electrified rail build menu */ + SPR_BUILD_NS_ELRAIL = SPR_ELRAIL_BASE + 39, + SPR_BUILD_X_ELRAIL = SPR_ELRAIL_BASE + 40, + SPR_BUILD_EW_ELRAIL = SPR_ELRAIL_BASE + 41, + SPR_BUILD_Y_ELRAIL = SPR_ELRAIL_BASE + 42, + SPR_BUILD_TUNNEL_ELRAIL = SPR_ELRAIL_BASE + 47, + /* road_gui.c */ SPR_IMG_ROAD_NW = 1309, SPR_IMG_ROAD_NE = 1310, @@ -1034,9 +1090,15 @@ typedef enum CursorSprites { SPR_CURSOR_EW_MAGLEV = 1273, SPR_CURSOR_NWSE_MAGLEV = 1274, + SPR_CURSOR_NS_ELRAIL = SPR_ELRAIL_BASE + 43, + SPR_CURSOR_SWNE_ELRAIL = SPR_ELRAIL_BASE + 44, + SPR_CURSOR_EW_ELRAIL = SPR_ELRAIL_BASE + 45, + SPR_CURSOR_NWSE_ELRAIL = SPR_ELRAIL_BASE + 46, + SPR_CURSOR_RAIL_STATION = 1300, SPR_CURSOR_TUNNEL_RAIL = 2434, + SPR_CURSOR_TUNNEL_ELRAIL = SPR_ELRAIL_BASE + 48, SPR_CURSOR_TUNNEL_MONO = 2435, SPR_CURSOR_TUNNEL_MAGLEV = 2436, diff --git a/train_cmd.c b/train_cmd.c index e5fd26772..4e433eef0 100644 --- a/train_cmd.c +++ b/train_cmd.c @@ -72,6 +72,46 @@ static void TrainCargoChanged(Vehicle* v) } /** + * Recalculates the cached total power of a train. Should be called when the consist is changed + * @param v First vehicle of the consist. + */ +void TrainPowerChanged(Vehicle* v) +{ + const RailVehicleInfo *rvi_v = RailVehInfo(v->engine_type); + Vehicle* u; + uint32 power = 0; + + for (u = v; u != NULL; u = u->next) { + const RailVehicleInfo *rvi_u; + bool engine_has_power = true; + bool wagon_has_power = true; + + /* Power is not added for articulated parts */ + if (IsArticulatedPart(u)) continue; + + if (IsBridgeTile(u->tile) && IsBridgeMiddle(u->tile) && DiagDirToAxis(DirToDiagDir(u->direction)) == GetBridgeAxis(u->tile)) { + if (!HasPowerOnRail(u->u.rail.railtype, GetRailTypeOnBridge(u->tile))) engine_has_power = false; + if (!HasPowerOnRail(v->u.rail.railtype, GetRailTypeOnBridge(u->tile))) wagon_has_power = false; + } else { + if (!HasPowerOnRail(u->u.rail.railtype, GetRailType(u->tile))) engine_has_power = false; + if (!HasPowerOnRail(v->u.rail.railtype, GetRailType(u->tile))) wagon_has_power = false; + } + + rvi_u = RailVehInfo(u->engine_type); + + if (engine_has_power) power += rvi_u->power; + if (HASBIT(u->u.rail.flags, VRF_POWEREDWAGON) && (wagon_has_power)) { + power += rvi_v->pow_wag_power; + } + } + + if (v->u.rail.cached_power != power) { + v->u.rail.cached_power = power; + InvalidateWindow(WC_VEHICLE_DETAILS, v->index); + } +} + +/** * Recalculates the cached stuff of a train. Should be called each time a vehicle is added * to/removed from the chain, and when the game is loaded. * Note: this needs to be called too for 'wagon chains' (in the depot, without an engine) @@ -82,7 +122,6 @@ void TrainConsistChanged(Vehicle* v) const RailVehicleInfo *rvi_v; Vehicle *u; uint16 max_speed = 0xFFFF; - uint32 power = 0; EngineID first_engine; assert(v->type == VEH_Train); @@ -92,6 +131,7 @@ void TrainConsistChanged(Vehicle* v) rvi_v = RailVehInfo(v->engine_type); first_engine = IsFrontEngine(v) ? v->engine_type : INVALID_ENGINE; v->u.rail.cached_total_length = 0; + v->u.rail.compatible_railtypes = 0; for (u = v; u != NULL; u = u->next) { const RailVehicleInfo *rvi_u = RailVehInfo(u->engine_type); @@ -102,6 +142,7 @@ void TrainConsistChanged(Vehicle* v) // update the 'first engine' u->u.rail.first_engine = (v == u) ? INVALID_ENGINE : first_engine; + u->u.rail.railtype = GetEngine(u->engine_type)->railtype; if (rvi_u->visual_effect != 0) { u->u.rail.cached_vis_effect = rvi_u->visual_effect; @@ -119,9 +160,6 @@ void TrainConsistChanged(Vehicle* v) } if (!IsArticulatedPart(u)) { - // power is the sum of the powers of all engines and powered wagons in the consist - power += rvi_u->power; - // check if its a powered wagon CLRBIT(u->u.rail.flags, VRF_POWEREDWAGON); if ((rvi_v->pow_wag_power != 0) && (rvi_u->flags & RVI_WAGON) && UsesWagonOverride(u)) { @@ -135,10 +173,15 @@ void TrainConsistChanged(Vehicle* v) if (u->u.rail.cached_vis_effect < 0x40) { /* wagon is powered */ SETBIT(u->u.rail.flags, VRF_POWEREDWAGON); // cache 'powered' status - power += rvi_v->pow_wag_power; } } + /* Do not count powered wagons for the compatible railtypes, as wagons always + have railtype normal */ + if (rvi_u->power > 0) { + v->u.rail.compatible_railtypes |= GetRailTypeInfo(u->u.rail.railtype)->powered_railtypes; + } + // max speed is the minimum of the speed limits of all vehicles in the consist if (!(rvi_u->flags & RVI_WAGON) || _patches.wagon_speed_limits) if (rvi_u->max_speed != 0 && !UsesWagonOverride(u)) @@ -159,7 +202,8 @@ void TrainConsistChanged(Vehicle* v) // store consist weight/max speed in cache v->u.rail.cached_max_speed = max_speed; - v->u.rail.cached_power = power; + + TrainPowerChanged(v); // recalculate cached weights too (we do this *after* the rest, so it is known which wagons are powered and need extra weight added) TrainCargoChanged(v); @@ -333,6 +377,7 @@ static int GetTrainAcceleration(Vehicle *v, bool mode) if (speed > 0) { switch (v->u.rail.railtype) { case RAILTYPE_RAIL: + case RAILTYPE_ELECTRIC: case RAILTYPE_MONO: force = power / speed; //[N] force *= 22; @@ -1468,6 +1513,9 @@ static void ReverseTrainSwapVeh(Vehicle *v, int l, int r) VehicleEnterTile(a, a->tile, a->x_pos, a->y_pos); } + + /* Update train's power incase tiles were different rail type */ + TrainPowerChanged(v); } /* Check if the vehicle is a train and is on the tile we are testing */ @@ -1786,7 +1834,7 @@ static TrainFindDepotData FindClosestTrainDepot(Vehicle *v) Trackdir trackdir_rev = ReverseTrackdir(GetVehicleTrackdir(last)); assert (trackdir != INVALID_TRACKDIR); - ftd = NPFRouteToDepotBreadthFirstTwoWay(v->tile, trackdir, last->tile, trackdir_rev, TRANSPORT_RAIL, v->owner, v->u.rail.railtype, NPF_INFINITE_PENALTY); + ftd = NPFRouteToDepotBreadthFirstTwoWay(v->tile, trackdir, last->tile, trackdir_rev, TRANSPORT_RAIL, v->owner, v->u.rail.compatible_railtypes, NPF_INFINITE_PENALTY); if (ftd.best_bird_dist == 0) { /* Found target */ tfdd.tile = ftd.node.tile; @@ -1805,7 +1853,7 @@ static TrainFindDepotData FindClosestTrainDepot(Vehicle *v) if (!(v->direction & 1) && v->u.rail.track != _state_dir_table[i]) { i = ChangeDiagDir(i, DIAGDIRDIFF_90LEFT); } - NewTrainPathfind(tile, 0, i, (NTPEnumProc*)NtpCallbFindDepot, &tfdd); + NewTrainPathfind(tile, 0, v->u.rail.compatible_railtypes, i, (NTPEnumProc*)NtpCallbFindDepot, &tfdd); if (tfdd.best_length == (uint)-1){ tfdd.reverse = true; // search in backwards direction @@ -1813,7 +1861,7 @@ static TrainFindDepotData FindClosestTrainDepot(Vehicle *v) if (!(v->direction & 1) && v->u.rail.track != _state_dir_table[i]) { i = ChangeDiagDir(i, DIAGDIRDIFF_90LEFT); } - NewTrainPathfind(tile, 0, i, (NTPEnumProc*)NtpCallbFindDepot, &tfdd); + NewTrainPathfind(tile, 0, v->u.rail.compatible_railtypes, i, (NTPEnumProc*)NtpCallbFindDepot, &tfdd); } } @@ -1899,7 +1947,7 @@ static void HandleLocomotiveSmokeCloud(const Vehicle* v) // no smoke? if ((RailVehInfo(engtype)->flags & RVI_WAGON && effect_type == 0) || disable_effect || - GetEngine(engtype)->railtype > RAILTYPE_RAIL || + GetEngine(engtype)->railtype > RAILTYPE_ELECTRIC || v->vehstatus & VS_HIDDEN || v->u.rail.track & 0xC0) { continue; @@ -1961,6 +2009,7 @@ static void TrainPlayLeaveStationSound(const Vehicle* v) switch (GetEngine(engtype)->railtype) { case RAILTYPE_RAIL: + case RAILTYPE_ELECTRIC: SndPlayVehicleFx(sfx[RailVehInfo(engtype)->engclass], v); break; @@ -2112,7 +2161,7 @@ static byte ChooseTrainTrack(Vehicle* v, TileIndex tile, DiagDirection enterdir, trackdir = GetVehicleTrackdir(v); assert(trackdir != 0xff); - ftd = NPFRouteToStationOrTile(tile - TileOffsByDir(enterdir), trackdir, &fstd, TRANSPORT_RAIL, v->owner, v->u.rail.railtype); + ftd = NPFRouteToStationOrTile(tile - TileOffsByDir(enterdir), trackdir, &fstd, TRANSPORT_RAIL, v->owner, v->u.rail.compatible_railtypes); if (ftd.best_trackdir == 0xff) { /* We are already at our target. Just do something */ @@ -2136,7 +2185,7 @@ static byte ChooseTrainTrack(Vehicle* v, TileIndex tile, DiagDirection enterdir, fd.best_track = 0xFF; NewTrainPathfind(tile - TileOffsByDir(enterdir), v->dest_tile, - enterdir, (NTPEnumProc*)NtpCallbFindStation, &fd); + v->u.rail.compatible_railtypes, enterdir, (NTPEnumProc*)NtpCallbFindStation, &fd); if (fd.best_track == 0xff) { // blaha @@ -2190,7 +2239,7 @@ static bool CheckReverseTrain(Vehicle *v) assert(trackdir != 0xff); assert(trackdir_rev != 0xff); - ftd = NPFRouteToStationOrTileTwoWay(v->tile, trackdir, last->tile, trackdir_rev, &fstd, TRANSPORT_RAIL, v->owner, v->u.rail.railtype); + ftd = NPFRouteToStationOrTileTwoWay(v->tile, trackdir, last->tile, trackdir_rev, &fstd, TRANSPORT_RAIL, v->owner, v->u.rail.compatible_railtypes); if (ftd.best_bird_dist != 0) { /* We didn't find anything, just keep on going straight ahead */ reverse_best = false; @@ -2206,7 +2255,7 @@ static bool CheckReverseTrain(Vehicle *v) fd.best_bird_dist = (uint)-1; fd.best_track_dist = (uint)-1; - NewTrainPathfind(v->tile, v->dest_tile, reverse ^ i, (NTPEnumProc*)NtpCallbFindStation, &fd); + NewTrainPathfind(v->tile, v->dest_tile, v->u.rail.compatible_railtypes, reverse ^ i, (NTPEnumProc*)NtpCallbFindStation, &fd); if (best_track != -1) { if (best_bird_dist != 0) { @@ -2575,7 +2624,7 @@ static bool CheckCompatibleRail(const Vehicle *v, TileIndex tile) return IsTileOwner(tile, v->owner) && ( !IsFrontEngine(v) || - IsCompatibleRail(v->u.rail.railtype, GetRailType(tile)) + HASBIT(v->u.rail.compatible_railtypes, GetRailType(tile)) ); } @@ -2585,9 +2634,10 @@ typedef struct { byte z_down; // fraction to remove when moving down } RailtypeSlowdownParams; -static const RailtypeSlowdownParams _railtype_slowdown[3] = { +static const RailtypeSlowdownParams _railtype_slowdown[] = { // normal accel {256/4, 256/2, 256/4, 2}, // normal + {256/4, 256/2, 256/4, 2}, // electrified {256/4, 256/2, 256/4, 2}, // monorail {0, 256/2, 256/4, 2}, // maglev }; @@ -2873,6 +2923,11 @@ static void TrainController(Vehicle *v) if (!(r&0x4)) { v->tile = gp.new_tile; + + if (GetTileRailType(gp.new_tile, chosen_track) != GetTileRailType(gp.old_tile, v->u.rail.track)) { + TrainPowerChanged(GetFirstVehicleInChain(v)); + } + v->u.rail.track = chosen_track; assert(v->u.rail.track); } diff --git a/train_gui.c b/train_gui.c index 51565980b..160e8fe7d 100644 --- a/train_gui.c +++ b/train_gui.c @@ -177,7 +177,7 @@ static void engine_drawing_loop(int *x, int *y, int *pos, int *sel, const Engine *e = GetEngine(i); const RailVehicleInfo *rvi = RailVehInfo(i); - if (!IsCompatibleRail(e->railtype, railtype) || !(rvi->flags & RVI_WAGON) != is_engine || + if (!HasPowerOnRail(e->railtype, railtype) || !(rvi->flags & RVI_WAGON) != is_engine || !HASBIT(e->player_avail, _local_player)) continue; @@ -208,7 +208,7 @@ static void NewRailVehicleWndProc(Window *w, WindowEvent *e) for (i = 0; i < NUM_TRAIN_ENGINES; i++) { const Engine *e = GetEngine(i); - if (IsCompatibleRail(e->railtype, railtype) + if (HasPowerOnRail(e->railtype, railtype) && HASBIT(e->player_avail, _local_player)) count++; } diff --git a/tunnelbridge_cmd.c b/tunnelbridge_cmd.c index b7d8466dd..7445db167 100644 --- a/tunnelbridge_cmd.c +++ b/tunnelbridge_cmd.c @@ -799,9 +799,9 @@ int32 DoConvertTunnelBridgeRail(TileIndex tile, uint totype, bool exec) // fast routine for getting the height of a middle bridge tile. 'tile' MUST be a middle bridge tile. -static uint GetBridgeHeight(const TileInfo *ti) +uint GetBridgeHeight(TileIndex t) { - TileIndex tile = GetSouthernBridgeEnd(ti->tile); + TileIndex tile = GetSouthernBridgeEnd(t); /* Return the height there (the height of the NORTH CORNER) * If the end of the bridge is on a tileh 7 (all raised, except north corner), @@ -930,6 +930,7 @@ static void DrawTile_TunnelBridge(TileInfo *ti) image += GetTunnelDirection(ti->tile) * 2; DrawGroundSprite(image); + if (GB(_m[ti->tile].m3, 0, 3) == RAILTYPE_ELECTRIC) DrawCatenary(ti); AddSortableSpriteToDraw(image+1, ti->x + 15, ti->y + 15, 1, 1, 8, (byte)ti->z); } else if (IsBridge(ti->tile)) { // XXX is this necessary? @@ -973,6 +974,8 @@ static void DrawTile_TunnelBridge(TileInfo *ti) DrawGroundSprite(SPR_FLAT_SNOWY_TILE + _tileh_to_sprite[ti->tileh]); } + if (GB(_m[ti->tile].m3, 0, 3) == RAILTYPE_ELECTRIC) DrawCatenary(ti); + // draw ramp if (_display_opt & DO_TRANS_BUILDINGS) MAKE_TRANSPARENT(image); AddSortableSpriteToDraw(image, ti->x, ti->y, 16, 16, 7, ti->z); @@ -1029,7 +1032,7 @@ static void DrawTile_TunnelBridge(TileInfo *ti) // get bridge sprites b = GetBridgeSpriteTable(GetBridgeType(ti->tile), GetBridgePiece(ti->tile)) + base_offset; - z = GetBridgeHeight(ti) + 5; + z = GetBridgeHeight(ti->tile) + 5; // draw rail or road component image = b[0]; @@ -1054,6 +1057,8 @@ static void DrawTile_TunnelBridge(TileInfo *ti) if (image & SPRITE_MASK) AddSortableSpriteToDraw(image, x, y, 1, 16, 0x28, z); } + if (GetRailType(ti->tile) == RAILTYPE_ELECTRIC || GetRailTypeOnBridge(ti->tile) == RAILTYPE_ELECTRIC) DrawCatenary(ti); + if (ti->z + 5 == z) { // draw poles below for small bridges image = b[2]; @@ -1107,7 +1112,7 @@ static uint GetSlopeZ_TunnelBridge(const TileInfo* ti) if (_get_z_hint >= z + 8) return _get_z_hint; // actually on the bridge, but not yet in the shared area. - if (!IS_INT_INSIDE(x, 5, 10 + 1)) return GetBridgeHeight(ti) + 8; + if (!IS_INT_INSIDE(x, 5, 10 + 1)) return GetBridgeHeight(ti->tile) + 8; // in the shared area, assume that we're below the bridge, cause otherwise the hint would've caught it. // if rail or road below then it means it's possibly build on slope below the bridge. diff --git a/variables.h b/variables.h index c7c051ad3..b63a0cee7 100644 --- a/variables.h +++ b/variables.h @@ -345,7 +345,6 @@ typedef struct { SpriteID sprites[NUM_CARGO]; byte transit_days_1[NUM_CARGO]; byte transit_days_2[NUM_CARGO]; - byte ai_railwagon[3][NUM_CARGO]; byte ai_roadveh_start[NUM_CARGO]; byte ai_roadveh_count[NUM_CARGO]; } CargoConst; @@ -218,6 +218,10 @@ void AfterLoadVehicles(void) FOR_ALL_VEHICLES(v) { v->first = NULL; + if (v->type == VEH_Train) v->u.rail.first_engine = INVALID_ENGINE; + } + + FOR_ALL_VEHICLES(v) { if (v->type == VEH_Train && (IsFrontEngine(v) || IsFreeWagon(v))) TrainConsistChanged(v); } @@ -72,6 +72,7 @@ typedef struct VehicleRail { byte track; byte force_proceed; byte railtype; + RailTypeMask compatible_railtypes; byte flags; @@ -307,6 +308,7 @@ UnitID GetFreeUnitNumber(byte type); int LoadUnloadVehicle(Vehicle *v); void TrainConsistChanged(Vehicle *v); +void TrainPowerChanged(Vehicle *v); int32 GetTrainRunningCost(const Vehicle *v); int CheckTrainStoppedInDepot(const Vehicle *v); diff --git a/vehicle_gui.c b/vehicle_gui.c index 25b0f6577..31b8e9bf0 100644 --- a/vehicle_gui.c +++ b/vehicle_gui.c @@ -76,6 +76,7 @@ const StringID _vehicle_sort_listing[] = { static const StringID _rail_types_list[] = { STR_RAIL_VEHICLES, + STR_ELRAIL_VEHICLES, STR_MONORAIL_VEHICLES, STR_MAGLEV_VEHICLES, INVALID_STRING_ID @@ -450,7 +451,7 @@ static int CDECL VehicleValueSorter(const void *a, const void *b) * if used compined with show_cars set to false, it will work as intended. Replace window do it like that * this was a big hack even before show_outdated was added. Stupid newgrf :p */ static void train_engine_drawing_loop(int *x, int *y, int *pos, int *sel, EngineID *selected_id, RailType railtype, - uint8 lines_drawn, bool is_engine, bool show_cars, bool show_outdated) + uint8 lines_drawn, bool is_engine, bool show_cars, bool show_outdated, bool show_compatible) { EngineID j; byte colour; @@ -472,7 +473,9 @@ static void train_engine_drawing_loop(int *x, int *y, int *pos, int *sel, Engine colour = *sel == 0 ? 0xC : 0x10; if (!(ENGINE_AVAILABLE && show_outdated && RailVehInfo(i)->power && e->railtype == railtype)) { - if (e->railtype != railtype || !(rvi->flags & RVI_WAGON) != is_engine || + if ((!HasPowerOnRail(e->railtype, railtype) && show_compatible) + || (e->railtype != railtype && !show_compatible) + || !(rvi->flags & RVI_WAGON) != is_engine || !HASBIT(e->player_avail, _local_player)) continue; } /*else { @@ -522,16 +525,15 @@ static void SetupScrollStuffForReplaceWindow(Window *w) const Engine* e = GetEngine(eid); const EngineInfo* info = &_engine_info[eid]; + // left window contains compatible engines while right window only contains engines of the selected type if (ENGINE_AVAILABLE && ( (RailVehInfo(eid)->power != 0 && WP(w, replaceveh_d).wagon_btnstate) || - (RailVehInfo(eid)->power == 0 && !WP(w, replaceveh_d).wagon_btnstate) - ) && - e->railtype == railtype) { - if (_player_num_engines[eid] > 0 || EngineHasReplacementForPlayer(p, eid)) { + (RailVehInfo(eid)->power == 0 && !WP(w, replaceveh_d).wagon_btnstate))) { + if (HasPowerOnRail(e->railtype, railtype) && (_player_num_engines[eid] > 0 || EngineHasReplacementForPlayer(p, eid))) { if (sel[0] == count) selected_id[0] = eid; count++; } - if (HASBIT(e->player_avail, _local_player)) { + if (e->railtype == railtype && HASBIT(e->player_avail, _local_player)) { if (sel[1] == count2) selected_id[1] = eid; count2++; } @@ -647,12 +649,12 @@ static void DrawEngineArrayInReplaceWindow(Window *w, int x, int y, int x2, int * engines to get more types.. Stays here until we have our own format * then it is exit!!! */ if (WP(w,replaceveh_d).wagon_btnstate) { - train_engine_drawing_loop(&x, &y, &pos, &sel[0], &selected_id[0], railtype, w->vscroll.cap, true, false, true); // True engines - train_engine_drawing_loop(&x2, &y2, &pos2, &sel[1], &selected_id[1], railtype, w->vscroll.cap, true, false, false); // True engines - train_engine_drawing_loop(&x2, &y2, &pos2, &sel[1], &selected_id[1], railtype, w->vscroll.cap, false, false, false); // Feeble wagons + train_engine_drawing_loop(&x, &y, &pos, &sel[0], &selected_id[0], railtype, w->vscroll.cap, true, false, true, true); // True engines + train_engine_drawing_loop(&x2, &y2, &pos2, &sel[1], &selected_id[1], railtype, w->vscroll.cap, true, false, false, false); // True engines + train_engine_drawing_loop(&x2, &y2, &pos2, &sel[1], &selected_id[1], railtype, w->vscroll.cap, false, false, false, false); // Feeble wagons } else { - train_engine_drawing_loop(&x, &y, &pos, &sel[0], &selected_id[0], railtype, w->vscroll.cap, false, true, true); - train_engine_drawing_loop(&x2, &y2, &pos2, &sel[1], &selected_id[1], railtype, w->vscroll.cap, false, true, false); + train_engine_drawing_loop(&x, &y, &pos, &sel[0], &selected_id[0], railtype, w->vscroll.cap, false, true, true, true); + train_engine_drawing_loop(&x2, &y2, &pos2, &sel[1], &selected_id[1], railtype, w->vscroll.cap, false, true, false, true); } break; } |