|
1 /* $Id$ */ |
|
2 |
|
3 /** @file tunnelbridge_cmd.c |
|
4 * This file deals with tunnels and bridges (non-gui stuff) |
|
5 * @todo seperate this file into two |
|
6 */ |
|
7 |
|
8 #include "stdafx.h" |
|
9 #include "openttd.h" |
|
10 #include "bridge_map.h" |
|
11 #include "rail_map.h" |
|
12 #include "road_map.h" |
|
13 #include "table/sprites.h" |
|
14 #include "table/strings.h" |
|
15 #include "functions.h" |
|
16 #include "map.h" |
|
17 #include "tile.h" |
|
18 #include "tunnel_map.h" |
|
19 #include "unmovable_map.h" |
|
20 #include "vehicle.h" |
|
21 #include "viewport.h" |
|
22 #include "command.h" |
|
23 #include "player.h" |
|
24 #include "town.h" |
|
25 #include "sound.h" |
|
26 #include "variables.h" |
|
27 #include "bridge.h" |
|
28 #include "train.h" |
|
29 #include "water_map.h" |
|
30 #include "yapf/yapf.h" |
|
31 #include "date.h" |
|
32 #include "newgrf_sound.h" |
|
33 |
|
34 #include "table/bridge_land.h" |
|
35 |
|
36 const Bridge orig_bridge[] = { |
|
37 /* |
|
38 year of availablity |
|
39 | minimum length |
|
40 | | maximum length |
|
41 | | | price |
|
42 | | | | maximum speed |
|
43 | | | | | sprite to use in GUI string with description |
|
44 | | | | | | | */ |
|
45 { 0, 0, 16, 80, 32, 0xA24 , STR_5012_WOODEN , NULL, 0 }, |
|
46 { 0, 0, 2, 112, 48, 0xA26 | PALETTE_TO_STRUCT_RED , STR_5013_CONCRETE , NULL, 0 }, |
|
47 { 1930, 0, 5, 144, 64, 0xA25 , STR_500F_GIRDER_STEEL , NULL, 0 }, |
|
48 { 0, 2, 10, 168, 80, 0xA22 | PALETTE_TO_STRUCT_CONCRETE, STR_5011_SUSPENSION_CONCRETE, NULL, 0 }, |
|
49 { 1930, 3, 16, 185, 96, 0xA22 , STR_500E_SUSPENSION_STEEL , NULL, 0 }, |
|
50 { 1930, 3, 16, 192, 112, 0xA22 | PALETTE_TO_STRUCT_YELLOW , STR_500E_SUSPENSION_STEEL , NULL, 0 }, |
|
51 { 1930, 3, 7, 224, 160, 0xA23 , STR_5010_CANTILEVER_STEEL , NULL, 0 }, |
|
52 { 1930, 3, 8, 232, 208, 0xA23 | PALETTE_TO_STRUCT_BROWN , STR_5010_CANTILEVER_STEEL , NULL, 0 }, |
|
53 { 1930, 3, 9, 248, 240, 0xA23 | PALETTE_TO_STRUCT_RED , STR_5010_CANTILEVER_STEEL , NULL, 0 }, |
|
54 { 1930, 0, 2, 240, 256, 0xA27 , STR_500F_GIRDER_STEEL , NULL, 0 }, |
|
55 { 1995, 2, 16, 255, 320, 0xA28 , STR_5014_TUBULAR_STEEL , NULL, 0 }, |
|
56 { 2005, 2, 32, 380, 512, 0xA28 | PALETTE_TO_STRUCT_YELLOW , STR_5014_TUBULAR_STEEL , NULL, 0 }, |
|
57 { 2010, 2, 32, 510, 608, 0xA28 | PALETTE_TO_STRUCT_GREY , STR_BRIDGE_TUBULAR_SILICON , NULL, 0 } |
|
58 }; |
|
59 |
|
60 Bridge _bridge[MAX_BRIDGES]; |
|
61 |
|
62 |
|
63 // calculate the price factor for building a long bridge. |
|
64 // basically the cost delta is 1,1, 1, 2,2, 3,3,3, 4,4,4,4, 5,5,5,5,5, 6,6,6,6,6,6, 7,7,7,7,7,7,7, 8,8,8,8,8,8,8,8, |
|
65 int CalcBridgeLenCostFactor(int x) |
|
66 { |
|
67 int n; |
|
68 int r; |
|
69 |
|
70 if (x < 2) return x; |
|
71 x -= 2; |
|
72 for (n = 0, r = 2;; n++) { |
|
73 if (x <= n) return r + x * n; |
|
74 r += n * n; |
|
75 x -= n; |
|
76 } |
|
77 } |
|
78 |
|
79 #define M(x) (1 << (x)) |
|
80 enum { |
|
81 // foundation, whole tile is leveled up --> 3 corners raised |
|
82 BRIDGE_FULL_LEVELED_FOUNDATION = M(SLOPE_WSE) | M(SLOPE_NWS) | M(SLOPE_ENW) | M(SLOPE_SEN), |
|
83 // foundation, tile is partly leveled up --> 1 corner raised |
|
84 BRIDGE_PARTLY_LEVELED_FOUNDATION = M(SLOPE_W) | M(SLOPE_S) | M(SLOPE_E) | M(SLOPE_N), |
|
85 // no foundations (X,Y direction) |
|
86 BRIDGE_NO_FOUNDATION = M(SLOPE_FLAT) | M(SLOPE_SW) | M(SLOPE_SE) | M(SLOPE_NW) | M(SLOPE_NE), |
|
87 BRIDGE_HORZ_RAMP = (BRIDGE_PARTLY_LEVELED_FOUNDATION | BRIDGE_NO_FOUNDATION) & ~M(SLOPE_FLAT) |
|
88 }; |
|
89 #undef M |
|
90 |
|
91 static inline const PalSpriteID *GetBridgeSpriteTable(int index, byte table) |
|
92 { |
|
93 const Bridge *bridge = &_bridge[index]; |
|
94 assert(table < 7); |
|
95 if (bridge->sprite_table == NULL || bridge->sprite_table[table] == NULL) { |
|
96 return _bridge_sprite_table[index][table]; |
|
97 } else { |
|
98 return bridge->sprite_table[table]; |
|
99 } |
|
100 } |
|
101 |
|
102 static inline byte GetBridgeFlags(int index) { return _bridge[index].flags;} |
|
103 |
|
104 |
|
105 /** Check the slope at the bridge ramps in three easy steps: |
|
106 * - valid slopes without foundation |
|
107 * - valid slopes with foundation |
|
108 * - rest is invalid |
|
109 */ |
|
110 #define M(x) (1 << (x)) |
|
111 static int32 CheckBridgeSlopeNorth(Axis axis, Slope tileh) |
|
112 { |
|
113 uint32 valid; |
|
114 |
|
115 valid = M(SLOPE_FLAT) | (axis == AXIS_X ? M(SLOPE_NE) : M(SLOPE_NW)); |
|
116 if (HASBIT(valid, tileh)) return 0; |
|
117 |
|
118 valid = |
|
119 BRIDGE_FULL_LEVELED_FOUNDATION | M(SLOPE_N) | M(SLOPE_STEEP_N) | |
|
120 (axis == AXIS_X ? M(SLOPE_E) | M(SLOPE_STEEP_E) : M(SLOPE_W) | M(SLOPE_STEEP_W)); |
|
121 if (HASBIT(valid, tileh)) return _price.terraform; |
|
122 |
|
123 return CMD_ERROR; |
|
124 } |
|
125 |
|
126 static int32 CheckBridgeSlopeSouth(Axis axis, Slope tileh) |
|
127 { |
|
128 uint32 valid; |
|
129 |
|
130 valid = M(SLOPE_FLAT) | (axis == AXIS_X ? M(SLOPE_SW) : M(SLOPE_SE)); |
|
131 if (HASBIT(valid, tileh)) return 0; |
|
132 |
|
133 valid = |
|
134 BRIDGE_FULL_LEVELED_FOUNDATION | M(SLOPE_S) | M(SLOPE_STEEP_S) | |
|
135 (axis == AXIS_X ? M(SLOPE_W) | M(SLOPE_STEEP_W) : M(SLOPE_E) | M(SLOPE_STEEP_E)); |
|
136 if (HASBIT(valid, tileh)) return _price.terraform; |
|
137 |
|
138 return CMD_ERROR; |
|
139 } |
|
140 #undef M |
|
141 |
|
142 |
|
143 uint32 GetBridgeLength(TileIndex begin, TileIndex end) |
|
144 { |
|
145 int x1 = TileX(begin); |
|
146 int y1 = TileY(begin); |
|
147 int x2 = TileX(end); |
|
148 int y2 = TileY(end); |
|
149 |
|
150 return abs(x2 + y2 - x1 - y1) - 1; |
|
151 } |
|
152 |
|
153 bool CheckBridge_Stuff(byte bridge_type, uint bridge_len) |
|
154 { |
|
155 const Bridge *b = &_bridge[bridge_type]; |
|
156 uint max; // max possible length of a bridge (with patch 100) |
|
157 |
|
158 if (bridge_type >= MAX_BRIDGES) return false; |
|
159 if (b->avail_year > _cur_year) return false; |
|
160 |
|
161 max = b->max_length; |
|
162 if (max >= 16 && _patches.longbridges) max = 100; |
|
163 |
|
164 return b->min_length <= bridge_len && bridge_len <= max; |
|
165 } |
|
166 |
|
167 /** Build a Bridge |
|
168 * @param end_tile end tile |
|
169 * @param p1 packed start tile coords (~ dx) |
|
170 * @param p2 various bitstuffed elements |
|
171 * - p2 = (bit 0- 7) - bridge type (hi bh) |
|
172 * - p2 = (bit 8-..) - rail type. bit15 ((x>>8)&0x80) means road bridge. |
|
173 */ |
|
174 int32 CmdBuildBridge(TileIndex end_tile, uint32 flags, uint32 p1, uint32 p2) |
|
175 { |
|
176 int bridge_type; |
|
177 TransportType transport; |
|
178 RailType railtype; |
|
179 uint x; |
|
180 uint y; |
|
181 uint sx; |
|
182 uint sy; |
|
183 TileIndex tile_start; |
|
184 TileIndex tile_end; |
|
185 Slope tileh_start; |
|
186 Slope tileh_end; |
|
187 uint z_start; |
|
188 uint z_end; |
|
189 TileIndex tile; |
|
190 TileIndexDiff delta; |
|
191 uint bridge_len; |
|
192 Axis direction; |
|
193 int32 cost, terraformcost, ret; |
|
194 bool allow_on_slopes; |
|
195 |
|
196 SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION); |
|
197 |
|
198 /* unpack parameters */ |
|
199 bridge_type = GB(p2, 0, 8); |
|
200 |
|
201 if (p1 >= MapSize()) return CMD_ERROR; |
|
202 |
|
203 // type of bridge |
|
204 if (HASBIT(p2, 15)) { |
|
205 railtype = 0; |
|
206 transport = TRANSPORT_ROAD; |
|
207 } else { |
|
208 if (!ValParamRailtype(GB(p2, 8, 8))) return CMD_ERROR; |
|
209 railtype = GB(p2, 8, 8); |
|
210 transport = TRANSPORT_RAIL; |
|
211 } |
|
212 |
|
213 x = TileX(end_tile); |
|
214 y = TileY(end_tile); |
|
215 sx = TileX(p1); |
|
216 sy = TileY(p1); |
|
217 |
|
218 /* check if valid, and make sure that (x,y) are smaller than (sx,sy) */ |
|
219 if (x == sx) { |
|
220 if (y == sy) return_cmd_error(STR_5008_CANNOT_START_AND_END_ON); |
|
221 direction = AXIS_Y; |
|
222 if (y > sy) uintswap(y,sy); |
|
223 } else if (y == sy) { |
|
224 direction = AXIS_X; |
|
225 if (x > sx) uintswap(x,sx); |
|
226 } else { |
|
227 return_cmd_error(STR_500A_START_AND_END_MUST_BE_IN); |
|
228 } |
|
229 |
|
230 /* set and test bridge length, availability */ |
|
231 bridge_len = sx + sy - x - y - 1; |
|
232 if (!CheckBridge_Stuff(bridge_type, bridge_len)) return_cmd_error(STR_5015_CAN_T_BUILD_BRIDGE_HERE); |
|
233 |
|
234 /* retrieve landscape height and ensure it's on land */ |
|
235 tile_start = TileXY(x, y); |
|
236 tile_end = TileXY(sx, sy); |
|
237 if (IsClearWaterTile(tile_start) || IsClearWaterTile(tile_end)) { |
|
238 return_cmd_error(STR_02A0_ENDS_OF_BRIDGE_MUST_BOTH); |
|
239 } |
|
240 |
|
241 tileh_start = GetTileSlope(tile_start, &z_start); |
|
242 tileh_end = GetTileSlope(tile_end, &z_end); |
|
243 |
|
244 if (IsSteepSlope(tileh_start)) z_start += TILE_HEIGHT; |
|
245 if (HASBIT(BRIDGE_FULL_LEVELED_FOUNDATION, tileh_start)) { |
|
246 z_start += TILE_HEIGHT; |
|
247 tileh_start = SLOPE_FLAT; |
|
248 } |
|
249 |
|
250 if (IsSteepSlope(tileh_end)) z_end += TILE_HEIGHT; |
|
251 if (HASBIT(BRIDGE_FULL_LEVELED_FOUNDATION, tileh_end)) { |
|
252 z_end += TILE_HEIGHT; |
|
253 tileh_end = SLOPE_FLAT; |
|
254 } |
|
255 |
|
256 if (z_start != z_end) return_cmd_error(STR_5009_LEVEL_LAND_OR_WATER_REQUIRED); |
|
257 |
|
258 // Towns are not allowed to use bridges on slopes. |
|
259 allow_on_slopes = (!_is_old_ai_player |
|
260 && _current_player != OWNER_TOWN && _patches.build_on_slopes); |
|
261 |
|
262 /* Try and clear the start landscape */ |
|
263 |
|
264 ret = DoCommand(tile_start, 0, 0, flags, CMD_LANDSCAPE_CLEAR); |
|
265 if (CmdFailed(ret)) return ret; |
|
266 cost = ret; |
|
267 |
|
268 terraformcost = CheckBridgeSlopeNorth(direction, tileh_start); |
|
269 if (CmdFailed(terraformcost) || (terraformcost != 0 && !allow_on_slopes)) |
|
270 return_cmd_error(STR_1000_LAND_SLOPED_IN_WRONG_DIRECTION); |
|
271 cost += terraformcost; |
|
272 |
|
273 /* Try and clear the end landscape */ |
|
274 |
|
275 ret = DoCommand(tile_end, 0, 0, flags, CMD_LANDSCAPE_CLEAR); |
|
276 if (CmdFailed(ret)) return ret; |
|
277 cost += ret; |
|
278 |
|
279 // false - end tile slope check |
|
280 terraformcost = CheckBridgeSlopeSouth(direction, tileh_end); |
|
281 if (CmdFailed(terraformcost) || (terraformcost != 0 && !allow_on_slopes)) |
|
282 return_cmd_error(STR_1000_LAND_SLOPED_IN_WRONG_DIRECTION); |
|
283 cost += terraformcost; |
|
284 |
|
285 { |
|
286 TileIndex Heads[] = {tile_start, tile_end}; |
|
287 int i; |
|
288 |
|
289 for (i = 0; i < 2; i++) { |
|
290 if (MayHaveBridgeAbove(Heads[i])) { |
|
291 if (IsBridgeAbove(Heads[i])) { |
|
292 TileIndex north_head = GetNorthernBridgeEnd(Heads[i]); |
|
293 |
|
294 if (direction == GetBridgeAxis(Heads[i])) return_cmd_error(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST); |
|
295 |
|
296 if (z_start + TILE_HEIGHT == GetBridgeHeight(north_head)) { |
|
297 return_cmd_error(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST); |
|
298 } |
|
299 } |
|
300 } |
|
301 } |
|
302 } |
|
303 |
|
304 /* do the drill? */ |
|
305 if (flags & DC_EXEC) { |
|
306 DiagDirection dir = AxisToDiagDir(direction); |
|
307 |
|
308 if (transport == TRANSPORT_RAIL) { |
|
309 MakeRailBridgeRamp(tile_start, _current_player, bridge_type, dir, railtype); |
|
310 MakeRailBridgeRamp(tile_end, _current_player, bridge_type, ReverseDiagDir(dir), railtype); |
|
311 } else { |
|
312 MakeRoadBridgeRamp(tile_start, _current_player, bridge_type, dir); |
|
313 MakeRoadBridgeRamp(tile_end, _current_player, bridge_type, ReverseDiagDir(dir)); |
|
314 } |
|
315 MarkTileDirtyByTile(tile_start); |
|
316 MarkTileDirtyByTile(tile_end); |
|
317 } |
|
318 |
|
319 delta = (direction == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1)); |
|
320 for (tile = tile_start + delta; tile != tile_end; tile += delta) { |
|
321 uint z; |
|
322 |
|
323 if (GetTileSlope(tile, &z) != SLOPE_FLAT && z >= z_start) return_cmd_error(STR_5009_LEVEL_LAND_OR_WATER_REQUIRED); |
|
324 |
|
325 if (MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) { |
|
326 /* Disallow crossing bridges for the time being */ |
|
327 return_cmd_error(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST); |
|
328 } |
|
329 |
|
330 switch (GetTileType(tile)) { |
|
331 case MP_WATER: |
|
332 if (!EnsureNoVehicle(tile)) return_cmd_error(STR_980E_SHIP_IN_THE_WAY); |
|
333 if (!IsWater(tile) && !IsCoast(tile)) goto not_valid_below; |
|
334 break; |
|
335 |
|
336 case MP_RAILWAY: |
|
337 if (!IsPlainRailTile(tile)) goto not_valid_below; |
|
338 break; |
|
339 |
|
340 case MP_STREET: |
|
341 if (GetRoadTileType(tile) == ROAD_TILE_DEPOT) goto not_valid_below; |
|
342 break; |
|
343 |
|
344 case MP_TUNNEL: |
|
345 break; |
|
346 |
|
347 case MP_STREET_BRIDGE: |
|
348 case MP_RAILWAY_BRIDGE: |
|
349 if (direction == DiagDirToAxis(GetBridgeRampDirection(tile))) goto not_valid_below; |
|
350 if (z_start < GetBridgeHeight(tile)) goto not_valid_below; |
|
351 break; |
|
352 |
|
353 case MP_UNMOVABLE: |
|
354 if (!IsOwnedLand(tile)) goto not_valid_below; |
|
355 break; |
|
356 |
|
357 case MP_CLEAR: |
|
358 if (IsBridgeAbove(tile)) return_cmd_error(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST); |
|
359 break; |
|
360 |
|
361 default: |
|
362 not_valid_below:; |
|
363 /* try and clear the middle landscape */ |
|
364 ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); |
|
365 if (CmdFailed(ret)) return ret; |
|
366 cost += ret; |
|
367 break; |
|
368 } |
|
369 |
|
370 if (flags & DC_EXEC) { |
|
371 SetBridgeMiddle(tile, direction); |
|
372 MarkTileDirtyByTile(tile); |
|
373 } |
|
374 } |
|
375 |
|
376 SetSignalsOnBothDir(tile_start, AxisToTrack(direction)); |
|
377 YapfNotifyTrackLayoutChange(tile_start, AxisToTrack(direction)); |
|
378 |
|
379 /* for human player that builds the bridge he gets a selection to choose from bridges (DC_QUERY_COST) |
|
380 * It's unnecessary to execute this command every time for every bridge. So it is done only |
|
381 * and cost is computed in "bridge_gui.c". For AI, Towns this has to be of course calculated |
|
382 */ |
|
383 if (!(flags & DC_QUERY_COST)) { |
|
384 const Bridge *b = &_bridge[bridge_type]; |
|
385 |
|
386 bridge_len += 2; // begin and end tiles/ramps |
|
387 |
|
388 if (IsValidPlayer(_current_player) && !_is_old_ai_player) |
|
389 bridge_len = CalcBridgeLenCostFactor(bridge_len); |
|
390 |
|
391 cost += (int64)bridge_len * _price.build_bridge * b->price >> 8; |
|
392 } |
|
393 |
|
394 return cost; |
|
395 } |
|
396 |
|
397 |
|
398 /** Build Tunnel. |
|
399 * @param tile start tile of tunnel |
|
400 * @param p1 railtype, 0x200 for road tunnel |
|
401 * @param p2 unused |
|
402 */ |
|
403 int32 CmdBuildTunnel(TileIndex start_tile, uint32 flags, uint32 p1, uint32 p2) |
|
404 { |
|
405 TileIndexDiff delta; |
|
406 TileIndex end_tile; |
|
407 DiagDirection direction; |
|
408 Slope start_tileh; |
|
409 Slope end_tileh; |
|
410 uint start_z; |
|
411 uint end_z; |
|
412 int32 cost; |
|
413 int32 ret; |
|
414 |
|
415 _build_tunnel_endtile = 0; |
|
416 |
|
417 if (p1 != 0x200 && !ValParamRailtype(p1)) return CMD_ERROR; |
|
418 |
|
419 start_tileh = GetTileSlope(start_tile, &start_z); |
|
420 |
|
421 switch (start_tileh) { |
|
422 case SLOPE_SW: direction = DIAGDIR_SW; break; |
|
423 case SLOPE_SE: direction = DIAGDIR_SE; break; |
|
424 case SLOPE_NW: direction = DIAGDIR_NW; break; |
|
425 case SLOPE_NE: direction = DIAGDIR_NE; break; |
|
426 default: return_cmd_error(STR_500B_SITE_UNSUITABLE_FOR_TUNNEL); |
|
427 } |
|
428 |
|
429 ret = DoCommand(start_tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); |
|
430 if (CmdFailed(ret)) return ret; |
|
431 |
|
432 /* XXX - do NOT change 'ret' in the loop, as it is used as the price |
|
433 * for the clearing of the entrance of the tunnel. Assigning it to |
|
434 * cost before the loop will yield different costs depending on start- |
|
435 * position, because of increased-cost-by-length: 'cost += cost >> 3' */ |
|
436 cost = 0; |
|
437 delta = TileOffsByDiagDir(direction); |
|
438 end_tile = start_tile; |
|
439 for (;;) { |
|
440 end_tile += delta; |
|
441 end_tileh = GetTileSlope(end_tile, &end_z); |
|
442 |
|
443 if (start_z == end_z) break; |
|
444 |
|
445 if (!_cheats.crossing_tunnels.value && IsTunnelInWay(end_tile, start_z)) { |
|
446 return_cmd_error(STR_5003_ANOTHER_TUNNEL_IN_THE_WAY); |
|
447 } |
|
448 |
|
449 cost += _price.build_tunnel; |
|
450 cost += cost >> 3; // add a multiplier for longer tunnels |
|
451 if (cost >= 400000000) cost = 400000000; |
|
452 } |
|
453 |
|
454 /* Add the cost of the entrance */ |
|
455 cost += _price.build_tunnel + ret; |
|
456 |
|
457 // if the command fails from here on we want the end tile to be highlighted |
|
458 _build_tunnel_endtile = end_tile; |
|
459 |
|
460 // slope of end tile must be complementary to the slope of the start tile |
|
461 if (end_tileh != ComplementSlope(start_tileh)) { |
|
462 ret = DoCommand(end_tile, end_tileh & start_tileh, 0, flags, CMD_TERRAFORM_LAND); |
|
463 if (CmdFailed(ret)) return_cmd_error(STR_5005_UNABLE_TO_EXCAVATE_LAND); |
|
464 } else { |
|
465 ret = DoCommand(end_tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); |
|
466 if (CmdFailed(ret)) return ret; |
|
467 } |
|
468 cost += _price.build_tunnel + ret; |
|
469 |
|
470 if (flags & DC_EXEC) { |
|
471 if (GB(p1, 9, 1) == TRANSPORT_RAIL) { |
|
472 MakeRailTunnel(start_tile, _current_player, direction, GB(p1, 0, 4)); |
|
473 MakeRailTunnel(end_tile, _current_player, ReverseDiagDir(direction), GB(p1, 0, 4)); |
|
474 UpdateSignalsOnSegment(start_tile, direction); |
|
475 YapfNotifyTrackLayoutChange(start_tile, AxisToTrack(DiagDirToAxis(direction))); |
|
476 } else { |
|
477 MakeRoadTunnel(start_tile, _current_player, direction); |
|
478 MakeRoadTunnel(end_tile, _current_player, ReverseDiagDir(direction)); |
|
479 } |
|
480 } |
|
481 |
|
482 return cost; |
|
483 } |
|
484 |
|
485 TileIndex CheckTunnelBusy(TileIndex tile, uint *length) |
|
486 { |
|
487 uint z = GetTileZ(tile); |
|
488 DiagDirection dir = GetTunnelDirection(tile); |
|
489 TileIndexDiff delta = TileOffsByDiagDir(dir); |
|
490 uint len = 0; |
|
491 TileIndex starttile = tile; |
|
492 Vehicle *v; |
|
493 |
|
494 do { |
|
495 tile += delta; |
|
496 len++; |
|
497 } while ( |
|
498 !IsTunnelTile(tile) || |
|
499 ReverseDiagDir(GetTunnelDirection(tile)) != dir || |
|
500 GetTileZ(tile) != z |
|
501 ); |
|
502 |
|
503 v = FindVehicleBetween(starttile, tile, z); |
|
504 if (v != NULL) { |
|
505 _error_message = v->type == VEH_Train ? |
|
506 STR_5000_TRAIN_IN_TUNNEL : STR_5001_ROAD_VEHICLE_IN_TUNNEL; |
|
507 return INVALID_TILE; |
|
508 } |
|
509 |
|
510 if (length != NULL) *length = len; |
|
511 return tile; |
|
512 } |
|
513 |
|
514 static inline bool CheckAllowRemoveTunnelBridge(TileIndex tile) |
|
515 { |
|
516 /* Floods can remove anything as well as the scenario editor */ |
|
517 if (_current_player == OWNER_WATER || _game_mode == GM_EDITOR) return true; |
|
518 /* Obviously if the bridge/tunnel belongs to us, or no-one, we can remove it */ |
|
519 if (CheckTileOwnership(tile) || IsTileOwner(tile, OWNER_NONE)) return true; |
|
520 /* Otherwise we can only remove town-owned stuff with extra patch-settings, or cheat */ |
|
521 if (IsTileOwner(tile, OWNER_TOWN) && (_patches.extra_dynamite || _cheats.magic_bulldozer.value)) return true; |
|
522 return false; |
|
523 } |
|
524 |
|
525 static int32 DoClearTunnel(TileIndex tile, uint32 flags) |
|
526 { |
|
527 Town *t = NULL; |
|
528 TileIndex endtile; |
|
529 uint length; |
|
530 |
|
531 SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION); |
|
532 |
|
533 if (!CheckAllowRemoveTunnelBridge(tile)) return CMD_ERROR; |
|
534 |
|
535 endtile = CheckTunnelBusy(tile, &length); |
|
536 if (endtile == INVALID_TILE) return CMD_ERROR; |
|
537 |
|
538 _build_tunnel_endtile = endtile; |
|
539 |
|
540 if (IsTileOwner(tile, OWNER_TOWN) && _game_mode != GM_EDITOR) { |
|
541 t = ClosestTownFromTile(tile, (uint)-1); // town penalty rating |
|
542 |
|
543 /* Check if you are allowed to remove the tunnel owned by a town |
|
544 * Removal depends on difficulty settings */ |
|
545 if (!CheckforTownRating(flags, t, TUNNELBRIDGE_REMOVE)) { |
|
546 SetDParam(0, t->index); |
|
547 return_cmd_error(STR_2009_LOCAL_AUTHORITY_REFUSES); |
|
548 } |
|
549 } |
|
550 |
|
551 if (flags & DC_EXEC) { |
|
552 // We first need to request the direction before calling DoClearSquare |
|
553 // else the direction is always 0.. dah!! ;) |
|
554 DiagDirection dir = GetTunnelDirection(tile); |
|
555 Track track; |
|
556 |
|
557 // Adjust the town's player rating. Do this before removing the tile owner info. |
|
558 if (IsTileOwner(tile, OWNER_TOWN) && _game_mode != GM_EDITOR) |
|
559 ChangeTownRating(t, RATING_TUNNEL_BRIDGE_DOWN_STEP, RATING_TUNNEL_BRIDGE_MINIMUM); |
|
560 |
|
561 DoClearSquare(tile); |
|
562 DoClearSquare(endtile); |
|
563 UpdateSignalsOnSegment(tile, ReverseDiagDir(dir)); |
|
564 UpdateSignalsOnSegment(endtile, dir); |
|
565 track = AxisToTrack(DiagDirToAxis(dir)); |
|
566 YapfNotifyTrackLayoutChange(tile, track); |
|
567 YapfNotifyTrackLayoutChange(endtile, track); |
|
568 } |
|
569 return _price.clear_tunnel * (length + 1); |
|
570 } |
|
571 |
|
572 |
|
573 static bool IsVehicleOnBridge(TileIndex starttile, TileIndex endtile, uint z) |
|
574 { |
|
575 const Vehicle *v; |
|
576 FOR_ALL_VEHICLES(v) { |
|
577 if ((v->tile == starttile || v->tile == endtile) && v->z_pos == z) { |
|
578 _error_message = VehicleInTheWayErrMsg(v); |
|
579 return true; |
|
580 } |
|
581 } |
|
582 return false; |
|
583 } |
|
584 |
|
585 static int32 DoClearBridge(TileIndex tile, uint32 flags) |
|
586 { |
|
587 DiagDirection direction; |
|
588 TileIndexDiff delta; |
|
589 TileIndex endtile; |
|
590 Town *t = NULL; |
|
591 |
|
592 SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION); |
|
593 |
|
594 if (!CheckAllowRemoveTunnelBridge(tile)) return CMD_ERROR; |
|
595 |
|
596 endtile = GetOtherBridgeEnd(tile); |
|
597 |
|
598 if (!EnsureNoVehicle(tile) || |
|
599 !EnsureNoVehicle(endtile) || |
|
600 IsVehicleOnBridge(tile, endtile, GetBridgeHeight(tile))) { |
|
601 return CMD_ERROR; |
|
602 } |
|
603 |
|
604 direction = GetBridgeRampDirection(tile); |
|
605 delta = TileOffsByDiagDir(direction); |
|
606 |
|
607 if (IsTileOwner(tile, OWNER_TOWN) && _game_mode != GM_EDITOR) { |
|
608 t = ClosestTownFromTile(tile, (uint)-1); // town penalty rating |
|
609 |
|
610 /* Check if you are allowed to remove the bridge owned by a town |
|
611 * Removal depends on difficulty settings */ |
|
612 if (!CheckforTownRating(flags, t, TUNNELBRIDGE_REMOVE)) { |
|
613 SetDParam(0, t->index); |
|
614 return_cmd_error(STR_2009_LOCAL_AUTHORITY_REFUSES); |
|
615 } |
|
616 } |
|
617 |
|
618 if (flags & DC_EXEC) { |
|
619 TileIndex c; |
|
620 Track track; |
|
621 |
|
622 //checks if the owner is town then decrease town rating by RATING_TUNNEL_BRIDGE_DOWN_STEP until |
|
623 // you have a "Poor" (0) town rating |
|
624 if (IsTileOwner(tile, OWNER_TOWN) && _game_mode != GM_EDITOR) |
|
625 ChangeTownRating(t, RATING_TUNNEL_BRIDGE_DOWN_STEP, RATING_TUNNEL_BRIDGE_MINIMUM); |
|
626 |
|
627 DoClearSquare(tile); |
|
628 DoClearSquare(endtile); |
|
629 for (c = tile + delta; c != endtile; c += delta) { |
|
630 ClearBridgeMiddle(c); |
|
631 MarkTileDirtyByTile(c); |
|
632 } |
|
633 |
|
634 UpdateSignalsOnSegment(tile, ReverseDiagDir(direction)); |
|
635 UpdateSignalsOnSegment(endtile, direction); |
|
636 track = AxisToTrack(DiagDirToAxis(direction)); |
|
637 YapfNotifyTrackLayoutChange(tile, track); |
|
638 YapfNotifyTrackLayoutChange(endtile, track); |
|
639 } |
|
640 |
|
641 return (DistanceManhattan(tile, endtile) + 1) * _price.clear_bridge; |
|
642 } |
|
643 |
|
644 static int32 ClearTile_Tunnel(TileIndex tile, byte flags) |
|
645 { |
|
646 assert(IsTunnelTile(tile)); |
|
647 if (flags & DC_AUTO) return_cmd_error(STR_5006_MUST_DEMOLISH_TUNNEL_FIRST); |
|
648 return DoClearTunnel(tile, flags); |
|
649 } |
|
650 |
|
651 static int32 ClearTile_Bridge(TileIndex tile, byte flags) |
|
652 { |
|
653 assert(IsBridgeTile(tile)); |
|
654 if (flags & DC_AUTO) return_cmd_error(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST); |
|
655 return DoClearBridge(tile, flags); |
|
656 } |
|
657 |
|
658 int32 DoConvertTunnelBridgeRail(TileIndex tile, RailType totype, bool exec) |
|
659 { |
|
660 TileIndex endtile; |
|
661 |
|
662 if (IsTunnelTile(tile) && GetTunnelTransportType(tile) == TRANSPORT_RAIL) { |
|
663 uint length; |
|
664 |
|
665 if (!CheckTileOwnership(tile)) return CMD_ERROR; |
|
666 |
|
667 if (GetRailType(tile) == totype) return CMD_ERROR; |
|
668 |
|
669 // 'hidden' elrails can't be downgraded to normal rail when elrails are disabled |
|
670 if (_patches.disable_elrails && totype == RAILTYPE_RAIL && GetRailType(tile) == RAILTYPE_ELECTRIC) return CMD_ERROR; |
|
671 |
|
672 endtile = CheckTunnelBusy(tile, &length); |
|
673 if (endtile == INVALID_TILE) return CMD_ERROR; |
|
674 |
|
675 if (exec) { |
|
676 Track track; |
|
677 SetRailType(tile, totype); |
|
678 SetRailType(endtile, totype); |
|
679 MarkTileDirtyByTile(tile); |
|
680 MarkTileDirtyByTile(endtile); |
|
681 |
|
682 track = AxisToTrack(DiagDirToAxis(GetTunnelDirection(tile))); |
|
683 YapfNotifyTrackLayoutChange(tile, track); |
|
684 YapfNotifyTrackLayoutChange(endtile, track); |
|
685 } |
|
686 return (length + 1) * (_price.build_rail >> 1); |
|
687 } else if (IsBridgeTile(tile) && GetBridgeTransportType(tile) == TRANSPORT_RAIL) { |
|
688 |
|
689 if (!CheckTileOwnership(tile)) return CMD_ERROR; |
|
690 |
|
691 endtile = GetOtherBridgeEnd(tile); |
|
692 |
|
693 if (!EnsureNoVehicle(tile) || |
|
694 !EnsureNoVehicle(endtile) || |
|
695 IsVehicleOnBridge(tile, endtile, GetBridgeHeight(tile))) { |
|
696 return CMD_ERROR; |
|
697 } |
|
698 |
|
699 if (GetRailType(tile) == totype) return CMD_ERROR; |
|
700 |
|
701 if (exec) { |
|
702 TileIndexDiff delta; |
|
703 Track track; |
|
704 |
|
705 SetRailType(tile, totype); |
|
706 SetRailType(endtile, totype); |
|
707 MarkTileDirtyByTile(tile); |
|
708 MarkTileDirtyByTile(endtile); |
|
709 |
|
710 track = AxisToTrack(DiagDirToAxis(GetBridgeRampDirection(tile))); |
|
711 YapfNotifyTrackLayoutChange(tile, track); |
|
712 YapfNotifyTrackLayoutChange(endtile, track); |
|
713 |
|
714 delta = TileOffsByDiagDir(GetBridgeRampDirection(tile)); |
|
715 for (tile += delta; tile != endtile; tile += delta) { |
|
716 MarkTileDirtyByTile(tile); // TODO encapsulate this into a function |
|
717 } |
|
718 } |
|
719 |
|
720 return (DistanceManhattan(tile, endtile) + 1) * (_price.build_rail >> 1); |
|
721 } else { |
|
722 return CMD_ERROR; |
|
723 } |
|
724 } |
|
725 |
|
726 |
|
727 static void DrawBridgePillars(PalSpriteID image, const TileInfo* ti, Axis axis, uint type, int x, int y, int z) |
|
728 { |
|
729 if (image != 0) { |
|
730 bool drawfarpillar = !HASBIT(GetBridgeFlags(type), 0); |
|
731 int back_height, front_height; |
|
732 int i = z; |
|
733 const byte *p; |
|
734 |
|
735 static const byte _tileh_bits[4][8] = { |
|
736 { 2, 1, 8, 4, 16, 2, 0, 9 }, |
|
737 { 1, 8, 4, 2, 2, 16, 9, 0 }, |
|
738 { 4, 8, 1, 2, 16, 2, 0, 9 }, |
|
739 { 2, 4, 8, 1, 2, 16, 9, 0 } |
|
740 }; |
|
741 |
|
742 if (_display_opt & DO_TRANS_BUILDINGS) MAKE_TRANSPARENT(image); |
|
743 |
|
744 p = _tileh_bits[(image & 1) * 2 + (axis == AXIS_X ? 0 : 1)]; |
|
745 front_height = ti->z + (ti->tileh & p[0] ? TILE_HEIGHT : 0); |
|
746 back_height = ti->z + (ti->tileh & p[1] ? TILE_HEIGHT : 0); |
|
747 |
|
748 if (IsSteepSlope(ti->tileh)) { |
|
749 if (!(ti->tileh & p[2])) front_height += TILE_HEIGHT; |
|
750 if (!(ti->tileh & p[3])) back_height += TILE_HEIGHT; |
|
751 } |
|
752 |
|
753 for (; z >= front_height || z >= back_height; z -= TILE_HEIGHT) { |
|
754 /* HACK set height of the BB of pillars to 1, because the origin of the |
|
755 * sprites is at the top |
|
756 */ |
|
757 if (z >= front_height) { // front facing pillar |
|
758 AddSortableSpriteToDraw(image, x, y, p[4], p[5], 1, z); |
|
759 } |
|
760 |
|
761 if (drawfarpillar && z >= back_height && z < i - TILE_HEIGHT) { // back facing pillar |
|
762 AddSortableSpriteToDraw(image, x - p[6], y - p[7], p[4], p[5], 1, z); |
|
763 } |
|
764 } |
|
765 } |
|
766 } |
|
767 |
|
768 uint GetBridgeFoundation(Slope tileh, Axis axis) |
|
769 { |
|
770 uint i; |
|
771 |
|
772 if (HASBIT(BRIDGE_FULL_LEVELED_FOUNDATION, tileh)) return tileh; |
|
773 |
|
774 // inclined sloped building |
|
775 switch (tileh) { |
|
776 case SLOPE_W: |
|
777 case SLOPE_STEEP_W: i = 0; break; |
|
778 case SLOPE_S: |
|
779 case SLOPE_STEEP_S: i = 2; break; |
|
780 case SLOPE_E: |
|
781 case SLOPE_STEEP_E: i = 4; break; |
|
782 case SLOPE_N: |
|
783 case SLOPE_STEEP_N: i = 6; break; |
|
784 default: return 0; |
|
785 } |
|
786 if (axis != AXIS_X) ++i; |
|
787 return i + 15; |
|
788 } |
|
789 |
|
790 /** |
|
791 * Draws a tunnel tile. |
|
792 * Please note that in this code, "roads" are treated as railtype 1, whilst the real railtypes are 0, 2 and 3 |
|
793 */ |
|
794 static void DrawTile_Tunnel(TileInfo *ti) |
|
795 { |
|
796 uint32 image; |
|
797 |
|
798 if (GetTunnelTransportType(ti->tile) == TRANSPORT_RAIL) { |
|
799 image = GetRailTypeInfo(GetRailType(ti->tile))->base_sprites.tunnel; |
|
800 } else { |
|
801 image = SPR_TUNNEL_ENTRY_REAR_ROAD; |
|
802 } |
|
803 |
|
804 if (HasTunnelSnowOrDesert(ti->tile)) image += 32; |
|
805 |
|
806 image += GetTunnelDirection(ti->tile) * 2; |
|
807 DrawGroundSprite(image); |
|
808 if (GetRailType(ti->tile) == RAILTYPE_ELECTRIC) DrawCatenary(ti); |
|
809 |
|
810 AddSortableSpriteToDraw(image+1, ti->x + TILE_SIZE - 1, ti->y + TILE_SIZE - 1, 1, 1, 8, (byte)ti->z); |
|
811 DrawBridgeMiddle(ti); |
|
812 } |
|
813 |
|
814 /** |
|
815 * Draws a bridge tile. |
|
816 * base_offset is where the sprite selection comes into play |
|
817 * and it works a bit like a bitmask.<p> For bridge heads: |
|
818 * <ul><li>Bit 0: direction</li> |
|
819 * <li>Bit 1: northern or southern heads</li> |
|
820 * <li>Bit 2: Set if the bridge head is sloped</li> |
|
821 * <li>Bit 3 and more: Railtype Specific subset</li> |
|
822 * </ul> |
|
823 * Please note that in this code, "roads" are treated as railtype 1, whilst the real railtypes are 0, 2 and 3 |
|
824 */ |
|
825 static void DrawTile_Bridge(TileInfo *ti) |
|
826 { |
|
827 uint32 image; |
|
828 |
|
829 int base_offset; |
|
830 bool ice = HasBridgeSnowOrDesert(ti->tile); |
|
831 |
|
832 if (GetBridgeTransportType(ti->tile) == TRANSPORT_RAIL) { |
|
833 base_offset = GetRailTypeInfo(GetRailType(ti->tile))->bridge_offset; |
|
834 assert(base_offset != 8); /* This one is used for roads */ |
|
835 } else { |
|
836 base_offset = 8; |
|
837 } |
|
838 |
|
839 /* as the lower 3 bits are used for other stuff, make sure they are clear */ |
|
840 assert( (base_offset & 0x07) == 0x00); |
|
841 |
|
842 if (!HASBIT(BRIDGE_NO_FOUNDATION, ti->tileh)) { |
|
843 int f = GetBridgeFoundation(ti->tileh, DiagDirToAxis(GetBridgeRampDirection(ti->tile))); |
|
844 if (f != 0) DrawFoundation(ti, f); |
|
845 } |
|
846 |
|
847 // HACK Wizardry to convert the bridge ramp direction into a sprite offset |
|
848 base_offset += (6 - GetBridgeRampDirection(ti->tile)) % 4; |
|
849 |
|
850 if (ti->tileh == SLOPE_FLAT) base_offset += 4; // sloped bridge head |
|
851 |
|
852 /* Table number 6 always refers to the bridge heads for any bridge type */ |
|
853 image = GetBridgeSpriteTable(GetBridgeType(ti->tile), 6)[base_offset]; |
|
854 |
|
855 if (!ice) { |
|
856 DrawClearLandTile(ti, 3); |
|
857 } else { |
|
858 DrawGroundSprite(SPR_FLAT_SNOWY_TILE + _tileh_to_sprite[ti->tileh]); |
|
859 } |
|
860 |
|
861 if (GetRailType(ti->tile) == RAILTYPE_ELECTRIC) DrawCatenary(ti); |
|
862 |
|
863 // draw ramp |
|
864 if (_display_opt & DO_TRANS_BUILDINGS) MAKE_TRANSPARENT(image); |
|
865 /* HACK set the height of the BB of a sloped ramp to 1 so a vehicle on |
|
866 * it doesn't disappear behind it |
|
867 */ |
|
868 AddSortableSpriteToDraw( |
|
869 image, ti->x, ti->y, 16, 16, ti->tileh == SLOPE_FLAT ? 1 : 8, ti->z |
|
870 ); |
|
871 |
|
872 DrawBridgeMiddle(ti); |
|
873 } |
|
874 |
|
875 |
|
876 /** Compute bridge piece. Computes the bridge piece to display depending on the position inside the bridge. |
|
877 * bridges pieces sequence (middle parts) |
|
878 * bridge len 1: 0 |
|
879 * bridge len 2: 0 1 |
|
880 * bridge len 3: 0 4 1 |
|
881 * bridge len 4: 0 2 3 1 |
|
882 * bridge len 5: 0 2 5 3 1 |
|
883 * bridge len 6: 0 2 3 2 3 1 |
|
884 * bridge len 7: 0 2 3 4 2 3 1 |
|
885 * #0 - always as first, #1 - always as last (if len>1) |
|
886 * #2,#3 are to pair in order |
|
887 * for odd bridges: #5 is going in the bridge middle if on even position, #4 on odd (counting from 0) |
|
888 * @param north Northernmost tile of bridge |
|
889 * @param south Southernmost tile of bridge |
|
890 * @return Index of bridge piece |
|
891 */ |
|
892 static uint CalcBridgePiece(uint north, uint south) |
|
893 { |
|
894 if (north == 1) { |
|
895 return 0; |
|
896 } else if (south == 1) { |
|
897 return 1; |
|
898 } else if (north < south) { |
|
899 return north & 1 ? 3 : 2; |
|
900 } else if (north > south) { |
|
901 return south & 1 ? 2 : 3; |
|
902 } else { |
|
903 return north & 1 ? 5 : 4; |
|
904 } |
|
905 } |
|
906 |
|
907 |
|
908 void DrawBridgeMiddle(const TileInfo* ti) |
|
909 { |
|
910 const PalSpriteID* b; |
|
911 PalSpriteID image; |
|
912 uint base_offset; |
|
913 TileIndex rampnorth; |
|
914 TileIndex rampsouth; |
|
915 Axis axis; |
|
916 uint piece; |
|
917 uint type; |
|
918 int x; |
|
919 int y; |
|
920 uint z; |
|
921 |
|
922 if (!IsBridgeAbove(ti->tile)) return; |
|
923 |
|
924 rampnorth = GetNorthernBridgeEnd(ti->tile); |
|
925 rampsouth = GetSouthernBridgeEnd(ti->tile); |
|
926 |
|
927 axis = GetBridgeAxis(ti->tile); |
|
928 piece = CalcBridgePiece( |
|
929 DistanceManhattan(ti->tile, rampnorth), |
|
930 DistanceManhattan(ti->tile, rampsouth) |
|
931 ); |
|
932 type = GetBridgeType(rampsouth); |
|
933 |
|
934 if (GetBridgeTransportType(rampsouth) == TRANSPORT_RAIL) { |
|
935 base_offset = GetRailTypeInfo(GetRailType(rampsouth))->bridge_offset; |
|
936 } else { |
|
937 base_offset = 8; |
|
938 } |
|
939 |
|
940 b = base_offset + GetBridgeSpriteTable(type, piece); |
|
941 if (axis != AXIS_X) b += 4; |
|
942 |
|
943 x = ti->x; |
|
944 y = ti->y; |
|
945 z = GetBridgeHeight(rampsouth) - 3; |
|
946 |
|
947 image = b[0]; |
|
948 if (_display_opt & DO_TRANS_BUILDINGS) MAKE_TRANSPARENT(image); |
|
949 if (axis == AXIS_X) { |
|
950 AddSortableSpriteToDraw(image, x, y, 16, 11, 1, z); |
|
951 } else { |
|
952 AddSortableSpriteToDraw(image, x, y, 11, 16, 1, z); |
|
953 } |
|
954 |
|
955 image = b[1]; |
|
956 if (_display_opt & DO_TRANS_BUILDINGS) MAKE_TRANSPARENT(image); |
|
957 |
|
958 // draw roof, the component of the bridge which is logically between the vehicle and the camera |
|
959 if (axis == AXIS_X) { |
|
960 y += 12; |
|
961 if (image & SPRITE_MASK) AddSortableSpriteToDraw(image, x, y, 16, 1, 0x28, z); |
|
962 } else { |
|
963 x += 12; |
|
964 if (image & SPRITE_MASK) AddSortableSpriteToDraw(image, x, y, 1, 16, 0x28, z); |
|
965 } |
|
966 |
|
967 if (GetRailType(rampsouth) == RAILTYPE_ELECTRIC) DrawCatenary(ti); |
|
968 |
|
969 if (ti->z + 5 == z) { |
|
970 // draw poles below for small bridges |
|
971 image = b[2]; |
|
972 if (image != 0) { |
|
973 if (_display_opt & DO_TRANS_BUILDINGS) MAKE_TRANSPARENT(image); |
|
974 DrawGroundSpriteAt(image, x, y, z); |
|
975 } |
|
976 } else if (_patches.bridge_pillars) { |
|
977 // draw pillars below for high bridges |
|
978 DrawBridgePillars(b[2], ti, axis, type, x, y, z); |
|
979 } |
|
980 } |
|
981 |
|
982 |
|
983 uint SetSpeedLimitOnBridge(Vehicle *v) |
|
984 { |
|
985 uint bridge_speed; |
|
986 if (v->vehstatus & VS_HIDDEN) return v->max_speed; /* in tunnel */ |
|
987 |
|
988 bridge_speed = _bridge[GetBridgeType(v->tile)].speed; |
|
989 |
|
990 if (v->type == VEH_Road) bridge_speed *= 2; /* XXX give vehicles proper speeds */ |
|
991 |
|
992 if (v->cur_speed > bridge_speed) v->cur_speed = bridge_speed; |
|
993 return bridge_speed; |
|
994 } |
|
995 |
|
996 |
|
997 /** Gets the absolute z coordinate of a point inside a tunnel tile |
|
998 * When we're on the track (that means between position 5 and 10) |
|
999 * on the coordinate perpendicular to the track it returns only the |
|
1000 * base height of the tile (because the track is horizontal). |
|
1001 * Outside this range (from 0 to 4 and from 11 to 15) it returns the |
|
1002 * "true" Z coordinate of the tile by taking the slope into account |
|
1003 * @param tile The index of the tile we are talking about |
|
1004 * @param x Absolute or relative x coordinate |
|
1005 * @param y Absolute or relative y coordinate |
|
1006 * @return Absolute z coordinate |
|
1007 */ |
|
1008 static uint GetSlopeZ_Tunnel(TileIndex tile, uint x, uint y) |
|
1009 { |
|
1010 uint z, pos; |
|
1011 Slope tileh = GetTileSlope(tile, &z); |
|
1012 |
|
1013 x &= 0xF; |
|
1014 y &= 0xF; |
|
1015 |
|
1016 pos = (DiagDirToAxis(GetTunnelDirection(tile)) == AXIS_X ? y : x); |
|
1017 |
|
1018 // In the tunnel entrance? |
|
1019 if (5 <= pos && pos <= 10) return z; |
|
1020 |
|
1021 return z + GetPartialZ(x, y, tileh); |
|
1022 } |
|
1023 |
|
1024 /** Gets the absolute z coordinate of a point inside a bridge tile |
|
1025 * When we're on the track (that means between position 5 and 10) |
|
1026 * on the coordinate perpendicular to the track it returns the base |
|
1027 * height of the ramp |
|
1028 * Outside this range (from 0 to 4 and from 11 to 15) it returns the |
|
1029 * "true" Z coordinate of the tile by taking the slope into account |
|
1030 * @param tile The index of the tile we are talking about |
|
1031 * @param x Absolute or relative x coordinate |
|
1032 * @param y Absolute or relative y coordinate |
|
1033 * @return Absolute z coordinate |
|
1034 */ |
|
1035 static uint GetSlopeZ_Bridge(TileIndex tile, uint x, uint y) |
|
1036 { |
|
1037 uint z, pos; |
|
1038 Slope tileh = GetTileSlope(tile, &z); |
|
1039 DiagDirection dir = GetBridgeRampDirection(tile); |
|
1040 |
|
1041 x &= 0xF; |
|
1042 y &= 0xF; |
|
1043 |
|
1044 pos = (DiagDirToAxis(dir) == AXIS_X ? y : x); |
|
1045 |
|
1046 // On the bridge ramp? |
|
1047 if (5 <= pos && pos <= 10) { |
|
1048 uint delta; |
|
1049 |
|
1050 if (IsSteepSlope(tileh)) return z + TILE_HEIGHT * 2; |
|
1051 |
|
1052 if (HASBIT(BRIDGE_HORZ_RAMP, tileh)) return z + TILE_HEIGHT; |
|
1053 |
|
1054 if (HASBIT(BRIDGE_FULL_LEVELED_FOUNDATION, tileh)) z += TILE_HEIGHT; |
|
1055 switch (dir) { |
|
1056 default: NOT_REACHED(); |
|
1057 case DIAGDIR_NE: delta = (TILE_SIZE - 1 - x) / 2; break; |
|
1058 case DIAGDIR_SE: delta = y / 2; break; |
|
1059 case DIAGDIR_SW: delta = x / 2; break; |
|
1060 case DIAGDIR_NW: delta = (TILE_SIZE - 1 - y) / 2; break; |
|
1061 } |
|
1062 return z + 1 + delta; |
|
1063 } else { |
|
1064 uint f = GetBridgeFoundation(tileh, DiagDirToAxis(dir)); |
|
1065 |
|
1066 if (f != 0) { |
|
1067 if (IsSteepSlope(tileh)) { |
|
1068 z += TILE_HEIGHT; |
|
1069 } else if (f < 15) { |
|
1070 return z + TILE_HEIGHT; |
|
1071 } |
|
1072 tileh = _inclined_tileh[f - 15]; |
|
1073 } |
|
1074 } |
|
1075 return z + GetPartialZ(x, y, tileh); |
|
1076 } |
|
1077 |
|
1078 static Slope GetSlopeTileh_Tunnel(TileIndex tile, Slope tileh) |
|
1079 { |
|
1080 return tileh; |
|
1081 } |
|
1082 |
|
1083 static Slope GetSlopeTileh_Bridge(TileIndex tile, Slope tileh) |
|
1084 { |
|
1085 if (HASBIT(BRIDGE_NO_FOUNDATION, tileh)) { |
|
1086 return tileh; |
|
1087 } else { |
|
1088 uint f = GetBridgeFoundation(tileh, DiagDirToAxis(GetBridgeRampDirection(tile))); |
|
1089 |
|
1090 if (f == 0) return tileh; |
|
1091 if (f < 15) return SLOPE_FLAT; |
|
1092 return _inclined_tileh[f - 15]; |
|
1093 } |
|
1094 } |
|
1095 |
|
1096 static void GetAcceptedCargo_Tunnel(TileIndex tile, AcceptedCargo ac) |
|
1097 { |
|
1098 /* not used */ |
|
1099 } |
|
1100 |
|
1101 static void GetAcceptedCargo_Bridge(TileIndex tile, AcceptedCargo ac) |
|
1102 { |
|
1103 /* not used */ |
|
1104 } |
|
1105 |
|
1106 static const StringID _bridge_tile_str[(MAX_BRIDGES + 3) + (MAX_BRIDGES + 3)] = { |
|
1107 STR_501F_WOODEN_RAIL_BRIDGE, |
|
1108 STR_5020_CONCRETE_RAIL_BRIDGE, |
|
1109 STR_501C_STEEL_GIRDER_RAIL_BRIDGE, |
|
1110 STR_501E_REINFORCED_CONCRETE_SUSPENSION, |
|
1111 STR_501B_STEEL_SUSPENSION_RAIL_BRIDGE, |
|
1112 STR_501B_STEEL_SUSPENSION_RAIL_BRIDGE, |
|
1113 STR_501D_STEEL_CANTILEVER_RAIL_BRIDGE, |
|
1114 STR_501D_STEEL_CANTILEVER_RAIL_BRIDGE, |
|
1115 STR_501D_STEEL_CANTILEVER_RAIL_BRIDGE, |
|
1116 STR_501C_STEEL_GIRDER_RAIL_BRIDGE, |
|
1117 STR_5027_TUBULAR_RAIL_BRIDGE, |
|
1118 STR_5027_TUBULAR_RAIL_BRIDGE, |
|
1119 STR_5027_TUBULAR_RAIL_BRIDGE, |
|
1120 0, 0, 0, |
|
1121 |
|
1122 STR_5025_WOODEN_ROAD_BRIDGE, |
|
1123 STR_5026_CONCRETE_ROAD_BRIDGE, |
|
1124 STR_5022_STEEL_GIRDER_ROAD_BRIDGE, |
|
1125 STR_5024_REINFORCED_CONCRETE_SUSPENSION, |
|
1126 STR_5021_STEEL_SUSPENSION_ROAD_BRIDGE, |
|
1127 STR_5021_STEEL_SUSPENSION_ROAD_BRIDGE, |
|
1128 STR_5023_STEEL_CANTILEVER_ROAD_BRIDGE, |
|
1129 STR_5023_STEEL_CANTILEVER_ROAD_BRIDGE, |
|
1130 STR_5023_STEEL_CANTILEVER_ROAD_BRIDGE, |
|
1131 STR_5022_STEEL_GIRDER_ROAD_BRIDGE, |
|
1132 STR_5028_TUBULAR_ROAD_BRIDGE, |
|
1133 STR_5028_TUBULAR_ROAD_BRIDGE, |
|
1134 STR_5028_TUBULAR_ROAD_BRIDGE, |
|
1135 0, 0, 0, |
|
1136 }; |
|
1137 |
|
1138 static void GetTileDesc_Tunnel(TileIndex tile, TileDesc *td) |
|
1139 { |
|
1140 td->str = (GetTunnelTransportType(tile) == TRANSPORT_RAIL) ? STR_5017_RAILROAD_TUNNEL : STR_5018_ROAD_TUNNEL; |
|
1141 td->owner = GetTileOwner(tile); |
|
1142 } |
|
1143 |
|
1144 static void GetTileDesc_Bridge(TileIndex tile, TileDesc *td) |
|
1145 { |
|
1146 td->str = _bridge_tile_str[GetBridgeTransportType(tile) << 4 | GetBridgeType(tile)]; |
|
1147 td->owner = GetTileOwner(tile); |
|
1148 } |
|
1149 |
|
1150 |
|
1151 static void AnimateTile_Tunnel(TileIndex tile) |
|
1152 { |
|
1153 /* not used */ |
|
1154 } |
|
1155 |
|
1156 static void AnimateTile_Bridge(TileIndex tile) |
|
1157 { |
|
1158 /* not used */ |
|
1159 } |
|
1160 |
|
1161 static void TileLoop_Tunnel(TileIndex tile) |
|
1162 { |
|
1163 bool snow_or_desert = HasTunnelSnowOrDesert(tile); |
|
1164 switch (_opt.landscape) { |
|
1165 case LT_HILLY: |
|
1166 if (snow_or_desert != (GetTileZ(tile) > _opt.snow_line)) { |
|
1167 SetTunnelSnowOrDesert(tile, !snow_or_desert); |
|
1168 MarkTileDirtyByTile(tile); |
|
1169 } |
|
1170 break; |
|
1171 |
|
1172 case LT_DESERT: |
|
1173 if (GetTropicZone(tile) == TROPICZONE_DESERT && !snow_or_desert) { |
|
1174 SetTunnelSnowOrDesert(tile, true); |
|
1175 MarkTileDirtyByTile(tile); |
|
1176 } |
|
1177 break; |
|
1178 } |
|
1179 } |
|
1180 |
|
1181 static void TileLoop_Bridge(TileIndex tile) |
|
1182 { |
|
1183 bool snow_or_desert = HasBridgeSnowOrDesert(tile); |
|
1184 switch (_opt.landscape) { |
|
1185 case LT_HILLY: |
|
1186 if (snow_or_desert != (GetTileZ(tile) > _opt.snow_line)) { |
|
1187 SetBridgeSnowOrDesert(tile, !snow_or_desert); |
|
1188 MarkTileDirtyByTile(tile); |
|
1189 } |
|
1190 break; |
|
1191 |
|
1192 case LT_DESERT: |
|
1193 if (GetTropicZone(tile) == TROPICZONE_DESERT && !snow_or_desert) { |
|
1194 SetBridgeSnowOrDesert(tile, true); |
|
1195 MarkTileDirtyByTile(tile); |
|
1196 } |
|
1197 break; |
|
1198 } |
|
1199 } |
|
1200 |
|
1201 static void ClickTile_Tunnel(TileIndex tile) |
|
1202 { |
|
1203 /* not used */ |
|
1204 } |
|
1205 |
|
1206 static void ClickTile_Bridge(TileIndex tile) |
|
1207 { |
|
1208 /* not used */ |
|
1209 } |
|
1210 |
|
1211 |
|
1212 static uint32 GetTileTrackStatus_Tunnel(TileIndex tile, TransportType mode) |
|
1213 { |
|
1214 if (GetTunnelTransportType(tile) != mode) return 0; |
|
1215 return AxisToTrackBits(DiagDirToAxis(GetTunnelDirection(tile))) * 0x101; |
|
1216 } |
|
1217 |
|
1218 static uint32 GetTileTrackStatus_Bridge(TileIndex tile, TransportType mode) |
|
1219 { |
|
1220 if (GetBridgeTransportType(tile) != mode) return 0; |
|
1221 return AxisToTrackBits(DiagDirToAxis(GetBridgeRampDirection(tile))) * 0x101; |
|
1222 } |
|
1223 |
|
1224 static void ChangeTileOwner_Tunnel(TileIndex tile, PlayerID old_player, PlayerID new_player) |
|
1225 { |
|
1226 if (!IsTileOwner(tile, old_player)) return; |
|
1227 |
|
1228 if (new_player != PLAYER_SPECTATOR) { |
|
1229 SetTileOwner(tile, new_player); |
|
1230 } else { |
|
1231 DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR); |
|
1232 } |
|
1233 } |
|
1234 |
|
1235 static void ChangeTileOwner_Bridge(TileIndex tile, PlayerID old_player, PlayerID new_player) |
|
1236 { |
|
1237 if (!IsTileOwner(tile, old_player)) return; |
|
1238 |
|
1239 if (new_player != PLAYER_SPECTATOR) { |
|
1240 SetTileOwner(tile, new_player); |
|
1241 } else { |
|
1242 DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR); |
|
1243 } |
|
1244 } |
|
1245 |
|
1246 |
|
1247 static const byte _tunnel_fractcoord_1[4] = {0x8E, 0x18, 0x81, 0xE8}; |
|
1248 static const byte _tunnel_fractcoord_2[4] = {0x81, 0x98, 0x87, 0x38}; |
|
1249 static const byte _tunnel_fractcoord_3[4] = {0x82, 0x88, 0x86, 0x48}; |
|
1250 static const byte _exit_tunnel_track[4] = {1, 2, 1, 2}; |
|
1251 |
|
1252 static const byte _road_exit_tunnel_state[4] = {8, 9, 0, 1}; |
|
1253 static const byte _road_exit_tunnel_frame[4] = {2, 7, 9, 4}; |
|
1254 |
|
1255 static const byte _tunnel_fractcoord_4[4] = {0x52, 0x85, 0x98, 0x29}; |
|
1256 static const byte _tunnel_fractcoord_5[4] = {0x92, 0x89, 0x58, 0x25}; |
|
1257 static const byte _tunnel_fractcoord_6[4] = {0x92, 0x89, 0x56, 0x45}; |
|
1258 static const byte _tunnel_fractcoord_7[4] = {0x52, 0x85, 0x96, 0x49}; |
|
1259 |
|
1260 static uint32 VehicleEnter_Tunnel(Vehicle *v, TileIndex tile, int x, int y) |
|
1261 { |
|
1262 int z = GetSlopeZ(x, y) - v->z_pos; |
|
1263 |
|
1264 byte fc; |
|
1265 DiagDirection dir; |
|
1266 DiagDirection vdir; |
|
1267 |
|
1268 if (myabs(z) > 2) return 8; |
|
1269 |
|
1270 if (v->type == VEH_Train) { |
|
1271 fc = (x & 0xF) + (y << 4); |
|
1272 |
|
1273 dir = GetTunnelDirection(tile); |
|
1274 vdir = DirToDiagDir(v->direction); |
|
1275 |
|
1276 if (v->u.rail.track != 0x40 && dir == vdir) { |
|
1277 if (IsFrontEngine(v) && fc == _tunnel_fractcoord_1[dir]) { |
|
1278 if (!PlayVehicleSound(v, VSE_TUNNEL) && v->spritenum < 4) { |
|
1279 SndPlayVehicleFx(SND_05_TRAIN_THROUGH_TUNNEL, v); |
|
1280 } |
|
1281 return 0; |
|
1282 } |
|
1283 if (fc == _tunnel_fractcoord_2[dir]) { |
|
1284 v->tile = tile; |
|
1285 v->u.rail.track = 0x40; |
|
1286 v->vehstatus |= VS_HIDDEN; |
|
1287 return 4; |
|
1288 } |
|
1289 } |
|
1290 |
|
1291 if (dir == ReverseDiagDir(vdir) && fc == _tunnel_fractcoord_3[dir] && z == 0) { |
|
1292 /* We're at the tunnel exit ?? */ |
|
1293 v->tile = tile; |
|
1294 v->u.rail.track = _exit_tunnel_track[dir]; |
|
1295 assert(v->u.rail.track); |
|
1296 v->vehstatus &= ~VS_HIDDEN; |
|
1297 return 4; |
|
1298 } |
|
1299 } else if (v->type == VEH_Road) { |
|
1300 fc = (x & 0xF) + (y << 4); |
|
1301 dir = GetTunnelDirection(tile); |
|
1302 vdir = DirToDiagDir(v->direction); |
|
1303 |
|
1304 // Enter tunnel? |
|
1305 if (v->u.road.state != 0xFF && dir == vdir) { |
|
1306 if (fc == _tunnel_fractcoord_4[dir] || |
|
1307 fc == _tunnel_fractcoord_5[dir]) { |
|
1308 v->tile = tile; |
|
1309 v->u.road.state = 0xFF; |
|
1310 v->vehstatus |= VS_HIDDEN; |
|
1311 return 4; |
|
1312 } else { |
|
1313 return 0; |
|
1314 } |
|
1315 } |
|
1316 |
|
1317 if (dir == ReverseDiagDir(vdir) && ( |
|
1318 /* We're at the tunnel exit ?? */ |
|
1319 fc == _tunnel_fractcoord_6[dir] || |
|
1320 fc == _tunnel_fractcoord_7[dir] |
|
1321 ) && |
|
1322 z == 0) { |
|
1323 v->tile = tile; |
|
1324 v->u.road.state = _road_exit_tunnel_state[dir]; |
|
1325 v->u.road.frame = _road_exit_tunnel_frame[dir]; |
|
1326 v->vehstatus &= ~VS_HIDDEN; |
|
1327 return 4; |
|
1328 } |
|
1329 } |
|
1330 return 0; |
|
1331 } |
|
1332 |
|
1333 static uint32 VehicleEnter_Bridge(Vehicle *v, TileIndex tile, int x, int y) |
|
1334 { |
|
1335 int z = GetSlopeZ(x, y) - v->z_pos; |
|
1336 |
|
1337 DiagDirection dir; |
|
1338 |
|
1339 if (myabs(z) > 2) return 8; |
|
1340 |
|
1341 if (v->type == VEH_Road || (v->type == VEH_Train && IsFrontEngine(v))) { |
|
1342 /* modify speed of vehicle */ |
|
1343 uint16 spd = _bridge[GetBridgeType(tile)].speed; |
|
1344 |
|
1345 if (v->type == VEH_Road) spd *= 2; |
|
1346 if (v->cur_speed > spd) v->cur_speed = spd; |
|
1347 } |
|
1348 |
|
1349 dir = GetBridgeRampDirection(tile); |
|
1350 if (DirToDiagDir(v->direction) == dir) { |
|
1351 switch (dir) { |
|
1352 default: NOT_REACHED(); |
|
1353 case DIAGDIR_NE: if ((x & 0xF) != 0) return 0; break; |
|
1354 case DIAGDIR_SE: if ((y & 0xF) != TILE_SIZE - 1) return 0; break; |
|
1355 case DIAGDIR_SW: if ((x & 0xF) != TILE_SIZE - 1) return 0; break; |
|
1356 case DIAGDIR_NW: if ((y & 0xF) != 0) return 0; break; |
|
1357 } |
|
1358 if (v->type == VEH_Train) { |
|
1359 v->u.rail.track = 0x40; |
|
1360 CLRBIT(v->u.rail.flags, VRF_GOINGUP); |
|
1361 CLRBIT(v->u.rail.flags, VRF_GOINGDOWN); |
|
1362 } else { |
|
1363 v->u.road.state = 0xFF; |
|
1364 } |
|
1365 return 4; |
|
1366 } else if (DirToDiagDir(v->direction) == ReverseDiagDir(dir)) { |
|
1367 v->tile = tile; |
|
1368 if (v->type == VEH_Train) { |
|
1369 if (v->u.rail.track == 0x40) { |
|
1370 v->u.rail.track = (DiagDirToAxis(dir) == AXIS_X ? 1 : 2); |
|
1371 return 4; |
|
1372 } |
|
1373 } else { |
|
1374 if (v->u.road.state == 0xFF) { |
|
1375 v->u.road.state = _road_exit_tunnel_state[dir]; |
|
1376 v->u.road.frame = 0; |
|
1377 return 4; |
|
1378 } |
|
1379 } |
|
1380 return 0; |
|
1381 } |
|
1382 return 0; |
|
1383 } |
|
1384 |
|
1385 const TileTypeProcs _tile_type_tunnel_procs = { |
|
1386 DrawTile_Tunnel, /* draw_tile_proc */ |
|
1387 GetSlopeZ_Tunnel, /* get_slope_z_proc */ |
|
1388 ClearTile_Tunnel, /* clear_tile_proc */ |
|
1389 GetAcceptedCargo_Tunnel, /* get_accepted_cargo_proc */ |
|
1390 GetTileDesc_Tunnel, /* get_tile_desc_proc */ |
|
1391 GetTileTrackStatus_Tunnel, /* get_tile_track_status_proc */ |
|
1392 ClickTile_Tunnel, /* click_tile_proc */ |
|
1393 AnimateTile_Tunnel, /* animate_tile_proc */ |
|
1394 TileLoop_Tunnel, /* tile_loop_clear */ |
|
1395 ChangeTileOwner_Tunnel, /* change_tile_owner_clear */ |
|
1396 NULL, /* get_produced_cargo_proc */ |
|
1397 VehicleEnter_Tunnel, /* vehicle_enter_tile_proc */ |
|
1398 GetSlopeTileh_Tunnel, /* get_slope_tileh_proc */ |
|
1399 }; |
|
1400 |
|
1401 const TileTypeProcs _tile_type_bridge_procs = { |
|
1402 DrawTile_Bridge, /* draw_tile_proc */ |
|
1403 GetSlopeZ_Bridge, /* get_slope_z_proc */ |
|
1404 ClearTile_Bridge, /* clear_tile_proc */ |
|
1405 GetAcceptedCargo_Bridge, /* get_accepted_cargo_proc */ |
|
1406 GetTileDesc_Bridge, /* get_tile_desc_proc */ |
|
1407 GetTileTrackStatus_Bridge, /* get_tile_track_status_proc */ |
|
1408 ClickTile_Bridge, /* click_tile_proc */ |
|
1409 AnimateTile_Bridge, /* animate_tile_proc */ |
|
1410 TileLoop_Bridge, /* tile_loop_clear */ |
|
1411 ChangeTileOwner_Bridge, /* change_tile_owner_clear */ |
|
1412 NULL, /* get_produced_cargo_proc */ |
|
1413 VehicleEnter_Bridge, /* vehicle_enter_tile_proc */ |
|
1414 GetSlopeTileh_Bridge, /* get_slope_tileh_proc */ |
|
1415 }; |