|
1 /* $Id$ */ |
|
2 |
|
3 #include "stdafx.h" |
|
4 #include "openttd.h" |
|
5 #include "bridge_map.h" |
|
6 #include "rail_map.h" |
|
7 #include "road_map.h" |
|
8 #include "sprite.h" |
|
9 #include "table/sprites.h" |
|
10 #include "table/strings.h" |
|
11 #include "functions.h" |
|
12 #include "map.h" |
|
13 #include "tile.h" |
|
14 #include "town_map.h" |
|
15 #include "vehicle.h" |
|
16 #include "viewport.h" |
|
17 #include "command.h" |
|
18 #include "player.h" |
|
19 #include "town.h" |
|
20 #include "gfx.h" |
|
21 #include "sound.h" |
|
22 #include "yapf/yapf.h" |
|
23 #include "depot.h" |
|
24 |
|
25 |
|
26 static uint CountRoadBits(RoadBits r) |
|
27 { |
|
28 uint count = 0; |
|
29 |
|
30 if (r & ROAD_NW) ++count; |
|
31 if (r & ROAD_SW) ++count; |
|
32 if (r & ROAD_SE) ++count; |
|
33 if (r & ROAD_NE) ++count; |
|
34 return count; |
|
35 } |
|
36 |
|
37 |
|
38 static bool CheckAllowRemoveRoad(TileIndex tile, RoadBits remove, bool* edge_road) |
|
39 { |
|
40 RoadBits present; |
|
41 RoadBits n; |
|
42 Owner owner; |
|
43 *edge_road = true; |
|
44 |
|
45 if (_game_mode == GM_EDITOR) return true; |
|
46 |
|
47 // Only do the special processing for actual players. |
|
48 if (!IsValidPlayer(_current_player)) return true; |
|
49 |
|
50 owner = IsLevelCrossingTile(tile) ? GetCrossingRoadOwner(tile) : GetTileOwner(tile); |
|
51 |
|
52 // Only do the special processing if the road is owned |
|
53 // by a town |
|
54 if (owner != OWNER_TOWN) return (owner == OWNER_NONE) || CheckOwnership(owner); |
|
55 |
|
56 if (_cheats.magic_bulldozer.value) return true; |
|
57 |
|
58 // Get a bitmask of which neighbouring roads has a tile |
|
59 n = 0; |
|
60 present = GetAnyRoadBits(tile); |
|
61 if (present & ROAD_NE && GetAnyRoadBits(TILE_ADDXY(tile,-1, 0)) & ROAD_SW) n |= ROAD_NE; |
|
62 if (present & ROAD_SE && GetAnyRoadBits(TILE_ADDXY(tile, 0, 1)) & ROAD_NW) n |= ROAD_SE; |
|
63 if (present & ROAD_SW && GetAnyRoadBits(TILE_ADDXY(tile, 1, 0)) & ROAD_NE) n |= ROAD_SW; |
|
64 if (present & ROAD_NW && GetAnyRoadBits(TILE_ADDXY(tile, 0,-1)) & ROAD_SE) n |= ROAD_NW; |
|
65 |
|
66 // If 0 or 1 bits are set in n, or if no bits that match the bits to remove, |
|
67 // then allow it |
|
68 if ((n & (n - 1)) != 0 && (n & remove) != 0) { |
|
69 Town *t; |
|
70 *edge_road = false; |
|
71 // you can remove all kind of roads with extra dynamite |
|
72 if (_patches.extra_dynamite) return true; |
|
73 |
|
74 t = ClosestTownFromTile(tile, _patches.dist_local_authority); |
|
75 |
|
76 SetDParam(0, t->index); |
|
77 _error_message = STR_2009_LOCAL_AUTHORITY_REFUSES; |
|
78 return false; |
|
79 } |
|
80 |
|
81 return true; |
|
82 } |
|
83 |
|
84 |
|
85 /** Delete a piece of road. |
|
86 * @param tile tile where to remove road from |
|
87 * @param p1 road piece flags |
|
88 * @param p2 unused |
|
89 */ |
|
90 int32 CmdRemoveRoad(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) |
|
91 { |
|
92 // cost for removing inner/edge -roads |
|
93 static const uint16 road_remove_cost[2] = {50, 18}; |
|
94 |
|
95 Owner owner; |
|
96 Town *t; |
|
97 /* true if the roadpiece was always removeable, |
|
98 * false if it was a center piece. Affects town ratings drop */ |
|
99 bool edge_road; |
|
100 RoadBits pieces; |
|
101 |
|
102 SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION); |
|
103 |
|
104 /* Road pieces are max 4 bitset values (NE, NW, SE, SW) */ |
|
105 if (p1 >> 4) return CMD_ERROR; |
|
106 pieces = p1; |
|
107 |
|
108 if (!IsTileType(tile, MP_STREET)) return CMD_ERROR; |
|
109 |
|
110 owner = IsLevelCrossingTile(tile) ? GetCrossingRoadOwner(tile) : GetTileOwner(tile); |
|
111 |
|
112 if (owner == OWNER_TOWN && _game_mode != GM_EDITOR) { |
|
113 t = GetTownByTile(tile); |
|
114 } else { |
|
115 t = NULL; |
|
116 } |
|
117 |
|
118 if (!CheckAllowRemoveRoad(tile, pieces, &edge_road)) return CMD_ERROR; |
|
119 |
|
120 if (!EnsureNoVehicle(tile)) return CMD_ERROR; |
|
121 |
|
122 // check if you're allowed to remove the street owned by a town |
|
123 // removal allowance depends on difficulty setting |
|
124 if (!CheckforTownRating(flags, t, ROAD_REMOVE)) return CMD_ERROR; |
|
125 |
|
126 switch (GetRoadTileType(tile)) { |
|
127 case ROAD_TILE_NORMAL: { |
|
128 RoadBits present = GetRoadBits(tile); |
|
129 RoadBits c = pieces; |
|
130 |
|
131 if (HasRoadWorks(tile)) return_cmd_error(STR_ROAD_WORKS_IN_PROGRESS); |
|
132 |
|
133 if (GetTileSlope(tile, NULL) != SLOPE_FLAT && |
|
134 (present == ROAD_Y || present == ROAD_X)) { |
|
135 c |= (c & 0xC) >> 2; |
|
136 c |= (c & 0x3) << 2; |
|
137 } |
|
138 |
|
139 // limit the bits to delete to the existing bits. |
|
140 c &= present; |
|
141 if (c == 0) return CMD_ERROR; |
|
142 |
|
143 if (flags & DC_EXEC) { |
|
144 ChangeTownRating(t, -road_remove_cost[(byte)edge_road], RATING_ROAD_MINIMUM); |
|
145 |
|
146 present ^= c; |
|
147 if (present == 0) { |
|
148 DoClearSquare(tile); |
|
149 } else { |
|
150 SetRoadBits(tile, present); |
|
151 MarkTileDirtyByTile(tile); |
|
152 } |
|
153 } |
|
154 return CountRoadBits(c) * _price.remove_road; |
|
155 } |
|
156 |
|
157 case ROAD_TILE_CROSSING: { |
|
158 if (pieces & ComplementRoadBits(GetCrossingRoadBits(tile))) { |
|
159 return CMD_ERROR; |
|
160 } |
|
161 |
|
162 if (flags & DC_EXEC) { |
|
163 ChangeTownRating(t, -road_remove_cost[(byte)edge_road], RATING_ROAD_MINIMUM); |
|
164 |
|
165 MakeRailNormal(tile, GetTileOwner(tile), GetCrossingRailBits(tile), GetRailTypeCrossing(tile)); |
|
166 MarkTileDirtyByTile(tile); |
|
167 YapfNotifyTrackLayoutChange(tile, FIND_FIRST_BIT(GetTrackBits(tile))); |
|
168 } |
|
169 return _price.remove_road * 2; |
|
170 } |
|
171 |
|
172 default: |
|
173 case ROAD_TILE_DEPOT: |
|
174 return CMD_ERROR; |
|
175 } |
|
176 } |
|
177 |
|
178 |
|
179 static const RoadBits _valid_tileh_slopes_road[][15] = { |
|
180 // set of normal ones |
|
181 { |
|
182 ROAD_ALL, 0, 0, |
|
183 ROAD_X, 0, 0, // 3, 4, 5 |
|
184 ROAD_Y, 0, 0, |
|
185 ROAD_Y, 0, 0, // 9, 10, 11 |
|
186 ROAD_X, 0, 0 |
|
187 }, |
|
188 // allowed road for an evenly raised platform |
|
189 { |
|
190 0, |
|
191 ROAD_SW | ROAD_NW, |
|
192 ROAD_SW | ROAD_SE, |
|
193 ROAD_Y | ROAD_SW, |
|
194 |
|
195 ROAD_SE | ROAD_NE, // 4 |
|
196 ROAD_ALL, |
|
197 ROAD_X | ROAD_SE, |
|
198 ROAD_ALL, |
|
199 |
|
200 ROAD_NW | ROAD_NE, // 8 |
|
201 ROAD_X | ROAD_NW, |
|
202 ROAD_ALL, |
|
203 ROAD_ALL, |
|
204 |
|
205 ROAD_Y | ROAD_NE, // 12 |
|
206 ROAD_ALL, |
|
207 ROAD_ALL |
|
208 }, |
|
209 }; |
|
210 |
|
211 |
|
212 static uint32 CheckRoadSlope(Slope tileh, RoadBits* pieces, RoadBits existing) |
|
213 { |
|
214 RoadBits road_bits; |
|
215 |
|
216 if (IsSteepSlope(tileh)) { |
|
217 if (existing == 0) { |
|
218 // force full pieces. |
|
219 *pieces |= (*pieces & 0xC) >> 2; |
|
220 *pieces |= (*pieces & 0x3) << 2; |
|
221 if (*pieces == ROAD_X || *pieces == ROAD_Y) return _price.terraform; |
|
222 } |
|
223 return CMD_ERROR; |
|
224 } |
|
225 road_bits = *pieces | existing; |
|
226 |
|
227 // no special foundation |
|
228 if ((~_valid_tileh_slopes_road[0][tileh] & road_bits) == 0) { |
|
229 // force that all bits are set when we have slopes |
|
230 if (tileh != SLOPE_FLAT) *pieces |= _valid_tileh_slopes_road[0][tileh]; |
|
231 return 0; // no extra cost |
|
232 } |
|
233 |
|
234 // foundation is used. Whole tile is leveled up |
|
235 if ((~_valid_tileh_slopes_road[1][tileh] & road_bits) == 0) { |
|
236 return existing != 0 ? 0 : _price.terraform; |
|
237 } |
|
238 |
|
239 // partly leveled up tile, only if there's no road on that tile |
|
240 if (existing == 0 && (tileh == SLOPE_W || tileh == SLOPE_S || tileh == SLOPE_E || tileh == SLOPE_N)) { |
|
241 // force full pieces. |
|
242 *pieces |= (*pieces & 0xC) >> 2; |
|
243 *pieces |= (*pieces & 0x3) << 2; |
|
244 if (*pieces == ROAD_X || *pieces == ROAD_Y) return _price.terraform; |
|
245 } |
|
246 return CMD_ERROR; |
|
247 } |
|
248 |
|
249 /** Build a piece of road. |
|
250 * @param tile tile where to build road |
|
251 * @param p1 road piece flags |
|
252 * @param p2 the town that is building the road (0 if not applicable) |
|
253 */ |
|
254 int32 CmdBuildRoad(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) |
|
255 { |
|
256 int32 cost = 0; |
|
257 int32 ret; |
|
258 RoadBits existing = 0; |
|
259 RoadBits pieces; |
|
260 Slope tileh; |
|
261 |
|
262 SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION); |
|
263 |
|
264 /* Road pieces are max 4 bitset values (NE, NW, SE, SW) and town can only be non-zero |
|
265 * if a non-player is building the road */ |
|
266 if ((p1 >> 4) || (IsValidPlayer(_current_player) && p2 != 0) || !IsValidTownID(p2)) return CMD_ERROR; |
|
267 pieces = p1; |
|
268 |
|
269 tileh = GetTileSlope(tile, NULL); |
|
270 |
|
271 switch (GetTileType(tile)) { |
|
272 case MP_STREET: |
|
273 switch (GetRoadTileType(tile)) { |
|
274 case ROAD_TILE_NORMAL: |
|
275 if (HasRoadWorks(tile)) return_cmd_error(STR_ROAD_WORKS_IN_PROGRESS); |
|
276 |
|
277 existing = GetRoadBits(tile); |
|
278 if ((existing & pieces) == pieces) { |
|
279 return_cmd_error(STR_1007_ALREADY_BUILT); |
|
280 } |
|
281 if (!EnsureNoVehicle(tile)) return CMD_ERROR; |
|
282 break; |
|
283 |
|
284 case ROAD_TILE_CROSSING: |
|
285 if (pieces != GetCrossingRoadBits(tile)) { // XXX is this correct? |
|
286 return_cmd_error(STR_1007_ALREADY_BUILT); |
|
287 } |
|
288 goto do_clear; |
|
289 |
|
290 default: |
|
291 case ROAD_TILE_DEPOT: |
|
292 goto do_clear; |
|
293 } |
|
294 break; |
|
295 |
|
296 case MP_RAILWAY: { |
|
297 Axis roaddir; |
|
298 |
|
299 if (IsSteepSlope(tileh)) { |
|
300 return_cmd_error(STR_1000_LAND_SLOPED_IN_WRONG_DIRECTION); |
|
301 } |
|
302 |
|
303 #define M(x) (1 << (x)) |
|
304 /* Level crossings may only be built on these slopes */ |
|
305 if (!HASBIT(M(SLOPE_SEN) | M(SLOPE_ENW) | M(SLOPE_NWS) | M(SLOPE_NS) | M(SLOPE_WSE) | M(SLOPE_EW) | M(SLOPE_FLAT), tileh)) { |
|
306 return_cmd_error(STR_1000_LAND_SLOPED_IN_WRONG_DIRECTION); |
|
307 } |
|
308 #undef M |
|
309 |
|
310 if (GetRailTileType(tile) != RAIL_TILE_NORMAL) goto do_clear; |
|
311 switch (GetTrackBits(tile)) { |
|
312 case TRACK_BIT_X: |
|
313 if (pieces & ROAD_X) goto do_clear; |
|
314 roaddir = AXIS_Y; |
|
315 break; |
|
316 |
|
317 case TRACK_BIT_Y: |
|
318 if (pieces & ROAD_Y) goto do_clear; |
|
319 roaddir = AXIS_X; |
|
320 break; |
|
321 |
|
322 default: goto do_clear; |
|
323 } |
|
324 |
|
325 if (!EnsureNoVehicle(tile)) return CMD_ERROR; |
|
326 |
|
327 if (flags & DC_EXEC) { |
|
328 YapfNotifyTrackLayoutChange(tile, FIND_FIRST_BIT(GetTrackBits(tile))); |
|
329 MakeRoadCrossing(tile, _current_player, GetTileOwner(tile), roaddir, GetRailType(tile), p2); |
|
330 MarkTileDirtyByTile(tile); |
|
331 } |
|
332 return _price.build_road * 2; |
|
333 } |
|
334 |
|
335 default: |
|
336 do_clear:; |
|
337 ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); |
|
338 if (CmdFailed(ret)) return ret; |
|
339 cost += ret; |
|
340 } |
|
341 |
|
342 ret = CheckRoadSlope(tileh, &pieces, existing); |
|
343 /* Return an error if we need to build a foundation (ret != 0) but the |
|
344 * current patch-setting is turned off (or stupid AI@work) */ |
|
345 if (CmdFailed(ret) || (ret != 0 && (!_patches.build_on_slopes || _is_old_ai_player))) |
|
346 return_cmd_error(STR_1000_LAND_SLOPED_IN_WRONG_DIRECTION); |
|
347 |
|
348 cost += ret; |
|
349 |
|
350 if (IsTileType(tile, MP_STREET)) { |
|
351 // Don't put the pieces that already exist |
|
352 pieces &= ComplementRoadBits(existing); |
|
353 } |
|
354 |
|
355 cost += CountRoadBits(pieces) * _price.build_road; |
|
356 |
|
357 if (flags & DC_EXEC) { |
|
358 if (IsTileType(tile, MP_STREET)) { |
|
359 SetRoadBits(tile, existing | pieces); |
|
360 } else { |
|
361 MakeRoadNormal(tile, _current_player, pieces, p2); |
|
362 } |
|
363 |
|
364 MarkTileDirtyByTile(tile); |
|
365 } |
|
366 return cost; |
|
367 } |
|
368 |
|
369 int32 DoConvertStreetRail(TileIndex tile, RailType totype, bool exec) |
|
370 { |
|
371 // not a railroad crossing? |
|
372 if (!IsLevelCrossing(tile)) return CMD_ERROR; |
|
373 |
|
374 // not owned by me? |
|
375 if (!CheckTileOwnership(tile) || !EnsureNoVehicle(tile)) return CMD_ERROR; |
|
376 |
|
377 if (GetRailTypeCrossing(tile) == totype) return CMD_ERROR; |
|
378 |
|
379 // 'hidden' elrails can't be downgraded to normal rail when elrails are disabled |
|
380 if (_patches.disable_elrails && totype == RAILTYPE_RAIL && GetRailTypeCrossing(tile) == RAILTYPE_ELECTRIC) return CMD_ERROR; |
|
381 |
|
382 if (exec) { |
|
383 SetRailTypeCrossing(tile, totype); |
|
384 MarkTileDirtyByTile(tile); |
|
385 YapfNotifyTrackLayoutChange(tile, FIND_FIRST_BIT(GetCrossingRailBits(tile))); |
|
386 } |
|
387 |
|
388 return _price.build_rail >> 1; |
|
389 } |
|
390 |
|
391 |
|
392 /** Build a long piece of road. |
|
393 * @param end_tile end tile of drag |
|
394 * @param p1 start tile of drag |
|
395 * @param p2 various bitstuffed elements |
|
396 * - p2 = (bit 0) - start tile starts in the 2nd half of tile (p2 & 1) |
|
397 * - p2 = (bit 1) - end tile starts in the 2nd half of tile (p2 & 2) |
|
398 * - p2 = (bit 2) - direction: 0 = along x-axis, 1 = along y-axis (p2 & 4) |
|
399 */ |
|
400 int32 CmdBuildLongRoad(TileIndex end_tile, uint32 flags, uint32 p1, uint32 p2) |
|
401 { |
|
402 TileIndex start_tile, tile; |
|
403 int32 cost, ret; |
|
404 |
|
405 SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION); |
|
406 |
|
407 if (p1 >= MapSize()) return CMD_ERROR; |
|
408 |
|
409 start_tile = p1; |
|
410 |
|
411 /* Only drag in X or Y direction dictated by the direction variable */ |
|
412 if (!HASBIT(p2, 2) && TileY(start_tile) != TileY(end_tile)) return CMD_ERROR; // x-axis |
|
413 if (HASBIT(p2, 2) && TileX(start_tile) != TileX(end_tile)) return CMD_ERROR; // y-axis |
|
414 |
|
415 /* Swap start and ending tile, also the half-tile drag var (bit 0 and 1) */ |
|
416 if (start_tile > end_tile || (start_tile == end_tile && HASBIT(p2, 0))) { |
|
417 TileIndex t = start_tile; |
|
418 start_tile = end_tile; |
|
419 end_tile = t; |
|
420 p2 ^= IS_INT_INSIDE(p2&3, 1, 3) ? 3 : 0; |
|
421 } |
|
422 |
|
423 cost = 0; |
|
424 tile = start_tile; |
|
425 // Start tile is the small number. |
|
426 for (;;) { |
|
427 RoadBits bits = HASBIT(p2, 2) ? ROAD_Y : ROAD_X; |
|
428 |
|
429 if (tile == end_tile && !HASBIT(p2, 1)) bits &= ROAD_NW | ROAD_NE; |
|
430 if (tile == start_tile && HASBIT(p2, 0)) bits &= ROAD_SE | ROAD_SW; |
|
431 |
|
432 ret = DoCommand(tile, bits, 0, flags, CMD_BUILD_ROAD); |
|
433 if (CmdFailed(ret)) { |
|
434 if (_error_message != STR_1007_ALREADY_BUILT) return CMD_ERROR; |
|
435 _error_message = INVALID_STRING_ID; |
|
436 } else { |
|
437 cost += ret; |
|
438 } |
|
439 |
|
440 if (tile == end_tile) break; |
|
441 |
|
442 tile += HASBIT(p2, 2) ? TileDiffXY(0, 1) : TileDiffXY(1, 0); |
|
443 } |
|
444 |
|
445 return (cost == 0) ? CMD_ERROR : cost; |
|
446 } |
|
447 |
|
448 /** Remove a long piece of road. |
|
449 * @param end_tile end tile of drag |
|
450 * @param p1 start tile of drag |
|
451 * @param p2 various bitstuffed elements |
|
452 * - p2 = (bit 0) - start tile starts in the 2nd half of tile (p2 & 1) |
|
453 * - p2 = (bit 1) - end tile starts in the 2nd half of tile (p2 & 2) |
|
454 * - p2 = (bit 2) - direction: 0 = along x-axis, 1 = along y-axis (p2 & 4) |
|
455 */ |
|
456 int32 CmdRemoveLongRoad(TileIndex end_tile, uint32 flags, uint32 p1, uint32 p2) |
|
457 { |
|
458 TileIndex start_tile, tile; |
|
459 int32 cost, ret; |
|
460 |
|
461 SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION); |
|
462 |
|
463 if (p1 >= MapSize()) return CMD_ERROR; |
|
464 |
|
465 start_tile = p1; |
|
466 |
|
467 /* Only drag in X or Y direction dictated by the direction variable */ |
|
468 if (!HASBIT(p2, 2) && TileY(start_tile) != TileY(end_tile)) return CMD_ERROR; // x-axis |
|
469 if (HASBIT(p2, 2) && TileX(start_tile) != TileX(end_tile)) return CMD_ERROR; // y-axis |
|
470 |
|
471 /* Swap start and ending tile, also the half-tile drag var (bit 0 and 1) */ |
|
472 if (start_tile > end_tile || (start_tile == end_tile && HASBIT(p2, 0))) { |
|
473 TileIndex t = start_tile; |
|
474 start_tile = end_tile; |
|
475 end_tile = t; |
|
476 p2 ^= IS_INT_INSIDE(p2 & 3, 1, 3) ? 3 : 0; |
|
477 } |
|
478 |
|
479 cost = 0; |
|
480 tile = start_tile; |
|
481 // Start tile is the small number. |
|
482 for (;;) { |
|
483 RoadBits bits = HASBIT(p2, 2) ? ROAD_Y : ROAD_X; |
|
484 |
|
485 if (tile == end_tile && !HASBIT(p2, 1)) bits &= ROAD_NW | ROAD_NE; |
|
486 if (tile == start_tile && HASBIT(p2, 0)) bits &= ROAD_SE | ROAD_SW; |
|
487 |
|
488 // try to remove the halves. |
|
489 if (bits != 0) { |
|
490 ret = DoCommand(tile, bits, 0, flags, CMD_REMOVE_ROAD); |
|
491 if (!CmdFailed(ret)) cost += ret; |
|
492 } |
|
493 |
|
494 if (tile == end_tile) break; |
|
495 |
|
496 tile += HASBIT(p2, 2) ? TileDiffXY(0, 1) : TileDiffXY(1, 0); |
|
497 } |
|
498 |
|
499 return (cost == 0) ? CMD_ERROR : cost; |
|
500 } |
|
501 |
|
502 /** Build a road depot. |
|
503 * @param tile tile where to build the depot |
|
504 * @param p1 entrance direction (DiagDirection) |
|
505 * @param p2 unused |
|
506 * |
|
507 * @todo When checking for the tile slope, |
|
508 * distingush between "Flat land required" and "land sloped in wrong direction" |
|
509 */ |
|
510 int32 CmdBuildRoadDepot(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) |
|
511 { |
|
512 int32 cost; |
|
513 Depot *dep; |
|
514 Slope tileh; |
|
515 |
|
516 SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION); |
|
517 |
|
518 if (p1 > 3) return CMD_ERROR; // check direction |
|
519 |
|
520 if (!EnsureNoVehicle(tile)) return CMD_ERROR; |
|
521 |
|
522 tileh = GetTileSlope(tile, NULL); |
|
523 if (tileh != SLOPE_FLAT && ( |
|
524 !_patches.build_on_slopes || |
|
525 IsSteepSlope(tileh) || |
|
526 !CanBuildDepotByTileh(p1, tileh) |
|
527 )) { |
|
528 return_cmd_error(STR_0007_FLAT_LAND_REQUIRED); |
|
529 } |
|
530 |
|
531 cost = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); |
|
532 if (CmdFailed(cost)) return CMD_ERROR; |
|
533 |
|
534 if (MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) return_cmd_error(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST); |
|
535 |
|
536 dep = AllocateDepot(); |
|
537 if (dep == NULL) return CMD_ERROR; |
|
538 |
|
539 if (flags & DC_EXEC) { |
|
540 dep->xy = tile; |
|
541 dep->town_index = ClosestTownFromTile(tile, (uint)-1)->index; |
|
542 |
|
543 MakeRoadDepot(tile, _current_player, p1); |
|
544 MarkTileDirtyByTile(tile); |
|
545 } |
|
546 return cost + _price.build_road_depot; |
|
547 } |
|
548 |
|
549 static int32 RemoveRoadDepot(TileIndex tile, uint32 flags) |
|
550 { |
|
551 if (!CheckTileOwnership(tile) && _current_player != OWNER_WATER) |
|
552 return CMD_ERROR; |
|
553 |
|
554 if (!EnsureNoVehicle(tile)) return CMD_ERROR; |
|
555 |
|
556 if (flags & DC_EXEC) DeleteDepot(GetDepotByTile(tile)); |
|
557 |
|
558 return _price.remove_road_depot; |
|
559 } |
|
560 |
|
561 #define M(x) (1<<(x)) |
|
562 |
|
563 static int32 ClearTile_Road(TileIndex tile, byte flags) |
|
564 { |
|
565 switch (GetRoadTileType(tile)) { |
|
566 case ROAD_TILE_NORMAL: { |
|
567 RoadBits b = GetRoadBits(tile); |
|
568 |
|
569 if (!((1 << b) & (M(1)|M(2)|M(4)|M(8))) && |
|
570 (!(flags & DC_AI_BUILDING) || !IsTileOwner(tile, OWNER_TOWN)) && |
|
571 flags & DC_AUTO) { |
|
572 return_cmd_error(STR_1801_MUST_REMOVE_ROAD_FIRST); |
|
573 } |
|
574 return DoCommand(tile, b, 0, flags, CMD_REMOVE_ROAD); |
|
575 } |
|
576 |
|
577 case ROAD_TILE_CROSSING: { |
|
578 int32 ret; |
|
579 |
|
580 if (flags & DC_AUTO) return_cmd_error(STR_1801_MUST_REMOVE_ROAD_FIRST); |
|
581 |
|
582 ret = DoCommand(tile, GetCrossingRoadBits(tile), 0, flags, CMD_REMOVE_ROAD); |
|
583 if (CmdFailed(ret)) return CMD_ERROR; |
|
584 |
|
585 if (flags & DC_EXEC) { |
|
586 DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); |
|
587 } |
|
588 return ret; |
|
589 } |
|
590 |
|
591 default: |
|
592 case ROAD_TILE_DEPOT: |
|
593 if (flags & DC_AUTO) { |
|
594 return_cmd_error(STR_2004_BUILDING_MUST_BE_DEMOLISHED); |
|
595 } |
|
596 return RemoveRoadDepot(tile, flags); |
|
597 } |
|
598 } |
|
599 |
|
600 |
|
601 typedef struct DrawRoadTileStruct { |
|
602 uint16 image; |
|
603 byte subcoord_x; |
|
604 byte subcoord_y; |
|
605 } DrawRoadTileStruct; |
|
606 |
|
607 #include "table/road_land.h" |
|
608 |
|
609 |
|
610 uint GetRoadFoundation(Slope tileh, RoadBits bits) |
|
611 { |
|
612 uint i; |
|
613 |
|
614 // normal level sloped building |
|
615 if (!IsSteepSlope(tileh) && |
|
616 (~_valid_tileh_slopes_road[1][tileh] & bits) == 0) { |
|
617 return tileh; |
|
618 } |
|
619 |
|
620 // inclined sloped building |
|
621 switch (bits) { |
|
622 case ROAD_X: i = 0; break; |
|
623 case ROAD_Y: i = 1; break; |
|
624 default: return 0; |
|
625 } |
|
626 switch (tileh) { |
|
627 case SLOPE_W: |
|
628 case SLOPE_STEEP_W: i += 0; break; |
|
629 case SLOPE_S: |
|
630 case SLOPE_STEEP_S: i += 2; break; |
|
631 case SLOPE_E: |
|
632 case SLOPE_STEEP_E: i += 4; break; |
|
633 case SLOPE_N: |
|
634 case SLOPE_STEEP_N: i += 6; break; |
|
635 default: return 0; |
|
636 } |
|
637 return i + 15; |
|
638 } |
|
639 |
|
640 const byte _road_sloped_sprites[14] = { |
|
641 0, 0, 2, 0, |
|
642 0, 1, 0, 0, |
|
643 3, 0, 0, 0, |
|
644 0, 0 |
|
645 }; |
|
646 |
|
647 /** |
|
648 * Draw ground sprite and road pieces |
|
649 * @param ti TileInfo |
|
650 * @param road RoadBits to draw |
|
651 */ |
|
652 static void DrawRoadBits(TileInfo* ti) |
|
653 { |
|
654 RoadBits road = GetRoadBits(ti->tile); |
|
655 const DrawRoadTileStruct *drts; |
|
656 PalSpriteID image = 0; |
|
657 Roadside roadside; |
|
658 |
|
659 if (ti->tileh != SLOPE_FLAT) { |
|
660 int foundation = GetRoadFoundation(ti->tileh, road); |
|
661 |
|
662 if (foundation != 0) DrawFoundation(ti, foundation); |
|
663 |
|
664 // DrawFoundation() modifies ti. |
|
665 // Default sloped sprites.. |
|
666 if (ti->tileh != SLOPE_FLAT) image = _road_sloped_sprites[ti->tileh - 1] + 0x53F; |
|
667 } |
|
668 |
|
669 if (image == 0) image = _road_tile_sprites_1[road]; |
|
670 |
|
671 roadside = GetRoadside(ti->tile); |
|
672 |
|
673 if (IsOnSnow(ti->tile)) { |
|
674 image += 19; |
|
675 } else { |
|
676 switch (roadside) { |
|
677 case ROADSIDE_BARREN: image |= PALETTE_TO_BARE_LAND; break; |
|
678 case ROADSIDE_GRASS: break; |
|
679 case ROADSIDE_GRASS_ROAD_WORKS: break; |
|
680 default: image -= 19; break; // Paved |
|
681 } |
|
682 } |
|
683 |
|
684 DrawGroundSprite(image); |
|
685 |
|
686 if (HasRoadWorks(ti->tile)) { |
|
687 // Road works |
|
688 DrawGroundSprite(road & ROAD_X ? SPR_EXCAVATION_X : SPR_EXCAVATION_Y); |
|
689 return; |
|
690 } |
|
691 |
|
692 // Return if full detail is disabled, or we are zoomed fully out. |
|
693 if (!(_display_opt & DO_FULL_DETAIL) || _cur_dpi->zoom == 2) return; |
|
694 |
|
695 // Draw extra details. |
|
696 for (drts = _road_display_table[roadside][road]; drts->image != 0; drts++) { |
|
697 int x = ti->x | drts->subcoord_x; |
|
698 int y = ti->y | drts->subcoord_y; |
|
699 byte z = ti->z; |
|
700 if (ti->tileh != SLOPE_FLAT) z = GetSlopeZ(x, y); |
|
701 AddSortableSpriteToDraw(drts->image, x, y, 2, 2, 0x10, z); |
|
702 } |
|
703 } |
|
704 |
|
705 static void DrawTile_Road(TileInfo *ti) |
|
706 { |
|
707 switch (GetRoadTileType(ti->tile)) { |
|
708 case ROAD_TILE_NORMAL: |
|
709 DrawRoadBits(ti); |
|
710 break; |
|
711 |
|
712 case ROAD_TILE_CROSSING: { |
|
713 PalSpriteID image; |
|
714 |
|
715 if (ti->tileh != SLOPE_FLAT) DrawFoundation(ti, ti->tileh); |
|
716 |
|
717 image = GetRailTypeInfo(GetRailTypeCrossing(ti->tile))->base_sprites.crossing; |
|
718 |
|
719 if (GetCrossingRoadAxis(ti->tile) == AXIS_X) image++; |
|
720 if (IsCrossingBarred(ti->tile)) image += 2; |
|
721 |
|
722 if (IsOnSnow(ti->tile)) { |
|
723 image += 8; |
|
724 } else { |
|
725 switch (GetRoadside(ti->tile)) { |
|
726 case ROADSIDE_BARREN: image |= PALETTE_TO_BARE_LAND; break; |
|
727 case ROADSIDE_GRASS: break; |
|
728 default: image += 4; break; // Paved |
|
729 } |
|
730 } |
|
731 |
|
732 DrawGroundSprite(image); |
|
733 if (GetRailTypeCrossing(ti->tile) == RAILTYPE_ELECTRIC) DrawCatenary(ti); |
|
734 break; |
|
735 } |
|
736 |
|
737 default: |
|
738 case ROAD_TILE_DEPOT: { |
|
739 const DrawTileSprites* dts; |
|
740 const DrawTileSeqStruct* dtss; |
|
741 uint32 palette; |
|
742 |
|
743 if (ti->tileh != SLOPE_FLAT) DrawFoundation(ti, ti->tileh); |
|
744 |
|
745 palette = PLAYER_SPRITE_COLOR(GetTileOwner(ti->tile)); |
|
746 |
|
747 dts = &_road_depot[GetRoadDepotDirection(ti->tile)]; |
|
748 DrawGroundSprite(dts->ground_sprite); |
|
749 |
|
750 for (dtss = dts->seq; dtss->image != 0; dtss++) { |
|
751 uint32 image = dtss->image; |
|
752 |
|
753 if (_display_opt & DO_TRANS_BUILDINGS) { |
|
754 MAKE_TRANSPARENT(image); |
|
755 } else if (image & PALETTE_MODIFIER_COLOR) { |
|
756 image |= palette; |
|
757 } |
|
758 |
|
759 AddSortableSpriteToDraw( |
|
760 image, |
|
761 ti->x + dtss->delta_x, ti->y + dtss->delta_y, |
|
762 dtss->size_x, dtss->size_y, |
|
763 dtss->size_z, ti->z |
|
764 ); |
|
765 } |
|
766 break; |
|
767 } |
|
768 } |
|
769 DrawBridgeMiddle(ti); |
|
770 } |
|
771 |
|
772 void DrawRoadDepotSprite(int x, int y, DiagDirection dir) |
|
773 { |
|
774 uint32 palette = PLAYER_SPRITE_COLOR(_local_player); |
|
775 const DrawTileSprites* dts = &_road_depot[dir]; |
|
776 const DrawTileSeqStruct* dtss; |
|
777 |
|
778 x += 33; |
|
779 y += 17; |
|
780 |
|
781 DrawSprite(dts->ground_sprite, x, y); |
|
782 |
|
783 for (dtss = dts->seq; dtss->image != 0; dtss++) { |
|
784 Point pt = RemapCoords(dtss->delta_x, dtss->delta_y, dtss->delta_z); |
|
785 uint32 image = dtss->image; |
|
786 |
|
787 if (image & PALETTE_MODIFIER_COLOR) image |= palette; |
|
788 |
|
789 DrawSprite(image, x + pt.x, y + pt.y); |
|
790 } |
|
791 } |
|
792 |
|
793 static uint GetSlopeZ_Road(TileIndex tile, uint x, uint y) |
|
794 { |
|
795 uint z; |
|
796 Slope tileh = GetTileSlope(tile, &z); |
|
797 |
|
798 if (tileh == SLOPE_FLAT) return z; |
|
799 if (GetRoadTileType(tile) == ROAD_TILE_NORMAL) { |
|
800 uint f = GetRoadFoundation(tileh, GetRoadBits(tile)); |
|
801 |
|
802 if (f != 0) { |
|
803 if (IsSteepSlope(tileh)) { |
|
804 z += TILE_HEIGHT; |
|
805 } else if (f < 15) { |
|
806 return z + TILE_HEIGHT; // leveled foundation |
|
807 } |
|
808 tileh = _inclined_tileh[f - 15]; // inclined foundation |
|
809 } |
|
810 return z + GetPartialZ(x & 0xF, y & 0xF, tileh); |
|
811 } else { |
|
812 return z + TILE_HEIGHT; |
|
813 } |
|
814 } |
|
815 |
|
816 static Slope GetSlopeTileh_Road(TileIndex tile, Slope tileh) |
|
817 { |
|
818 if (tileh == SLOPE_FLAT) return SLOPE_FLAT; |
|
819 if (GetRoadTileType(tile) == ROAD_TILE_NORMAL) { |
|
820 uint f = GetRoadFoundation(tileh, GetRoadBits(tile)); |
|
821 |
|
822 if (f == 0) return tileh; |
|
823 if (f < 15) return SLOPE_FLAT; // leveled foundation |
|
824 return _inclined_tileh[f - 15]; // inclined foundation |
|
825 } else { |
|
826 return SLOPE_FLAT; |
|
827 } |
|
828 } |
|
829 |
|
830 static void GetAcceptedCargo_Road(TileIndex tile, AcceptedCargo ac) |
|
831 { |
|
832 /* not used */ |
|
833 } |
|
834 |
|
835 static void AnimateTile_Road(TileIndex tile) |
|
836 { |
|
837 if (IsLevelCrossing(tile)) MarkTileDirtyByTile(tile); |
|
838 } |
|
839 |
|
840 |
|
841 static const Roadside _town_road_types[][2] = { |
|
842 { ROADSIDE_GRASS, ROADSIDE_GRASS }, |
|
843 { ROADSIDE_PAVED, ROADSIDE_PAVED }, |
|
844 { ROADSIDE_PAVED, ROADSIDE_PAVED }, |
|
845 { ROADSIDE_TREES, ROADSIDE_TREES }, |
|
846 { ROADSIDE_STREET_LIGHTS, ROADSIDE_PAVED } |
|
847 }; |
|
848 |
|
849 static const Roadside _town_road_types_2[][2] = { |
|
850 { ROADSIDE_GRASS, ROADSIDE_GRASS }, |
|
851 { ROADSIDE_PAVED, ROADSIDE_PAVED }, |
|
852 { ROADSIDE_STREET_LIGHTS, ROADSIDE_PAVED }, |
|
853 { ROADSIDE_STREET_LIGHTS, ROADSIDE_PAVED }, |
|
854 { ROADSIDE_STREET_LIGHTS, ROADSIDE_PAVED } |
|
855 }; |
|
856 |
|
857 |
|
858 static void TileLoop_Road(TileIndex tile) |
|
859 { |
|
860 switch (_opt.landscape) { |
|
861 case LT_HILLY: |
|
862 if (IsOnSnow(tile) != (GetTileZ(tile) > _opt.snow_line)) { |
|
863 ToggleSnow(tile); |
|
864 MarkTileDirtyByTile(tile); |
|
865 } |
|
866 break; |
|
867 |
|
868 case LT_DESERT: |
|
869 if (GetTropicZone(tile) == TROPICZONE_DESERT && !IsOnDesert(tile)) { |
|
870 ToggleDesert(tile); |
|
871 MarkTileDirtyByTile(tile); |
|
872 } |
|
873 break; |
|
874 } |
|
875 |
|
876 if (GetRoadTileType(tile) == ROAD_TILE_DEPOT) return; |
|
877 |
|
878 if (!HasRoadWorks(tile)) { |
|
879 const Town* t = ClosestTownFromTile(tile, (uint)-1); |
|
880 int grp = 0; |
|
881 |
|
882 if (t != NULL) { |
|
883 grp = GetTownRadiusGroup(t, tile); |
|
884 |
|
885 // Show an animation to indicate road work |
|
886 if (t->road_build_months != 0 && |
|
887 (DistanceManhattan(t->xy, tile) < 8 || grp != 0) && |
|
888 GetRoadTileType(tile) == ROAD_TILE_NORMAL && (GetRoadBits(tile) == ROAD_X || GetRoadBits(tile) == ROAD_Y)) { |
|
889 if (GetTileSlope(tile, NULL) == SLOPE_FLAT && EnsureNoVehicle(tile) && CHANCE16(1, 20)) { |
|
890 StartRoadWorks(tile); |
|
891 |
|
892 SndPlayTileFx(SND_21_JACKHAMMER, tile); |
|
893 CreateEffectVehicleAbove( |
|
894 TileX(tile) * TILE_SIZE + 7, |
|
895 TileY(tile) * TILE_SIZE + 7, |
|
896 0, |
|
897 EV_BULLDOZER); |
|
898 MarkTileDirtyByTile(tile); |
|
899 return; |
|
900 } |
|
901 } |
|
902 } |
|
903 |
|
904 { |
|
905 /* Adjust road ground type depending on 'grp' (grp is the distance to the center) */ |
|
906 const Roadside* new_rs = (_opt.landscape == LT_CANDY) ? _town_road_types_2[grp] : _town_road_types[grp]; |
|
907 Roadside cur_rs = GetRoadside(tile); |
|
908 |
|
909 /* We have our desired type, do nothing */ |
|
910 if (cur_rs == new_rs[0]) return; |
|
911 |
|
912 /* We have the pre-type of the desired type, switch to the desired type */ |
|
913 if (cur_rs == new_rs[1]) { |
|
914 cur_rs = new_rs[0]; |
|
915 /* We have barren land, install the pre-type */ |
|
916 } else if (cur_rs == ROADSIDE_BARREN) { |
|
917 cur_rs = new_rs[1]; |
|
918 /* We're totally off limits, remove any installation and make barren land */ |
|
919 } else { |
|
920 cur_rs = ROADSIDE_BARREN; |
|
921 } |
|
922 SetRoadside(tile, cur_rs); |
|
923 MarkTileDirtyByTile(tile); |
|
924 } |
|
925 } else if (IncreaseRoadWorksCounter(tile)) { |
|
926 TerminateRoadWorks(tile); |
|
927 MarkTileDirtyByTile(tile); |
|
928 } |
|
929 } |
|
930 |
|
931 static void ClickTile_Road(TileIndex tile) |
|
932 { |
|
933 if (GetRoadTileType(tile) == ROAD_TILE_DEPOT) ShowDepotWindow(tile, VEH_Road); |
|
934 } |
|
935 |
|
936 static const byte _road_trackbits[16] = { |
|
937 0x0, 0x0, 0x0, 0x10, 0x0, 0x2, 0x8, 0x1A, 0x0, 0x4, 0x1, 0x15, 0x20, 0x26, 0x29, 0x3F, |
|
938 }; |
|
939 |
|
940 static uint32 GetTileTrackStatus_Road(TileIndex tile, TransportType mode) |
|
941 { |
|
942 switch (mode) { |
|
943 case TRANSPORT_RAIL: |
|
944 if (!IsLevelCrossing(tile)) return 0; |
|
945 return GetCrossingRailBits(tile) * 0x101; |
|
946 |
|
947 case TRANSPORT_ROAD: |
|
948 switch (GetRoadTileType(tile)) { |
|
949 case ROAD_TILE_NORMAL: |
|
950 return HasRoadWorks(tile) ? 0 : _road_trackbits[GetRoadBits(tile)] * 0x101; |
|
951 |
|
952 case ROAD_TILE_CROSSING: { |
|
953 uint32 r = AxisToTrackBits(GetCrossingRoadAxis(tile)) * 0x101; |
|
954 |
|
955 if (IsCrossingBarred(tile)) r *= 0x10001; |
|
956 return r; |
|
957 } |
|
958 |
|
959 default: |
|
960 case ROAD_TILE_DEPOT: |
|
961 return AxisToTrackBits(DiagDirToAxis(GetRoadDepotDirection(tile))) * 0x101; |
|
962 } |
|
963 break; |
|
964 |
|
965 default: break; |
|
966 } |
|
967 return 0; |
|
968 } |
|
969 |
|
970 static const StringID _road_tile_strings[] = { |
|
971 STR_1814_ROAD, |
|
972 STR_1814_ROAD, |
|
973 STR_1814_ROAD, |
|
974 STR_1815_ROAD_WITH_STREETLIGHTS, |
|
975 STR_1814_ROAD, |
|
976 STR_1816_TREE_LINED_ROAD, |
|
977 STR_1814_ROAD, |
|
978 STR_1814_ROAD, |
|
979 }; |
|
980 |
|
981 static void GetTileDesc_Road(TileIndex tile, TileDesc *td) |
|
982 { |
|
983 td->owner = GetTileOwner(tile); |
|
984 switch (GetRoadTileType(tile)) { |
|
985 case ROAD_TILE_CROSSING: td->str = STR_1818_ROAD_RAIL_LEVEL_CROSSING; break; |
|
986 case ROAD_TILE_DEPOT: td->str = STR_1817_ROAD_VEHICLE_DEPOT; break; |
|
987 default: td->str = _road_tile_strings[GetRoadside(tile)]; break; |
|
988 } |
|
989 } |
|
990 |
|
991 static const byte _roadveh_enter_depot_unk0[4] = { |
|
992 8, 9, 0, 1 |
|
993 }; |
|
994 |
|
995 static uint32 VehicleEnter_Road(Vehicle *v, TileIndex tile, int x, int y) |
|
996 { |
|
997 switch (GetRoadTileType(tile)) { |
|
998 case ROAD_TILE_CROSSING: |
|
999 if (v->type == VEH_Train && !IsCrossingBarred(tile)) { |
|
1000 /* train crossing a road */ |
|
1001 SndPlayVehicleFx(SND_0E_LEVEL_CROSSING, v); |
|
1002 BarCrossing(tile); |
|
1003 MarkTileDirtyByTile(tile); |
|
1004 } |
|
1005 break; |
|
1006 |
|
1007 case ROAD_TILE_DEPOT: |
|
1008 if (v->type == VEH_Road && |
|
1009 v->u.road.frame == 11 && |
|
1010 _roadveh_enter_depot_unk0[GetRoadDepotDirection(tile)] == v->u.road.state) { |
|
1011 VehicleEnterDepot(v); |
|
1012 return 4; |
|
1013 } |
|
1014 break; |
|
1015 |
|
1016 default: break; |
|
1017 } |
|
1018 return 0; |
|
1019 } |
|
1020 |
|
1021 |
|
1022 static void ChangeTileOwner_Road(TileIndex tile, PlayerID old_player, PlayerID new_player) |
|
1023 { |
|
1024 if (IsLevelCrossing(tile) && GetCrossingRoadOwner(tile) == old_player) { |
|
1025 SetCrossingRoadOwner(tile, new_player == PLAYER_SPECTATOR ? OWNER_NONE : new_player); |
|
1026 } |
|
1027 |
|
1028 if (!IsTileOwner(tile, old_player)) return; |
|
1029 |
|
1030 if (new_player != PLAYER_SPECTATOR) { |
|
1031 SetTileOwner(tile, new_player); |
|
1032 } else { |
|
1033 switch (GetRoadTileType(tile)) { |
|
1034 case ROAD_TILE_NORMAL: |
|
1035 SetTileOwner(tile, OWNER_NONE); |
|
1036 break; |
|
1037 |
|
1038 case ROAD_TILE_CROSSING: |
|
1039 MakeRoadNormal(tile, GetCrossingRoadOwner(tile), GetCrossingRoadBits(tile), GetTownIndex(tile)); |
|
1040 break; |
|
1041 |
|
1042 default: |
|
1043 case ROAD_TILE_DEPOT: |
|
1044 DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR); |
|
1045 break; |
|
1046 } |
|
1047 } |
|
1048 } |
|
1049 |
|
1050 |
|
1051 const TileTypeProcs _tile_type_road_procs = { |
|
1052 DrawTile_Road, /* draw_tile_proc */ |
|
1053 GetSlopeZ_Road, /* get_slope_z_proc */ |
|
1054 ClearTile_Road, /* clear_tile_proc */ |
|
1055 GetAcceptedCargo_Road, /* get_accepted_cargo_proc */ |
|
1056 GetTileDesc_Road, /* get_tile_desc_proc */ |
|
1057 GetTileTrackStatus_Road, /* get_tile_track_status_proc */ |
|
1058 ClickTile_Road, /* click_tile_proc */ |
|
1059 AnimateTile_Road, /* animate_tile_proc */ |
|
1060 TileLoop_Road, /* tile_loop_clear */ |
|
1061 ChangeTileOwner_Road, /* change_tile_owner_clear */ |
|
1062 NULL, /* get_produced_cargo_proc */ |
|
1063 VehicleEnter_Road, /* vehicle_enter_tile_proc */ |
|
1064 GetSlopeTileh_Road, /* get_slope_tileh_proc */ |
|
1065 }; |