elrail.c
changeset 3355 e414a0b104a6
child 3367 a995838e8d85
equal deleted inserted replaced
3354:245157a5eafe 3355:e414a0b104a6
       
     1 /* $Id$ */
       
     2 /** @file elrail.c
       
     3   * This file deals with displaying wires and pylons for electric railway systems.
       
     4 <h2>Basics</h2>
       
     5 
       
     6 <h3>Tile Types</h3>
       
     7 
       
     8 We have two different types of tiles in the drawing code:
       
     9 Normal Railway Tiles (NRTs) which can have more than one track on it, and
       
    10 Special Railways tiles (SRTs) which have only one track (like crossings, depots
       
    11 stations, etc).
       
    12 
       
    13 <h3>Location Categories</h3>
       
    14 
       
    15 All tiles are categorized into three location groups (TLG):
       
    16 Group 0: Tiles with both an even X coordinate and an even Y coordinate
       
    17 Group 1: Tiles with an even X and an odd Y coordinate
       
    18 Group 2: Tiles with an odd X and an even Y coordinate
       
    19 Group 3: Tiles with both an odd X and Y coordnate.
       
    20 
       
    21 <h3>Pylon Points</h3>
       
    22 <h4>Control Points</h4>
       
    23 A Pylon Control Point (PCP) is a position where a wire (or rather two)
       
    24 is mounted onto a pylon.
       
    25 Each NRT does contain 4 PCPs which are mapped to a byte
       
    26 variable and are represented by the DiagDirection enum:
       
    27 
       
    28 A wire that ends on the PCP has a dark ending, otherwise the end is bright.<p>
       
    29 
       
    30 Now on each edge there are two PCPs: One from each adjacent tile. Both PCPs are merged
       
    31 using an OR matrix (i. e. if one tile needs a PCP at the postion in question, both
       
    32 tiles get it).
       
    33 
       
    34 <h4>Position Points</h4>
       
    35 A Pylon Position Point (PPP) is a position where a pylon is located on the ground.
       
    36 Each PCP owns 8 in (45 degree steps) PPPs that are located around it. PPPs are numbered
       
    37 0 to 7 with 0 starting north and numbering in clockwise direction. Each track bit has PPPs
       
    38 that are impossible (because the pylon would be situated on the track), preferred (because
       
    39 the pylon would be rectangular to the track). PPPs are represented by the Direction enum.
       
    40 
       
    41 <img src="../../elrail_tile.png">
       
    42 <img src="../../elrail_track.png">
       
    43 
       
    44   */
       
    45 
       
    46 #include "stdafx.h"
       
    47 #include "openttd.h"
       
    48 #include "tile.h"
       
    49 #include "viewport.h"
       
    50 #include "functions.h" /* We should REALLY get rid of this goddamn file, as it is butt-ugly */
       
    51 #include "variables.h" /* ... same here */
       
    52 #include "rail.h"
       
    53 #include "debug.h"
       
    54 #include "tunnel_map.h"
       
    55 #include "road_map.h"
       
    56 #include "bridge_map.h"
       
    57 #include "bridge.h"
       
    58 #include "rail_map.h"
       
    59 #include "table/sprites.h"
       
    60 #include "table/elrail_data.h"
       
    61 
       
    62 static inline TLG GetTLG(TileIndex t)
       
    63 {
       
    64 	return (HASBIT(TileX(t), 0) << 1) + HASBIT(TileY(t), 0);
       
    65 }
       
    66 
       
    67 /** Finds which Rail Bits are present on a given tile. For bridge tiles,
       
    68   * returns track bits under the bridge
       
    69   */
       
    70 static TrackBits GetRailTrackBitsUniversal(TileIndex t, byte *override)
       
    71 {
       
    72 	switch (GetTileType(t)) {
       
    73 		case MP_RAILWAY:
       
    74 			if (GetRailType(t) != RAILTYPE_ELECTRIC) return 0;
       
    75 			switch (GetRailTileType(t)) {
       
    76 				case RAIL_TYPE_NORMAL: case RAIL_TYPE_SIGNALS:
       
    77 					return GetTrackBits(t);
       
    78 				default:
       
    79 					return 0;
       
    80 			}
       
    81 			break;
       
    82 		case MP_TUNNELBRIDGE:
       
    83 			if (IsTunnel(t)) {
       
    84 				if (GetRailType(t) != RAILTYPE_ELECTRIC) return 0;
       
    85 				if (override != NULL) *override = 1 << GetTunnelDirection(t);
       
    86 				return (_m[t].m5 & 1) ? TRACK_BIT_Y : TRACK_BIT_X;
       
    87 			} else {
       
    88 				if (GetRailType(t) != RAILTYPE_ELECTRIC) return 0;
       
    89 				if (
       
    90 					IsBridgeMiddle(t) &&
       
    91 					IsTransportUnderBridge(t) &&
       
    92 					GetTransportTypeUnderBridge(t) == TRANSPORT_RAIL) {
       
    93 					return GetRailBitsUnderBridge(t);
       
    94 				} else {
       
    95 					if (override != NULL && DistanceMax(t, GetOtherBridgeEnd(t)) > 1) *override = 1 << GetBridgeRampDirection(t);
       
    96 
       
    97 					return GetBridgeAxis(t) == AXIS_X ? TRACK_BIT_X : TRACK_BIT_Y;
       
    98 				}
       
    99 			}
       
   100 		case MP_STREET:
       
   101 			if ((_m[t].m4 & 0xF) != RAILTYPE_ELECTRIC) return 0;
       
   102 			return GetCrossingRailBits(t);
       
   103 		case MP_STATION:
       
   104 			if (GetRailType(t) != RAILTYPE_ELECTRIC) return 0;
       
   105 			return _m[t].m5 & 1 ? TRACK_BIT_Y : TRACK_BIT_X;
       
   106 		default:
       
   107 			return 0;
       
   108 	}
       
   109 }
       
   110 
       
   111 /** Draws wires and, if required, pylons on a given tile
       
   112   * @param ti The Tileinfo to draw the tile for
       
   113   * @todo Currently, each pylon is drawn twice (once for each neighbouring tiles use OwnedPPPonPCP for this)
       
   114   */
       
   115 static void DrawCatenaryRailway(const TileInfo *ti)
       
   116 {
       
   117 	/* Pylons are placed on a tile edge, so we need to take into account
       
   118 	   the track configuration of 2 adjacent tiles. trackconfig[0] stores the
       
   119 	   current tile (home tile) while [1] holds the neighbour */
       
   120 	TrackBits trackconfig[TS_END];
       
   121 	bool isflat[TS_END];
       
   122 	/* Note that ti->tileh has already been adjusted for Foundations */
       
   123 	uint tileh[TS_END] = {ti->tileh, 0};
       
   124 
       
   125 	TLG tlg = GetTLG(ti->tile);
       
   126 	byte PCPstatus = 0;
       
   127 	byte OverridePCP = 0;
       
   128 	byte PPPpreferred[DIAGDIR_END] = {0xFF, 0xFF, 0xFF, 0xFF};
       
   129 	byte PPPallowed[DIAGDIR_END] = {AllowedPPPonPCP[0], AllowedPPPonPCP[1], AllowedPPPonPCP[2], AllowedPPPonPCP[3]};
       
   130 	byte PPPbuffer[DIAGDIR_END];
       
   131 	DiagDirection i;
       
   132 	Track t;
       
   133 
       
   134 	/* Find which rail bits are present, and select the override points.
       
   135 	   We don't draw a pylon:
       
   136 	   1) INSIDE a tunnel (we wouldn't see it anyway)
       
   137 	   2) on the "far" end of a bridge head (the one that connects to bridge middle),
       
   138 	      because that one is drawn on the bridge. Exception is for length 0 bridges
       
   139 	      which have no middle tiles */
       
   140 	trackconfig[TS_HOME] = GetRailTrackBitsUniversal(ti->tile, &OverridePCP);
       
   141 	/* If a track bit is present that is not in the main direction, the track is level */
       
   142 	isflat[TS_HOME] = trackconfig[TS_HOME] & (TRACK_BIT_UPPER | TRACK_BIT_LOWER | TRACK_BIT_LEFT | TRACK_BIT_RIGHT);
       
   143 
       
   144 	if (IsTunnelTile(ti->tile)) tileh[TS_HOME] = 0;
       
   145 	if (IsBridgeTile(ti->tile) && IsBridgeRamp(ti->tile)) {
       
   146 		if (tileh[TS_HOME] != 0) {
       
   147 			tileh[TS_HOME] = 0;
       
   148 		} else {
       
   149 			switch (GetBridgeRampDirection(ti->tile)) {
       
   150 				case DIAGDIR_NE: tileh[TS_HOME] = 12; break;
       
   151 				case DIAGDIR_SE: tileh[TS_HOME] =  6; break;
       
   152 				case DIAGDIR_SW: tileh[TS_HOME] =  3; break;
       
   153 				case DIAGDIR_NW: tileh[TS_HOME] =  9; break;
       
   154 				default: break;
       
   155 			}
       
   156 		}
       
   157 	}
       
   158 
       
   159 	for (i = DIAGDIR_NE; i < DIAGDIR_END; i++) {
       
   160 		extern const TileIndexDiffC _tileoffs_by_dir[];
       
   161 		TileIndex neighbour = ti->tile + TileOffsByDir(i);
       
   162 		uint foundation = 0;
       
   163 		int k;
       
   164 
       
   165 		/* Here's one of the main headaches. GetTileSlope does not correct for possibly
       
   166 		   existing foundataions, so we do have to do that manually later on.*/
       
   167 		tileh[TS_NEIGHBOUR] = GetTileSlope(neighbour, NULL);
       
   168 		trackconfig[TS_NEIGHBOUR] = GetRailTrackBitsUniversal(neighbour, NULL);
       
   169 		isflat[TS_NEIGHBOUR] = trackconfig[TS_NEIGHBOUR] & (TRACK_BIT_UPPER | TRACK_BIT_LOWER | TRACK_BIT_LEFT | TRACK_BIT_RIGHT);
       
   170 
       
   171 		/* We cycle through all the existing tracks at a PCP and see what
       
   172 		   PPPs we want to have, or may not have at all */
       
   173 		for (k = 0; k < TRACKS_AT_PCP; k++) {
       
   174 			/* Next to us, we have a bridge head, don't worry about that one, if it shows away from us */
       
   175 			if (
       
   176 					trackorigin[i][k] == TS_NEIGHBOUR &&
       
   177 					IsBridgeTile(neighbour) && IsBridgeRamp(neighbour) &&
       
   178 					GetBridgeRampDirection(neighbour) == ReverseDiagDir(i)
       
   179 			   ) continue;
       
   180 
       
   181 			if (HASBIT(trackconfig[trackorigin[i][k]], PPPtracks[i][k])) {
       
   182 				DiagDirection PCPpos = (trackorigin[i][k] == 0) ? i : ReverseDiagDir(i);
       
   183 				PCPstatus |= 1 << i; /* This PCP is in use */
       
   184 				PPPpreferred[i] &= PreferredPPPofTrackBitAtPCP[PPPtracks[i][k]][PCPpos];
       
   185 				PPPallowed[i] &= ~DisallowedPPPofTrackBitAtPCP[PPPtracks[i][k]][PCPpos];
       
   186 			}
       
   187 		}
       
   188 
       
   189 		/* Deactivate all PPPs if PCP is not used */
       
   190 		PPPpreferred[i] *= HASBIT(PCPstatus, i);
       
   191 		PPPallowed[i] *= HASBIT(PCPstatus, i);
       
   192 
       
   193 		/* Station on a non-flat tile means foundation. add one height level and adjust tileh */
       
   194 		if (IsTileType(neighbour, MP_STATION) && tileh[TS_NEIGHBOUR] != 0) tileh[TS_NEIGHBOUR] = 0;
       
   195 
       
   196 		/* Read the foundataions if they are present, and adjust the tileh */
       
   197 		if (IsTileType(neighbour, MP_RAILWAY)) foundation = GetRailFoundation(tileh[TS_NEIGHBOUR], trackconfig[TS_NEIGHBOUR]);
       
   198 		if (IsBridgeTile(neighbour) && IsBridgeRamp(neighbour)) foundation = GetBridgeFoundation(tileh[TS_NEIGHBOUR], GetBridgeAxis(neighbour));
       
   199 		if (foundation != 0) {
       
   200 			if (foundation < 15) {
       
   201 				tileh[TS_NEIGHBOUR] = 0;
       
   202 			} else {
       
   203 				tileh[TS_NEIGHBOUR] = _inclined_tileh[foundation - 15];
       
   204 			}
       
   205 		}
       
   206 
       
   207 		/* Convert the real tileh into a pseudo-tileh for the track */
       
   208 		if (IsTunnelTile(neighbour)) tileh[TS_NEIGHBOUR] = 0;
       
   209 		if (IsBridgeTile(neighbour) && IsBridgeRamp(neighbour)) {
       
   210 			if (tileh[TS_NEIGHBOUR] != 0) {
       
   211 				tileh[TS_NEIGHBOUR] = 0;
       
   212 			} else {
       
   213 				switch (GetBridgeRampDirection(neighbour)) {
       
   214 					case DIAGDIR_NE: tileh[TS_NEIGHBOUR] = 12; break;
       
   215 					case DIAGDIR_SE: tileh[TS_NEIGHBOUR] =  6; break;
       
   216 					case DIAGDIR_SW: tileh[TS_NEIGHBOUR] =  3; break;
       
   217 					case DIAGDIR_NW: tileh[TS_NEIGHBOUR] =  9; break;
       
   218 					default: break;
       
   219 				}
       
   220 			}
       
   221 		}
       
   222 
       
   223 		/* If we have a straight (and level) track, we want a pylon only every 2 tiles
       
   224 		   Delete the PCP if this is the case. */
       
   225 		/* Level means that the slope is the same, or the track is flat */
       
   226 		if (tileh[TS_HOME] == tileh[TS_NEIGHBOUR] || (isflat[TS_HOME] && isflat[TS_NEIGHBOUR])) {
       
   227 			for (k = 0; k < NUM_IGNORE_GROUPS; k++)
       
   228 				if (PPPpreferred[i] == IgnoredPCP[k][tlg][i]) PCPstatus &= ~(1 << i);
       
   229 		}
       
   230 
       
   231 		/* Now decide where we draw our tiles. First try the preferred PPPs, but they may not exist.
       
   232 		   In that case, we try the any of the allowed ones. if they don't exist either, don't draw
       
   233 		   anything */
       
   234 		if (PPPpreferred[i] != 0) {
       
   235 			/* Some of the preferred PPPs (the ones in direct extension of the track bit)
       
   236 			   have been used as an "end of line" marker. As these are not ALLOWED, this operation
       
   237 			   cancles them out */
       
   238 			PPPbuffer[i] = PPPpreferred[i] & PPPallowed[i];
       
   239 			/* We haven't any buffer yet, so try something else. Fixes 90° curves */
       
   240 			if (PPPbuffer[i] == 0) PPPbuffer[i] = PPPallowed[i];
       
   241 		} else {
       
   242 			PPPbuffer[i] = PPPallowed[i];
       
   243 		}
       
   244 
       
   245 		if (PPPbuffer[i] != 0 && HASBIT(PCPstatus, i) && !HASBIT(OverridePCP, i)) {
       
   246 			for (k = 0; k < DIR_END; k++) {
       
   247 				byte temp = PPPorder[i][GetTLG(ti->tile)][k];
       
   248 				if (HASBIT(PPPbuffer[i], temp)) {
       
   249 					uint x  = ti->x + x_pcp_offsets[i] + x_ppp_offsets[temp];
       
   250 					uint y  = ti->y + y_pcp_offsets[i] + y_ppp_offsets[temp];
       
   251 
       
   252 					/* Don't build the pylon if it would be outside the tile */
       
   253 					if (!HASBIT(OwnedPPPonPCP[i], temp)) {
       
   254 						/* We have a neighour that will draw it, bail out */
       
   255 						if (trackconfig[TS_NEIGHBOUR] != 0) break;
       
   256 						continue; /* No neighbour, go looking for a better position */
       
   257 					}
       
   258 
       
   259 					AddSortableSpriteToDraw(pylons_normal[temp], x, y, 1, 1, 10,
       
   260 							GetSlopeZ(ti->x + x_pcp_offsets[i], ti->y + y_pcp_offsets[i]));
       
   261 					break; /* We already have drawn a pylon, bail out */
       
   262 				}
       
   263 			}
       
   264 		}
       
   265 	}
       
   266 
       
   267 	/* Drawing of pylons is finished, now draw the wires */
       
   268 	for (t = 0; t < TRACK_END; t++) {
       
   269 		if (HASBIT(trackconfig[TS_HOME], t)) {
       
   270 
       
   271 			byte PCPconfig = HASBIT(PCPstatus, PCPpositions[t][0]) +
       
   272 				(HASBIT(PCPstatus, PCPpositions[t][1]) << 1);
       
   273 
       
   274 			const SortableSpriteStruct *sss;
       
   275 			int tileh_selector = !(tileh[TS_HOME] % 3) * tileh[TS_HOME] / 3; /* tileh for the slopes, 0 otherwise */
       
   276 
       
   277 			if ( /* We are not drawing a wire under a low bridge */
       
   278 					IsBridgeTile(ti->tile) &&
       
   279 					IsBridgeMiddle(ti->tile) &&
       
   280 					!(_display_opt & DO_TRANS_BUILDINGS) &&
       
   281 					GetBridgeHeight(t) <= TilePixelHeight(t)
       
   282 			   ) return;
       
   283 
       
   284 			assert(PCPconfig != 0); /* We have a pylon on neither end of the wire, that doesn't work (since we have no sprites for that) */
       
   285 			assert(!IsSteepTileh(tileh[TS_HOME]));
       
   286 			sss = &CatenarySpriteData[Wires[tileh_selector][t][PCPconfig]];
       
   287 
       
   288 			AddSortableSpriteToDraw( sss->image, ti->x + sss->x_offset, ti->y + sss->y_offset,
       
   289 				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);
       
   290 		}
       
   291 	}
       
   292 }
       
   293 
       
   294 static void DrawCatenaryOnBridge(const TileInfo *ti)
       
   295 {
       
   296 	TileIndex start = GetOtherBridgeEnd(GetSouthernBridgeEnd(ti->tile));
       
   297 	uint length = GetBridgeLength(GetSouthernBridgeEnd(ti->tile), GetOtherBridgeEnd(GetSouthernBridgeEnd(ti->tile)));
       
   298 	uint num = DistanceMax(ti->tile, start);
       
   299 	const SortableSpriteStruct *sss;
       
   300 	Axis axis = GetBridgeAxis(ti->tile);
       
   301 	TLG tlg = GetTLG(ti->tile);
       
   302 
       
   303 	CatenarySprite offset = axis == AXIS_X ? 0 : WIRE_Y_FLAT_BOTH - WIRE_X_FLAT_BOTH;
       
   304 
       
   305 	if ((length % 2) && num == length) {
       
   306 		sss = &CatenarySpriteData[WIRE_X_FLAT_BOTH + offset];
       
   307 	} else {
       
   308 		sss = &CatenarySpriteData[WIRE_X_FLAT_SW + (num % 2) + offset];
       
   309 	}
       
   310 
       
   311 	if (num % 2) {
       
   312 		if (axis == AXIS_X) {
       
   313 			AddSortableSpriteToDraw( pylons_bridge[0 + HASBIT(tlg, 0)], ti->x, ti->y + 4 + 8 * HASBIT(tlg, 0), 1, 1, 10, GetBridgeHeight(ti->tile) + 8);
       
   314 		} else {
       
   315 			AddSortableSpriteToDraw( pylons_bridge[2 + HASBIT(tlg, 1)], ti->x + 4 + 8 * HASBIT(tlg, 1), ti->y, 1, 1, 10, GetBridgeHeight(ti->tile) + 8);
       
   316 		}
       
   317 	}
       
   318 
       
   319 	if (DistanceMax(ti->tile, start) == length) { /* need a pylon here (the southern end) */
       
   320 		if (axis == AXIS_X) {
       
   321 			AddSortableSpriteToDraw( pylons_bridge[0 + HASBIT(tlg, 0)], ti->x + 16, ti->y + 4 + 8 * HASBIT(tlg, 0), 1, 1, 10, GetBridgeHeight(ti->tile) + 8);
       
   322 		} else {
       
   323 			AddSortableSpriteToDraw( pylons_bridge[2 + HASBIT(tlg, 1)], ti->x + 4 + 8 * HASBIT(tlg, 1), ti->y + 16, 1, 1, 10, GetBridgeHeight(ti->tile) + 8);
       
   324 		}
       
   325 	}
       
   326 
       
   327 	AddSortableSpriteToDraw( sss->image, ti->x + sss->x_offset, ti->y + sss->y_offset,
       
   328 			sss->x_size, sss->y_size, sss->z_size, GetBridgeHeight(ti->tile) + sss->z_offset + 8);
       
   329 }
       
   330 
       
   331 void DrawCatenary(const TileInfo *ti)
       
   332 {
       
   333 	switch (GetTileType(ti->tile)) {
       
   334 		case MP_RAILWAY:
       
   335 			if (GetRailTileType(ti->tile) == RAIL_TYPE_DEPOT_WAYPOINT && GetRailTileSubtype(ti->tile) == RAIL_SUBTYPE_DEPOT) {
       
   336 				const SortableSpriteStruct *sss = &CatenarySpriteData[WIRE_DEPOT_SW + ReverseDiagDir(GetRailDepotDirection(ti->tile))];
       
   337 				AddSortableSpriteToDraw( sss->image, ti->x + sss->x_offset, ti->y + sss->y_offset,
       
   338 					sss->x_size, sss->y_size, sss->z_size, GetSlopeZ(ti->x, ti->y) + sss->z_offset);
       
   339 				return;
       
   340 			}
       
   341 			/* Fall through */
       
   342 		case MP_TUNNELBRIDGE:
       
   343 			if (IsBridgeTile(ti->tile) && IsBridgeMiddle(ti->tile) && GetRailTypeOnBridge(ti->tile) == RAILTYPE_ELECTRIC) DrawCatenaryOnBridge(ti);
       
   344 			/* Fall further */
       
   345 		case MP_STREET: case MP_STATION:
       
   346 			DrawCatenaryRailway(ti);
       
   347 			break;
       
   348 		default:
       
   349 			break;
       
   350 	}
       
   351 }
       
   352